Repository navigation
Conversation
This script provides tools for recovering from a deceased baron/count/duke. When the landholder dies, the game selects a successor, but this successor is almost always at another site. The game is supposed to push the successor into an army that will path to the site and join it as the new landholder, but the vanilla code to do this includes a test that prevents it from working for landholders (it works for any _other_ civ-level noble position where this happens, just not for landholders. go figure.) so clearly this is what is _supposed_ to happen, it just doesn't because Toady's code is broken. This script finds the already-appointed successor, creates an army, shoves the successor into it, and emulates a unitless "makeown" so that when the successor arrives they'll (re)join the site as the new landholder automatically. Tested on a fort with a dead baron obtained from BlindIRL.
| print((' [%s] %s'):format(ok and 'PASS' or 'FAIL', name)) | ||
| return ok | ||
| end | ||
| if not gate(rd_i32(hf, 0x30) == -1, |
There was a problem hiding this comment.
Why is this not just fetching hf.died_year directly?
(also, this offset might be invalid on Linux)
| dest = df.world_site.find(last_asg.st_id) | ||
| print((' [dest] st_id path (asg %d st_id=%d) -> site %s'): | ||
| format(last_asg.id, last_asg.st_id, dest and dest.id or 'nil')) | ||
| elseif tostring(ent.type):find('CIVILIZATION') then |
There was a problem hiding this comment.
Does this even work? "CIVILIZATION" is the Bay12 name, while ours is title-case ("Civilization").
This should probably be checking ent.type == df.historical_entity_type.Civilization instead, like the ones below.
There was a problem hiding this comment.
it doesn't work. it returns a string converted from the integer type, e.g. 0.
| gate(lm_hf ~= nil and lm_hf == lm_dest, | ||
| ('landmass match %s == %s'):format(tostring(lm_hf), tostring(lm_dest))) | ||
| local _, saddr = df.sizeof(dest) | ||
| local g = df.reinterpret_cast('int32_t', saddr + 0x348).value |
There was a problem hiding this comment.
dest.min_depth?
(also, Linux)
| local home = nil | ||
| for _, l in ipairs(hf.site_links) do | ||
| local t = l:getType() | ||
| if t >= 3 and t <= 7 and l.site ~= -1 then home = l.site break end |
There was a problem hiding this comment.
These should be matching against individual df.histfig_site_link_type entries
There was a problem hiding this comment.
this would be any of HOME_SITE_ABSTRACT_BUILDING, HOME_SITE_REALIZATION_BUILDING, LAIR, HOME_SITE_REALIZATION_SUL, or HOME_SITE_SAVED_CIVZONE
not sure why toady's code has that specific set of requirements
| then return end | ||
| for _, l in ipairs(hf.entity_links) do | ||
| local t = l:getType() | ||
| if (t == 6 or t == 4) and l.link_strength ~= 0 then |
There was a problem hiding this comment.
And df.histfig_entity_link_type here
| end | ||
| end | ||
| for _, l in ipairs(hf.histfig_links) do | ||
| if tostring(l._type):find('IMPRISONER') and l.link_strength ~= 0 then |
There was a problem hiding this comment.
df.histfig_hf_link_type.IMPRISONER
| c and tostring(c.site_id) or '?') | ||
| end | ||
|
|
||
| -- native gate at world_site+0x348 == mined_hours (FUN_14147d730 @0x14147e08c): |
There was a problem hiding this comment.
Offset 0x348 should be min_depth, not mined_hours (presumably to make sure nobles can't migrate to subterranean sites)
| local site = df.world_site.find(site_id) | ||
| if not site then return end | ||
| local _, saddr = df.sizeof(site) | ||
| local gate = df.reinterpret_cast('int32_t', saddr + 0x348).value |
| -- HOME_SITE* site links (types 3-7): native treats the first as "home" | ||
| for _, l in ipairs(hf.site_links) do | ||
| local t = l:getType() | ||
| if t >= df.histfig_site_link_type.HOME_SITE_ABSTRACT_BUILDING |
There was a problem hiding this comment.
This should be an array of distinct entries, not a range check (in case Toady rearranges them some day)
| ent and ent.id or 'nil', best_prec)) then return end | ||
| local wb = hf.info and hf.info.whereabouts | ||
| if not gate(wb ~= nil, 'whereabouts present') then return end | ||
| if not gate(wb.state ~= 3 and wb.state ~= 4, |
There was a problem hiding this comment.
whereabouts_type.army_died / whereabouts_type.army_survived
SilasD
left a comment
There was a problem hiding this comment.
nitpicky stuff, not the important stuff Quietust flagged.
I'll try to give it more attention later.
| 'has returned' announcement) rather than a guest. The native | ||
| army-advance code does the march; no ``settle`` step is needed. | ||
|
|
||
| ]====] |
There was a problem hiding this comment.
I think all of this isn't needed, because you have the .rst help file, which takes precedence.
It also look to be out-of-sync with the .rst file, from a glance at dead-noble fetch.
| dest = df.world_site.find(last_asg.st_id) | ||
| print((' [dest] st_id path (asg %d st_id=%d) -> site %s'): | ||
| format(last_asg.id, last_asg.st_id, dest and dest.id or 'nil')) | ||
| elseif tostring(ent.type):find('CIVILIZATION') then |
There was a problem hiding this comment.
it doesn't work. it returns a string converted from the integer type, e.g. 0.
| ev.id = df.global.hist_event_next_id - 1 | ||
| ev.year = df.global.cur_year | ||
| ev.seconds = df.global.cur_year_tick | ||
| ev.hfid = hf.id | ||
| ev.state = state | ||
| ev.reason = reason or df.history_event_reason.none | ||
| ev.site = site_id | ||
| ev.region = -1 | ||
| ev.layer = -1 | ||
| if site then ev.region_pos:assign(site.pos) end | ||
| world.history.events:insert('#', ev) |
There was a problem hiding this comment.
as a style & consistency thing, I think auto-vivification would be better here, like the above functions have.
There was a problem hiding this comment.
yeah this is copying the structure in the actual code which really does do it this way - most of toady's constructors are default-only and it's up to the consumer to actually fill in the details
There was a problem hiding this comment.
also in this case autovivificaton is messier because of the conditional logic for setting site position
| end | ||
|
|
||
| local function new_remove_link_event(entity, hf, position_id) | ||
| df.global.hist_event_next_id = df.global.hist_event_next_id + 1 |
There was a problem hiding this comment.
I think, for these history events, you should increment the global after the insertion;
that way if the insertion fails, the state remains consistent.
that's a nitpick, not a big deal.
if you do change it, you will also have to change the id math below, just delete the - 1
There was a problem hiding this comment.
DF's own code tends to do it the other way around, but you do have a point
|
Verified that the updated version still works in the test fort I'm using, at least for |
Bumber64
left a comment
There was a problem hiding this comment.
Just skimmed, but noticed a few things.
| ('figure+0x30 (died_year?) == -1 [=%d]'):format(rd_i32(hf, 0x30))) | ||
| then return end | ||
| if not gate(nem.group_leader_id == -1, 'group_leader_id == -1') then return end | ||
| local uok = nem.unit == nil or ((nem.unit.flags1.whole >> 31) & 1) == 0 |
There was a problem hiding this comment.
This is flags1.important_historical_figure (Bay12: NEMESIS), right? It's used below in cmd_army also.
| g.reason = df.history_event_reason.none | ||
| c.data.goal_move_to_site = g | ||
| c.goal = df.army_controller_goal_type.MOVE_TO_SITE | ||
| world.army_controllers.all:insert('#', c) |
There was a problem hiding this comment.
A civ's historical_entity also has vectors for armies and army_controllers. Is DF doing that for us later?
| end | ||
| end | ||
|
|
||
| -- emulate FUN_14147d730's gate chain for one nemesis, printing each verdict |
There was a problem hiding this comment.
List DF version when referencing FUN_*?
| end | ||
|
|
||
| local function hf_of(hfid) | ||
| return hfid ~= -1 and df.historical_figure.find(hfid) or nil |
There was a problem hiding this comment.
IIRC, find already returns nil on -1?
| - ``recall`` for land-holder positions is limited by a native gate: the | ||
| successor-travel sweep skips any position whose definition has | ||
| ``land_holder > 0`` *before* reading the destination, so a baron, | ||
| count, or duke never gets a native travel army no matter what | ||
| ``st_id`` says. The ``st_id`` write is still correct and consumed | ||
| by the same native path -- it just needs the one native check | ||
| bypassed for the target only. A production fix should scope the | ||
| bypass (call the creation path for the single intended nemesis, or | ||
| patch for a single sweep window); a global patch makes every | ||
| stranded land-holder in the world travel at once. |
| end | ||
| for _, l in ipairs(hf.histfig_links) do | ||
| if tostring(l._type):find('IMPRISONER') and l.link_strength ~= 0 then | ||
| if l._type == df.histfig_hf_link_type.IMPRISONER and l.link_strength ~= 0 then |
There was a problem hiding this comment.
I think this actually needs to check :getType()
| @@ -329,26 +254,31 @@ local function trace_sweep(nem, hf) | |||
| gate(lm_hf ~= nil and lm_hf == lm_dest, | |||
| ('landmass match %s == %s'):format(tostring(lm_hf), tostring(lm_dest))) | |||
| local _, saddr = df.sizeof(dest) | |||
There was a problem hiding this comment.
Is saddr still needed for anything?
| end | ||
| if not gate(rd_i32(hf, 0x30) == -1, | ||
| if not gate(hf.died_year == -1, | ||
| ('figure+0x30 (died_year?) == -1 [=%d]'):format(rd_i32(hf, 0x30))) |
This script provides tools for recovering from a deceased baron/count/duke.
When the landholder dies, the game selects a successor, but this successor is almost always at another site. The game is supposed to push the successor into an army that will path to the site and join it as the new landholder, but the vanilla code to do this includes a test that prevents it from working for landholders (it works for any other civ-level noble position where this happens, just not for landholders. go figure.) so clearly this is what is supposed to happen, it just doesn't because Toady's code is broken.
This script finds the already-appointed successor, creates an army, shoves the successor into it, and emulates a unitless "makeown" so that when the successor arrives they'll (re)join the site as the new landholder automatically.
Tested on a fort with a dead baron obtained from BlindIRL.