diff --git a/TODO b/TODO index c91dd84..6d046bf 100644 --- a/TODO +++ b/TODO @@ -60,105 +60,6 @@ Combat: skills determine speed, acceleration, and accuracy accuracy is diff between where you aimed and where you actually applied the impulse - - как устроена тушка: - - иерархически - есть корень, у которого есть ветки и листья - как в классическом дереве, Ветки - [конечности]. Для каждой указан [относительный размер]. - - функциональные системы - - [скелет] : череп, хребет, кости - отвечает за движение и применения силы - [Полезные инструменты]: - челюсти и зубы, когти, хвосты, хватательные всякие пальцы и ладони, лапы, копыта, рога, жала - - [обмен жидкостью] - сосуды, артерии, сердце, селезенка(иммунитет втч) - доставка энергии до всех конечностей - именно по этой жидкости считается гидроудар - - [получение и накопление энергии (ЖРАТ)] - желудок, кишки, печень(сопротивляемость ядам втч), селезенка(да, 2 системы) - - [нервные импульсы] - мозги, нервная ткань - передача урпавляющих импульсов - [Полезные инструменты]: - - [зрение] - глаза (или аналоги) и нервы - - [слух] - уши (иил аналоги) и нервы - - [обоняние] - нос и нервы - - [осязание] - кожа (вибриссы, антенны) и нервы - - [полезные инструменты] - . - крепятся к скелету - дают эффекты вроде возможности видеть, нюхать, хватать, стоять и ходить, ползать, бросать, царапать, бить, кусать и колоть, одевать итп - - В этом смысле экипировка тоже входит в организм через конечность с соответствующей функцией, и требует дополнительного - - - Для каждого узла дерева веток/конечностей есть список [слоев тканей] (для каждого указана толщина) и [органов], которые там располагаются. - (прим - сделать наследуемые шаблоны [конечностей] - для гуманоида, четвероногого итп и [слоев тканей] - - млекопитающее, земноводное, рептилия, головоногое и шаблон [органов]) - Для каждой системы есть необходимый для функционирования список органов и реально располагаемый (два сердца anyone?) - Для каждого слоя в конечностях обозначается система, которой он принадлежит. - Для каждого органа в конечностях обозначается система, которой он принадлежит. - Пока органов, соединенных по всему дереву тканями больше, чем необходимо по минимальному списку - все в порядке. При потерях система выходит из строя: - - Нервная система - отключается _все_ действия ниже по дереву. Полная остановка/уничтожение - помираем. - - Обмен жидкостью - начинаем терять уровень внутреннего давления, как только он ниже критического - помираем - - Скелет - теряем возможности соответствующие поврежденной части отсюда и ниже по дереву. При обнажении CORE - помираем (разрубили пополам, блин!) - - Энергия (ЖРАТ) - при повреждении чувствуем себя плохо (деориентация), в перспективе - прекращение притока энергии и остановка обмена жидкостью и неврной деятельности, т.е. помираем - Кроме того, при потерях растет уровень боли. При превышении критического порога - помираем от болевого шока - (оторвали мишке лапы? помирает прямо тут, не ждем потери давления крови). - - - - как считать дамаг: - находим [кинетическую энергию] = масса на скорость движения. Для массы плотность, для скорости - характеристики тушки - находим цель и в ней часть куда всё это попадет (применяется скилл меткости для нахождения разницы между заявленным и получившимся) - Это не только смещение по частям, но и максимальная глубина проникновения в слоях (исходя из скиллов, удади и профиля оружия) - (тут нужно подумать о кинематике движения, хотя можно обойтись рандомом) - находим площадь, на котрую придется кинетическая энергия (профиль оружия - колющий, режущий, давящий) - [импульс] = [остаток импульса] = [кинетическая энергия] / [площадь] - - находим слои, покрывающие эту [площадь] (от наружных к внутренним до core) - - Инициализируем [счетчик всего времени соударения] - - По каждому слою внутрь: - Делим [остаток импульса] на [время соударения] (определяется _упругостью_ материала СЛОЯ) - Добавляем [время соударения] к [счетчику всего времени соударения] (нужен для расчета гидроудара позже) - Делим [остаток импульса] на [площадь поперечного сечения ударной части] - (скажем для давящего - результат 5/2 , для режущего - 1, для колющего - 1/4) - получаем характеристику удара: [давление] силу поделенную на площадь (скажем в ньютонах / м2) - Сравниваем с хрупкостью материала слоя: если [давление] * [счетчик всего времени соударения] > [предела хрупкости] - то провалено - разрушается ВЕСЬ СЛОЙ в пределах части -> переходим к следующему слою, не уменьшая [остаток импульса] - Сравниваем [остаток импульса] с [ударной вязкостью материала]: - Так как ударная вязкость в джоулях, а импульс в ньютонах - умножаем остаток импульса на [толщину слоя] / [площадь], - это произведение вычитаем из [остатка импульса] - Если от импульса еще что-то осталось -> переходим к следующему слою, иначе выходим - Если слои кончились, а от импульса еще что-то осталось: - Считаем off-balance / сбитие с ног / отправление в полет - для дубин это будет часто =) - Пороговые значния придется подбирать. - - foreach [список поврежденных слоев]: - смотрим какие штуки находились в этой части в этом слое, роллим [поцарапан / проткнут / ушиблен / уничтожен]. - - Для экипировки - применяем повреждения (ухудшаем зарактеристики) - - Для органов - добавляем к кровотечению кровооборот органа, добавляем к уровню нервной нагрузки "важность" органа - - При разрушении связок / нервов / мыщц / костей - отключаем всё что их требует ниже по дереву тушки - - - Расчет гидроудара (специально для дубин и проч): - есть [площадь], [время], [импульс] - но считать колебания и скорость звука в жидкой среде (а это километры в секунды) считать дорого, просто передаем - топ-3 внутренним [слоям] и [органам], чья [жесткость? слоя] и [относительный размер|органа] больше всех остальных - тут присутствующих 30% от [импульса] умноженного на [площадь] / [счетчик всего времени соударения]. Таким образом, - надетые доспехи и поддоспешник оставоляют вероятность сломать кость, а вот три слоя брони - уже нет. - - -Но поскольку такой симулятор скотобойни за одно и то же тело быстро приестся - играем за иллитида/мозгового слизня, который -берет разые тушки под контроль, выбрасывает по мере использования и берет новые. - -Соответственно направления развития -- Зов - приманивает тушки получше -- Контроль - позволяет более полно использовать инзначальные возможности тушки, или даже применять новые -- Мутации - отращивание и улучшение тушки, замена материалов, дополнительные конечности/органы, смена шаблона тканей, переход на другой [метаболизм] итп. -- Внедрение и изъятие - запоминание умений тушки и внедрение в другие - от пользования дубиной до телепортационных заклинаний - -За опыт открываются [слоты] для этих возможностей. Макисмальное количество ограничено (скажем 7) -За применения [умений тушки] можно получить ее умения в [слот] (направление контроль). -Любое [умение] занимает какой-то % от слота, может быть больше размера слота. [Объединить слоты] в один больший (но -меньший по размеру чем сумма начальных двух) можно только за [эпические свершения] - сдачу квеста или победу над OOD. -За жти же [эпические очки] открываются новые [умения] в ветках (ограничить деревом скиллов?). -При [гибели тушки] теряются рандомные [слоты] вместе с [умениями] что в них были (не складывайте яйца в одну корзину!). -Если заметят и уничтожат собственно иллитида - game over, high score. - - - - no hitpoints! blood is the life source ``` diff --git a/cmd/game/main.go b/cmd/game/main.go index aa372bf..d01e179 100644 --- a/cmd/game/main.go +++ b/cmd/game/main.go @@ -133,7 +133,7 @@ func main() { }).MakeList(), ) - screenMgr.AddScreen("inventory", screens.InventoryScreen{ + inv := screens.InventoryScreen{ MenuScreen: screens.NewMenuScreen( mw, screenMgr, @@ -144,10 +144,8 @@ func main() { types.NewCenteredRect(mw.Rect, 70, 25), true, ). SetBgColor("#ef305c70"). - SetFgColor("white"). - SetItems([]interface{}{}).MakeList() - }, - ) + SetFgColor("white"), + } screenMgr.AddScreen("devmenu", screens.NewDevmenuScreen( mw, @@ -188,6 +186,7 @@ func main() { controller.AddComponent(potion, items.Carried{Mass:5, Bulk:3}) controller.AddComponent(potion, items.Usable{}) controller.AddComponent(potion, items.Consumable{}) + controller.AddComponent(potion, ecs.Named{Name:"first potion"}) potion2 := controller.CreateEntity([]ecs.Component{}) controller.AddComponent(potion2, types.Appearance{ @@ -200,11 +199,15 @@ func main() { controller.AddComponent(potion2, items.Carried{Mass:5, Bulk:3}) controller.AddComponent(potion2, items.Usable{}) controller.AddComponent(potion2, items.Consumable{}) + controller.AddComponent(potion2, ecs.Named{Name:"second potion"}) //fixme end setting up items State.Player = player State.Controller = controller + screenMgr.AddScreen("inventory", inv.MakeInverntory(player)) + + //but every call to bearlibterminal must be wrapped to closure and passed to mainfunc var exit = false for !exit { diff --git a/coremech.txt b/coremech.txt new file mode 100644 index 0000000..96842bc --- /dev/null +++ b/coremech.txt @@ -0,0 +1,98 @@ + + - как устроена тушка: + - иерархически - есть корень, у которого есть ветки и листья - как в классическом дереве, Ветки - [конечности]. Для каждой указан [относительный размер]. + - функциональные системы + - [скелет] : череп, хребет, кости - отвечает за движение и применения силы + [Полезные инструменты]: + челюсти и зубы, когти, хвосты, хватательные всякие пальцы и ладони, лапы, копыта, рога, жала + - [обмен жидкостью] - сосуды, артерии, сердце, селезенка(иммунитет втч) - доставка энергии до всех конечностей + именно по этой жидкости считается гидроудар + - [получение и накопление энергии (ЖРАТ)] - желудок, кишки, печень(сопротивляемость ядам втч), селезенка(да, 2 системы) + - [нервные импульсы] - мозги, нервная ткань - передача урпавляющих импульсов + [Полезные инструменты]: + - [зрение] - глаза (или аналоги) и нервы + - [слух] - уши (иил аналоги) и нервы + - [обоняние] - нос и нервы + - [осязание] - кожа (вибриссы, антенны) и нервы + - [полезные инструменты] - . + крепятся к скелету + дают эффекты вроде возможности видеть, нюхать, хватать, стоять и ходить, ползать, бросать, царапать, бить, кусать и колоть, одевать итп + + В этом смысле экипировка тоже входит в организм через конечность с соответствующей функцией, и требует дополнительного + + + Для каждого узла дерева веток/конечностей есть список [слоев тканей] (для каждого указана толщина) и [органов], которые там располагаются. + (прим - сделать наследуемые шаблоны [конечностей] - для гуманоида, четвероногого итп и [слоев тканей] - + млекопитающее, земноводное, рептилия, головоногое и шаблон [органов]) + Для каждой системы есть необходимый для функционирования список органов и реально располагаемый (два сердца anyone?) + Для каждого слоя в конечностях обозначается система, которой он принадлежит. + Для каждого органа в конечностях обозначается система, которой он принадлежит. + Пока органов, соединенных по всему дереву тканями больше, чем необходимо по минимальному списку - все в порядке. При потерях система выходит из строя: + - Нервная система - отключается _все_ действия ниже по дереву. Полная остановка/уничтожение - помираем. + - Обмен жидкостью - начинаем терять уровень внутреннего давления, как только он ниже критического - помираем + - Скелет - теряем возможности соответствующие поврежденной части отсюда и ниже по дереву. При обнажении CORE - помираем (разрубили пополам, блин!) + - Энергия (ЖРАТ) - при повреждении чувствуем себя плохо (деориентация), в перспективе - прекращение притока энергии и остановка обмена жидкостью и неврной деятельности, т.е. помираем + Кроме того, при потерях растет уровень боли. При превышении критического порога - помираем от болевого шока + (оторвали мишке лапы? помирает прямо тут, не ждем потери давления крови). + + + - как считать дамаг: + находим [кинетическую энергию] = масса на скорость движения. Для массы плотность, для скорости - характеристики тушки + находим цель и в ней часть куда всё это попадет (применяется скилл меткости для нахождения разницы между заявленным и получившимся) + Это не только смещение по частям, но и максимальная глубина проникновения в слоях (исходя из скиллов, удади и профиля оружия) + (тут нужно подумать о кинематике движения, хотя можно обойтись рандомом) + находим площадь, на котрую придется кинетическая энергия (профиль оружия - колющий, режущий, давящий) + [импульс] = [остаток импульса] = [кинетическая энергия] / [площадь] + + находим слои, покрывающие эту [площадь] (от наружных к внутренним до core) + + Инициализируем [счетчик всего времени соударения] + + По каждому слою внутрь: + Делим [остаток импульса] на [время соударения] (определяется _упругостью_ материала СЛОЯ) + Добавляем [время соударения] к [счетчику всего времени соударения] (нужен для расчета гидроудара позже) + Делим [остаток импульса] на [площадь поперечного сечения ударной части] + (скажем для давящего - результат 5/2 , для режущего - 1, для колющего - 1/4) + получаем характеристику удара: [давление] силу поделенную на площадь (скажем в ньютонах / м2) + Сравниваем с хрупкостью материала слоя: если [давление] * [счетчик всего времени соударения] > [предела хрупкости] + то провалено - разрушается ВЕСЬ СЛОЙ в пределах части -> переходим к следующему слою, не уменьшая [остаток импульса] + Сравниваем [остаток импульса] с [ударной вязкостью материала]: + Так как ударная вязкость в джоулях, а импульс в ньютонах - умножаем остаток импульса на [толщину слоя] / [площадь], + это произведение вычитаем из [остатка импульса] + Если от импульса еще что-то осталось -> переходим к следующему слою, иначе выходим + Если слои кончились, а от импульса еще что-то осталось: + Считаем off-balance / сбитие с ног / отправление в полет - для дубин это будет часто =) + Пороговые значния придется подбирать. + + foreach [список поврежденных слоев]: + смотрим какие штуки находились в этой части в этом слое, роллим [поцарапан / проткнут / ушиблен / уничтожен]. + - Для экипировки - применяем повреждения (ухудшаем зарактеристики) + - Для органов - добавляем к кровотечению кровооборот органа, добавляем к уровню нервной нагрузки "важность" органа + - При разрушении связок / нервов / мыщц / костей - отключаем всё что их требует ниже по дереву тушки + + + Расчет гидроудара (специально для дубин и проч): + есть [площадь], [время], [импульс] + но считать колебания и скорость звука в жидкой среде (а это километры в секунды) считать дорого, просто передаем + топ-3 внутренним [слоям] и [органам], чья [жесткость? слоя] и [относительный размер|органа] больше всех остальных + тут присутствующих 30% от [импульса] умноженного на [площадь] / [счетчик всего времени соударения]. Таким образом, + надетые доспехи и поддоспешник оставоляют вероятность сломать кость, а вот три слоя брони - уже нет. + + +Но поскольку такой симулятор скотобойни за одно и то же тело быстро приестся - играем за иллитида/мозгового слизня, который +берет разые тушки под контроль, выбрасывает по мере использования и берет новые. + +Соответственно направления развития +- Зов - приманивает тушки получше +- Контроль - позволяет более полно использовать инзначальные возможности тушки, или даже применять новые +- Мутации - отращивание и улучшение тушки, замена материалов, дополнительные конечности/органы, смена шаблона тканей, переход на другой [метаболизм] итп. +- Внедрение и изъятие - запоминание умений тушки и внедрение в другие - от пользования дубиной до телепортационных заклинаний + +За опыт открываются [слоты] для этих возможностей. Макисмальное количество ограничено (скажем 7) +За применения [умений тушки] можно получить ее умения в [слот] (направление контроль). +Любое [умение] занимает какой-то % от слота, может быть больше размера слота. [Объединить слоты] в один больший (но +меньший по размеру чем сумма начальных двух) можно только за [эпические свершения] - сдачу квеста или победу над OOD. +За жти же [эпические очки] открываются новые [умения] в ветках (ограничить деревом скиллов?). +При [гибели тушки] теряются рандомные [слоты] вместе с [умениями] что в них были (не складывайте яйца в одну корзину!). +Если заметят и уничтожат собственно иллитида - game over, high score. + diff --git a/engine/items/common.go b/engine/items/common.go index b5cc484..5c686d6 100644 --- a/engine/items/common.go +++ b/engine/items/common.go @@ -2,6 +2,11 @@ package items import "lab.zaar.be/thefish/alchemyst-go/engine/ecs" +type Named interface { + GetName() string +} + + var Controller *ecs.Controller func Init(ctrl *ecs.Controller) { diff --git a/engine/screens/inventory.go b/engine/screens/inventory.go index e08ac2b..b586c2c 100644 --- a/engine/screens/inventory.go +++ b/engine/screens/inventory.go @@ -1,32 +1,79 @@ package screens import ( + "fmt" "lab.zaar.be/thefish/alchemyst-go/engine/ecs" + "lab.zaar.be/thefish/alchemyst-go/engine/items" "lab.zaar.be/thefish/alchemyst-go/engine/types" + blt "lab.zaar.be/thefish/bearlibterminal" "strings" ) type InventoryScreen struct { *MenuScreen - items []ecs.Entity - - cursor int - offset int + who ecs.Entity + + cursor int + offset int pageSize int + selected []int + + prepared preparedList } -func (is *InventoryScreen) MakeInverntory() *InventoryScreen { +type displayItem struct { + Entity ecs.Entity + Name string + Index string + Selected bool +} + +type preparedList []displayItem + +func (p *preparedList) Prepare(is *InventoryScreen) { + //fixme add item class sorting + //fixme text instead of list to rpint and use printinside() + bp := items.Controller.GetComponent(is.who, ecs.BackpackComponent).(items.Backpack) + is.prepared = make([]displayItem, len(bp.GetItems())) + //todo prepare item list + for i, ent := range bp.GetItems() { + namec := items.Controller.GetComponent(ent, ecs.Named{}.Type()).(ecs.Named) + is.prepared[i] = displayItem{ + Entity: ent, + Name: namec.Name, + Index: string(runeIndex[i]), + } + } +} + +func (is *InventoryScreen) MakeInverntory(who ecs.Entity) *InventoryScreen { is.drawFunc = is.InventoryRender is.inputFunc = is.HandleInput + is.who = who return is } -func (is *InventoryScreen) SetItems(items []ecs.Entity) *InventoryScreen { - is.items = items - return is +func (is *InventoryScreen) Enter() { + is.redraw = true + is.offset = 0 + is.cursor = 0 + + is.prepared = preparedList{} + is.prepared.Prepare(is) } func (is *InventoryScreen) HandleInput(input string) { + if strings.Contains(string(runeIndex), strings.Replace(input, "Shift+", "", -1)) { + if strings.Contains("Shift+", input) { + input = strings.ToUpper(input) + } + for i, _ := range is.prepared { + if is.prepared[i].Index == input { + is.prepared[i].Selected = !is.prepared[i].Selected + } + } + return + } switch input { case "PageUp": is.offset = is.offset - 1 @@ -36,16 +83,13 @@ func (is *InventoryScreen) HandleInput(input string) { break case "PageDown": is.offset = is.offset + 1 - if is.offset > len(is.items)-1 { - is.offset = len(is.items) - 1 + if is.offset > len(is.prepared)-1 { + is.offset = len(is.prepared) - 1 } break case "enter": //select current under cursor break; - case "a","b","c","d","e","f","g","h","i","j","k","l": - //selct by letter - break; case "Escape": fallthrough case "Space": @@ -54,8 +98,6 @@ func (is *InventoryScreen) HandleInput(input string) { } } - - func (is *InventoryScreen) InventoryRender() { menuLayer := is.mw.GetLayer("menu") menuLayer.ClearRect(is.Rect) @@ -72,26 +114,35 @@ func (is *InventoryScreen) InventoryRender() { _, headerHeight := menuLayer.PrintInside(is.Rect, is.header, blt.TK_ALIGN_LEFT) itemField := types.Rect{is.X, is.Y + headerHeight + 1, is.W, is.H - headerHeight - footerHeight} _ = itemField - var ilw, ilh int - if (len(is.items) > 0) { - //fixme itemfield object, scroller, inputhandler, current selected item - menuItems := make([]string, 0) - for i := is.offset; i < len(is.items); i++ { - if string(is.items[i].(string)) != "" { - menuItems = append(menuItems, is.items[i].(string)) + + if (len(is.prepared) > 0) { + + for i := is.offset; i < itemField.H; i++ { + if i < len(is.prepared) { + selectedColor := "blue" + selectedDash := "-" + if is.prepared[i].Selected { + selectedColor = "green" + selectedDash = "+" + } + if is.cursor == i { + menuLayer.WithColor("yellow") + } else { + menuLayer.WithColor("white") + } + menuStr := fmt.Sprintf("[color=%s]%s[/color] %s %s", selectedColor, is.prepared[i].Index, selectedDash, is.prepared[i].Name) + if len(menuStr) > itemField.W - 2 { + menuStr = menuStr[0:itemField.W - 2] + } + menuLayer.PutStringInto(itemField,i,menuStr,1) } } - ilw, ilh = menuLayer.PrintInside(itemField, strings.Join(menuItems, "\n"), blt.TK_ALIGN_LEFT) } - if ilh < len(is.items) { + if is.cursor < len(is.prepared) { is.drawScrollBar(menuLayer, itemField) } - if ilw > itemField.W-4 { - fmt.Printf("Excess width of item names found! Need h-scroll of certain names") - } } - //func (ms *InventoryScreen) UseEcs() bool {return true} //func (ms *InventoryScreen) Enter() {} diff --git a/ui/mainwindow/layer.go b/ui/mainwindow/layer.go index 483c0db..a7e4c41 100644 --- a/ui/mainwindow/layer.go +++ b/ui/mainwindow/layer.go @@ -97,6 +97,17 @@ func (layer Layer) PrintInside(rect types.Rect, text string, alignment int) (wid return blt.PrintExt(rect.X + 2, rect.Y + 2, rect.W - 4, rect.H - 4, alignment, text) } +func (layer Layer) PutStringInto(rect types.Rect, topOffset int, string string, alignment int) bool { + if len(string) > rect.W - 2 { + string = string[:rect.W-5]+"..." + } + if topOffset > rect.H - 2 { + return false + } + blt.PrintExt(rect.X + 2, rect.Y + topOffset, rect.W - 4, rect.H - 4, alignment, string) + return true +} + func (layer *Layer) Decorate(f func(args ...interface{})) func(args ...interface{}) { return func(args ...interface{}) { layer.before()