SHOW:
|
|
- or go back to the newest paste.
1 | local helptext = [=[ | |
2 | relations-indicator | |
3 | =================== | |
4 | - | v1.13 |
4 | + | v1.14 |
5 | Displays the pregnancy, fertility and romantic status of unit(s). | |
6 | For singular unit, in (v)iew-(g)eneral mode. | |
7 | For multiple units, in Citizens, Pets/Livestock and Others lists. | |
8 | By default, displays just pregnancies in those, | |
9 | as well as binding itself to be called there via keybinding plugin. | |
10 | Can be called with following arguments | |
11 | Relating to instructions | |
12 | -help, help, ?, -? | |
13 | Displays this text and exits | |
14 | explain colors | |
15 | Explains the meanings of various colors used and exits | |
16 | bindings | |
17 | Lists the keybindings relations-indicator uses with bind and exits | |
18 | bind | |
19 | adds context-specific keybindings | |
20 | unbind | |
21 | removes context-specific keybindings and exits]=] | |
22 | ||
23 | -- ======================================== -- | |
24 | -- Color shades -- | |
25 | -- ======================================== -- | |
26 | ||
27 | local COLOR_DARKGRAY = 8 -- British spelling | |
28 | local COLOR_LIGHTGRAY = 7 -- ditto | |
29 | ||
30 | local straightMaleShade = COLOR_LIGHTCYAN -- feels like this should be pregnant color, but tweak uses green | |
31 | local straightFemaleShade = COLOR_LIGHTMAGENTA -- DT colors. Could alternatively flicker with symbol. | |
32 | local gayMaleShade = COLOR_YELLOW -- Blue makes more sense, but dark blue on black is hard to see. | |
33 | local gayFemaleShade = COLOR_LIGHTRED -- originally love or straight female color. | |
34 | local pregnantColor = COLOR_LIGHTGREEN -- base tweak color, might wap with yellow | |
35 | local infertileColor = COLOR_LIGHTGRAY | |
36 | local offsiteShade = COLOR_WHITE -- issue: orientation-offsite? Blinking can solve this, ofc. | |
37 | local deadColor = COLOR_DARKGRAY -- still, should avoid blinking with symbols if possible. | |
38 | -- 8 shades, but I only have 7 available. Plus default blue is not so great. | |
39 | ||
40 | ||
41 | local function colorExplanation() | |
42 | function printlnc(text, color) | |
43 | dfhack.color(color) | |
44 | dfhack.println(text) | |
45 | dfhack.color(COLOR_RESET) | |
46 | end | |
47 | dfhack.println("relations-indicator marks the following traits with following colour:") | |
48 | dfhack.print("Straight Male ") printlnc("Cyan", straightMaleShade) | |
49 | dfhack.print("Gay Male ") printlnc("Yellow", gayMaleShade) | |
50 | dfhack.print("Straight Female ") printlnc("Magenta", straightFemaleShade) | |
51 | dfhack.print("Gay Female ") printlnc("Red", gayFemaleShade) | |
52 | dfhack.print("Pregnant ") printlnc("Green", pregnantColor) | |
53 | dfhack.println("") | |
54 | dfhack.println("For the first four, darker shade indicates unwillingness to marry.") | |
55 | dfhack.println("") | |
56 | dfhack.println("The below three by default replace the first four in top-down hiearchy:") | |
57 | dfhack.println("") | |
58 | dfhack.print("Dead partner ") printlnc("Dark gray ", deadColor) | |
59 | dfhack.print("Infertile/aromantic or infertile/aromantic hetero partner ") printlnc("Light gray", infertileColor) | |
60 | dfhack.print("Offsite partner ") printlnc("White", offsiteShade) | |
61 | end | |
62 | ||
63 | -- ======================================== -- | |
64 | -- Indicator Symbols -- | |
65 | -- ======================================== -- | |
66 | ||
67 | local loversSymbol = "\148" | |
68 | local marriedSymbol = "\3" | |
69 | local singleMaleSymbol = "\11" | |
70 | local singleFemaleSymbol = "\12" | |
71 | local fertileBirdSymbol = "\8" -- used only in unitlist | |
72 | local pregnantCreatureSymbol = "\20" -- also only in unitlist | |
73 | local diedS, diedE = "\197", "\197" --Used just for viewing a single unit. | |
74 | local offsite = "..." --Also for just a single unit only. | |
75 | local blinkingdelay = 650 | |
76 | --How often does it blink between colors and/or symbols. | |
77 | ||
78 | -- ======================================== -- | |
79 | -- Affection thresholds -- | |
80 | -- ======================================== -- | |
81 | ||
82 | local friendlinessthreshold = 0 | |
83 | local loversthreshold = 14 | |
84 | local marriagethreshold = -40 | |
85 | ||
86 | -- ======================================== -- | |
87 | -- Keybindings used -- | |
88 | -- ======================================== -- | |
89 | ||
90 | local keybinding_list = {} | |
91 | table.insert(keybinding_list, "U@dwarfmode/Default relations-indicator") | |
92 | ||
93 | table.insert(keybinding_list, "V@dwarfmode/Default relations-indicator") | |
94 | table.insert(keybinding_list, "Z@unitlist/Citizens relations-indicator") | |
95 | table.insert(keybinding_list, "Z@dfhack/unitlabors relations-indicator") | |
96 | table.insert(keybinding_list, "Z@unitlist/Livestock relations-indicator") | |
97 | table.insert(keybinding_list, "Z@unitlist/Citizens relations-indicator") | |
98 | table.insert(keybinding_list, "Z@unitlist/Others relations-indicator") | |
99 | table.insert(keybinding_list, "Z@layer_unit_relationship relations-indicator") | |
100 | ||
101 | ||
102 | local function relationsIndicatorPowerState(enable) | |
103 | for bindingi=1, #keybinding_list do | |
104 | dfhack.run_command_silent("keybinding " .. (enable and "add " or "clear ") .. keybinding_list[bindingi]) | |
105 | end | |
106 | end | |
107 | ||
108 | local args = {...} | |
109 | ||
110 | local argsline = table.concat(args, " ") | |
111 | --utils.processArgs is neat but requires - and not necessary here | |
112 | if argsline:find("help") or | |
113 | argsline:find("?") then | |
114 | dfhack.println(helptext) | |
115 | qerror("") | |
116 | --Not, strictly speaking, a proper exit but it'll do | |
117 | end | |
118 | if argsline:find("explain colors") then colorExplanation() qerror("") end | |
119 | ||
120 | if argsline:find("bindings") then | |
121 | for bindingi=1, #keybinding_list do | |
122 | dfhack.println(keybinding_list[bindingi]) | |
123 | end | |
124 | dfhack.println("(Internal) Z@dfhack/lua/manipulator relations-indicator") | |
125 | qerror("") | |
126 | end | |
127 | ||
128 | if argsline:find("unbind") then relationsIndicatorPowerState(false) qerror("") end | |
129 | if argsline:find("bind") then | |
130 | relationsIndicatorPowerState(true) | |
131 | end | |
132 | ||
133 | -- ======================================== -- | |
134 | -- Loading required modules -- | |
135 | -- ======================================== -- | |
136 | ||
137 | local gui = require 'gui' | |
138 | ||
139 | local screenconstructor = dfhack.script_environment("gui/indicator_screen") | |
140 | ||
141 | -- ======================================== -- | |
142 | -- Utility functions -- | |
143 | -- ======================================== -- | |
144 | ||
145 | ||
146 | local function getLen(data) | |
147 | -- Can't # a hashed table, must use pairs. | |
148 | local len = 0 | |
149 | for i, val in pairs(data) do | |
150 | len = len +1 | |
151 | end | |
152 | return len | |
153 | end | |
154 | ||
155 | function blinkergenerator(tableofblinkers) | |
156 | -- takes numerically indexed table of values | |
157 | -- returns either single value if table has a single value, or function that alternates which one it returns | |
158 | -- local manyblinkers = #tableofblinkers | |
159 | if #tableofblinkers == 1 then | |
160 | return tableofblinkers[1] | |
161 | else | |
162 | function blinkingfunction() | |
163 | local blinkertable = tableofblinkers | |
164 | local blinkernr = #tableofblinkers | |
165 | ||
166 | return blinkertable[1+math.floor(dfhack.getTickCount()/blinkingdelay) % blinkernr] | |
167 | end | |
168 | return blinkingfunction | |
169 | end | |
170 | end | |
171 | ||
172 | function getFlag(object, flag) | |
173 | -- Utility function for safely requesting info from userdata | |
174 | -- Returns nil if the object doesn't have flag attribute, else returns it's value | |
175 | -- Because well, ordinarily, {}[flag] returns nil. | |
176 | -- However, if object is unit - or some other type, it may instead throw an error | |
177 | local a = {} | |
178 | if not object or not flag then return nil end | |
179 | --Crash is still possible for attempting to pairs a nil | |
180 | for index, value in pairs(object) do | |
181 | a[index] = value | |
182 | end | |
183 | local returnvalue = a[flag] | |
184 | a = nil --lua automatically garbage cleans tables without variable that links to them. | |
185 | return returnvalue | |
186 | end | |
187 | ||
188 | function getBottomMostViewscreenWithFocus(text, targetscreen) | |
189 | --Finds and returns the screen closest to root screen whose path includes text | |
190 | --duplicated from getScreen, admittedly. | |
191 | if targetscreen and | |
192 | dfhack.gui.getFocusString(targetscreen):find(text) then | |
193 | return targetscreen | |
194 | elseif targetscreen and targetscreen.child then --Attempting to call nil.child will crash this | |
195 | return getBottomMostViewscreenWithFocus(text, targetscreen.child) | |
196 | end | |
197 | -- if there's no child, it didn't find a screen with text in focus and returns nil | |
198 | end | |
199 | ||
200 | function writeoverTable(modifiedtable, copiedtable) | |
201 | --Takes two tables as input | |
202 | --Removes all the values in first table | |
203 | --Then places all the values in second table into it | |
204 | --Returns the first table after that | |
205 | for index, value in pairs(modifiedtable) do | |
206 | modifiedtable[index] = nil | |
207 | end | |
208 | for index, value in pairs(copiedtable) do | |
209 | modifiedtable[index] = value | |
210 | end | |
211 | return modifiedtable | |
212 | end | |
213 | ||
214 | function hasKeyOverlap (searchtable, searchingtable) | |
215 | -- Looks for a key in searchtable whose name contains the name of a key in searchtable | |
216 | -- returns true if finds it. | |
217 | for aindex, avalue in pairs(searchingtable) do | |
218 | for bindex, bvalue in pairs(searchtable) do | |
219 | if tostring(bindex):find(tostring(aindex)) then | |
220 | return true | |
221 | end | |
222 | end | |
223 | end | |
224 | end | |
225 | ||
226 | -- ======================================== -- | |
227 | -- Tier 1 (df) functions -- | |
228 | -- ======================================== -- | |
229 | ||
230 | function isItBird(unit) | |
231 | return (df.global.world.raws.creatures.all[unit.race].caste[0].flags.LAYS_EGGS) | |
232 | --because it lists all available flags as true or false doesn't need to invoke getFlag | |
233 | end | |
234 | ||
235 | function isItSmart(unit) | |
236 | if (df.global.world.raws.creatures.all[unit.race].caste[0].flags.CAN_LEARN and | |
237 | not df.global.world.raws.creatures.all[unit.race].caste[0].flags.SLOW_LEARNER) then | |
238 | return true | |
239 | else | |
240 | return false | |
241 | end | |
242 | ||
243 | end | |
244 | ||
245 | function getGenderInInfertileColor(unit) | |
246 | local symbolColor = {} | |
247 | symbolColor.text = unit.sex == 1 and | |
248 | singleMaleSymbol or | |
249 | ( unit.sex == 0 and | |
250 | singleFemaleSymbol or "") | |
251 | symbolColor.color = infertileColor | |
252 | return symbolColor | |
253 | end | |
254 | -- ======================================== -- | |
255 | -- 43.05 vs earlier structure differences -- | |
256 | -- ======================================== -- | |
257 | ||
258 | function getPregnancyTimer(unit) | |
259 | -- Takes local unit, returns time until birth in steps. | |
260 | -- In case of historical unit always returns -1; don't know about their pregnancy structures. | |
261 | -- utilizes getFlag | |
262 | if getFlag(unit, "info") then return -1 end -- so assume they're always not pregnant. They usually aren't | |
263 | return (getFlag(unit, "pregnancy_timer") or getFlag(unit.relations, "pregnancy_timer")) | |
264 | --Differences between 43.05 and earlier dfhack. | |
265 | end | |
266 | ||
267 | function getLocalRelationship(unit, SpouseOrLover) | |
268 | --Takes local unit, boolean | |
269 | -- returns spouse id in the case of true for second value, | |
270 | -- lover in the case of false or if nil is used and spouse isn't present. | |
271 | -- utilizes getFlag | |
272 | if getFlag(unit, "info") then return nil end | |
273 | --Not intended to be used on historical figure structure, as that is different. | |
274 | --Also, using nil when number is expected will throw an error, so this points out those mistakes | |
275 | local is4305p = getFlag(unit, "relationship_ids") and true or false | |
276 | local relationships = is4305p and unit.relationship_ids or unit.relations | |
277 | local spousevalue = is4305p and relationships.Spouse or relationships.spouse_id | |
278 | local lovervalue = is4305p and relationships.Lover or relationships.lover_id | |
279 | --Again, differences between 43.05 and earlier dfhack. | |
280 | --Further issue: 43.03 uses spouse_id, 43.05 uses Spouse | |
281 | --This is not as extensible, but since I only get spouse or lover for now... | |
282 | if SpouseOrLover == true then return spousevalue end | |
283 | if SpouseOrLover == false then return lovervalue end | |
284 | if spousevalue > -1 then | |
285 | return spousevalue | |
286 | else | |
287 | return lovervalue | |
288 | end | |
289 | end | |
290 | ||
291 | -- ======================================== -- | |
292 | -- Tier 2 functions -- | |
293 | -- ======================================== -- | |
294 | ||
295 | function isItGelded(unit) | |
296 | -- Either local or historical unit | |
297 | -- returns true or nil | |
298 | -- utilizes getFlag | |
299 | if getFlag(unit, "status") then | |
300 | --local unit | |
301 | if unit.flags3.gelded then | |
302 | --usually only pets and guys are gelded, but can be set on anyone | |
303 | return true | |
304 | elseif unit.curse.add_tags2.STERILE then | |
305 | --occurs for vampires and husks | |
306 | return true | |
307 | elseif not getFlag(unit.status, "current_soul") then | |
308 | --occurs for animated corpses | |
309 | --Could also use unit.curse.add_tags1.OPPOSED_TO_LIFE | |
310 | --Though I'm not certain you need a soul to breed, lack of soul messes up checking personality | |
311 | return true | |
312 | end | |
313 | elseif getFlag(unit, "info") then | |
314 | --historical unit | |
315 | if (getFlag(unit.info, "wounds") and getFlag(unit.info, "wounds").anon_3 ==1 ) then | |
316 | --suspected gelding flag. 0 is dead, -1 is default? | |
317 | return true | |
318 | elseif (getFlag(unit.info,"curse") and getFlag(unit.info.curse, "active_interactions") ) then | |
319 | for i, interaction in pairs(unit.info.curse.active_interactions) do | |
320 | --Here, could just check that it's name has VAMPIRE in it in vanilla, but could have modded vamps | |
321 | --Interestingly, soul is not mentioned in historical unit data. Presumably it is hiding. Fallback plan | |
322 | for j, text in pairs(interaction.str) do | |
323 | - | if text:find("STERILE") or |
323 | + | if text.value:find("STERILE") or |
324 | - | text:find("OPPOSED_TO_LIFE") then |
324 | + | text.value:find("OPPOSED_TO_LIFE") then |
325 | --side effect: False positive on syndromes that remove those tags. | |
326 | --ex: modded-in gonads that remove sterility from otherwise-sterile creature | |
327 | --TODO: fix | |
328 | return true | |
329 | end | |
330 | end | |
331 | end | |
332 | end | |
333 | end | |
334 | return nil | |
335 | end | |
336 | ||
337 | function getPregnancyText(unit) | |
338 | -- Takes local unit, returns single line string that is "" for no pregnancy | |
339 | -- utilizes getFlag, getPregnancyTimer | |
340 | if not getFlag(unit, "status") or | |
341 | unit.caste > 0 then | |
342 | return "" | |
343 | else | |
344 | local howfar = getPregnancyTimer(unit) | |
345 | ||
346 | local pregnancy_duration = df.global.world.raws.creatures.all[unit.race].caste[0].flags.CAN_LEARN and 9 or 6 | |
347 | if howfar < 1 then return "" else | |
348 | local returnstring = "" | |
349 | if isItBird(unit) then | |
350 | if (howfar/33600)>1 then returnstring = "Was wooed " .. tostring(math.floor(pregnancy_duration-howfar/33600)) .. " months ago" end | |
351 | if (howfar/33600)<1 then returnstring = "Last seed withers in " .. tostring(math.floor(howfar/1200)) .. " days " end | |
352 | if (howfar/33600)>(pregnancy_duration-1) then returnstring = "Was wooed recently" end | |
353 | ||
354 | else | |
355 | if (howfar/33600)>0 then returnstring = "Sick in the morning recently" end | |
356 | if (howfar/33600)<(pregnancy_duration-1) then returnstring = "Missed a period" end | |
357 | if (howfar/33600)<(pregnancy_duration-2) then returnstring = tostring(math.floor(pregnancy_duration-howfar/33600)) .. " months pregnant" end | |
358 | if (howfar/33600)<1 then returnstring = "Will give birth in " .. tostring(math.floor(howfar/1200)) .. " days" end | |
359 | end | |
360 | return returnstring; | |
361 | end | |
362 | end | |
363 | end | |
364 | ||
365 | function getSpouseAndLover(unit) | |
366 | --Takes a local unit, returns either local or historical spouse and lover of that unit if present | |
367 | --if the spouse/lover is historically set but culled (single parents who can marry), returns false for those | |
368 | --else, returns for that value | |
369 | -- utilizes getLocalRelationship, df.historical_figure.find | |
370 | local historical_unit = df.historical_figure.find(unit.hist_figure_id) | |
371 | local spouse,lover, unithistoricalrelations | |
372 | local spouseid = getLocalRelationship(unit, true) | |
373 | local loverid = getLocalRelationship(unit, false) | |
374 | if spouseid > -1 then | |
375 | spouse = df.unit.find(spouseid) | |
376 | --shortcut | |
377 | end | |
378 | if not spouse and --shortcut failed due spouse never arriving, or having left the site | |
379 | historical_unit then --pets brought on embark aren't historical | |
380 | --got to dig into historical unit values | |
381 | unithistoricalrelations = historical_unit.histfig_links | |
382 | for index, relation in pairs(unithistoricalrelations) do | |
383 | --no local spouse? Mark both global spouse and lover | |
384 | if (relation._type == df.histfig_hf_link_spousest) then | |
385 | spouse=df.historical_figure.find(relation.target_hf) | |
386 | if not spouse then spouse = false | |
387 | --there was an id, but there wasn't a histfig with that id (due culling) | |
388 | end | |
389 | -- small distinction between nil and false: is it better to have loved and lost, or never loved? | |
390 | elseif (relation._type == df.histfig_hf_link_loverst) then | |
391 | lover=df.historical_figure.find(relation.target_hf) | |
392 | if not lover then lover = false | |
393 | end | |
394 | end | |
395 | end | |
396 | end | |
397 | if loverid > -1 then | |
398 | lover=df.unit.find(loverid) --can be nil for offsite lover | |
399 | if not lover then lover = false end --false instead of nil to indicate having checked | |
400 | end | |
401 | if not lover and historical_unit then | |
402 | --No local lover? Maybe lover is global | |
403 | unithistoricalrelations = historical_unit.histfig_links | |
404 | for index, relation in pairs(unithistoricalrelations) do | |
405 | if (relation._type == df.histfig_hf_link_loverst) then | |
406 | lover=df.historical_figure.find(relation.target_hf) | |
407 | if not lover then lover = false end | |
408 | end | |
409 | end | |
410 | end | |
411 | return spouse, lover | |
412 | end | |
413 | ||
414 | function areCompatible(unitA,unitB) | |
415 | --Checks if two local units make compatible pair and returns true if they are, false if not | |
416 | --Utilizes getFlag, requires them to be historical to check relationships | |
417 | ||
418 | -- Lets check if one of them is married. | |
419 | -- If they are, can do hanky panky with spouse alone | |
420 | local spouseA, loverA = getSpouseAndLover(unitA) | |
421 | local spouseB, loverB = getSpouseAndLover(unitB) | |
422 | if spouseA or loverA or | |
423 | spouseB or loverB then | |
424 | if spouseA == unitB or | |
425 | loverA == unitB then | |
426 | return true | |
427 | else | |
428 | return false | |
429 | end | |
430 | end | |
431 | ||
432 | -- Do I check if one is a child? | |
433 | -- I think not. Arranged marriages can be planned a decade before they happen. | |
434 | -- Still, age is most common disqualifying factor | |
435 | if unitA.race ~= unitB.race then return false end | |
436 | --multi-racial fortress are nice, but humans still can't into dwarves; not like this. | |
437 | local is4305p = getFlag(unitA, "relationship_ids") and true or false | |
438 | local relationshipsA = is4305p and unitA or unitA.relations | |
439 | local relationshipsB = is4305p and unitB or unitB.relations | |
440 | --age is stored in relations for 4303 but on base level in 43.05, so... | |
441 | local ageA = relationshipsA.birth_year+relationshipsA.birth_time/403200 | |
442 | local ageB = relationshipsB.birth_year+relationshipsB.birth_time/403200 | |
443 | --exact age matters | |
444 | if (ageA-ageB) > 10 or (ageB-ageA) > 10 then | |
445 | --over 10 year age difference | |
446 | return false | |
447 | end | |
448 | ||
449 | --Lets check if they have compatible orientations for marrying each other. | |
450 | local attractionA = getAttraction(unitA) | |
451 | local attractionB = getAttraction(unitB) | |
452 | if not ((2 == attractionA[(unitB.sex == 1 and "guy" or "girl")]) and | |
453 | (2 == attractionB[(unitA.sex == 1 and "guy" or "girl")])) then | |
454 | -- Admittedly, this means that someone who only romances has no suitable pairings. | |
455 | return false | |
456 | end | |
457 | ||
458 | --Lets check if personalities are compatible. | |
459 | local unwillingToFriendA, unwillingToLoveA, unwillingToMarryA = getAromantism(unitA) | |
460 | local unwillingToFriendB, unwillingToLoveB, unwillingToMarryB = getAromantism(unitB) | |
461 | if unwillingToFriendA or unwillingToLoveA or unwillingToMarryA or | |
462 | unwillingToFriendB or unwillingToLoveB or unwillingToMarryB then | |
463 | --If either one as baggage about progressing through a relationship, no babies | |
464 | return false | |
465 | end | |
466 | --Checking for relationships requires digging into historical unit values. | |
467 | local hfA = unitA.hist_figure_id > -1 and df.historical_figure.find(unitA.hist_figure_id) or nil | |
468 | local hfB = unitB.hist_figure_id > -1 and df.historical_figure.find(unitB.hist_figure_id) or nil | |
469 | if hfA and hfB then --basic sanity check. | |
470 | -- Function to check for being a sibling. | |
471 | -- Half-siblings...Possible with hacking, and I bet they block | |
472 | function gethfParent(hfunit, retrieveMother) | |
473 | --Returns historical mother or father of a historical unit if possible | |
474 | --otherwise returns nil | |
475 | for index, relationship_link in pairs(hfunit.histfig_links) do | |
476 | if retrieveMother and relationship_link._type == df.histfig_hf_link_motherst or | |
477 | (not retrieveMother and relationship_link._type == df.histfig_hf_link_fatherst) then | |
478 | return df.historical_figure.find(relationship_link.target_hf) | |
479 | end | |
480 | end | |
481 | end | |
482 | ||
483 | local momA = gethfParent(hfA, true) | |
484 | local momB = gethfParent(hfB, true) | |
485 | local dadA = gethfParent(hfA) | |
486 | local dadB = gethfParent(hfB) | |
487 | if momA and momB and momA == momB or --existence of moms must be checked since nil == nil | |
488 | (dadA and dadB and dadA == dadB) then | |
489 | --siblings or half-siblings are not allowed | |
490 | return false | |
491 | end | |
492 | ||
493 | --Function to check for grudge: | |
494 | -- (As it is not used outside parent function, not encapsulating elsewhere despite size) | |
495 | function hasGrudgeTowards(hfUnitChecked, hfUnitFuckThisCreatureInParticular) | |
496 | -- print("Checking for grudge between " .. dfhack.TranslateName(hfUnitChecked.name) .. " and " .. dfhack.TranslateName(hfUnitFuckThisCreatureInParticular.name)) | |
497 | -- Triple-loops checking info.relationships.list[#].anon_3[#]. | |
498 | -- Admittedly, doing this repeatedly for every unit is inefficient. | |
499 | -- Better would be finding all grudges in fortress at start and cross-checking that. | |
500 | if hfUnitChecked.info.relationships then | |
501 | --Invaders, for instance, may have it absent | |
502 | --Though I wonder if it is even possible to marry off invaders, even after peace settlement | |
503 | for index, relationship in pairs (hfUnitChecked.info.relationships.list) do | |
504 | if hfUnitFuckThisCreatureInParticular.id == relationship.histfig_id then | |
505 | --Found a relationship between the two units. Now for grudge! | |
506 | local attitude | |
507 | if getFlag(relationship,'anon_3') ~= nil then attitude = relationship.anon_3 else attitude = relationship.attitude end | |
508 | for feelingindex, feelingtype in pairs(attitude) do | |
509 | --A dwarf can have multiple feelings/relationship types with someone. | |
510 | if feelingtype == 2 then | |
511 | --[[List of options I've noticed with changing that value: | |
512 | 0: Hero | |
513 | 1: Friend | |
514 | 2: Grudge | |
515 | 3: Bonded | |
516 | 6: Good for Business | |
517 | 7: Friendly Terms? (unsure) | |
518 | 10: Comrade | |
519 | 17: Loyal Soldier | |
520 | 18: Considers Monster (hm, could be interesting RP-wise) | |
521 | 26: Protector of the Weak | |
522 | ||
523 | Others seemed to default to Friendly terms | |
524 | with just few points on 7 as second relation. | |
525 | ||
526 | Perhaps anon_1 and anon_5 may also matter. | |
527 | --]] | |
528 | return true | |
529 | end | |
530 | end | |
531 | --Found unit without grudge. | |
532 | attitude = nil | |
533 | return false | |
534 | end | |
535 | end | |
536 | end | |
537 | end | |
538 | ||
539 | if hasGrudgeTowards(hfA, hfB) or | |
540 | hasGrudgeTowards(hfB, hfA) then | |
541 | --Either one having a grudge? Welp, no marriage for you two. | |
542 | return false | |
543 | end | |
544 | end | |
545 | -- No other disqualifing factors? It's a go. | |
546 | ||
547 | return true | |
548 | end | |
549 | ||
550 | function getInclinationIfPresent(unit, inclinationnumber) | |
551 | --takes ensouled unit and numerical index of inclination | |
552 | --returns the value of inclination or 0 or -1000 in case of divorce from local and seeing the world. | |
553 | -- utilizes getFlag | |
554 | local values | |
555 | if getFlag(unit,"status") then | |
556 | values = unit.status.current_soul.personality.values | |
557 | elseif getFlag(unit.info,"personality") then | |
558 | --can be nil for local units who have never updated their hfunit | |
559 | --comes up in the case of divorce. | |
560 | values = unit.info.personality.values | |
561 | else | |
562 | return -1000 --buggy placeholder: divorced partners are super-incapable of progressing their relationship. | |
563 | end | |
564 | -- Do need to check hfunits, since both parties of a ship must be willing to embark on the waters of marriage | |
565 | for index, value in pairs(values) do | |
566 | if value.type == inclinationnumber then | |
567 | return value.strength | |
568 | end | |
569 | end | |
570 | return 0 | |
571 | end | |
572 | ||
573 | function getAttraction(unit) | |
574 | --unit can't be nil. The nothingness where there should be something doesn't have sex, y'see. | |
575 | --Outputs a table of levels of sexual attraction an unit has. | |
576 | -- utilizes getFlag | |
577 | local attraction = {guy = 0, girl = 0} | |
578 | local orientation | |
579 | if unit.sex~=-1 then | |
580 | if getFlag(unit, "status") then | |
581 | --local unit | |
582 | orientation= getFlag(unit.status, "current_soul") and unit.status.current_soul.orientation_flags or false | |
583 | --alas, creatures can be soulless. | |
584 | else | |
585 | --historical unit | |
586 | orientation = unit.orientation_flags | |
587 | ||
588 | end | |
589 | end | |
590 | if orientation then | |
591 | if orientation.romance_male then | |
592 | attraction.guy = 1 | |
593 | elseif orientation.marry_male then | |
594 | attraction.guy = 2 | |
595 | end | |
596 | if orientation.romance_female then | |
597 | attraction.girl = 1 | |
598 | elseif orientation.marry_female then | |
599 | attraction.girl = 2 | |
600 | end | |
601 | end | |
602 | return attraction | |
603 | end | |
604 | ||
605 | -- ======================================== -- | |
606 | -- Tier 3 functions -- | |
607 | -- ======================================== -- | |
608 | ||
609 | function getAromantism(unit) | |
610 | --Takes local unit | |
611 | --returns following series of values | |
612 | local unwillingToFriend, unwillingToLove, unwillingToMarry | |
613 | --utilizes getFlag, getIncinationIfPresent | |
614 | --utilizes these internally: | |
615 | local smittedness, friendly, bonding | |
616 | ||
617 | --failure conditions : hating friendship and having no eligble friends (not certain, might be enough to just have one-sided relation), hating romance, hating marriage. | |
618 | -- unit.status.current_soul.personality.values. | |
619 | -- Type 29: romance, type 2: family, type 3: friendship, type 18: harmony. | |
620 | -- hfunit.info.personality.values - nil for embark dwarves, present on visitors, like visitors can have nil spouse relation but histfig value set | |
621 | -- poults born on-site from embark turkeys don't have hfid or more than 0 values. | |
622 | -- unit.status.current_soul.personality.traits .LOVE_PROPENSITY (if this is high and romance is low can still marry, Putnam suggests from 2015 suggests it is almost the only trait that matters) | |
623 | -- unknown: Type 3 - friendship, .LUST_PROPENSITY, FRIENDLINESS | |
624 | -- unknown: how much traits must differ in general for marriage to happen instead of a grudge. | |
625 | -- also, grudges could be prevented or perhaps later removed by social skills, shared skills and preferences. | |
626 | ||
627 | ||
628 | --local smittedness, friendly, bonding, unwillingToFriend, unWillingToLove, unwillingToMarry | |
629 | ||
630 | smittedness = unit.status.current_soul.personality.traits.LOVE_PROPENSITY | |
631 | -- again, always present on units that are on-site; but obviously histfigs will have to be handled differently | |
632 | ||
633 | friendly =unit.status.current_soul.personality.traits.FRIENDLINESS | |
634 | --FRIENDLINESS. I think I've seen ever dyed-in-the-wool quarrels have friends. | |
635 | ||
636 | bonding = unit.status.current_soul.personality.traits.EMOTIONALLY_OBSESSIVE -- how easily they make connections | |
637 | -- per Sarias's research in 2013, dwarves must be friends before becoming lovers. | |
638 | -- local cheerfulness -- happier dwarves make relationships more easily, buuut everyone is at -99999 anyway | |
639 | -- local lustfulness -- science required to see if it affects whether relationship can form | |
640 | -- local trust -- relationships should be about trust, but lying scumbags can get married too irl. Who knows? | |
641 | -- Eventually, for specific units would have to check how well they match up and return granual value. | |
642 | ||
643 | unwillingToFriend =(getInclinationIfPresent(unit, 3)+friendly+bonding) < friendlinessthreshold and true or false | |
644 | -- avg 100, min -50, max 250 | |
645 | -- currently requires ~roughly two lowest possible and 1 second lowest possible values. | |
646 | -- Starting seven do have friends, which can override this. | |
647 | ||
648 | -- While I've failed to friend off dwarves due personality, | |
649 | -- those dwarves have managed friendships with at least 1 other person, | |
650 | -- and several times have managed a marriage with someone else. | |
651 | -- 18-year old Erush ShoduksÇŽkzul has 3 friends, having 1 lower FRIENDSHIP, bonding | |
652 | ||
653 | unwillingToLove = (getInclinationIfPresent(unit, 29)+smittedness) < loversthreshold and true or false | |
654 | --not using bonding, maybe should. They already have emotional bond with others, though. | |
655 | --50 is average. 14 might be too low, allowing second lowest value on 1 with other average | |
656 | --20 is maximum for non-mentioned propensity and hating even the idea of romance, but can sometimes prevent two 1 worse than average values | |
657 | ||
658 | -- What numercal indicators can I fit into 1 tile, anyway? Blinking, I guess. TWBT would enable gradual color | |
659 | -- blinking has problems at low fps, but viewing unit and unit list have game paused. | |
660 | -- Tests should be done with 50 fps/10 gfps, since those are lowest maximums I know of. | |
661 | -- 30 GFPS can blend-ish, but 10 is blinky. Maybe use -blinking_at input with default value only_if_above_29 | |
662 | -- should check if it blinks on returning to low fps. | |
663 | unwillingToMarry = getInclinationIfPresent(unit, 2) < marriagethreshold and true or false | |
664 | --as long as they don't find family loathsome, it's a-ok. | |
665 | ||
666 | return unwillingToFriend, unwillingToLove, unwillingToMarry | |
667 | end | |
668 | ||
669 | function getSpouseAndLoverLink(unit) | |
670 | -- Currently takes local unit only | |
671 | -- Returns eight values: spouse and lover names, spouse and lover sites, spouse and lover aliveness, spouse and lover gender | |
672 | -- Doesn't check the hf relationships if the hf_only unit doesn't have a spouse in histfig links | |
673 | -- might be an issue if a visitor comes on map, romances someone, and then leaves. Never heard of that happening, but hey | |
674 | -- utilizes getFlag, getSpouseAndLover, dfhack.units.isAlive, dfhack.TranslateName, df.historical_figure.find | |
675 | local spouse,lover | |
676 | local spousesite, loversite = df.global.ui.site_id, df.global.ui.site_id | |
677 | --blanket current site, unless I assign them differently later | |
678 | --this is fine as I have indicator for off-site spouse, not on-site spouse | |
679 | local spouselives, loverlives | |
680 | spouse, lover = getSpouseAndLover(unit) | |
681 | if spouse and getFlag(spouse,"info") then spousesite = spouse.info.unk_14.site end | |
682 | if lover and getFlag(lover,"info") then loversite = lover.info.unk_14.site end | |
683 | local spousename, lovername | |
684 | if spouse == false then | |
685 | for index, relation in pairs(df.historical_figure.find(unit.hist_figure_id).histfig_links) do | |
686 | --Spouse has been culled? Maybe they're single parent. | |
687 | if (relation._type == df.histfig_hf_link_childst) then | |
688 | spousename = "Single parent" | |
689 | spouselives = true --More like that they're not dead. Visual thing. | |
690 | end | |
691 | end | |
692 | elseif spouse then | |
693 | spousename = dfhack.TranslateName(spouse.name) | |
694 | -- here, as spouse without name doesn't have gender either | |
695 | if getFlag(spouse, "flags1") then --local unit | |
696 | spouselives = dfhack.units.isAlive(spouse) | |
697 | else | |
698 | spouselives = spouse.died_year < 0 | |
699 | end | |
700 | end | |
701 | if lover then | |
702 | lovername = dfhack.TranslateName(lover.name) | |
703 | if getFlag(lover, "flags1") then --local unit | |
704 | loverlives = dfhack.units.isAlive(lover) | |
705 | else | |
706 | loverlives = lover.died_year < 0 | |
707 | end | |
708 | end | |
709 | -- lovers can't have children, so it's entirely pointless to speak of lost love. | |
710 | return spousename, lovername, spousesite, loversite, spouselives, loverlives, spouse, lover | |
711 | end | |
712 | ||
713 | function getSymbolizedAnimalPreference(unit, unwrapped) | |
714 | --Returns symbolized pregnancy if animal is pregnant. | |
715 | --Else, returns gender symbol, color: string and number | |
716 | --color is a function whose return value blinks between modes if appropriate. | |
717 | --unwrapped doesn't pass the colors through blinkergenerator. | |
718 | --utilizes getAttraction, isItBird, isItGelded | |
719 | local attraction = getAttraction(unit) | |
720 | local symbolColor = {text, color} | |
721 | local prefColorTable | |
722 | if getPregnancyTimer(unit) > 0 then | |
723 | symbolColor.text = isItBird(unit) and fertileBirdSymbol or pregnantCreatureSymbol | |
724 | symbolColor.color = pregnantColor | |
725 | else | |
726 | symbolColor.text = unit.sex == 1 and | |
727 | singleMaleSymbol or | |
728 | ( unit.sex == 0 and | |
729 | singleFemaleSymbol or "") | |
730 | if unit.sex == -1 or --flesh balls | |
731 | (attraction.guy == 0 and attraction.girl == 0) or --asexual | |
732 | isItGelded(unit) then --some tomcats | |
733 | symbolColor.color = infertileColor | |
734 | return symbolColor | |
735 | --strictly speaking, not necessary, due light gray being default color | |
736 | end | |
737 | prefColorTable = {} | |
738 | if unit.sex == 0 then | |
739 | if attraction.guy > 0 then table.insert(prefColorTable, straightFemaleShade) end | |
740 | if attraction.girl > 0 then table.insert(prefColorTable, gayFemaleShade) end | |
741 | else | |
742 | if attraction.girl > 0 then table.insert(prefColorTable, straightMaleShade) end | |
743 | if attraction.guy > 0 then table.insert(prefColorTable, gayMaleShade) end | |
744 | end | |
745 | end | |
746 | if unwrapped then | |
747 | if prefColorTable and #prefColorTable > 0 then | |
748 | symbolColor.color = prefColorTable | |
749 | end | |
750 | return symbolColor | |
751 | else | |
752 | if prefColorTable then symbolColor.color = blinkergenerator(prefColorTable) end | |
753 | if getPregnancyTimer(unit) > 0 then | |
754 | symbolColor.onhovertext = { | |
755 | color = pregnantColor, | |
756 | text = tostring(math.floor((isItSmart(unit) and 9 or 6) -getPregnancyTimer(unit)/33600)) | |
757 | } | |
758 | end | |
759 | return symbolColor | |
760 | end | |
761 | end | |
762 | ||
763 | function getSymbolizedSpouse(unit) | |
764 | -- Currently takes local unit only | |
765 | -- Returns {} with text and color which are string or function and number or function | |
766 | -- utilizes getSpouseAndLoverLink, getAttraction, getInclinationIfPresent, isItSmart,isItBird, isItGelded | |
767 | local spousename, lovername, spousesite, loversite, spouselives, loverlives, spouse, lover = getSpouseAndLoverLink(unit) | |
768 | local symbolColor = {text, color} | |
769 | ||
770 | ||
771 | local attraction = getAttraction(unit) | |
772 | --plain sexual attraction table | |
773 | --it'd be more compact to code if instead of guy and girl values would use 0 and 1 indices | |
774 | --could call attraction[unit.sex] == 2 to see if they're willing to engage in gay marriage, for example | |
775 | --however, code is more self-explanatory with variables having names that explain what they're for | |
776 | local unwillingToFriend, unwillingToLove, unwillingToMarry | |
777 | if (attraction.guy+attraction.girl) == 0 then | |
778 | unwillingToFriend, unwillingToLove, unwillingToMarry = true, true, true | |
779 | else | |
780 | unwillingToFriend, unwillingToLove, unwillingToMarry = getAromantism(unit) | |
781 | end | |
782 | --series of disqualifying boolean values; though if orientation is already zero better not check | |
783 | ||
784 | local symbolTable = {} | |
785 | local colorTable = {} | |
786 | if getPregnancyTimer(unit) > 0 and | |
787 | isItSmart(unit) then --necessary due otherwise doubling up on the indicator with animal preferences. | |
788 | --Normally, would lose nothing by having pregnancy highest hiearchy, could just check first and skip the above | |
789 | --However, in cases of modding or father dying in battle (such as in fucduck's elven outpost) info is lost | |
790 | if isItBird(unit) then | |
791 | --possible with bird-women adventurers joining the fortress | |
792 | table.insert(symbolTable,fertileBirdSymbol) | |
793 | else | |
794 | table.insert(symbolTable,pregnantCreatureSymbol) | |
795 | end | |
796 | table.insert(colorTable,pregnantColor) | |
797 | end | |
798 | ||
799 | if not isItSmart(unit) then --it's an animal | |
800 | local animalprefs = getSymbolizedAnimalPreference(unit,true) | |
801 | table.insert(symbolTable, animalprefs.text) | |
802 | if type(animalprefs.color) == "table" then | |
803 | table.insert(colorTable, animalprefs.color[0]) | |
804 | table.insert(colorTable, animalprefs.color[1]) --two gender prefs at most. | |
805 | table.insert(symbolTable, animalprefs.text) --going to mess up timing otherwise. | |
806 | else | |
807 | table.insert(colorTable, animalprefs.color) | |
808 | end | |
809 | end | |
810 | if not lovername and | |
811 | (not spousename or spousename == "Single parent") then | |
812 | if isItSmart(unit) then --otherwise already handled earlier, don't need to add anything. | |
813 | table.insert(symbolTable, | |
814 | ((unit.sex == 0) and singleFemaleSymbol or (unit.sex == 1 and singleMaleSymbol or ""))) | |
815 | --creatures without gender don't get a symbol. | |
816 | if (attraction.guy == 0 and attraction.girl == 0) or --asexual | |
817 | isItGelded(unit) or -- gelded. Aw. | |
818 | unwillingToLove or --aromantic. Requires soul. | |
819 | sex == -1 then --creatures like iron men and bronze colossi indicate that genderless creatures can't breed | |
820 | ||
821 | table.insert(colorTable, infertileColor) | |
822 | ||
823 | else | |
824 | if unit.sex == 0 then | |
825 | if attraction.girl > 0 then | |
826 | table.insert(colorTable, | |
827 | (gayFemaleShade - 8*( (unwillingToMarry or attraction.girl == 1) and 1 or 0 ))) | |
828 | --darker shade for lover-only relationships | |
829 | end | |
830 | if attraction.guy > 0 then | |
831 | table.insert(colorTable, | |
832 | (straightFemaleShade - 8*( (unwillingToMarry or attraction.guy == 1) and 1 or 0 ))) | |
833 | end | |
834 | else | |
835 | if attraction.girl > 0 then | |
836 | table.insert(colorTable, | |
837 | (straightMaleShade - 8*( (unwillingToMarry or attraction.girl == 1) and 1 or 0 ))) | |
838 | end | |
839 | if attraction.guy > 0 then | |
840 | table.insert(colorTable, | |
841 | (gayMaleShade - 8*( (unwillingToMarry or attraction.guy == 1) and 1 or 0 ))) | |
842 | end | |
843 | end | |
844 | if #colorTable>#symbolTable and --Our table has more colors than symbols. Noprobs, | |
845 | #symbolTable>1 then --unless there's pregnant single present | |
846 | table.insert(symbolTable, symbolTable[#symbolTable]) | |
847 | --Pregnant singles screw up timing, unless we double up on last symbol. | |
848 | end | |
849 | end | |
850 | end | |
851 | else | |
852 | if spousename and spousename ~= "Single parent" then | |
853 | table.insert(symbolTable, marriedSymbol) | |
854 | --table for on-site, alive, not infertile spouse | |
855 | -- Using hiearchy: | |
856 | -- Dead > Infertile (unless gay) > Offsite > Normal bright color. | |
857 | local spousecolor = (unit.sex == 0) and | |
858 | (spouse.sex==1 and straightFemaleShade or gayFemaleShade) or | |
859 | (spouse.sex==0 and straightMaleShade or gayMaleShade) | |
860 | -- live marriage | |
861 | spousecolor = df.global.ui.site_id == spousesite and spousecolor or offsiteShade | |
862 | --spouse is offsite. Can have problems with divorcing. | |
863 | spousecolor = (isItGelded(unit) or isItGelded(spouse)) and | |
864 | not (unit.sex == spouse.sex) | |
865 | and infertileColor or spousecolor | |
866 | --spouse is infertile and not gay-only marriage | |
867 | spousecolor = spouselives and spousecolor or deadColor | |
868 | table.insert(colorTable, spousecolor) | |
869 | end | |
870 | if lovername then | |
871 | table.insert(symbolTable, loversSymbol) | |
872 | -- Two types of lovers: ones willing to progress to marriage, ones unwilling | |
873 | -- The unwilling ones get darker shade | |
874 | -- Both parties must be willing | |
875 | -- Lovers and spouses may also be off-site, dead or infertile. | |
876 | -- While can display all with blinky things, should minimize needless UI churn. | |
877 | -- Dead > Infertile (unless gay) > Offsite > Unwilling to progress to marriage > Normal bright color. | |
878 | local lovercolor = (unit.sex == 0) and | |
879 | (lover.sex==0 and gayFemaleShade or straightFemaleShade) or | |
880 | (lover.sex==0 and straightMaleShade or gayMaleShade) | |
881 | --baseline is willing to marry | |
882 | lovercolor = (attraction[((lover.sex==1) and "guy" or "girl")] < 2 or | |
883 | getAttraction(lover)[((unit.sex==1) and "guy" or "girl")] < 2 or | |
884 | unwillingToMarry or | |
885 | (getInclinationIfPresent(lover, 2) < marriagethreshold and true or false)) and | |
886 | (lovercolor - 8) or lovercolor | |
887 | -- if the unit or their lover has personality or attraction failure, the relationship will not progress | |
888 | lovercolor = df.global.ui.site_id == loversite and lovercolor or offsiteShade | |
889 | --lover is offsite. Can have problems with divorcing. | |
890 | --Happens mostly in case of visitors or married migrants. | |
891 | --Issue: Either a possible false hope of offsite lover eventually arriving. | |
892 | -- or a false indicator of lover being on-site. | |
893 | --Blinking and blurring could solve this, but not hiearchy. | |
894 | lovercolor = (isItGelded(unit) or isItGelded(lover)) and | |
895 | not (unit.sex == lover.sex) | |
896 | and infertileColor or lovercolor | |
897 | --lover is infertile and not gay-only marriage | |
898 | lovercolor = loverlives and lovercolor or deadColor | |
899 | --lover is dead | |
900 | table.insert(colorTable, lovercolor) | |
901 | end | |
902 | ||
903 | end | |
904 | ||
905 | symbolColor.text = blinkergenerator(symbolTable) | |
906 | ||
907 | symbolColor.color = blinkergenerator(colorTable) | |
908 | ||
909 | if getPregnancyTimer(unit) > 0 then | |
910 | symbolColor.onhovertext = { | |
911 | color = pregnantColor, | |
912 | text = tostring(math.floor((isItSmart(unit) and 9 or 6) -getPregnancyTimer(unit)/33600)) | |
913 | --Needs to be converted to string since #text is called for width | |
914 | } | |
915 | end | |
916 | ||
917 | return symbolColor | |
918 | ||
919 | end | |
920 | ||
921 | function getSpouseText(unit) | |
922 | -- Takes local unit, returns single line string that is "" if there's no romantic relation | |
923 | local spousename, lovername, spousesite, loversite, spouselives, loverlives = getSpouseAndLoverLink(unit) | |
924 | --An unit can have both lover and spouse in vanilla with retirement shenanigans | |
925 | local returnstring = "" | |
926 | if spousename then | |
927 | --spouse matters more so goes first. | |
928 | returnstring = returnstring .. marriedSymbol | |
929 | if spousesite ~= df.global.ui.site_id then | |
930 | returnstring = returnstring .. offsite | |
931 | end | |
932 | if spouselives then | |
933 | returnstring = returnstring .. spousename | |
934 | else | |
935 | returnstring = returnstring .. diedS .. spousename ..diedE | |
936 | end | |
937 | end | |
938 | if lovername then | |
939 | returnstring = returnstring .. loversSymbol | |
940 | if loversite ~= df.global.ui.site_id then | |
941 | returnstring = returnstring .. offsite | |
942 | end | |
943 | if loverlives then | |
944 | returnstring = returnstring .. lovername | |
945 | else | |
946 | returnstring = returnstring .. diedS .. lovername ..diedE | |
947 | end | |
948 | end | |
949 | ||
950 | return returnstring | |
951 | end | |
952 | ||
953 | function getSuitableMatches(unit, unitlist, joblist) | |
954 | --Takes a local unit, local unitlist and local joblist | |
955 | --Returns an unitlist that includes unit and then all it's suitable candidates. | |
956 | --And joblist that has only those same indices, as unitlist viewscreen uses that data. | |
957 | --utilizes areCompatible | |
958 | local matchlist, jobmatchlist = {}, {} | |
959 | --The unit we've checking always comes first | |
960 | for index=0, #unitlist-1 do | |
961 | if (unit == unitlist[index]) or -- self | |
962 | areCompatible(unit, unitlist[index]) then --suitable match | |
963 | matchlist[index] = true | |
964 | jobmatchlist[index] = true | |
965 | end | |
966 | end | |
967 | return matchlist, jobmatchlist | |
968 | end | |
969 | ||
970 | -- ======================================== -- | |
971 | -- Dynamic text {} output functions -- | |
972 | -- ======================================== -- | |
973 | ||
974 | ||
975 | function getViewUnitPairs(unit) | |
976 | local returnpairs = {} | |
977 | local pregnantRichText = {} | |
978 | pregnantRichText.text = getPregnancyText(unit) | |
979 | --bit of fluff for pregnancy | |
980 | pregnantRichText.color = pregnantColor | |
981 | table.insert(returnpairs, pregnantRichText) | |
982 | --First line is for pregnancy, second line is for spouse/lover | |
983 | local spouseRichText = {} | |
984 | --Also gets lover text | |
985 | spouseRichText.text = getSpouseText(unit) | |
986 | function nabNonPregnantColor(unit) | |
987 | --I want spouse text to be coloured appropriately for the relationship. | |
988 | local previouscolor, returncolor | |
989 | local basecolor = getSymbolizedSpouse(unit).color | |
990 | if type(basecolor) == "number" then | |
991 | return basecolor | |
992 | else | |
993 | previouscolor = getSymbolizedSpouse(unit).color() | |
994 | function returnfunction() | |
995 | --In case the romantic relationship is blinky - typically that means pregnancy and/or lover. | |
996 | local unit = unit | |
997 | returncolor = getSymbolizedSpouse(unit).color() | |
998 | --Might as well use code already in place | |
999 | if returncolor == pregnantColor then | |
1000 | --Of course, if the unit is pregnant, that's shown above, not here. | |
1001 | --Visual bug: Can still start out as pregnant color. | |
1002 | return previouscolor | |
1003 | else | |
1004 | previouscolor = returncolor | |
1005 | return returncolor | |
1006 | end | |
1007 | end | |
1008 | return returnfunction | |
1009 | end | |
1010 | end | |
1011 | ||
1012 | if spouseRichText.text then | |
1013 | spouseRichText.color = nabNonPregnantColor(unit) | |
1014 | end | |
1015 | table.insert(returnpairs, spouseRichText) | |
1016 | return returnpairs | |
1017 | end | |
1018 | ||
1019 | local HavePrinted, oldCitizens, oldJobs, oldIndices = false | |
1020 | function showUnitPairs(unit) | |
1021 | local unitscreen = getBottomMostViewscreenWithFocus("unitlist", df.global.gview.view.child.child) | |
1022 | if not HavePrinted then | |
1023 | oldCitizens, oldJobs, oldIndices = {}, {}, {} | |
1024 | local index = 0 | |
1025 | while getFlag(unitscreen.units.Citizens, index) do | |
1026 | if (unit == unitscreen.units.Citizens[index]) or -- self | |
1027 | areCompatible(unit, unitscreen.units.Citizens[index]) then | |
1028 | index = 1+index | |
1029 | else | |
1030 | table.insert(oldCitizens, unitscreen.units.Citizens[index]) | |
1031 | table.insert(oldIndices, index) | |
1032 | oldJobs[#oldIndices] = unitscreen.jobs.Citizens[index] | |
1033 | unitscreen.units.Citizens:erase(index) | |
1034 | unitscreen.jobs.Citizens:erase(index) | |
1035 | end | |
1036 | end | |
1037 | HavePrinted = true | |
1038 | for ci = 0, #unitscreen.units.Citizens -1 do | |
1039 | if (unit == unitscreen.units.Citizens[ci]) then | |
1040 | unitscreen.cursor_pos.Citizens = ci | |
1041 | break; | |
1042 | end | |
1043 | end | |
1044 | end | |
1045 | end | |
1046 | ||
1047 | function hideUnitPairs() | |
1048 | if HavePrinted then | |
1049 | local unitscreen = getBottomMostViewscreenWithFocus("unitlist", df.global.gview.view.child.child) | |
1050 | for i=#oldCitizens, 1, -1 do | |
1051 | unitscreen.units.Citizens:insert(oldIndices[i], oldCitizens[i]) | |
1052 | unitscreen.jobs.Citizens:insert(oldIndices[i], oldJobs[i]) | |
1053 | end | |
1054 | HavePrinted = false | |
1055 | end | |
1056 | end | |
1057 | ||
1058 | ||
1059 | local pagelength, currentpage, visitorpopupdims, symbolizedList = df.global.gps.dimy - 9, 0, {x = -30, y = 4} | |
1060 | -- pagelength needs to be exposed due manipulators having two lines shorter pages than standard view. | |
1061 | -- Also due resizing. | |
1062 | -- currentpage needs to be exposed due traversing the lists. | |
1063 | function getUnitListPairs() | |
1064 | local unitscreen = getBottomMostViewscreenWithFocus("unitlist", df.global.gview.view.child.child) | |
1065 | --note: Counts from 0, unlike lua tables | |
1066 | local returntable = {} | |
1067 | local cursorposition, unitlist, iter | |
1068 | if unitscreen.page == 0 then --Citizen list | |
1069 | cursorposition = unitscreen.cursor_pos.Citizens | |
1070 | currentpage = math.floor(cursorposition / pagelength) | |
1071 | cursorposition = cursorposition % pagelength --cursor position within a page | |
1072 | unitlist = unitscreen.units.Citizens | |
1073 | for iter = (0+currentpage*pagelength), | |
1074 | ( (((1+currentpage)*pagelength-1)<(#unitlist -1)) and | |
1075 | ((1+currentpage)*pagelength-1) or | |
1076 | (#unitlist -1)) do | |
1077 | table.insert(returntable, getSymbolizedSpouse(unitlist[iter])) | |
1078 | returntable[#returntable].onclick = function() | |
1079 | local tile = nil | |
1080 | if dfhack.gui.getCurFocus():find("unitlist") then tile = dfhack.screen.readTile(39,df.global.gps.dimy-2) end | |
1081 | --search plugin support | |
1082 | if not tile or (tile and tile.ch == 95 and tile.fg == 2 or tile.ch == 0) then | |
1083 | if HavePrinted then hideUnitPairs() else | |
1084 | showUnitPairs(unitlist[iter]) end | |
1085 | writeoverTable(symbolizedList, getUnitListPairs()) | |
1086 | end | |
1087 | end | |
1088 | end | |
1089 | elseif unitscreen.page == 1 or unitscreen.page == 2 then --Livestock or Others | |
1090 | local pageName = (unitscreen.page == 1) and "Livestock" or "Others" | |
1091 | cursorposition = unitscreen.cursor_pos[pageName] | |
1092 | currentpage = math.floor(cursorposition / pagelength) | |
1093 | cursorposition = cursorposition % pagelength --cursor position within a page | |
1094 | unitlist = unitscreen.units[pageName] | |
1095 | for iter = (0+currentpage*pagelength), | |
1096 | ( (((1+currentpage)*pagelength-1)<(#unitlist -1)) and | |
1097 | ((1+currentpage)*pagelength-1) or | |
1098 | (#unitlist -1)) do | |
1099 | local unit = unitlist[iter] | |
1100 | -- What goes on with combination of pet and intelligent? | |
1101 | -- Tests reveals failure to bear children and love for even histfigged intelligent dogs | |
1102 | -- Perhaps only dumb pets of non-your civ can screw. | |
1103 | -- Of course, might want accurate indicator then anyway, as you might make them your civ members | |
1104 | ||
1105 | --Near as I can tell, for historical figures: | |
1106 | -- pet 1 0 | |
1107 | -- smart 1 - Fertile | |
1108 | -- 0 Fer Fertile(trogs, trolls) | |
1109 | ||
1110 | if isItSmart(unit) then | |
1111 | if (df.global.world.raws.creatures.all[unit.race].caste[0].flags.PET or | |
1112 | df.global.world.raws.creatures.all[unit.race].caste[0].flags.PET_EXOTIC) | |
1113 | -- Intelligent pets seem unable to breed | |
1114 | or unit.hist_figure_id < 0 then | |
1115 | --I think marriage requires being historical, | |
1116 | -- collaborated by historical turkeys being able to marry during retirement | |
1117 | table.insert(returntable,getGenderInInfertileColor(unit)) | |
1118 | end | |
1119 | if unit.hist_figure_id > -1 then | |
1120 | table.insert(returntable, getSymbolizedSpouse(unit)) | |
1121 | returntable[#returntable].onclick = function() | |
1122 | --Something to display visitor dating pool | |
1123 | --creates several tables per call, but one doesn't usually call it. | |
1124 | local fortressmatches = getSuitableMatches(unit, unitscreen.units.Citizens, unitscreen.jobs.Others) | |
1125 | --Lets find the candidates for a given visitor | |
1126 | -- this is a table of index, true values, not units. | |
1127 | -- print("entered onclick " .. getLen(fortressmatches)) | |
1128 | if getLen(fortressmatches) > 0 then | |
1129 | local fortressmatchlist = {} | |
1130 | for index, value in pairs(fortressmatches) do | |
1131 | table.insert(fortressmatchlist, getSymbolizedSpouse(unitscreen.units.Citizens[index])) | |
1132 | fortressmatchlist[#fortressmatchlist].notEndOfLine = true | |
1133 | table.insert(fortressmatchlist, {text = dfhack.TranslateName(unitscreen.units.Citizens[index].name)}) | |
1134 | --Lets convert the unit to nicely colored name to display. | |
1135 | end | |
1136 | --print(#fortressmatchlist) | |
1137 | local popupscreen = screenconstructor.getScreen( | |
1138 | fortressmatchlist, | |
1139 | {x = math.floor((df.global.gps.dimx-screenconstructor.getLongestLength(fortressmatchlist,"text"))/2), | |
1140 | y = math.floor((df.global.gps.dimy-screenconstructor.getHeight(fortressmatchlist))/2)}, | |
1141 | {x = math.floor((df.global.gps.dimx-screenconstructor.getLongestLength(fortressmatchlist,"text"))/2 -1), | |
1142 | y = math.floor((df.global.gps.dimy-screenconstructor.getHeight(fortressmatchlist))/2 -1), | |
1143 | width = 2+ screenconstructor.getLongestLength(fortressmatchlist,"text"), | |
1144 | height = 2+ screenconstructor.getHeight(fortressmatchlist)} | |
1145 | ) | |
1146 | popupscreen:show() | |
1147 | popupscreen.onInput = function() popupscreen:dismiss() end | |
1148 | --There's no input on which I wont want to dismiss the screen. | |
1149 | end | |
1150 | end | |
1151 | end | |
1152 | else | |
1153 | --It doesn't matter if a pet/troglodyte has killed someone or not, they'll breed either way. | |
1154 | if unit.hist_figure_id > -1 then | |
1155 | --Nonetheless, historical pets can marry in retired forts | |
1156 | table.insert(returntable, getSymbolizedSpouse(unit)) | |
1157 | --getSymbolizedSpouse calls on below functions for not smart creatures | |
1158 | else | |
1159 | table.insert(returntable, getSymbolizedAnimalPreference(unit)) | |
1160 | end | |
1161 | end | |
1162 | ||
1163 | --[[ Deprecated logic based on previously believed data. | |
1164 | if unit.hist_figure_id > 0 then | |
1165 | --Can be married. Visitor, murderer, pet... | |
1166 | if isItSmart(unit) and | |
1167 | (df.global.world.raws.creatures.all[unit.race].caste[0].flags.PET or | |
1168 | df.global.world.raws.creatures.all[unit.race].caste[0].flags.PET_EXOTIC) then | |
1169 | -- | |
1170 | table.insert(returntable,getGenderInInfertileColor(unit)) | |
1171 | ||
1172 | end | |
1173 | --Distinction between being smart or not (aka free love) is handled inside. | |
1174 | table.insert(returntable, getSymbolizedSpouse(unit)) | |
1175 | else | |
1176 | if isItSmart(unit) then | |
1177 | --Wild animal men and non-historical gremlins | |
1178 | --Infertile until they're able to marry - like after having killed someone important | |
1179 | table.insert(returntable,getGenderInInfertileColor(unit)) | |
1180 | else | |
1181 | --normal turkeys brought on embark and wild animals. | |
1182 | table.insert(returntable, getSymbolizedAnimalPreference(unit)) | |
1183 | ||
1184 | end | |
1185 | end]]-- | |
1186 | end | |
1187 | ||
1188 | end | |
1189 | return returntable | |
1190 | end | |
1191 | ||
1192 | ||
1193 | -- ======================================== -- | |
1194 | -- Initialization and placement -- | |
1195 | -- ======================================== -- | |
1196 | ||
1197 | local unitscreen = getBottomMostViewscreenWithFocus("unitlist",df.global.gview.view.child.child) | |
1198 | --gets unitlist viewscreen | |
1199 | local viewscreen | |
1200 | if unitscreen then | |
1201 | symbolizedList = getUnitListPairs() | |
1202 | local list_rect = {x = 1, y = 4, width = 1, height = pagelength} | |
1203 | local newscreen = screenconstructor.getScreen(symbolizedList, list_rect, nil) | |
1204 | newscreen:show() | |
1205 | local listtable = {} | |
1206 | listtable[0] = "Citizens" | |
1207 | listtable[1] = "Livestock" | |
1208 | listtable[2] = "Others" | |
1209 | listtable[3] = "Dead" | |
1210 | local oldwhichlist,lenlist, doNotUseBase, manitimeout, manicursorpos = listtable[unitscreen.page] | |
1211 | ||
1212 | local manipulatorkeytable = {} | |
1213 | local upkeys = {CURSOR_UP = true} | |
1214 | local downkeys = {CURSOR_DOWN = true} | |
1215 | newscreen.onclick = function() | |
1216 | local tile = nil | |
1217 | if dfhack.gui.getCurFocus():find("unitlist") then tile = dfhack.screen.readTile(39,df.global.gps.dimy-2) end | |
1218 | --search plugin support | |
1219 | --if prevents failing to work in manipulator/main | |
1220 | if not tile or (tile and tile.ch == 95 and tile.fg == 2 or tile.ch == 0) then | |
1221 | if HavePrinted then hideUnitPairs() end --restoring units on random clicks | |
1222 | writeoverTable(symbolizedList, getUnitListPairs()) --restoring appearance too | |
1223 | end | |
1224 | end | |
1225 | local baseonInput = newscreen.onInput | |
1226 | local function onListInput(self, keys) | |
1227 | lenlist = #(unitscreen.units[oldwhichlist]) | |
1228 | ||
1229 | if keys.LEAVESCREEN and 2 == dfhack.screen.readTile(29,df.global.gps.dimy-2).fg then | |
1230 | --Search plugin support. Tbh, would have been ultimately easier to disable dismiss_on_zoom | |
1231 | local dismissfunc = self.dismiss | |
1232 | self.dismiss = function () end | |
1233 | dfhack.timeout(1, "frames", function() self.dismiss = dismissfunc end ) | |
1234 | end | |
1235 | ||
1236 | if not doNotUseBase then baseonInput(self, keys) end | |
1237 | local manipulatorscript = getBottomMostViewscreenWithFocus("dfhack/lua/manipulator",df.global.gview.view.child.child) | |
1238 | --Lua manipulator viewscreen is present | |
1239 | local whichlist = listtable[unitscreen.page] | |
1240 | --duplicated from indicator_screen (ugh). Feels like there should be a way to better determine this. | |
1241 | ||
1242 | if (currentpage ~= math.floor(unitscreen.cursor_pos[whichlist]/ pagelength) and whichlist ~= "Dead") or | |
1243 | --up-down paging | |
1244 | (oldwhichlist and oldwhichlist ~= whichlist) or --left-right paging | |
1245 | (lenlist ~= #(unitscreen.units[oldwhichlist])) then --search plugin support | |
1246 | oldwhichlist = whichlist | |
1247 | doNotUseBase = true | |
1248 | lenlist = #(unitscreen.units[oldwhichlist]) | |
1249 | writeoverTable(symbolizedList, getUnitListPairs()) | |
1250 | dfhack.timeout(2, "frames", function() doNotUseBase = false end) | |
1251 | --Something weird happens with writeoverTable here, where it sometimes parses input twice. | |
1252 | --In the absence of other solutions, merely avoid relaying input for two frames. | |
1253 | end | |
1254 | ||
1255 | function mv_cursor(keys) | |
1256 | -- Function for the purpose of moving cursor alongside the manipulator. | |
1257 | -- They don't do this natively - a bugging disrepacy. | |
1258 | if keys.CURSOR_UP or keys.CURSOR_DOWN then | |
1259 | unitscreen.cursor_pos[whichlist] = --set new cursor position | |
1260 | (unitscreen.cursor_pos[whichlist] +(keys.CURSOR_DOWN and 1 or -1)) --to 1 up or down from previous | |
1261 | % #(unitscreen.units[whichlist]) --with overflow accounted for. | |
1262 | end | |
1263 | end | |
1264 | ||
1265 | --manipulator/main.lua scrolls differently than default interface, got to handle it | |
1266 | --TODO: fix the mess with numpad keys and manipulator/main.lua | |
1267 | if manipulatorscript and manipulatorscript.breakdown_level ~= 2 then | |
1268 | --Finds manipulator here on both Alt-L and Escape back out | |
1269 | --breakdown level check prevents that. | |
1270 | if #(unitscreen.units[whichlist]) > (df.global.gps.dimy -11) then | |
1271 | --multi-page manipulator unitlist handling | |
1272 | if pagelength ~= lenlist then | |
1273 | --Instead of using a sublist that is refreshed manipulator/main uses whole list that is moved | |
1274 | pagelength = lenlist | |
1275 | writeoverTable(symbolizedList, getUnitListPairs()) | |
1276 | self:adjustDims(true,nil,nil,nil, pagelength) | |
1277 | self.frame_body.clip_y2 = df.global.gps.dimy - 8 | |
1278 | manicursorpos = unitscreen.cursor_pos[whichlist] > (df.global.gps.dimy - 12) and | |
1279 | (df.global.gps.dimy - 12) or unitscreen.cursor_pos[whichlist] | |
1280 | self.frame_body.y1 = 4 - (unitscreen.cursor_pos[whichlist] > (df.global.gps.dimy - 12) and | |
1281 | unitscreen.cursor_pos[whichlist] - (df.global.gps.dimy - 12) or | |
1282 | 0) | |
1283 | end | |
1284 | --scrolling occurs if manipulator's cursor position has reached the edge and tries to keep going. | |
1285 | --Manipulator's initial cursor position is either current cursor position or bottom, whichever is smaller | |
1286 | --Successive changes can divorce the two, so need to have internal check. | |
1287 | ||
1288 | --Two functions to follow the cursor position of manipulator | |
1289 | function manidown() | |
1290 | if manicursorpos ~= (df.global.gps.dimy - 12) then | |
1291 | manicursorpos = 1 + manicursorpos | |
1292 | elseif manicursorpos == (df.global.gps.dimy - 12) then | |
1293 | if (unitscreen.cursor_pos[whichlist] +1 ) ~= #(unitscreen.units[whichlist]) then | |
1294 | self.frame_body.y1 = -1 + self.frame_body.y1 | |
1295 | else | |
1296 | self.frame_body.y1 = 4 | |
1297 | manicursorpos = 0 | |
1298 | end | |
1299 | end | |
1300 | end | |
1301 | function maniup() | |
1302 | if manicursorpos ~= 0 then | |
1303 | manicursorpos = -1 + manicursorpos | |
1304 | elseif manicursorpos == 0 then | |
1305 | if unitscreen.cursor_pos[whichlist] ~= 0 then | |
1306 | self.frame_body.y1 = 1 + self.frame_body.y1 | |
1307 | else | |
1308 | self.frame_body.y1 = (df.global.gps.dimy -7) - #(unitscreen.units[whichlist]) | |
1309 | manicursorpos = (df.global.gps.dimy - 12) | |
1310 | end | |
1311 | end | |
1312 | end | |
1313 | ||
1314 | --manipulator/main allows shift+up/down scrolling, which has unique behaviour | |
1315 | if hasKeyOverlap(keys, downkeys) then | |
1316 | if not hasKeyOverlap(keys, {["_FAST"] = true}) then | |
1317 | manidown() | |
1318 | else | |
1319 | for i=1, 10 do | |
1320 | if (unitscreen.cursor_pos[whichlist] +1 ) ~= #(unitscreen.units[whichlist]) then | |
1321 | manidown() | |
1322 | mv_cursor(downkeys) | |
1323 | else | |
1324 | if i == 1 then | |
1325 | manidown() | |
1326 | mv_cursor(downkeys) | |
1327 | end | |
1328 | break; | |
1329 | end | |
1330 | end | |
1331 | end | |
1332 | end | |
1333 | if hasKeyOverlap(keys, upkeys) then | |
1334 | if not hasKeyOverlap(keys, {["_FAST"] = true}) then | |
1335 | maniup() | |
1336 | else | |
1337 | for i=1, 10 do | |
1338 | if unitscreen.cursor_pos[whichlist] ~= 0 then | |
1339 | maniup() | |
1340 | mv_cursor(upkeys) | |
1341 | else | |
1342 | if i == 1 then | |
1343 | maniup() | |
1344 | mv_cursor(upkeys) | |
1345 | end | |
1346 | break; | |
1347 | end | |
1348 | end | |
1349 | end | |
1350 | end | |
1351 | end | |
1352 | mv_cursor(keys) --adjust outside cursor, does nothing on shift-scrolling | |
1353 | if df.global.gps.mouse_x == 1 and --clicked on the indicator line | |
1354 | (keys._MOUSE_L or keys._MOUSE_L_DOWN) then | |
1355 | if manipulatorscript and not manitimeout then | |
1356 | manitimeout = true | |
1357 | --timeout necessary due otherwise causing errors with multiple rapid commands | |
1358 | self._native.parent.breakdown_level = 2 | |
1359 | self._native.parent.parent.child = self._native.parent.child | |
1360 | self._native.parent = self._native.parent.parent | |
1361 | dfhack.timeout(2, "frames", function() | |
1362 | dfhack.run_command("gui/indicator_screen execute_hook manipulator/main") | |
1363 | end) | |
1364 | dfhack.timeout(2, "frames", function() manitimeout = false end) | |
1365 | end | |
1366 | end | |
1367 | elseif manipulatorscript and manipulatorscript.breakdown_level == 2 then | |
1368 | if keys.LEAVESCREEN then | |
1369 | hideUnitPairs() | |
1370 | dfhack.run_command("relations-indicator") | |
1371 | end | |
1372 | if keys.UNITJOB_ZOOM_CRE then | |
1373 | dfhack.timeout(4, "frames", function() dfhack.run_command("relations-indicator") end) | |
1374 | end | |
1375 | end | |
1376 | end | |
1377 | newscreen.onInput = onListInput | |
1378 | ||
1379 | local baseOnResize = newscreen.onResize | |
1380 | ||
1381 | function onListResize(self) | |
1382 | -- Unlike with View-unit, the data might change depending on the size of the window. | |
1383 | baseOnResize(self) | |
1384 | if pagelength ~= (df.global.gps.dimy - 9) then | |
1385 | --If window length changed, better refresh the data. | |
1386 | pagelength = df.global.gps.dimy - 9 | |
1387 | writeoverTable(symbolizedList, getUnitListPairs()) | |
1388 | self:adjustDims(true,list_rect.x, list_rect.y,list_rect.width, pagelength) | |
1389 | --Not adjusting height here would result in situation where making screen shorter works, but taller not. | |
1390 | end | |
1391 | end | |
1392 | ||
1393 | newscreen.onResize = onListResize | |
1394 | ||
1395 | -- ======================================== -- | |
1396 | -- View-unit section -- | |
1397 | -- ======================================== -- | |
1398 | ||
1399 | ||
1400 | else | |
1401 | local function viewunit() | |
1402 | viewscreen = dfhack.gui.getCurViewscreen() | |
1403 | local unit = dfhack.gui.getSelectedUnit() | |
1404 | local symbolizedSingle = getViewUnitPairs(unit) | |
1405 | local view_rect = {x = (-30 -(screenconstructor.isWideView() and 24 or 0)), y = 17, width = 28, height = 2} | |
1406 | local newscreen = screenconstructor.getScreen(symbolizedSingle, view_rect, nil) | |
1407 | newscreen:show() | |
1408 | if not dfhack.gui.getFocusString(viewscreen) | |
1409 | :find("dwarfmode/ViewUnits/Some/General") then | |
1410 | --Can enter in inventory view with v if one previously exited inventory view | |
1411 | newscreen:removeFromView() --Gotta hide in that case | |
1412 | end | |
1413 | local baseonInput = newscreen.onInput | |
1414 | local function onViewInput(self, keys) | |
1415 | --handling changing the menu width and units: | |
1416 | --Capturing the state before it changes: | |
1417 | local oldUnit, oldScreen | |
1418 | local sameUnit, sameScreen = true, true | |
1419 | --Tab changing menu width is handled below. | |
1420 | --storing pre-keypress identifiers for unit and viewscreen state | |
1421 | if not keys.CHANGETAB then | |
1422 | oldUnit = df.global.ui_selected_unit | |
1423 | oldScreen = dfhack.gui.getFocusString(viewscreen) | |
1424 | --Merely checking viewscreen match will not work, given that sideview only modifies existing screen | |
1425 | end | |
1426 | ||
1427 | baseonInput(self,keys) --Doing baseline housekeeping and passing input to parent | |
1428 | ||
1429 | --Finding out if anything changed after parent got the input | |
1430 | if not keys.CHANGETAB then | |
1431 | sameUnit = (oldUnit == df.global.ui_selected_unit) | |
1432 | --could also use dfhack.gui.getSelectedUnit() | |
1433 | sameScreen = (oldScreen == dfhack.gui.getFocusString(viewscreen)) | |
1434 | end | |
1435 | ||
1436 | if keys.CHANGETAB then | |
1437 | --Tabbing moves around the sideview, so got to readjust screen position. | |
1438 | view_rect.x = -30 -(screenconstructor.isWideView() and 24 or 0) | |
1439 | self:adjustDims(true, view_rect.x) | |
1440 | --unlike text tables, position tables aren't dynamic, to allow them to be incomplete | |
1441 | end | |
1442 | ||
1443 | --If unit changed, got to replace the indicator text | |
1444 | if not sameUnit then | |
1445 | writeoverTable(symbolizedSingle,getViewUnitPairs(df.global.world.units.active[df.global.ui_selected_unit])) | |
1446 | elseif not sameScreen then | |
1447 | --Don't want to display the screen if there isn't an unit present, but don't want to spam blinks either | |
1448 | if not dfhack.gui.getFocusString(viewscreen) | |
1449 | :find("dwarfmode/ViewUnits/Some/General") then | |
1450 | --Different screen doesn't mean it's different in same way - need to check here too. | |
1451 | self:removeFromView() | |
1452 | else | |
1453 | --It's general, so better fix it...Thoug well - should change mostly nothing | |
1454 | self:adjustDims(true, view_rect.x, view_rect.y, view_rect.width, view_rect.height) | |
1455 | self.signature = true | |
1456 | end | |
1457 | end | |
1458 | ||
1459 | end | |
1460 | ||
1461 | newscreen.onInput = onViewInput | |
1462 | ||
1463 | end | |
1464 | if dfhack.gui.getCurFocus():find("dwarfmode/ViewUnits/Some") | |
1465 | then viewunit() | |
1466 | else dfhack.timeout(2, "frames", function() | |
1467 | if getBottomMostViewscreenWithFocus("dwarfmode/ViewUnits/Some", df.global.gview.view.child) then | |
1468 | viewunit() | |
1469 | end | |
1470 | end) | |
1471 | end | |
1472 | end |