diff --git a/sote/engine/ui.lua b/sote/engine/ui.lua index 1152377d..80ced52a 100644 --- a/sote/engine/ui.lua +++ b/sote/engine/ui.lua @@ -1433,8 +1433,13 @@ function ui.table(rect, data, columns, state, circle_style, slider_arrow_images) :position(rect.x, rect.y) :spacing(0) :build() + local total_weight = 0 for index = 1, #columns do - local header_rect = layout:next(columns[index].width, state.individual_height) + total_weight = total_weight + columns[index].width + end + local weight = (rect.width - 20) / total_weight + for index = 1, #columns do + local header_rect = layout:next(columns[index].width * weight, state.individual_height) header_rect.height = rect.height if not columns[index].active and ui.text_button("", header_rect) then if state.sorted_field == index then @@ -1477,7 +1482,7 @@ function ui.table(rect, data, columns, state, circle_style, slider_arrow_images) :spacing(0) :build() for index = 1, #columns do - local temp = columns[index].render_closure(layout:next(columns[index].width, state.individual_height), entry.key, entry.value) + local temp = columns[index].render_closure(layout:next(columns[index].width * weight, state.individual_height), entry.key, entry.value) if temp then result = temp end diff --git a/sote/game/economy/production-and-consumption.lua b/sote/game/economy/production-and-consumption.lua index 47c2e7e7..e771cac1 100644 --- a/sote/game/economy/production-and-consumption.lua +++ b/sote/game/economy/production-and-consumption.lua @@ -832,14 +832,16 @@ function pro.run(province) end -- community helps children as well - local siphon_to_child = math.min(food_price * 0.5, province.local_wealth * 1 / 512) - if siphon_to_child > 0 then - economic_effects.add_pop_savings(pop, siphon_to_child, economic_effects.reasons.Donation) - economic_effects.change_local_wealth( - province, - - siphon_to_child, - economic_effects.reasons.Donation - ) + if pop.home_province == pop.province then + local siphon_to_child = math.min(food_price * 0.5, province.local_wealth * 1 / 512) + if siphon_to_child > 0 then + economic_effects.add_pop_savings(pop, siphon_to_child, economic_effects.reasons.Donation) + economic_effects.change_local_wealth( + province, + - siphon_to_child, + economic_effects.reasons.Donation + ) + end end -- children spend time on games and growing up: diff --git a/sote/game/entities/pop.lua b/sote/game/entities/pop.lua index bd311b61..40369f73 100644 --- a/sote/game/entities/pop.lua +++ b/sote/game/entities/pop.lua @@ -8,6 +8,7 @@ ---@field name string ---@field savings number ---@field parent POP? +---@field children table ---@field life_needs_satisfaction number from 0 to 1 ---@field basic_needs_satisfaction number from 0 to 1 ---@field popularity table @@ -73,6 +74,7 @@ function rtab.POP:new(race, faith, culture, female, age, home, location, charact r.owned_buildings = {} r.inventory = {} r.price_memory = {} + r.children = {} r.successor_of = {} r.need_satisfaction = {} diff --git a/sote/game/entities/province.lua b/sote/game/entities/province.lua index 571f3440..1f37908e 100644 --- a/sote/game/entities/province.lua +++ b/sote/game/entities/province.lua @@ -300,6 +300,14 @@ function prov.Province:transfer_pop(pop, target) target.all_pops[pop] = pop pop.province = target + + local children = tabb.filter(pop.children, function(c) + return self.all_pops[c] and c.home_province ~= self + and not c.unit_of_warband and not c.employer + end) + for _,c in pairs(children) do + self:transfer_pop(c,target) + end end --- Changes home province of a pop/character to the target province @@ -353,6 +361,12 @@ function prov.Province:kill_pop(pop) if pop.home_province then pop.home_province:unset_home(pop) end + + if pop.parent then pop.parent.children[pop] = nil end + for _,c in pairs(pop.children) do + c.parent = nil + pop.children[c] = nil + end end function prov.Province:local_army_size() diff --git a/sote/game/raws/effects/death.lua b/sote/game/raws/effects/death.lua index 3a20e444..fb7f7a43 100644 --- a/sote/game/raws/effects/death.lua +++ b/sote/game/raws/effects/death.lua @@ -9,6 +9,12 @@ function effects.death(character) WORLD:emit_notification(character.name .. " had died.") end + if character.parent then character.parent.children[character] = nil end + for _,c in pairs(character.children) do + c.parent = nil + character.children[c] = nil + end + for _, target in pairs(character.successor_of) do target.successor = nil character.successor_of[target] = nil diff --git a/sote/game/scenes/game/inspector-character.lua b/sote/game/scenes/game/inspector-character.lua index 50c4da2a..0b5146d3 100644 --- a/sote/game/scenes/game/inspector-character.lua +++ b/sote/game/scenes/game/inspector-character.lua @@ -9,6 +9,7 @@ local TRAIT_ICONS = require "game.raws.traits.trait_to_icon" local trade_good = require "game.raws.raws-utils".trade_good local characters_list_widget = require "game.scenes.game.widgets.character-list" +local custom_characters_list_widget = require "game.scenes.game.widgets.list-widget" local character_decisions_widget = require "game.scenes.game.widgets.decision-selection-character" local character_name_widget = require "game.scenes.game.widgets.character-name" @@ -20,6 +21,7 @@ local decision_target_secondary = nil local traits_slider = 0 local inventory_slider = 0 +local character_list_tab = "Local" ---@return Rect function window.rect() @@ -111,6 +113,7 @@ function window.draw(game) local decisions_label_panel = layout:next(unit * 16, unit * 1) local decisions_panel = layout:next(unit * 16, unit * 7) local decisions_confirmation_panel = layout:next(unit * 16, unit * 1) + local character_tab = layout:next(unit * 16, unit * 1) local characters_list = layout:next(unit * 16, unit * 8) character_name_widget(name_panel, character) @@ -152,7 +155,7 @@ function window.draw(game) location_panel.y = location_panel.y - unit ui.left_text("Location: ", location_panel) - ut.data_entry("", character.culture.name, culture_panel, "Culture") + ut.data_entry("", character.culture.name, culture_panel, character.culture.name) culture_panel.y = culture_panel.y - unit ut.data_entry("", character.faith.name, culture_panel, "Faith") @@ -229,15 +232,116 @@ function window.draw(game) decision_target_primary = nil decision_target_secondary = nil end - + ---@type table + local tabs = {} if province and province_visible then - local response = characters_list_widget(characters_list, character.province.characters, "Local Characters", true)() - if response then - game.selected.character = response + tabs[1] = { + text = "Local", + tooltip = "Characters in the same province.", + closure = function () + local response = characters_list_widget(characters_list, character.province.characters, nil, true)() + if response then + game.selected.character = response + end + end + } + end + tabs[2] = { + text = "Children", + tooltip = "This character's children.", + closure = function () + local response = characters_list_widget(characters_list, character.children, nil, true)() + if response then + game.selected.character = response + end end + } + if character.leading_warband then + local function pop_sex(pop) + local f = "m" + if pop.female then f = "f" end + return f + end + local function render_name(rect, k, v) + if ut.text_button(v.name, rect) then + return v + end + end + tabs[3] = { + text = "Warriors", + tooltip = "Warriors in the character's warband.", + closure = function() + local response = custom_characters_list_widget(characters_list, character.leading_warband.pops, { + { + header = ".", + render_closure = function(rect, k, v) + require "game.scenes.game.widgets.portrait" (rect, v) + end, + width = UI_STYLE.scrollable_list_small_item_height, + value = function(k, v) + ---@type POP + v = v + return v.race.name + end + }, + { + header = "race", + render_closure = function (rect, k, v) + ui.right_text(v.race.name, rect) + end, + width = ut.BASE_HEIGHT * 4, + value = function(k, v) + return v.race.name + end + }, + { + header = "culture", + render_closure = function (rect, k, v) + ui.right_text(v.culture.name, rect) + end, + width = ut.BASE_HEIGHT * 4, + value = function(k, v) + return v.culture.name + end + }, + { + header = "faith", + render_closure = function (rect, k, v) + ui.right_text(v.faith.name, rect) + end, + width = ut.BASE_HEIGHT * 3, + value = function(k, v) + return v.faith.name + end + }, + { + header = "age", + render_closure = function (rect, k, v) + ui.right_text(tostring(v.age), rect) + end, + width = ut.BASE_HEIGHT * 2, + value = function(k, v) + return v.age + end + }, + { + header = "sex", + render_closure = function (rect, k, v) + ui.centered_text(pop_sex(v), rect) + end, + width = ut.BASE_HEIGHT * 1, + value = function(k, v) + return pop_sex(v) + end + } + }, nil, true)() + end + } end + local tab_layout = ui.layout_builder():position(character_tab.x, character_tab.y):horizontal():build() + character_list_tab = ut.tabs(character_list_tab, tab_layout, tabs, 1, ut.BASE_HEIGHT * 4) ut.coa(character.realm, coa) end -return window \ No newline at end of file +return window diff --git a/sote/game/scenes/game/widgets/list-widget.lua b/sote/game/scenes/game/widgets/list-widget.lua new file mode 100644 index 00000000..e7afb44f --- /dev/null +++ b/sote/game/scenes/game/widgets/list-widget.lua @@ -0,0 +1,64 @@ +local tabb = require "engine.table" +local ui = require "engine.ui" +local ut = require "game.ui-utils" + +---@type TableState +local state = nil + +---comment +---@param compact boolean +local function init_state(compact) + local entry_height = UI_STYLE.scrollable_list_item_height + if compact then + entry_height = UI_STYLE.scrollable_list_small_item_height + end + + if state == nil then + state = { + header_height = UI_STYLE.table_header_height, + individual_height = entry_height, + slider_level = 0, + slider_width = UI_STYLE.slider_width, + sorted_field = 1, + sorting_order = true + } + else + state.header_height = UI_STYLE.table_header_height + state.individual_height = entry_height + state.slider_width = UI_STYLE.slider_width + end +end + +---@generic K, V +---@param rect Rect +---@param table table +---@param columns TableColumn[] +---@param title string? +---@param compact boolean? +return function(rect, table, columns, title, compact) + if compact == nil then + compact = false + end + + local portrait_width = UI_STYLE.scrollable_list_item_height + if compact then + portrait_width = UI_STYLE.scrollable_list_small_item_height + end + + local rest_width = rect.width - portrait_width + local width_unit = rest_width / 12 + return function() + + init_state(compact) + local bottom_height = rect.height + local bottom_y = 0 + if title then + bottom_height = bottom_height - UI_STYLE.table_header_height + bottom_y = UI_STYLE.table_header_height + local top = rect:subrect(0, 0, rect.width, UI_STYLE.table_header_height, "left", "up") + ui.centered_text(title, top) + end + local bottom = rect:subrect(0, bottom_y, rect.width, bottom_height, "left", "up") + return ut.table(bottom, table, columns, state) + end +end diff --git a/sote/game/society/pop-growth.lua b/sote/game/society/pop-growth.lua index 802a2b6d..6066a860 100644 --- a/sote/game/society/pop-growth.lua +++ b/sote/game/society/pop-growth.lua @@ -1,6 +1,7 @@ local pg = {} local POP = require "game.entities.pop".POP +local tabb = require "engine.table" ---Runs natural growth and decay on a single province. ---@param province Province @@ -18,6 +19,8 @@ function pg.growth(province) ---@type POP[] local to_add = {} + local race_sex = {} + for _, pp in pairs(province.outlaws) do if pp.age > pp.race.max_age then to_remove[#to_remove + 1] = pp @@ -33,6 +36,19 @@ function pg.growth(province) end else local sex_prob = 0.1 + + if not race_sex[pp.race] then + race_sex[pp.race] = {} + race_sex[pp.race][pp.female] = true + elseif race_sex[pp.race][not pp.female] == nil then + if tabb.size(tabb.filter(province.all_pops, function (a) + return a.race == pp.race and a.female ~= pp.female + end)) > 0 then + race_sex[pp.race][not pp.female] = true + else race_sex[pp.race][not pp.female] = false end + end + if race_sex[pp.race][not pp.female] then sex_prob = 0 end + if pp.female then sex_prob = 1.0 end @@ -77,9 +93,10 @@ function pg.growth(province) pp.culture, love.math.random() > pp.race.males_per_hundred_females / (100 + pp.race.males_per_hundred_females), 0, - province, province + pp.home_province, province ) newborn.parent = pp + pp.children[newborn] = newborn end -- province:validate_population() diff --git a/sote/game/world-gen/spawn-tribes.lua b/sote/game/world-gen/spawn-tribes.lua index e7012e7e..da9f8aed 100644 --- a/sote/game/world-gen/spawn-tribes.lua +++ b/sote/game/world-gen/spawn-tribes.lua @@ -96,6 +96,19 @@ local function make_new_realm(capitol, race, culture, faith) -- capitol:validate_population() + -- try to assign a parent to generated children + for _,x in pairs(capitol.all_pops) do + if x.age > x.race.adult_age then + local potentials = tabb.filter(capitol.all_pops, + function(a) return a.age >= x.age + x.race.adult_age and a.age < x.race.elder_age + x.age end) + local size = #potentials + local parent = tabb.nth(potentials, math.random(1,size)) + if parent then + parent.children[x] = x + x.parent = parent + end + end + end -- print("test battle") -- local size_1, size_2 = love.math.random(50) + 10, love.math.random(50) + 10