Added many forum scripts, and a makeshift onReport/onStrike event that will be merged into eventful/EventManager later.
parent
f5de76e2cc
commit
596ab0e1b8
@ -0,0 +1,74 @@
|
||||
--onReport.lua
|
||||
--author expwnent
|
||||
--contains the "ON_REPORT" event: triggered when there is a new report in df.global.world.status.reports
|
||||
|
||||
--example
|
||||
--local onReport = require 'plugins.onReport'
|
||||
--onReport.triggers.someName = function (reportId)
|
||||
-- --do stuff with that id
|
||||
--end
|
||||
|
||||
local _ENV = mkmodule('onReport')
|
||||
local utils = require 'utils'
|
||||
local repeatUtil = require 'plugins.repeatUtil'
|
||||
|
||||
lastReport = lastReport or -1
|
||||
triggers = triggers or {}
|
||||
|
||||
monitorFrequency = monitorFrequency or nil
|
||||
eventToDwarf = eventToDwarf or {}
|
||||
|
||||
function updateEventToDwarf(reportId)
|
||||
if not eventToDwarf[reportId] then
|
||||
eventToDwarf[reportId] = {}
|
||||
end
|
||||
for _,unit in ipairs(df.global.world.units.all) do
|
||||
for _,reportType in ipairs(unit.reports.log) do
|
||||
for _,report in ipairs(reportType) do
|
||||
if report == reportId then
|
||||
eventToDwarf[reportId][unit.id] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function monitor()
|
||||
local reports = df.global.world.status.reports
|
||||
if df.global.world.status.next_report_id-1 <= lastReport then
|
||||
return
|
||||
end
|
||||
-- if #reports == 0 or reports[#reports-1].id <= lastReport then
|
||||
-- return
|
||||
-- end
|
||||
_,_,start = utils.binsearch(reports,lastReport,"id")
|
||||
while start < #reports and reports[start].id <= lastReport do
|
||||
start = start+1
|
||||
end
|
||||
for i=start,#reports-1,1 do
|
||||
updateEventToDwarf(reports[i].id)
|
||||
for _,callBack in pairs(triggers) do
|
||||
callBack(reports[i].id)
|
||||
end
|
||||
lastReport = reports[i].id
|
||||
end
|
||||
end
|
||||
|
||||
monitorEvery = function(n)
|
||||
if n <= 0 then
|
||||
print('cannot monitor onReport every '..n..' ticks.')
|
||||
return
|
||||
end
|
||||
if monitorFrequency and monitorFrequency < n then
|
||||
print('NOT decreasing frequency of onReport monitoring from every '..monitorFrequency..' ticks to every '..n..' ticks')
|
||||
return
|
||||
end
|
||||
print('monitor onReport every '..n..' ticks')
|
||||
monitorFrequency = n
|
||||
repeatUtil.scheduleEvery('onReportMonitoring', n, 'ticks', monitor)
|
||||
end
|
||||
|
||||
monitorEvery(1)
|
||||
|
||||
return _ENV
|
||||
|
@ -0,0 +1,255 @@
|
||||
|
||||
local _ENV = mkmodule('onStrike')
|
||||
|
||||
local onReport = require 'plugins.onReport'
|
||||
local utils = require 'utils'
|
||||
|
||||
debug = debug or true
|
||||
|
||||
triggers = triggers or {}
|
||||
|
||||
function getReportString(reportId)
|
||||
local report = df.report.find(reportId)
|
||||
local result = report.text
|
||||
local i = 1
|
||||
local report2 = df.report.find(reportId+i)
|
||||
while report2 and report2.flags.continuation do
|
||||
result = result .. ' ' .. report2.text
|
||||
i = i+1
|
||||
report2 = df.report.find(reportId+i)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
onReport.triggers.onStrike = function(reportId)
|
||||
local report = df.report.find(reportId)
|
||||
if report["type"] ~= df.announcement_type.COMBAT_STRIKE_DETAILS then
|
||||
return
|
||||
end
|
||||
if report.flags.continuation then
|
||||
return
|
||||
end
|
||||
-- print('\n')
|
||||
local fighters = {}
|
||||
for unitId,_ in pairs(onReport.eventToDwarf[reportId]) do
|
||||
table.insert(fighters,unitId)
|
||||
end
|
||||
local reportString = getReportString(reportId)
|
||||
if #fighters ~= 2 then
|
||||
if debug then
|
||||
local ok = string.find(reportString,' skids along ')
|
||||
if not ok then
|
||||
print('onStrike: #fighters = ' .. #fighters)
|
||||
print(reportString)
|
||||
df.global.pause_state = true
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
local info = {}
|
||||
local count = 0
|
||||
fighters[1] = df.unit.find(fighters[1])
|
||||
fighters[2] = df.unit.find(fighters[2])
|
||||
local function getWound(fighterA,fighterB)
|
||||
local wound
|
||||
for i=#fighterB.body.wounds-1,0,-1 do
|
||||
local w = fighterB.body.wounds[i]
|
||||
if w.unit_id == fighterA.id and w.age <= 1 then
|
||||
wound = w
|
||||
break
|
||||
end
|
||||
end
|
||||
--[[name,_ = tryParse(reportString,getNames(fighterA))
|
||||
if not name then
|
||||
wound = nil
|
||||
end
|
||||
--]]
|
||||
return wound
|
||||
end
|
||||
local wound1 = getWound(fighters[1],fighters[2])
|
||||
local wound2 = getWound(fighters[2],fighters[1])
|
||||
local flying = string.find(reportString,'The flying')
|
||||
local name1
|
||||
local name2
|
||||
if flying then
|
||||
name1 = findAny(reportString,getNames(fighters[2]))
|
||||
name2 = findAny(reportString,getNames(fighters[1]))
|
||||
else
|
||||
name1 = tryParse(reportString,getNames(fighters[1]))
|
||||
name2 = tryParse(reportString,getNames(fighters[2]))
|
||||
end
|
||||
if name1 and wound1 and name2 and wound2 then
|
||||
if debug then
|
||||
print('ambiguous wounds: ' .. reportString)
|
||||
print('fighter1 = ' .. fighters[1].id)
|
||||
print('fighter2 = ' .. fighters[2].id)
|
||||
df.global.pause_state = true
|
||||
end
|
||||
return
|
||||
elseif not wound1 and not wound2 then
|
||||
local ok = fighters[1].flags1.dead or fighters[2].flags1.dead or string.find(reportString,' grabs ') or string.find(reportString,'snatches at') or string.find(reportString,'glances away!') or string.find(reportString, ' shakes ')
|
||||
if not ok and debug then
|
||||
print('neither wound works: ' .. reportString)
|
||||
print('fighter1 = ' .. fighters[1].id)
|
||||
print('fighter2 = ' .. fighters[2].id)
|
||||
df.global.pause_state = true
|
||||
end
|
||||
return
|
||||
elseif not name1 and not name2 and not string.find(reportString,'The flying ') then
|
||||
if debug then
|
||||
print('WTF?')
|
||||
print('fighter1 = ' .. fighters[1].id)
|
||||
print('fighter2 = ' .. fighters[2].id)
|
||||
df.global.pause_state = true
|
||||
end
|
||||
return
|
||||
elseif name1 and wound1 then
|
||||
else
|
||||
local temp = fighters[1]
|
||||
fighters[1] = fighters[2]
|
||||
fighters[2] = temp
|
||||
end
|
||||
local wound = wound1 or wound2
|
||||
|
||||
--is it a weapon attack?
|
||||
local isWeaponAttack
|
||||
if getWeapon(fighters[1]) and string.find(reportString,getWeaponString(fighters[1])) then
|
||||
isWeaponAttack = true
|
||||
else
|
||||
isWeaponAttack = false
|
||||
end
|
||||
isWeaponAttack = isWeaponAttack or flying
|
||||
local weapon
|
||||
if isWeaponAttack then
|
||||
weapon = getWeapon(fighters[1])
|
||||
end
|
||||
-- print('triggers')
|
||||
for _,trigger in pairs(triggers) do
|
||||
-- print('trigger')
|
||||
trigger(fighters[1],fighters[2],weapon,wound)
|
||||
end
|
||||
end
|
||||
|
||||
function myConcat(table1,table2)
|
||||
local result = {}
|
||||
for _,v in pairs(table1) do
|
||||
table.insert(result,v)
|
||||
end
|
||||
for _,v in pairs(table2) do
|
||||
table.insert(result,v)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function getUnitAttackStrings(unit)
|
||||
local result = {}
|
||||
for _,attack in ipairs(unit.body.body_plan.attacks) do
|
||||
table.insert(result,attack.verb_3rd..' ')
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function getUnitAttack(unit,parsedAttack)
|
||||
for _,attack in ipairs(unit.body.body_plan.attacks) do
|
||||
if attack.verb_3rd..' ' == parsedAttack then
|
||||
return result
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function getWeapon(unit)
|
||||
function dumb(item)
|
||||
-- print('\n')
|
||||
-- print(item)
|
||||
-- printall(item)
|
||||
if item.mode ~= df.unit_inventory_item.T_mode.Weapon then
|
||||
--print('item.mode ' .. item.mode .. ' /= Weapon ' .. df.unit_inventory_item.T_mode.Weapon)
|
||||
return false
|
||||
end
|
||||
if item.item._type ~= df.item_weaponst then
|
||||
--print('item.item._type ' .. item.item._type .. ' /= df.item_weaponst ' .. df.item_weaponst)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
for _,item in ipairs(unit.inventory) do
|
||||
if dumb(item) then
|
||||
return item.item
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function getWeaponAttackStrings(unit)
|
||||
local result = {}
|
||||
local weapon = getWeapon(unit)
|
||||
if not weapon then
|
||||
print('no weapon')
|
||||
return result
|
||||
end
|
||||
for _,attack in ipairs(weapon.subtype.attacks) do
|
||||
table.insert(result,attack.verb_3rd..' ')
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
function getWeaponAttack(unit,parsedAttack)
|
||||
local weapon = getWeapon(unit)
|
||||
if not weapon then
|
||||
return nil
|
||||
end
|
||||
for _,attack in ipairs(weapon.subtype.attacks) do
|
||||
if attack.verb_3rd..' ' == parsedAttack then
|
||||
return attack
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function getNames(unit)
|
||||
local result = {}
|
||||
table.insert(result,unit.name.first_name .. ' ')
|
||||
table.insert(result,'The '..dfhack.units.getProfessionName(unit)..' ')
|
||||
table.insert(result,'the '..dfhack.units.getProfessionName(unit)..' ')
|
||||
table.insert(result,'The Stray '..dfhack.units.getProfessionName(unit)..' ')
|
||||
table.insert(result,'the stray '..dfhack.units.getProfessionName(unit)..' ')
|
||||
return result
|
||||
end
|
||||
|
||||
function getWeaponString(unit,suffix)
|
||||
local weapon = getWeapon(unit)
|
||||
if not weapon then
|
||||
return ''
|
||||
end
|
||||
local material = getMaterialString(weapon)
|
||||
return material .. ' ' .. weapon.subtype.name .. (suffix or '')
|
||||
end
|
||||
|
||||
function getMaterialString(item)
|
||||
local material = dfhack.matinfo.decode(item.mat_type,item.mat_index)
|
||||
return material.material.state_name[df.matter_state.Solid]
|
||||
end
|
||||
|
||||
function findAny(parseString,strs)
|
||||
for _,str in ipairs(strs) do
|
||||
if string.find(parseString,str) then
|
||||
return str
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function tryParse(parseString,strs)
|
||||
for _,str in ipairs(strs) do
|
||||
if string.sub(parseString,1,#str) == str then
|
||||
--print('\n"' .. str .. '" matches "' .. parseString .. '"\n')
|
||||
return str,string.sub(parseString,#str+1,#parseString)
|
||||
end
|
||||
--print('\n"' .. str .. '" doesn\'t match "' .. parseString .. '"\n')
|
||||
end
|
||||
return nil,nil
|
||||
end
|
||||
|
||||
return _ENV
|
||||
|
@ -0,0 +1,34 @@
|
||||
-- lua/plugins/repeatUtil.lua
|
||||
-- author expwnent
|
||||
-- vaguely based on a script by Putnam
|
||||
|
||||
local _ENV = mkmodule("repeatUtil")
|
||||
|
||||
repeating = repeating or {}
|
||||
|
||||
dfhack.onStateChange.repeatUtilStateChange = function(code)
|
||||
if code == SC_WORLD_UNLOADED then
|
||||
repeating = {}
|
||||
end
|
||||
end
|
||||
|
||||
function cancel(name)
|
||||
if not repeating[name] then
|
||||
return false
|
||||
end
|
||||
dfhack.timeout_active(repeating[name],nil)
|
||||
repeating[name] = nil
|
||||
return true
|
||||
end
|
||||
|
||||
function scheduleEvery(name,time,timeUnits,func)
|
||||
cancel(name)
|
||||
local function helper()
|
||||
func()
|
||||
repeating[name] = dfhack.timeout(time,timeUnits,helper)
|
||||
end
|
||||
helper()
|
||||
end
|
||||
|
||||
return _ENV
|
||||
|
@ -0,0 +1,154 @@
|
||||
--syndrome.lua
|
||||
--author expwnent
|
||||
--some utilities for adding syndromes to units
|
||||
|
||||
local _ENV = mkmodule("syndromeUtil")
|
||||
local Utils = require("utils")
|
||||
|
||||
function findUnitSyndrome(unit,syn_id)
|
||||
for index,syndrome in ipairs(unit.syndromes.active) do
|
||||
if syndrome.type == syn_id then
|
||||
return syndrome
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
--usage: syndrome.ResetPolicy.DoNothing, syndrome.ResetPolicy.ResetDuration, etc
|
||||
ResetPolicy = ResetPolicy or Utils.reverse({
|
||||
"DoNothing",
|
||||
"ResetDuration",
|
||||
"AddDuration",
|
||||
"NewInstance"
|
||||
})
|
||||
|
||||
function eraseSyndrome(unit,syndromeId,oldestFirst)
|
||||
local i1
|
||||
local iN
|
||||
local d
|
||||
if oldestFirst then
|
||||
i1 = 0
|
||||
iN = #unit.syndromes.active-1
|
||||
d = 1
|
||||
else
|
||||
i1 = #unit.syndromes.active-1
|
||||
iN = 0
|
||||
d = -1
|
||||
end
|
||||
local syndromes = unit.syndromes.active
|
||||
for i=i1,iN,d do
|
||||
if syndromes[i].type == syndromeId then
|
||||
syndromes:erase(i)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function eraseSyndromes(unit,syndromeId)
|
||||
local count=0
|
||||
while eraseSyndrome(unit,syndromeId,true) do
|
||||
count = count+1
|
||||
end
|
||||
return count
|
||||
end
|
||||
--target is a df.unit, syndrome is a df.syndrome, resetPolicy is one of syndrome.ResetPolicy
|
||||
--if the target has an instance of the syndrome already, the reset policy takes effect
|
||||
--returns true if the unit did not have the syndrome before calling and false otherwise
|
||||
function infectWithSyndrome(target,syndrome,resetPolicy)
|
||||
local oldSyndrome = findUnitSyndrome(target,syndrome.id)
|
||||
if oldSyndrome == nil or resetPolicy == nil or resetPolicy == ResetPolicy.NewInstance then
|
||||
local unitSyndrome = df.unit_syndrome:new()
|
||||
unitSyndrome.type = syndrome.id
|
||||
unitSyndrome.year = df.global.cur_year
|
||||
unitSyndrome.year_time = df.global.cur_year_tick
|
||||
unitSyndrome.ticks = 0
|
||||
unitSyndrome.wound_id = -1
|
||||
unitSyndrome.flags.bits.active = 1
|
||||
for k,v in ipairs(syndrome.ce) do
|
||||
local symptom = df.unit_syndrome.T_symptoms:new()
|
||||
symptom.quantity = 0
|
||||
symptom.delay = 0
|
||||
symptom.ticks = 0
|
||||
symptom.flags.bits.active = true
|
||||
unitSyndrome.symptoms:insert("#",symptom)
|
||||
end
|
||||
target.syndromes.active:insert("#",unitSyndrome)
|
||||
elseif resetPolicy == ResetPolicy.DoNothing then
|
||||
elseif resetPolicy == ResetPolicy.ResetDuration then
|
||||
for k,symptom in ipairs(oldSyndrome.symptoms) do
|
||||
symptom.ticks = 0
|
||||
end
|
||||
oldSyndrome.ticks = 0
|
||||
elseif resetPolicy == ResetPolicy.AddDuration then
|
||||
for k,symptom in ipairs(oldSyndrome.symptoms) do
|
||||
--really it's syndrome.ce[k].end, but lua doesn't like that because keywords
|
||||
if syndrome.ce[k]["end"] ~= -1 then
|
||||
symptom.ticks = symptom.ticks - syndrome.ce[k]["end"]
|
||||
end
|
||||
end
|
||||
else qerror("Bad reset policy: " .. resetPolicy)
|
||||
end
|
||||
return (oldSyndrome == nil)
|
||||
end
|
||||
|
||||
function isValidTarget(unit,syndrome)
|
||||
--mostly copied from itemsyndrome, which is based on autoSyndrome
|
||||
if
|
||||
#syndrome.syn_affected_class==0
|
||||
and #syndrome.syn_affected_creature==0
|
||||
and #syndrome.syn_affected_caste==0
|
||||
and #syndrome.syn_immune_class==0
|
||||
and #syndrome.syn_immune_creature==0
|
||||
and #syndrome.syn_immune_caste==0
|
||||
then
|
||||
return true
|
||||
end
|
||||
local affected = false
|
||||
local unitRaws = df.creature_raw.find(unit.race)
|
||||
local casteRaws = unitRaws.caste[unit.caste]
|
||||
local unitRaceName = unitRaws.creature_id
|
||||
local casteName = casteRaws.caste_id
|
||||
local unitClasses = casteRaws.creature_class
|
||||
for _,unitClass in ipairs(unitClasses) do
|
||||
for _,syndromeClass in ipairs(syndrome.syn_affected_class) do
|
||||
if unitClass.value==syndromeClass.value then
|
||||
affected = true
|
||||
end
|
||||
end
|
||||
end
|
||||
for caste,creature in ipairs(syndrome.syn_affected_creature) do
|
||||
local affectedCreature = creature.value
|
||||
local affectedCaste = syndrome.syn_affectedCaste[caste].value
|
||||
if affectedCreature == unitRaceName and (affectedCaste == casteName or affectedCaste == "ALL") then
|
||||
affected = true
|
||||
end
|
||||
end
|
||||
for _,unitClass in ipairs(unitClasses) do
|
||||
for _,syndromeClass in ipairs(syndrome.syn_immune_class) do
|
||||
if unitClass.value == syndromeClass.value then
|
||||
affected = false
|
||||
end
|
||||
end
|
||||
end
|
||||
for caste,creature in ipairs(syndrome,syn_immune_creature) do
|
||||
local immuneCreature = creature.value
|
||||
local immuneCaste = syndrome.syn_immune_caste[caste].value
|
||||
if immuneCreature == unitRaceName and (immuneCaste == casteName or immuneCaste == "ALL") then
|
||||
affected = false
|
||||
end
|
||||
end
|
||||
return affected
|
||||
end
|
||||
|
||||
function infectWithSyndromeIfValidTarget(target,syndrome,resetPolicy)
|
||||
if isValidTarget(unit,syndrome) then
|
||||
infectWithSyndrome(target,syndrome,resetPolicy)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return _ENV
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,260 @@
|
||||
--Spawnunit.lua
|
||||
--create unit at pointer. Usage e.g. "spawnunit DWARF 0 Dwarfy"
|
||||
--author Warmist, Runrusher?
|
||||
--edited by expwnent
|
||||
|
||||
function findCasteIndex(race_id,casteName)
|
||||
local cr=df.creature_raw.find(race_id)
|
||||
for casteIndex,caste in ipairs(cr.caste) do
|
||||
if caste.caste_id == casteName then
|
||||
return casteIndex
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
function getCaste(race_id,caste_id)
|
||||
local cr=df.creature_raw.find(race_id)
|
||||
return cr.caste[caste_id]
|
||||
end
|
||||
function genBodyModifier(body_app_mod)
|
||||
local a=math.random(0,#body_app_mod.ranges-2)
|
||||
return math.random(body_app_mod.ranges[a],body_app_mod.ranges[a+1])
|
||||
end
|
||||
function getBodySize(caste,time)
|
||||
--todo real body size...
|
||||
return caste.body_size_1[#caste.body_size_1-1] --returns last body size
|
||||
end
|
||||
function genAttribute(array)
|
||||
local a=math.random(0,#array-2)
|
||||
return math.random(array[a],array[a+1])
|
||||
end
|
||||
function norm()
|
||||
return math.sqrt((-2)*math.log(math.random()))*math.cos(2*math.pi*math.random())
|
||||
end
|
||||
function normalDistributed(mean,sigma)
|
||||
return mean+sigma*norm()
|
||||
end
|
||||
function clampedNormal(min,median,max)
|
||||
local val=normalDistributed(median,math.sqrt(max-min))
|
||||
if val<min then return min end
|
||||
if val>max then return max end
|
||||
return val
|
||||
end
|
||||
function makeSoul(unit,caste)
|
||||
local tmp_soul=df.unit_soul:new()
|
||||
tmp_soul.unit_id=unit.id
|
||||
tmp_soul.name:assign(unit.name)
|
||||
tmp_soul.race=unit.race
|
||||
tmp_soul.sex=unit.sex
|
||||
tmp_soul.caste=unit.caste
|
||||
--todo skills,preferences,traits.
|
||||
local attrs=caste.attributes
|
||||
for k,v in pairs(attrs.ment_att_range) do
|
||||
local max_percent=attrs.ment_att_cap_perc[k]/100
|
||||
local cvalue=genAttribute(v)
|
||||
tmp_soul.mental_attrs[k]={value=cvalue,max_value=cvalue*max_percent}
|
||||
end
|
||||
for k,v in pairs(tmp_soul.traits) do
|
||||
local min,mean,max
|
||||
min=caste.personality.a[k]
|
||||
mean=caste.personality.b[k]
|
||||
max=caste.personality.c[k]
|
||||
tmp_soul.traits[k]=clampedNormal(min,mean,max)
|
||||
end
|
||||
unit.status.souls:insert("#",tmp_soul)
|
||||
unit.status.current_soul=tmp_soul
|
||||
end
|
||||
function CreateUnit(race_id,caste_id)
|
||||
local race=df.creature_raw.find(race_id)
|
||||
if race==nil then error("Invalid race_id") end
|
||||
local caste=getCaste(race_id,caste_id)
|
||||
local unit=df.unit:new()
|
||||
unit.race=race_id
|
||||
unit.caste=caste_id
|
||||
unit.id=df.global.unit_next_id
|
||||
df.global.unit_next_id=df.global.unit_next_id+1
|
||||
if caste.misc.maxage_max==-1 then
|
||||
unit.relations.old_year=-1
|
||||
else
|
||||
unit.relations.old_year=math.random(caste.misc.maxage_min,caste.misc.maxage_max)
|
||||
end
|
||||
unit.sex=caste.gender
|
||||
local body=unit.body
|
||||
body.body_plan=caste.body_info
|
||||
local body_part_count=#body.body_plan.body_parts
|
||||
local layer_count=#body.body_plan.layer_part
|
||||
--components
|
||||
unit.relations.birth_year=df.global.cur_year
|
||||
--unit.relations.birth_time=??
|
||||
|
||||
--unit.relations.old_time=?? --TODO add normal age
|
||||
local cp=body.components
|
||||
cp.body_part_status:resize(body_part_count)
|
||||
cp.numbered_masks:resize(#body.body_plan.numbered_masks)
|
||||
for num,v in ipairs(body.body_plan.numbered_masks) do
|
||||
cp.numbered_masks[num]=v
|
||||
end
|
||||
|
||||
cp.layer_status:resize(layer_count)
|
||||
cp.layer_wound_area:resize(layer_count)
|
||||
cp.layer_cut_fraction:resize(layer_count)
|
||||
cp.layer_dent_fraction:resize(layer_count)
|
||||
cp.layer_effect_fraction:resize(layer_count)
|
||||
local attrs=caste.attributes
|
||||
for k,v in pairs(attrs.phys_att_range) do
|
||||
local max_percent=attrs.phys_att_cap_perc[k]/100
|
||||
local cvalue=genAttribute(v)
|
||||
unit.body.physical_attrs[k]={value=cvalue,max_value=cvalue*max_percent}
|
||||
--unit.body.physical_attrs:insert(k,{new=true,max_value=genMaxAttribute(v),value=genAttribute(v)})
|
||||
end
|
||||
|
||||
body.blood_max=getBodySize(caste,0) --TODO normal values
|
||||
body.blood_count=body.blood_max
|
||||
body.infection_level=0
|
||||
unit.status2.body_part_temperature:resize(body_part_count)
|
||||
for k,v in pairs(unit.status2.body_part_temperature) do
|
||||
unit.status2.body_part_temperature[k]={new=true,whole=10067,fraction=0}
|
||||
end
|
||||
--------------------
|
||||
local stuff=unit.enemy
|
||||
stuff.body_part_878:resize(body_part_count) -- all = 3
|
||||
stuff.body_part_888:resize(body_part_count) -- all = 3
|
||||
stuff.body_part_relsize:resize(body_part_count) -- all =0
|
||||
|
||||
--TODO add correct sizes. (calculate from age)
|
||||
local size=caste.body_size_2[#caste.body_size_2-1]
|
||||
body.size_info.size_cur=size
|
||||
body.size_info.size_base=size
|
||||
body.size_info.area_cur=math.pow(size,0.666)
|
||||
body.size_info.area_base=math.pow(size,0.666)
|
||||
body.size_info.area_cur=math.pow(size*10000,0.333)
|
||||
body.size_info.area_base=math.pow(size*10000,0.333)
|
||||
|
||||
stuff.were_race=race_id
|
||||
stuff.were_caste=caste_id
|
||||
stuff.normal_race=race_id
|
||||
stuff.normal_caste=caste_id
|
||||
stuff.body_part_8a8:resize(body_part_count) -- all = 1
|
||||
stuff.body_part_base_ins:resize(body_part_count)
|
||||
stuff.body_part_clothing_ins:resize(body_part_count)
|
||||
stuff.body_part_8d8:resize(body_part_count)
|
||||
unit.recuperation.healing_rate:resize(layer_count)
|
||||
--appearance
|
||||
|
||||
local app=unit.appearance
|
||||
app.body_modifiers:resize(#caste.body_appearance_modifiers) --3
|
||||
for k,v in pairs(app.body_modifiers) do
|
||||
app.body_modifiers[k]=genBodyModifier(caste.body_appearance_modifiers[k])
|
||||
end
|
||||
app.bp_modifiers:resize(#caste.bp_appearance.modifier_idx) --0
|
||||
for k,v in pairs(app.bp_modifiers) do
|
||||
app.bp_modifiers[k]=genBodyModifier(caste.bp_appearance.modifiers[caste.bp_appearance.modifier_idx[k]])
|
||||
end
|
||||
--app.unk_4c8:resize(33)--33
|
||||
app.tissue_style:resize(#caste.bp_appearance.style_part_idx)
|
||||
app.tissue_style_civ_id:resize(#caste.bp_appearance.style_part_idx)
|
||||
app.tissue_style_id:resize(#caste.bp_appearance.style_part_idx)
|
||||
app.tissue_style_type:resize(#caste.bp_appearance.style_part_idx)
|
||||
app.tissue_length:resize(#caste.bp_appearance.style_part_idx)
|
||||
app.genes.appearance:resize(#caste.body_appearance_modifiers+#caste.bp_appearance.modifiers) --3
|
||||
app.genes.colors:resize(#caste.color_modifiers*2) --???
|
||||
app.colors:resize(#caste.color_modifiers)--3
|
||||
|
||||
makeSoul(unit,caste)
|
||||
|
||||
df.global.world.units.all:insert("#",unit)
|
||||
df.global.world.units.active:insert("#",unit)
|
||||
--todo set weapon bodypart
|
||||
|
||||
local num_inter=#caste.body_info.interactions
|
||||
--used to be anon_5 and anon_6: I guessed at what those were before the df-structures update. It seems to work at least a bit. ~expwnent
|
||||
unit.curse.own_interaction:resize(num_inter)
|
||||
unit.curse.own_interaction_delay:resize(num_inter)
|
||||
return unit
|
||||
end
|
||||
function findRace(name)
|
||||
for k,v in pairs(df.global.world.raws.creatures.all) do
|
||||
if v.creature_id==name then
|
||||
return k
|
||||
end
|
||||
end
|
||||
qerror("Race:"..name.." not found!")
|
||||
end
|
||||
function PlaceUnit(raceName,casteName,name,position)
|
||||
local pos
|
||||
if position.x==-30000 then
|
||||
pos = copyall(df.global.cursor)
|
||||
else
|
||||
pos = position
|
||||
end
|
||||
if pos.x==-30000 then
|
||||
qerror("Spawnunit: specify location or place the cursor where you want the unit to be created.")
|
||||
end
|
||||
local race=findRace(raceName)
|
||||
local caste=findCasteIndex(race,casteName)
|
||||
local u=CreateUnit(race,tonumber(caste) or 0)
|
||||
u.pos:assign(pos)
|
||||
if name then
|
||||
u.name.first_name=name
|
||||
u.name.has_name=true
|
||||
end
|
||||
u.civ_id=df.global.ui.civ_id
|
||||
|
||||
local desig,ocupan=dfhack.maps.getTileFlags(pos)
|
||||
ocupan.unit=true
|
||||
--createNemesis(u)
|
||||
end
|
||||
function createFigure(trgunit)
|
||||
local hf=df.historical_figure:new()
|
||||
hf.id=df.global.hist_figure_next_id
|
||||
hf.race=trgunit.race
|
||||
hf.caste=trgunit.caste
|
||||
df.global.hist_figure_next_id=df.global.hist_figure_next_id+1
|
||||
hf.name.first_name=trgunit.name.first_name
|
||||
hf.name.has_name=true
|
||||
df.global.world.history.figures:insert("#",hf)
|
||||
return hf
|
||||
end
|
||||
function createNemesis(trgunit)
|
||||
local id=df.global.nemesis_next_id
|
||||
local nem=df.nemesis_record:new()
|
||||
nem.id=id
|
||||
nem.unit_id=trgunit.id
|
||||
nem.unit=trgunit
|
||||
nem.flags:resize(1)
|
||||
nem.flags[4]=true
|
||||
nem.flags[5]=true
|
||||
nem.flags[6]=true
|
||||
nem.flags[7]=true
|
||||
nem.flags[8]=true
|
||||
nem.flags[9]=true
|
||||
--[[for k=4,8 do
|
||||
nem.flags[k]=true
|
||||
end]]
|
||||
df.global.world.nemesis.all:insert("#",nem)
|
||||
df.global.nemesis_next_id=id+1
|
||||
trgunit.general_refs:insert("#",{new=df.general_ref_is_nemesisst,nemesis_id=id})
|
||||
trgunit.flags1.important_historical_figure=true
|
||||
local gen=df.global.world.worldgen
|
||||
nem.save_file_id=gen.next_unit_chunk_id;
|
||||
gen.next_unit_chunk_id=gen.next_unit_chunk_id+1
|
||||
gen.next_unit_chunk_offset=gen.next_unit_chunk_offset+1
|
||||
|
||||
--[[ local gen=df.global.world.worldgen
|
||||
gen.next_unit_chunk_id
|
||||
gen.next_unit_chunk_offset
|
||||
]]
|
||||
nem.figure=createFigure(trgunit)
|
||||
end
|
||||
|
||||
args={...}
|
||||
|
||||
pos = df.new(df.coord)
|
||||
if #args > 3 then
|
||||
pos.x = tonumber(args[4]) or -30000
|
||||
pos.y = tonumber(args[5]) or -30000
|
||||
pos.z = tonumber(args[6]) or -30000
|
||||
end
|
||||
|
||||
PlaceUnit(args[1],args[2],args[3],pos)
|
||||
|
@ -0,0 +1,48 @@
|
||||
--blooddel.lua
|
||||
--makes it so that civs won't come with barrels full of blood, ichor, or goo
|
||||
--author Urist Da Vinci
|
||||
--edited by expwnent
|
||||
|
||||
local my_entity=df.historical_entity.find(df.global.ui.civ_id)
|
||||
local sText=" "
|
||||
local k=0
|
||||
local v=1
|
||||
|
||||
for x,y in pairs(df.global.world.entities.all) do
|
||||
my_entity=y
|
||||
k=0
|
||||
while k < #my_entity.resources.misc_mat.extracts.mat_index do
|
||||
v=my_entity.resources.misc_mat.extracts.mat_type[k]
|
||||
sText=dfhack.matinfo.decode(v,my_entity.resources.misc_mat.extracts.mat_index[k])
|
||||
if (sText==nil) then
|
||||
--LIQUID barrels
|
||||
my_entity.resources.misc_mat.extracts.mat_type:erase(k)
|
||||
my_entity.resources.misc_mat.extracts.mat_index:erase(k)
|
||||
k=k-1
|
||||
else
|
||||
if(sText.material.id=="BLOOD") then
|
||||
my_entity.resources.misc_mat.extracts.mat_type:erase(k)
|
||||
my_entity.resources.misc_mat.extracts.mat_index:erase(k)
|
||||
k=k-1
|
||||
end
|
||||
if(sText.material.id=="ICHOR") then
|
||||
my_entity.resources.misc_mat.extracts.mat_type:erase(k)
|
||||
my_entity.resources.misc_mat.extracts.mat_index:erase(k)
|
||||
k=k-1
|
||||
end
|
||||
if(sText.material.id=="GOO") then
|
||||
my_entity.resources.misc_mat.extracts.mat_type:erase(k)
|
||||
my_entity.resources.misc_mat.extracts.mat_index:erase(k)
|
||||
k=k-1
|
||||
end
|
||||
--VENOM
|
||||
--POISON
|
||||
--FLUID
|
||||
--MILK
|
||||
--EXTRACT
|
||||
|
||||
end
|
||||
k=k+1
|
||||
end
|
||||
end
|
||||
|
@ -0,0 +1,37 @@
|
||||
-- feeding-timers.lua
|
||||
-- original author: tejón
|
||||
-- rewritten by expwnent
|
||||
-- see repeat.lua for how to run this every so often automatically
|
||||
|
||||
local args = {...}
|
||||
if args[1] ~= nil then
|
||||
print("feeding-timers usage")
|
||||
print(" feeding-timers")
|
||||
print(" reset the feeding timers of all units as appropriate")
|
||||
print(" feeding-timers help")
|
||||
print(" print this help message")
|
||||
print(" repeat enable [n] [years/months/ticks/days/etc] feeding-timers")
|
||||
print(" run this script every n time units")
|
||||
print(" repeat disable feeding-timers")
|
||||
print(" stop automatically running this script")
|
||||
return
|
||||
end
|
||||
|
||||
local fixcount = 0
|
||||
for _,unit in ipairs(df.global.world.units.all) do
|
||||
if dfhack.units.isCitizen(unit) and not (unit.flags1.dead) then
|
||||
for _,v in pairs(unit.status.misc_traits) do
|
||||
local didfix = 0
|
||||
if v.id == 0 then -- I think this should have additional conditions...
|
||||
v.value = 0 -- GiveWater cooldown set to zero
|
||||
didfix = 1
|
||||
end
|
||||
if v.id == 1 then -- I think this should have additional conditions...
|
||||
v.value = 0 -- GiveFood cooldown set to zero
|
||||
didfix = 1
|
||||
end
|
||||
fixcount = fixcount + didfix
|
||||
end
|
||||
end
|
||||
end
|
||||
print("Fixed feeding timers for " .. fixcount .. " citizens.")
|
@ -0,0 +1,84 @@
|
||||
-- force.lua
|
||||
-- author Putnam
|
||||
-- edited by expwnent
|
||||
-- Forces an event.
|
||||
|
||||
local function findCiv(arg)
|
||||
local entities = df.global.world.entities.all
|
||||
if tonumber(arg) then return arg end
|
||||
if arg then
|
||||
for eid,entity in ipairs(entities) do
|
||||
if entity.entity_raw.code == arg then return entity end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local args = {...}
|
||||
|
||||
if not args or not args[1] then
|
||||
qerror('Needs an argument. Valid arguments are caravan, migrants, diplomat, megabeast, curiousbeast, mischievousbeast, flier, siege and nightcreature. Second argument is civ, either raw entity ID or "player" for player\'s civ.')
|
||||
end
|
||||
|
||||
local eventType = string.lower(args[1])
|
||||
|
||||
forceEntity = args[2]=="player" and df.historical_entity.find(df.global.ui.civ_id) or findCiv(args[2])
|
||||
|
||||
if (eventType == "caravan" or eventType == "diplomat" or eventType == "siege") and not forceEntity then
|
||||
qerror('caravan, diplomat and siege require a civilization ID to be included.')
|
||||
end
|
||||
|
||||
local function eventTypeIsNotValid()
|
||||
local eventTypes = {
|
||||
"caravan",
|
||||
"migrants",
|
||||
"diplomat",
|
||||
"megabeast",
|
||||
"curiousbeast",
|
||||
"mischevousbeast",
|
||||
"mischeviousbeast",
|
||||
"flier",
|
||||
"siege",
|
||||
"nightcreature"
|
||||
}
|
||||
for _,v in ipairs(eventTypes) do
|
||||
if args[1] == v then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--code may be kind of bad below :V Putnam ain't experienced in lua... --Putnam's comment, not mine ~expwnent
|
||||
if eventTypeIsNotValid() then
|
||||
qerror('Invalid argument. Valid arguments are caravan, migrants, diplomat, megabeast, curiousbeast, mischievousbeast, flier, siege and nightcreature.')
|
||||
end
|
||||
|
||||
allEventTypes={}
|
||||
allEventTypes["megabeast"]=function()
|
||||
df.global.timed_events:insert('#', { new = df.timed_event, type = df.timed_event_type.Megabeast, season = df.global.cur_season, season_ticks = df.global.cur_season_tick } )
|
||||
end
|
||||
allEventTypes["migrants"]=function()
|
||||
df.global.timed_events:insert('#', { new = df.timed_event, type = df.timed_event_type.Migrants, season = df.global.cur_season, season_ticks = df.global.cur_season_tick, entity = df.global.world.entities.all[df.global.ui.civ_id] } )
|
||||
end
|
||||
allEventTypes["caravan"]=function()
|
||||
df.global.timed_events:insert('#', { new = df.timed_event, type = df.timed_event_type.Caravan, season = df.global.cur_season, season_ticks = df.global.cur_season_tick, entity = forceEntity } )
|
||||
end
|
||||
allEventTypes["diplomat"]=function()
|
||||
df.global.timed_events:insert('#', { new = df.timed_event, type = df.timed_event_type.Diplomat, season = df.global.cur_season, season_ticks = df.global.cur_season_tick, entity = forceEntity } )
|
||||
end
|
||||
allEventTypes["curious"]=function()
|
||||
df.global.timed_events:insert('#', { new = df.timed_event, type = df.timed_event_type.WildlifeCurious, season = df.global.cur_season, season_ticks = df.global.cur_season_tick } )
|
||||
end
|
||||
allEventTypes["mischevousbeast"]=function()
|
||||
df.global.timed_events:insert('#', { new = df.timed_event, type = df.timed_event_type.WildlifeMichievous, season = df.global.cur_season, season_ticks = df.global.cur_season_tick } )
|
||||
end
|
||||
allEventTypes["flier"]=function()
|
||||
df.global.timed_events:insert('#', { new = df.timed_event, type = df.timed_event_type.WildlifeFlier, season = df.global.cur_season, season_ticks = df.global.cur_season_tick } )
|
||||
end
|
||||
allEventTypes["siege"]=function()
|
||||
df.global.timed_events:insert('#', { new = df.timed_event, type = df.timed_event_type.CivAttack, season = df.global.cur_season, season_ticks = df.global.cur_season_tick, entity = forceEntity } )
|
||||
end
|
||||
allEventTypes["nightcreature"]=function()
|
||||
df.global.timed_events:insert('#', { new = df.timed_event, type = df.timed_event_type.NightCreature, season = df.global.cur_season, season_ticks = df.global.cur_season_tick } )
|
||||
end
|
||||
|
||||
allEventTypes[eventType]()
|
@ -0,0 +1,125 @@
|
||||
-- Save a copy of a text screen for the DF forums. Use 'forumdwarves help' for more details.
|
||||
-- original author: Caldfir
|
||||
-- edited by expwnent
|
||||
|
||||
local args = {...}
|
||||
|
||||
if args[1] == 'help' then
|
||||
print([[
|
||||
description:
|
||||
This script will attempt to read the current df-screen, and if it is a
|
||||
text-viewscreen (such as the dwarf 'thoughts' screen or an item
|
||||
'description') then append a marked-up version of this text to the
|
||||
target file. Previous entries in the file are not overwritten, so you
|
||||
may use the 'forumdwarves' command multiple times to create a single
|
||||
document containing the text from multiple screens (eg: text screens
|
||||
from several dwarves, or text screens from multiple artifacts/items,
|
||||
or some combination).
|
||||
known screens:
|
||||
The screens which have been tested and known to function properly with
|
||||
this script are:
|
||||
1: dwarf/unit 'thoughts' screen
|
||||
2: item/art 'description' screen
|
||||
3: individual 'historical item/figure' screens
|
||||
There may be other screens to which the script applies. It should be
|
||||
safe to attempt running the script with any screen active, with an
|
||||
error message to inform you when the selected screen is not appropriate
|
||||
for this script.
|
||||
target file:
|
||||
The target file's name is 'forumdwarves.txt'. A remider to this effect
|
||||
will be displayed if the script is successful.
|
||||
character encoding:
|
||||
The text will likely be using system-default encoding, and as such
|
||||
will likely NOT display special characters (eg:é,õ,ç) correctly. To
|
||||
fix this, you need to modify the character set that you are reading
|
||||
the document with. 'Notepad++' is a freely available program which
|
||||
can do this using the following steps:
|
||||
1: open the document in Notepad++
|
||||
2: in the menu-bar, select
|
||||
Encoding->Character Sets->Western European->OEM-US
|
||||
3: copy the text normally to wherever you want to use it
|
||||
]])
|
||||
return
|
||||
end
|
||||
local utils = require 'utils'
|
||||
local gui = require 'gui'
|
||||
local dialog = require 'gui.dialogs'
|
||||
local colors_css = {
|
||||
[0] = 'black',
|
||||
[1] = 'navy',
|
||||
[2] = 'green',
|
||||
[3] = 'teal',
|
||||
[4] = 'maroon',
|
||||
[5] = 'purple',
|
||||
[6] = 'olive',
|
||||
[7] = 'silver',
|
||||
[8] = 'gray',
|
||||
[9] = 'blue',
|
||||
[10] = 'lime',
|
||||
[11] = 'cyan',
|
||||
[12] = 'red',
|
||||
[13] = 'magenta',
|
||||
[14] = 'yellow',
|
||||
[15] = 'white'
|
||||
}
|
||||
|
||||
local scrn = dfhack.gui.getCurViewscreen()
|
||||
local flerb = dfhack.gui.getFocusString(scrn)
|
||||
|
||||
local function format_for_forum(strin)
|
||||
local strout = strin
|
||||
|
||||
local newline_idx = string.find(strout, '[P]', 1, true)
|
||||
while newline_idx ~= nil do
|
||||
strout = string.sub(strout,1, newline_idx-1)..'\n'..string.sub(strout,newline_idx+3)
|
||||
newline_idx = string.find(strout, '[P]', 1, true)
|
||||
end
|
||||
|
||||
newline_idx = string.find(strout, '[B]', 1, true)
|
||||
while newline_idx ~= nil do
|
||||
strout = string.sub(strout,1, newline_idx-1)..'\n'..string.sub(strout,newline_idx+3)
|
||||
newline_idx = string.find(strout, '[B]', 1, true)
|
||||
end
|
||||
|
||||
newline_idx = string.find(strout, '[R]', 1, true)
|
||||
while newline_idx ~= nil do
|
||||
strout = string.sub(strout,1, newline_idx-1)..'\n'..string.sub(strout,newline_idx+3)
|
||||
newline_idx = string.find(strout, '[R]', 1, true)
|
||||
end
|
||||
|
||||
local color_idx = string.find(strout, '[C:', 1, true)
|
||||
while color_idx ~= nil do
|
||||
local colormatch = (string.byte(strout, color_idx+3)-48)+((string.byte(strout, color_idx+7)-48)*8)
|
||||
strout = string.sub(strout,1, color_idx-1)..'[/color][color='..colors_css[colormatch]..']'..string.sub(strout,color_idx+9)
|
||||
color_idx = string.find(strout, '[C:', 1, true)
|
||||
end
|
||||
|
||||
return strout
|
||||
end
|
||||
|
||||
if flerb == 'textviewer' then
|
||||
print(scrn)
|
||||
printall(scrn)
|
||||
local lines = scrn.formatted_text
|
||||
local line = ""
|
||||
|
||||
if lines ~= nil then
|
||||
local log = io.open('forumdwarves.txt', 'a')
|
||||
log:write("[color=silver]")
|
||||
for n,x in ipairs(lines) do
|
||||
print(x)
|
||||
printall(x)
|
||||
print(x.text)
|
||||
printall(x.text)
|
||||
if (x ~= nil) and (x.text ~= nil) then
|
||||
log:write(format_for_forum(x.text), ' ')
|
||||
--log:write(x[0],'\n')
|
||||
end
|
||||
end
|
||||
log:write("[/color]\n")
|
||||
log:close()
|
||||
end
|
||||
print 'data prepared for forum in \"forumdwarves.txt\"'
|
||||
else
|
||||
print 'this is not a textview screen'
|
||||
end
|
@ -0,0 +1,127 @@
|
||||
--fullheal.lua
|
||||
--author Kurik Amudnil, Urist DaVinci
|
||||
--edited by expwnent
|
||||
|
||||
-- attempt to fully heal a selected unit, option -r to attempt to resurrect the unit
|
||||
local args = {...}
|
||||
local resurrect = false
|
||||
local i=0
|
||||
for _,arg in ipairs(args) do
|
||||
if arg == '-r' or arg == '-R' then
|
||||
resurrect = true
|
||||
elseif tonumber(arg) then
|
||||
unit = df.unit.find(tonumber(arg))
|
||||
elseif arg == 'help' or arg == '-help' or arg == '-h' then
|
||||
print('fullheal: heal a unit completely from anything, optionally including death.')
|
||||
print(' fullheal [unitId]')
|
||||
print(' heal the unit with the given id')
|
||||
print(' fullheal -r [unitId]')
|
||||
print(' heal the unit with the given id and bring them back from death if they are dead')
|
||||
print(' fullheal')
|
||||
print(' heal the currently selected unit')
|
||||
print(' fullheal -r')
|
||||
print(' heal the currently selected unit and bring them back from death if they are dead')
|
||||
print(' fullheal help')
|
||||
print(' print this help message')
|
||||
return
|
||||
end
|
||||
end
|
||||
unit = unit or dfhack.gui.getSelectedUnit()
|
||||
if not unit then
|
||||
qerror('Error: please select a unit or pass its id as an argument.')
|
||||
end
|
||||
|
||||
if unit then
|
||||
if resurrect then
|
||||
if unit.flags1.dead then
|
||||
--print("Resurrecting...")
|
||||
unit.flags2.slaughter = false
|
||||
unit.flags3.scuttle = false
|
||||
end
|
||||
unit.flags1.dead = false
|
||||
unit.flags2.killed = false
|
||||
unit.flags3.ghostly = false
|
||||
--unit.unk_100 = 3
|
||||
end
|
||||
|
||||
--print("Erasing wounds...")
|
||||
while #unit.body.wounds > 0 do
|
||||
unit.body.wounds:erase(#unit.body.wounds-1)
|
||||
end
|
||||
unit.body.wound_next_id=1
|
||||
|
||||
--print("Refilling blood...")
|
||||
unit.body.blood_count=unit.body.blood_max
|
||||
|
||||
--print("Resetting grasp/stand status...")
|
||||
unit.status2.limbs_stand_count=unit.status2.limbs_stand_max
|
||||
unit.status2.limbs_grasp_count=unit.status2.limbs_grasp_max
|
||||
|
||||
--print("Resetting status flags...")
|
||||
unit.flags2.has_breaks=false
|
||||
unit.flags2.gutted=false
|
||||
unit.flags2.circulatory_spray=false
|
||||
unit.flags2.vision_good=true
|
||||
unit.flags2.vision_damaged=false
|
||||
unit.flags2.vision_missing=false
|
||||
unit.flags2.breathing_good=true
|
||||
unit.flags2.breathing_problem=false
|
||||
|
||||
unit.flags2.calculated_nerves=false
|
||||
unit.flags2.calculated_bodyparts=false
|
||||
unit.flags2.calculated_insulation=false
|
||||
unit.flags3.compute_health=true
|
||||
|
||||
--print("Resetting counters...")
|
||||
unit.counters.winded=0
|
||||
unit.counters.stunned=0
|
||||
unit.counters.unconscious=0
|
||||
unit.counters.webbed=0
|
||||
unit.counters.pain=0
|
||||
unit.counters.nausea=0
|
||||
unit.counters.dizziness=0
|
||||
|
||||
unit.counters2.paralysis=0
|
||||
unit.counters2.fever=0
|
||||
unit.counters2.exhaustion=0
|
||||
unit.counters2.hunger_timer=0
|
||||
unit.counters2.thirst_timer=0
|
||||
unit.counters2.sleepiness_timer=0
|
||||
unit.counters2.vomit_timeout=0
|
||||
|
||||
--print("Resetting body part status...")
|
||||
v=unit.body.components
|
||||
for i=0,#v.nonsolid_remaining - 1,1 do
|
||||
v.nonsolid_remaining[i] = 100 -- percent remaining of fluid layers (Urist Da Vinci)
|
||||
end
|
||||
|
||||
v=unit.body.components
|
||||
for i=0,#v.layer_wound_area - 1,1 do
|
||||
v.layer_status[i].whole = 0 -- severed, leaking layers (Urist Da Vinci)
|
||||
v.layer_wound_area[i] = 0 -- wound contact areas (Urist Da Vinci)
|
||||
v.layer_cut_fraction[i] = 0 -- 100*surface percentage of cuts/fractures on the body part layer (Urist Da Vinci)
|
||||
v.layer_dent_fraction[i] = 0 -- 100*surface percentage of dents on the body part layer (Urist Da Vinci)
|
||||
v.layer_effect_fraction[i] = 0 -- 100*surface percentage of "effects" on the body part layer (Urist Da Vinci)
|
||||
end
|
||||
|
||||
v=unit.body.components.body_part_status
|
||||
for i=0,#v-1,1 do
|
||||
v[i].on_fire = false
|
||||
v[i].missing = false
|
||||
v[i].organ_loss = false
|
||||
v[i].organ_damage = false
|
||||
v[i].muscle_loss = false
|
||||
v[i].muscle_damage = false
|
||||
v[i].bone_loss = false
|
||||
v[i].bone_damage = false
|
||||
v[i].skin_damage = false
|
||||
v[i].motor_nerve_severed = false
|
||||
v[i].sensory_nerve_severed = false
|
||||
end
|
||||
|
||||
if unit.job.current_job and unit.job.current_job.job_type == df.job_type.Rest then
|
||||
--print("Wake from rest -> clean self...")
|
||||
unit.job.current_job = df.job_type.CleanSelf
|
||||
end
|
||||
end
|
||||
|
@ -0,0 +1,26 @@
|
||||
--growthbug: units only grow when the current tick is 0 mod 10, so only 1/10 units will grow naturally. this script periodically sets the birth time of each unit so that it will grow
|
||||
--to run periodically, use "repeat enable 2 months growthBug now". see repeat.lua for details
|
||||
-- author expwnent
|
||||
|
||||
local args = {...}
|
||||
if args[1] ~= nil then
|
||||
print("growthbug usage")
|
||||
print(" growthbug")
|
||||
print(" fix the growth bug for all units on the map")
|
||||
print(" growthbug help")
|
||||
print(" print this help message")
|
||||
print(" repeat enable [n] [years/months/ticks/days/etc] growthbug")
|
||||
print(" run this script every n time units")
|
||||
print(" repeat disable growthbug")
|
||||
print(" stop automatically running this script")
|
||||
end
|
||||
|
||||
local count = 0
|
||||
for _,unit in ipairs(df.world.units.all) do
|
||||
local offset = unit.relations.birth_time % 10;
|
||||
if offset ~= 0 then
|
||||
count = count+1
|
||||
unit.relations.birth_time = unit.relations.birth_time - offset
|
||||
end
|
||||
end
|
||||
print("Fixed growth bug for "..count.." units.")
|
@ -0,0 +1,251 @@
|
||||
-- hackWish.lua
|
||||
-- Allows for script-based wishing.
|
||||
-- author Putnam
|
||||
-- edited by expwnent
|
||||
|
||||
function getGenderString(gender)
|
||||
local genderStr
|
||||
if gender==0 then
|
||||
genderStr=string.char(12)
|
||||
elseif gender==1 then
|
||||
genderStr=string.char(11)
|
||||
else
|
||||
return ""
|
||||
end
|
||||
return string.char(40)..genderStr..string.char(41)
|
||||
end
|
||||
|
||||
function getCreatureList()
|
||||
local crList={}
|
||||
for k,cr in ipairs(df.global.world.raws.creatures.alphabetic) do
|
||||
for kk,ca in ipairs(cr.caste) do
|
||||
local str=ca.caste_name[0]
|
||||
str=str..' '..getGenderString(ca.gender)
|
||||
table.insert(crList,{str,nil,ca})
|
||||
end
|
||||
end
|
||||
return crList
|
||||
end
|
||||
|
||||
function getMatFilter(itemtype)
|
||||
local itemTypes={
|
||||
SEEDS=function(mat,parent,typ,idx)
|
||||
return mat.flags.SEED_MAT
|
||||
end,
|
||||
PLANT=function(mat,parent,typ,idx)
|
||||
return mat.flags.STRUCTURAL_PLANT_MAT
|
||||
end,
|
||||
LEAVES=function(mat,parent,typ,idx)
|
||||
return mat.flags.LEAF_MAT
|
||||
end,
|
||||
MEAT=function(mat,parent,typ,idx)
|
||||
return mat.flags.MEAT
|
||||
end,
|
||||
CHEESE=function(mat,parent,typ,idx)
|
||||
return (mat.flags.CHEESE_PLANT or mat.flags.CHEESE_CREATURE)
|
||||
end,
|
||||
LIQUID_MISC=function(mat,parent,typ,idx)
|
||||
return (mat.flags.LIQUID_MISC_PLANT or mat.flags.LIQUID_MISC_CREATURE or mat.flags.LIQUID_MISC_OTHER)
|
||||
end,
|
||||
POWDER_MISC=function(mat,parent,typ,idx)
|
||||
return (mat.flags.POWDER_MISC_PLANT or mat.flags.POWDER_MISC_CREATURE)
|
||||
end,
|
||||
DRINK=function(mat,parent,typ,idx)
|
||||
return (mat.flags.ALCOHOL_PLANT or mat.flags.ALCOHOL_CREATURE)
|
||||
end,
|
||||
GLOB=function(mat,parent,typ,idx)
|
||||
return (mat.flags.STOCKPILE_GLOB)
|
||||
end,
|
||||
WOOD=function(mat,parent,typ,idx)
|
||||
return (mat.flags.WOOD)
|
||||
end,
|
||||
THREAD=function(mat,parent,typ,idx)
|
||||
return (mat.flags.THREAD_PLANT)
|
||||
end,
|
||||
LEATHER=function(mat,parent,typ,idx)
|
||||
return (mat.flags.LEATHER)
|
||||
end
|
||||
}
|
||||
return itemTypes[df.item_type[itemtype]] or getRestrictiveMatFilter(itemtype)
|
||||
end
|
||||
|
||||
function getRestrictiveMatFilter(itemType)
|
||||
if not args.veryRestrictive then return nil else
|
||||
local itemTypes={
|
||||
WEAPON=function(mat,parent,typ,idx)
|
||||
return (mat.flags.ITEMS_WEAPON or mat.flags.ITEMS_WEAPON_RANGED)
|
||||
end,
|
||||
AMMO=function(mat,parent,typ,idx)
|
||||
return (mat.flags.ITEMS_AMMO)
|
||||
end,
|
||||
ARMOR=function(mat,parent,typ,idx)
|
||||
return (mat.flags.ITEMS_ARMOR)
|
||||
end,
|
||||
SHOES,SHIELD,HELM,GLOVES=ARMOR,ARMOR,ARMOR,ARMOR,
|
||||
INSTRUMENT=function(mat,parent,typ,idx)
|
||||
return (mat.flags.ITEMS_HARD)
|
||||
end,
|
||||
GOBLET,FLASK,TOY,RING,CROWN,SCEPTER,FIGURINE,TOOL=INSTRUMENT,INSTRUMENT,INSTRUMENT,INSTRUMENT,INSTRUMENT,INSTRUMENT,INSTRUMENT,
|
||||
AMULET=function(mat,parent,typ,idx)
|
||||
return (mat.flags.ITEMS_SOFT or mat.flags.ITEMS_HARD)
|
||||
end,
|
||||
EARRING,BRACELET=AMULET,AMULET,
|
||||
ROCK=function(mat,parent,typ,idx)
|
||||
return (mat.flags.IS_STONE)
|
||||
end,
|
||||
BOULDER=ROCK,
|
||||
BAR=function(mat,parent,typ,idx)
|
||||
return (mat.flags.IS_METAL or mat.flags.SOAP or mat.id==COAL)
|
||||
end
|
||||
}
|
||||
return itemTypes[df.item_type[itemType]]
|
||||
end
|
||||
end
|
||||
|
||||
function createItem(mat,itemType,quality,pos,description)
|
||||
local item=df[df.item_type.attrs[itemType[1]].classname]:new()
|
||||
item.id=df.global.item_next_id
|
||||
df.global.world.items.all:insert('#',item)
|
||||
df.global.item_next_id=df.global.item_next_id+1
|
||||
item:setSubtype(itemType[2])
|
||||
item:setMaterial(mat[1])
|
||||
item:setMaterialIndex(mat[2])
|
||||
if df.item_type[itemType[1]]=='EGG' then
|
||||
local creature=df.creature_raw.find(mat[1])
|
||||
local eggMat={}
|
||||
eggMat[1]=dfhack.matinfo.find(creature.creature_id..':EGGSHELL')
|
||||
if eggMat[1] then
|
||||
eggMat[2]=dfhack.matinfo.find(creature.creature_id..':EGG_WHITE')
|
||||
eggMat[3]=dfhack.matinfo.find(creature.creature_id..'EGG_YOLK')
|
||||
for k,v in ipairs(eggMat) do
|
||||
item.egg_materials.mat_type:insert('#',v.type)
|
||||
item.egg_materials.mat_index:insert('#',v.index)
|
||||
end
|
||||
else
|
||||
eggMat=dfhack.matinfo.find(creature.creature_id..':MUSCLE')
|
||||
item.egg_materials.mat_type:insert('#',eggMat.type)
|
||||
item.egg_materials.mat_index:insert('#',eggMat.index)
|
||||
end
|
||||
end
|
||||
item:categorize(true)
|
||||
item.flags.removed=true
|
||||
item:setSharpness(1,0)
|
||||
item:setQuality(quality-1)
|
||||
if df.item_type[itemType[1]]=='SLAB' then
|
||||
item.description=description
|
||||
end
|
||||
dfhack.items.moveToGround(item,{x=pos.x,y=pos.y,z=pos.z})
|
||||
end
|
||||
|
||||
--TODO: should this be a function?
|
||||
function qualityTable()
|
||||
return {{'None'},
|
||||
{'-Well-crafted-'},
|
||||
{'+Finely-crafted+'},
|
||||
{'*Superior*'},
|
||||
{string.char(240)..'Exceptional'..string.char(240)},
|
||||
{string.char(15)..'Masterwork'..string.char(15)}
|
||||
}
|
||||
end
|
||||
|
||||
local script=require('gui/script')
|
||||
local guimaterials=require('gui.materials')
|
||||
|
||||
function showItemPrompt(text,item_filter,hide_none)
|
||||
guimaterials.ItemTypeDialog{
|
||||
prompt=text,
|
||||
item_filter=item_filter,
|
||||
hide_none=hide_none,
|
||||
on_select=script.mkresume(true),
|
||||
on_cancel=script.mkresume(false),
|
||||
on_close=script.qresume(nil)
|
||||
}:show()
|
||||
|
||||
return script.wait()
|
||||
end
|
||||
|
||||
function showMaterialPrompt(title, prompt, filter, inorganic, creature, plant) --the one included with DFHack doesn't have a filter or the inorganic, creature, plant things available
|
||||
guimaterials.MaterialDialog{
|
||||
frame_title = title,
|
||||
prompt = prompt,
|
||||
mat_filter = filter,
|
||||
use_inorganic = inorganic,
|
||||
use_creature = creature,
|
||||
use_plant = plant,
|
||||
on_select = script.mkresume(true),
|
||||
on_cancel = script.mkresume(false),
|
||||
on_close = script.qresume(nil)
|
||||
}:show()
|
||||
|
||||
return script.wait()
|
||||
end
|
||||
|
||||
function usesCreature(itemtype)
|
||||
typesThatUseCreatures={REMAINS=true,FISH=true,FISH_RAW=true,VERMIN=true,PET=true,EGG=true,CORPSE=true,CORPSEPIECE=true}
|
||||
return typesThatUseCreatures[df.item_type[itemtype]]
|
||||
end
|
||||
|
||||
function getCreatureRaceAndCaste(caste)
|
||||
return df.global.world.raws.creatures.list_creature[caste.index],df.global.world.raws.creatures.list_caste[caste.index]
|
||||
end
|
||||
|
||||
function hackWish(posOrUnit)
|
||||
local pos = df.unit:is_instance(posOrUnit) and posOrUnit.pos or posOrUnit
|
||||
script.start(function()
|
||||
--local amountok, amount
|
||||
local matok,mattype,matindex,matFilter
|
||||
local itemok,itemtype,itemsubtype=showItemPrompt('What item do you want?',function(itype) return df.item_type[itype]~='CORPSE' and df.item_type[itype]~='FOOD' end ,true)
|
||||
if not args.notRestrictive then
|
||||
matFilter=getMatFilter(itemtype)
|
||||
end
|
||||
if not usesCreature(itemtype) then
|
||||
matok,mattype,matindex=showMaterialPrompt('Wish','And what material should it be made of?',matFilter)
|
||||
else
|
||||
local creatureok,useless,creatureTable=script.showListPrompt('Wish','What creature should it be?',COLOR_LIGHTGREEN,getCreatureList())
|
||||
mattype,matindex=getCreatureRaceAndCaste(creatureTable[3])
|
||||
end
|
||||
local qualityok,quality=script.showListPrompt('Wish','What quality should it be?',COLOR_LIGHTGREEN,qualityTable())
|
||||
local description
|
||||
if df.item_type[itemtype]=='SLAB' then
|
||||
local descriptionok
|
||||
descriptionok,description=script.showInputPrompt('Slab','What should the slab say?',COLOR_WHITE)
|
||||
end
|
||||
--repeat amountok,amount=script.showInputPrompt('Wish','How many do you want? (numbers only!)',COLOR_LIGHTGREEN) until tonumber(amount)
|
||||
if mattype and itemtype then
|
||||
--for i=1,tonumber(amount) do
|
||||
createItem({mattype,matindex},{itemtype,itemsubtype},quality,pos,description)
|
||||
--end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
scriptArgs={...}
|
||||
|
||||
args={}
|
||||
|
||||
for k,v in ipairs(scriptArgs) do
|
||||
v=v:lower()
|
||||
if v=='startup' then args.startup=true end
|
||||
if v=='all' then args.notRestrictive=true end
|
||||
if v=='restrictive' then args.veryRestrictive=true end
|
||||
if v=='unit' then args.unitNum=args[k+1] end
|
||||
if v=='x' then args.x=args[k+1] end
|
||||
if v=='y' then args.y=args[k+1] end
|
||||
if v=='z' then args.z=args[k+1] end
|
||||
end
|
||||
|
||||
eventful=require('plugins.eventful')
|
||||
|
||||
function posIsValid(pos)
|
||||
return pos.x~=-30000 and pos or false
|
||||
end
|
||||
|
||||
if not args.startup then
|
||||
local posOrUnit=args.x and {x=args.x,y=args.y,z=args.z} or args.unitNum and df.unit.find(args.unitNum) or posIsValid(df.global.cursor) or dfhack.gui.getSelectedUnit(true)
|
||||
hackWish(posOrUnit)
|
||||
else
|
||||
eventful.onReactionComplete.hackWishP=function(reaction,unit,input_items,input_reagents,output_items, call_native)
|
||||
if not reaction.code:find('DFHACK_WISH') then return nil end
|
||||
hackWish(unit)
|
||||
end
|
||||
end
|
@ -0,0 +1,223 @@
|
||||
-- itemsyndrome.lua
|
||||
-- author: Putnam
|
||||
-- edited by expwnent
|
||||
-- Checks for inventory changes and applies or removes syndromes that items or their materials have. Use "disable" (minus quotes) to disable and "help" to get help.
|
||||
|
||||
eventful=eventful or require("plugins.eventful")
|
||||
syndromeUtil = syndromeUtil or require("plugins.syndromeUtil")
|
||||
|
||||
local function printItemSyndromeHelp()
|
||||
print("Arguments (non case-sensitive):")
|
||||
print(' "help": displays this dialogue.')
|
||||
print(" ")
|
||||
print(' "disable": disables the script.')
|
||||
print(" ")
|
||||
print(' "debugon/debugoff": debug mode.')
|
||||
print(" ")
|
||||
print(' "contaminantson/contaminantsoff": toggles searching for contaminants.')
|
||||
print(' Disabling speeds itemsyndrome up greatly.')
|
||||
print(' "transformReEquipOn/TransformReEquipOff": toggles transformation auto-reequip.')
|
||||
end
|
||||
|
||||
itemsyndromedebug=false
|
||||
|
||||
local args = {...}
|
||||
for k,v in ipairs(args) do
|
||||
v=v:lower()
|
||||
if v == "help" then printItemSyndromeHelp() return end
|
||||
if v == "debugon" then itemsyndromedebug = true end
|
||||
if v == "debugoff" then itemsyndromedebug = false end
|
||||
if v == "contaminantson" then itemsyndromecontaminants = true end
|
||||
if v == "contaminantsoff" then itemsyndromecontaminants = false end
|
||||
if v == "transformreequipon" then transformationReEquip = true end
|
||||
if v == "transformreequipoff" then transformationReEquip = false end
|
||||
end
|
||||
|
||||
local function getMaterial(item)
|
||||
--What does this line mean?
|
||||
local material = dfhack.matinfo.decode(item) and dfhack.matinfo.decode(item) or false
|
||||
if not material then return nil end
|
||||
if material.mode ~= "inorganic" then
|
||||
return nil
|
||||
else
|
||||
return material.material --the "material" thing up there contains a bit more info which is all pretty important but impertinent, like the creature that the material comes from
|
||||
end
|
||||
end
|
||||
|
||||
local function findItemSyndromeInorganic()
|
||||
local allInorganics = {}
|
||||
for matID,material in ipairs(df.global.world.raws.inorganics) do
|
||||
if string.find(material.id,"DFHACK_ITEMSYNDROME_MATERIAL_") then table.insert(allInorganics,matID) end --the last underscore is needed to prevent duped raws; I want good modder courtesy if it kills me, dammit!
|
||||
end
|
||||
if itemsyndromedebug then printall(allInorganics) end
|
||||
if #allInorganics>0 then return allInorganics else return nil end
|
||||
end
|
||||
|
||||
local function getAllItemSyndromeMats(itemSyndromeMatIDs)
|
||||
local allActualInorganics = {}
|
||||
for _,itemSyndromeMatID in ipairs(itemSyndromeMatIDs) do
|
||||
table.insert(allActualInorganics,df.global.world.raws.inorganics[itemSyndromeMatID].material)
|
||||
end
|
||||
if itemsyndromedebug then printall(allActualInorganics) end
|
||||
return allActualInorganics
|
||||
end
|
||||
|
||||
local function syndromeIsDfHackSyndrome(syndrome)
|
||||
for k,v in ipairs(syndrome.syn_class) do
|
||||
if v.value=="DFHACK_ITEM_SYNDROME" then
|
||||
if itemsyndromedebug then print('Syndrome is DFHack syndrome, checking if creature is affected...') end
|
||||
return true
|
||||
end
|
||||
end
|
||||
if itemsyndromedebug then print('Syndrome is not DFHack syndrome. Cancelling.') end
|
||||
return false
|
||||
end
|
||||
|
||||
local function itemHasNoSubtype(item)
|
||||
return item:getSubtype()==-1
|
||||
end
|
||||
|
||||
local function itemHasSyndrome(item)
|
||||
if itemHasNoSubtype(item) or not itemSyndromeMats then return nil end
|
||||
local allItemSyndromes={}
|
||||
for _,material in ipairs(itemSyndromeMats) do
|
||||
for __,syndrome in ipairs(material.syndrome) do
|
||||
if syndrome.syn_name == item.subtype.name then table.insert(allItemSyndromes,syndrome) end
|
||||
end
|
||||
end
|
||||
return #allItemSyndromes>0 and allItemSyndromes or false
|
||||
end
|
||||
|
||||
local function getValidPositions(syndrome)
|
||||
local returnTable={AffectsHauler=false,AffectsStuckins=false,IsArmorOnly=false,IsWieldedOnly=false,OnlyAffectsStuckins=false}
|
||||
for k,v in ipairs(syndrome.syn_class) do
|
||||
if v.value:find('DFHACK') then
|
||||
if v.value=="DFHACK_AFFECTS_HAULER" then returnTable.AffectsHauler=true end
|
||||
if v.value=="DFHACK_AFFECTS_STUCKIN" then returnTable.AffectsStuckins=true end
|
||||
if v.value=="DFHACK_STUCKINS_ONLY" then returnTable.OnlyAffectsStuckins=true end
|
||||
if v.value=="DFHACK_WIELDED_ONLY" then returnTable.IsWieldedOnly=true end
|
||||
if v.value=="DFHACK_ARMOR_ONLY" then returnTable.IsArmorOnly=true end
|
||||
end
|
||||
end
|
||||
return returnTable
|
||||
end
|
||||
|
||||
local function itemIsInValidPosition(item_inv, syndrome)
|
||||
local item = getValidPositions(syndrome)
|
||||
if not item_inv then print("You shouldn't see this error! At all! Putnam f'd up! Tell him off!") return false end
|
||||
local isInValidPosition=not ((item_inv.mode == 0 and not item.AffectsHauler) or (item_inv.mode == 7 and not item.AffectsStuckins) or (item_inv.mode ~= 2 and item.IsArmorOnly) or (item_inv.mode ~=1 and item.IsWieldedOnly) or (item_inv.mode ~=7 and item.OnlyAffectsStuckins))
|
||||
if itemsyndromedebug then print(isInValidPosition and 'Item is in correct position.' or 'Item is not in correct position.') end
|
||||
return isInValidPosition
|
||||
end
|
||||
|
||||
local function syndromeIsTransformation(syndrome)
|
||||
for _,effect in ipairs(syndrome.ce) do
|
||||
if df.creature_interaction_effect_body_transformationst:is_instance(effect) then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function rememberInventory(unit)
|
||||
local invCopy = {}
|
||||
for inv_id,item_inv in ipairs(unit.inventory) do
|
||||
invCopy[inv_id+1] = {}
|
||||
local itemToWorkOn = invCopy[inv_id+1]
|
||||
itemToWorkOn.item = item_inv.item
|
||||
itemToWorkOn.mode = item_inv.mode
|
||||
itemToWorkOn.body_part_id = item_inv.body_part_id
|
||||
end
|
||||
return invCopy
|
||||
end
|
||||
|
||||
local function moveAllToInventory(unit,invTable)
|
||||
for _,item_inv in ipairs(invTable) do
|
||||
dfhack.items.moveToInventory(item_inv.item,unit,item_inv.mode,item_inv.body_part_id)
|
||||
end
|
||||
end
|
||||
|
||||
local function syndromeIsOnUnequip(syndrome)
|
||||
for k,v in ipairs(syndrome.syn_class) do
|
||||
if v.value:upper()=='DFHACK_ON_UNEQUIP' then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function addOrRemoveSyndromeDepending(unit,old_equip,new_equip,syndrome)
|
||||
local item_inv=new_equip or old_equip
|
||||
if not syndromeIsDfHackSyndrome(syndrome) then
|
||||
return
|
||||
end
|
||||
local equippedOld = itemIsInValidPosition(old_equip,syndrome)
|
||||
local equippedNew = itemIsInValidPosition(new_equip,syndrome)
|
||||
if equippedOld == equippedNew then
|
||||
return
|
||||
end
|
||||
local isOnEquip = not syndromeIsOnUnequip(syndrome)
|
||||
local apply = (isOnEquip and equippedNew) or (not isOnEquip and not equippedNew)
|
||||
if apply then
|
||||
syndromeUtil.infectWithSyndrome(unit,syndrome,syndromeUtil.ResetPolicy.ResetDuration)
|
||||
else
|
||||
syndromeUtil.eraseSyndrome(unit,syndrome)
|
||||
end
|
||||
end
|
||||
|
||||
eventful.enableEvent(eventful.eventType.INVENTORY_CHANGE,5)
|
||||
|
||||
eventful.onInventoryChange.itemsyndrome=function(unit_id,item_id,old_equip,new_equip)
|
||||
local item = df.item.find(item_id)
|
||||
--if not item then return false end
|
||||
if not item then
|
||||
return
|
||||
end
|
||||
local unit = df.unit.find(unit_id)
|
||||
if unit.flags1.dead then return false end
|
||||
if itemsyndromedebug then print("Checking unit #" .. unit_id) end
|
||||
local transformation = false
|
||||
if itemsyndromedebug then print("checking item #" .. item_id .." on unit #" .. unit_id) end
|
||||
local itemMaterial=getMaterial(item)
|
||||
local function manageSyndromes(syndromes)
|
||||
for k,syndrome in ipairs(syndromes) do
|
||||
if itemsyndromedebug then print("item has a syndrome, checking if syndrome is valid for application...") end
|
||||
if syndromeIsTransformation(syndrome) then
|
||||
--unitInventory = rememberInventory(unit)
|
||||
rememberInventory(unit)
|
||||
transformation = true
|
||||
end
|
||||
addOrRemoveSyndromeDepending(unit,old_equip,new_equip,syndrome)
|
||||
end
|
||||
end
|
||||
if itemMaterial then
|
||||
manageSyndromes(itemMaterial.syndrome)
|
||||
end
|
||||
local itemSyndromes = itemHasSyndrome(item)
|
||||
if itemSyndromes then
|
||||
if itemsyndromedebug then print("Item itself has a syndrome, checking if item is in correct position and creature is affected") end
|
||||
manageSyndromes(itemSyndromes)
|
||||
end
|
||||
if itemsyndromecontaminants and item.contaminants then
|
||||
if itemsyndromedebug then print("Item has contaminants. Checking for syndromes...") end
|
||||
for _,contaminant in ipairs(item.contaminants) do
|
||||
local contaminantMaterial=getMaterial(contaminant)
|
||||
if contaminantMaterial then
|
||||
manageSyndromes(contaminantMaterial.syndrome)
|
||||
end
|
||||
end
|
||||
end
|
||||
if transformation and transformationReEquip then dfhack.timeout(2,"ticks",function() moveAllToInventory(unit,unitInventory) end) end
|
||||
end
|
||||
|
||||
dfhack.onStateChange.itemsyndrome=function(code)
|
||||
if code==SC_WORLD_LOADED then
|
||||
itemSyndromeMatIDs = findItemSyndromeInorganic()
|
||||
if itemSyndromeMatIDs then itemSyndromeMats = getAllItemSyndromeMats(itemSyndromeMatIDs) end
|
||||
end
|
||||
end
|
||||
|
||||
if disable then
|
||||
eventful.onInventoryChange.itemsyndrome=nil
|
||||
print("Disabled itemsyndrome.")
|
||||
disable = false
|
||||
else
|
||||
print("Enabled itemsyndrome.")
|
||||
end
|
||||
|
@ -0,0 +1,152 @@
|
||||
-- moddableGods.lua
|
||||
-- Sets player-defined gods to correct civilizations.
|
||||
-- author: Putnam
|
||||
-- edited by expwnent
|
||||
|
||||
--[[Here's an example of how to make a god:
|
||||
|
||||
[CREATURE:SHEOGORATH]
|
||||
[DOES_NOT_EXIST]
|
||||
[MALE]
|
||||
[NAME:jovial man:Daedra:madness] "Sheogorath, madness god." "Often depicted as a jovial man"
|
||||
[CASTE_NAME:Sheogorath:Sheogorath:Sheogorath]
|
||||
[DESCRIPTION:The Daedric Prince of madness.]
|
||||
[CREATURE_CLASS:DFHACK_GOD]
|
||||
[SPHERE:MUSIC]
|
||||
[SPHERE:ART]
|
||||
[SPHERE:CHAOS]
|
||||
]]
|
||||
|
||||
local function getCreatureClasses(creatureRaw)
|
||||
local creatureClasses = {}
|
||||
for _,caste in ipairs(creatureRaw.caste) do
|
||||
for k,class in ipairs(caste.creature_class) do
|
||||
table.insert(creatureClasses,class.value)
|
||||
end
|
||||
end
|
||||
return creatureClasses
|
||||
end
|
||||
|
||||
local function deityIsOfSpecialCreature(creatureRaw)
|
||||
for k,class in ipairs(getCreatureClasses(creatureRaw)) do
|
||||
if class=="DFHACK_GOD" then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function scriptAlreadyRunOnThisWorld()
|
||||
local figures = df.global.world.history.figures
|
||||
for i=#figures-1,0,-1 do --goes through the table backwards because the particular hist figs involved are probably going to be the last
|
||||
local figure = figures[i]
|
||||
if not df.isnull(figure.flags) and figure.flags.deity and deityIsOfSpecialCreature(df.global.world.raws.creatures.all[figure.race]) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function findAGod()
|
||||
for k,fig in ipairs(df.global.world.history.figures) do
|
||||
if fig.flags.deity then
|
||||
return fig
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function putListOfAllSpecialCreatureGodsTogether()
|
||||
local specialCreatures = {}
|
||||
for k,creatureRaw in ipairs(df.global.world.raws.creatures.all) do
|
||||
if deityIsOfSpecialCreature(creatureRaw) then
|
||||
table.insert(specialCreatures,{creatureRaw,k})
|
||||
end
|
||||
end
|
||||
return specialCreatures
|
||||
end
|
||||
|
||||
local function stringStarts(String,Start)
|
||||
return string.sub(String,1,string.len(Start))==Start
|
||||
end
|
||||
|
||||
local function getRacesOfGod(god)
|
||||
local civList={}
|
||||
for k,class in ipairs(getCreatureClasses(god)) do
|
||||
if stringStarts(class,"WORSHIP_ENTITY_") then
|
||||
table.insert(civList,string.sub(class,15))
|
||||
end
|
||||
end
|
||||
return civList
|
||||
end
|
||||
|
||||
local function entityIsInGodsDomain(entity,entityRacesTable)
|
||||
for k,v in ipairs(entityRacesTable) do
|
||||
if v==entity.entity_raw.code then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function setUpTemplate(godFig,templateGod)
|
||||
godFig.appeared_year=-1
|
||||
godFig.born_year=-1
|
||||
godFig.born_seconds=-1
|
||||
godFig.curse_year=-1
|
||||
godFig.curse_seconds=-1
|
||||
godFig.old_year=-1
|
||||
godFig.old_seconds=-1
|
||||
godFig.died_year=-1
|
||||
godFig.died_seconds=-1
|
||||
godFig.name.has_name=true
|
||||
godFig.breed_id=-1
|
||||
godFig.flags:assign(templateGod.flags)
|
||||
godFig.id = df.global.hist_figure_next_id
|
||||
godFig.info = df.historical_figure_info:new()
|
||||
godFig.info.spheres={new=true}
|
||||
godFig.info.secret=df.historical_figure_info.T_secret:new()
|
||||
end
|
||||
|
||||
local function setUpGod(god,godID,templateGod)
|
||||
local godFig = df.historical_figure:new()
|
||||
setUpTemplate(godFig,templateGod)
|
||||
godFig.sex=god.caste[0].gender
|
||||
godFig.race=godID
|
||||
godFig.name.first_name=god.caste[0].caste_name[2] --the adjectival form of the caste_name is used for the god's name, E.G, [CASTE_NAME:god:god:armok]
|
||||
for k,v in ipairs(god.sphere) do --assigning spheres
|
||||
godFig.info.spheres:insert('#',v)
|
||||
end
|
||||
df.global.world.history.figures:insert('#',godFig)
|
||||
df.global.hist_figure_next_id=df.global.hist_figure_next_id+1
|
||||
return godFig
|
||||
end
|
||||
|
||||
--[[this function isn't really working right now so it's dummied out
|
||||
function setGodAsOfficialDeityOfItsParticularEntity(god,godFig)
|
||||
local entityRaces=getRacesOfGod(god)
|
||||
for k,entity in ipairs(df.global.world.entities.all) do
|
||||
if entityIsInGodsDomain(entity,entityRaces) then
|
||||
entity.unknown1b.worship:insert('#',godFig.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
]]
|
||||
local function moddableGods()
|
||||
if scriptAlreadyRunOnThisWorld() then
|
||||
print("Already run on world...")
|
||||
return false
|
||||
end
|
||||
local gods = putListOfAllSpecialCreatureGodsTogether()
|
||||
local templateGod=findAGod()
|
||||
for k,v in ipairs(gods) do --creature raws first
|
||||
local god = v[1]
|
||||
local godID = v[2]
|
||||
local godFig = setUpGod(god,godID,templateGod)
|
||||
--setGodAsOfficialDeityOfItsParticularEntity(god,godFig)
|
||||
end
|
||||
end
|
||||
|
||||
dfhack.onStateChange.letThereBeModdableGods = function(state)
|
||||
if state == SC_WORLD_LOADED and df.global.gamemode~=3 then --make sure that the gods show up only after the rest of the histfigs do
|
||||
moddableGods()
|
||||
end
|
||||
end
|
@ -0,0 +1,19 @@
|
||||
|
||||
local onReport = require 'plugins.onReport'
|
||||
reload('plugins.onReport')
|
||||
|
||||
onReport.triggers.onReportExample = function(reportId)
|
||||
-- print('report '..reportId..' happened!')
|
||||
local report = df.report.find(reportId)
|
||||
if not report then
|
||||
return
|
||||
end
|
||||
-- printall(report)
|
||||
-- print('\n')
|
||||
print(reportId .. ': ' .. df.announcement_type[report["type"]])
|
||||
for unitId,_ in pairs(onReport.eventToDwarf[reportId]) do
|
||||
print('relevant dwarf: ' .. unitId)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -0,0 +1,16 @@
|
||||
|
||||
local onStrike = require 'plugins.onStrike'
|
||||
local eventful = require 'plugins.eventful'
|
||||
--print(onStrike)
|
||||
|
||||
--onStrike.triggers.onStrikeExample = function(information)
|
||||
-- print(information.attacker .. ' attacks ' .. information.defender .. ': ' .. information.announcement)
|
||||
onStrike.triggers.onStrikeExample = function(attacker, defender, weapon, wound)
|
||||
if weapon then
|
||||
print(attacker.id..' weapon attacks '..defender.id .. ' with ' .. weapon.id)
|
||||
--df.global.pause_state = true
|
||||
else
|
||||
print(attacker.id..' attacks '..defender.id)
|
||||
end
|
||||
end
|
||||
|
@ -0,0 +1,9 @@
|
||||
--printArgs.lua
|
||||
--author expwnent
|
||||
--prints all the arguments on their own line with quotes around them. useful for debugging
|
||||
|
||||
local args = {...}
|
||||
print("printArgs")
|
||||
for _,arg in ipairs(args) do
|
||||
print("'"..arg.."'")
|
||||
end
|
@ -0,0 +1,190 @@
|
||||
-- projectileExpansion.lua
|
||||
-- author: Putnam
|
||||
-- edited by expwnent
|
||||
-- Adds extra functionality to projectiles. Use the argument "disable" (minus quotes) to disable.
|
||||
|
||||
local syndromeUtil = require("syndromeUtil")
|
||||
local events=require "plugins.eventful"
|
||||
|
||||
local flowtypes = {
|
||||
miasma = 0,
|
||||
mist = 1,
|
||||
mist2 = 2,
|
||||
dust = 3,
|
||||
lavaMist = 4,
|
||||
smoke = 5,
|
||||
dragonFire = 6,
|
||||
fireBreath = 7,
|
||||
web = 8,
|
||||
undirectedGas = 9,
|
||||
undirectedVapor = 10,
|
||||
oceanWave = 11,
|
||||
seaFoam = 12
|
||||
}
|
||||
|
||||
local function posIsEqual(pos1,pos2)
|
||||
if pos1.x ~= pos2.x or pos1.y ~= pos2.y or pos1.z ~= pos2.z then return false end
|
||||
return true
|
||||
end
|
||||
|
||||
local function getMaterial(item)
|
||||
if not dfhack.matinfo.decode(item) then return nil end
|
||||
local matinfo = dfhack.matinfo.decode(item)
|
||||
return matinfo and matinfo.material
|
||||
-- return dfhack.matinfo.decode(item).material
|
||||
end
|
||||
|
||||
local function getSyndrome(material)
|
||||
if material==nil then return nil end
|
||||
if #material.syndrome>0 then return material.syndrome[0]
|
||||
else return nil end
|
||||
end
|
||||
|
||||
local function removeItem(item)
|
||||
item.flags.garbage_collect = true
|
||||
end
|
||||
|
||||
local function findInorganicWithName(matString)
|
||||
for inorganicID,material in ipairs(df.global.world.raws.inorganics) do
|
||||
if material.id == matString then return inorganicID end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function getScriptFromMaterial(material)
|
||||
local commandStart
|
||||
local commandEnd
|
||||
local reactionClasses = material.reaction_class
|
||||
for classNumber,reactionClass in ipairs(reactionClasses) do
|
||||
if reactionClass.value == "\\COMMAND" then commandStart = classNumber end
|
||||
if reactionClass.value == "\\ENDCOMMAND" then commandEnd = classNumber break end
|
||||
end
|
||||
local script = {}
|
||||
if commandStart and commandEnd then
|
||||
for i = commandStart+1, commandEnd-1, 1 do
|
||||
table.insert(script,reactionClasses[i].value)
|
||||
end
|
||||
end
|
||||
return script
|
||||
end
|
||||
|
||||
local function getUnitHitByProjectile(projectile)
|
||||
for uid,unit in ipairs(df.global.world.units.active) do
|
||||
if posIsEqual(unit.pos,projectile.cur_pos) then return uid,unit end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function matCausesSyndrome(material)
|
||||
for _,reactionClass in ipairs(material.reaction_class) do
|
||||
if reactionClass.value == "DFHACK_CAUSES_SYNDROME" then return true end --the syndrome is the syndrome local to the projectile material
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
projectileExpansionFlags = projectilExpansionFlags or {
|
||||
matWantsSpecificInorganic = 0,
|
||||
matWantsSpecificSize = 50000,
|
||||
matCausesDragonFire = false,
|
||||
matCausesMiasma = false,
|
||||
matCausesMist = false,
|
||||
matCausesMist2 = false,
|
||||
matCausesDust = false,
|
||||
matCausesLavaMist = false,
|
||||
matCausesSmoke = false,
|
||||
matCausesFireBreath = false,
|
||||
matCausesWeb = false,
|
||||
matCausesUndirectedGas = false,
|
||||
matCausesUndirectedVapor = false,
|
||||
matCausesOceanWave = false,
|
||||
matCausesSeaFoam = false,
|
||||
matHasScriptAttached = false,
|
||||
matCausesSyndrome = false,
|
||||
returnLocation = false,
|
||||
matDisappearsOnHit = false
|
||||
}
|
||||
|
||||
local function getProjectileExpansionFlags(material)
|
||||
local matName = nil
|
||||
for k,reactionClass in ipairs(material.reaction_class) do
|
||||
if debugProjExp then print("checking reaction class #" .. k .. "...",reactionClass.value) end
|
||||
if string.find(reactionClass.value,"DFHACK") then
|
||||
if debugProjExp then print("DFHack reaction class found!") end
|
||||
if reactionClass.value == "DFHACK_SPECIFIC_MAT" then matName = material.reaction_class[k+1].value end
|
||||
if reactionClass.value == "DFHACK_FLOW_SIZE" then projectileExpansionFlags.matWantsSpecificSize = tonumber(material.reaction_class[k+1].value) end
|
||||
if reactionClass.value == "DFHACK_CAUSES_SYNDROME" then projectileExpansionFlags.matCausesSyndrome = true end
|
||||
if reactionClass.value == "DFHACK_DRAGONFIRE" then projectileExpansionFlags.matCausesDragonFire = true end
|
||||
if reactionClass.value == "DFHACK_MIASMA" then projectileExpansionFlags.matCausesMiasma = true end
|
||||
if reactionClass.value == "DFHACK_MIST" then projectileExpansionFlags.matCausesMist = true end
|
||||
if reactionClass.value == "DFHACK_MIST2" then projectileExpansionFlags.matCausesMist2 = true end
|
||||
if reactionClass.value == "DFHACK_DUST" then projectileExpansionFlags.matCausesDust = true end
|
||||
if reactionClass.value == "DFHACK_LAVAMIST" then projectileExpansionFlags.matCausesLavaMist = true end
|
||||
if reactionClass.value == "DFHACK_SMOKE" then projectileExpansionFlags.matCausesSmoke = true end
|
||||
if reactionClass.value == "DFHACK_FIREBREATH" then projectileExpansionFlags.matCausesFireBreath = true end
|
||||
if reactionClass.value == "DFHACK_WEB" then projectileExpansionFlags.matCausesWeb = true end
|
||||
if reactionClass.value == "DFHACK_GAS_UNDIRECTED" then projectileExpansionFlags.matCausesUndirectedGas = true end
|
||||
if reactionClass.value == "DFHACK_VAPOR_UNDIRECTED" then projectileExpansionFlags.matCausesUndirectedVapor = true end
|
||||
if reactionClass.value == "DFHACK_OCEAN_WAVE" then projectileExpansionFlags.matCausesOceanWave = true end
|
||||
if reactionClass.value == "DFHACK_SEA_FOAM" then projectileExpansionFlags.matCausesSeaFoam = true end
|
||||
if reactionClass.value == "DFHACK_DISAPPEARS" then projectileExpansionFlags.matDisappearsOnHit = true end
|
||||
end
|
||||
if reactionClass.value == "\\COMMAND" then projectileExpansionFlags.matHasScriptAttached = true end
|
||||
end
|
||||
if matName then projectileExpansionFlags.matWantsSpecificInorganic = findInorganicWithName(matName) end
|
||||
return projectileExpansionFlags
|
||||
end
|
||||
|
||||
debugProjExp=false
|
||||
|
||||
events.onProjItemCheckImpact.expansion=function(projectile)
|
||||
if debugProjExp then print("Thwack! Projectile item hit. Running projectileExpansion.") end
|
||||
if projectile then
|
||||
if debugProjExp then print("Found the item. Working on it.") end
|
||||
local material = getMaterial(projectile.item)
|
||||
if not material then return nil end
|
||||
local projectileExpansionFlags=getProjectileExpansionFlags(material)
|
||||
if debugProjExp then print(projectileExpansionFlags) printall(projectileExpansionFlags) end
|
||||
local syndrome = getSyndrome(material)
|
||||
local emissionMat = projectileExpansionFlags.matWantsSpecificInorganic --defaults to iron
|
||||
local flowSize = projectileExpansionFlags.matWantsSpecificSize --defaults to 50000
|
||||
if projectileExpansionFlags.matCausesDragonFire then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.dragonFire,0,0,flowSize) end
|
||||
if projectileExpansionFlags.matCausesMiasma then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.miasma,0,0,flowSize) end
|
||||
if projectileExpansionFlags.matCausesMist then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.mist,0,0,flowSize) end
|
||||
if projectileExpansionFlags.matCausesMist2 then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.mist2,0,0,flowSize) end
|
||||
if projectileExpansionFlags.matCausesDust then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.dust,0,emissionMat,flowSize) end
|
||||
if projectileExpansionFlags.matCausesLavaMist then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.lavaMist,0,emissionMat,flowSize) end
|
||||
if projectileExpansionFlags.matCausesSmoke then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.smoke,0,0,flowSize) end
|
||||
if projectileExpansionFlags.matCausesFireBreath then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.fireBreath,0,0,flowSize) end
|
||||
if projectileExpansionFlags.matCausesWeb then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.web,0,emissionMat,flowSize) end
|
||||
if projectileExpansionFlags.matCausesUndirectedGas then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.undirectedGas,0,emissionMat,flowSize) end
|
||||
if projectileExpansionFlags.matCausesUndirectedVapor then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.undirectedVapor,0,emissionMat,flowSize) end
|
||||
if projectileExpansionFlags.matCausesOceanWave then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.oceanWave,0,0,flowSize) end
|
||||
if projectileExpansionFlags.matCausesSeaFoam then dfhack.maps.spawnFlow(projectile.cur_pos,flowtypes.seaFoam,0,0,flowSize) end
|
||||
if projectileExpansionFlags.matHasScriptAttached or projectileExpansionFlags.matCausesSyndrome then
|
||||
local uid,unit = getUnitHitByProjectile(projectile)
|
||||
if projectileExpansionFlags.matHasScriptAttached then
|
||||
local script = getScriptFromMaterial(material)
|
||||
for k,v in ipairs(script) do
|
||||
if script[k] == "\\UNIT_HIT_ID" then script[k] = unit.id end
|
||||
if script[k] == "\\LOCATION" then
|
||||
script[k] = projectile.cur_pos.x
|
||||
table.insert(script,projectile.cur_pos.y,k+1)
|
||||
table.insert(script,projectile.cur_pos.z,k+2)
|
||||
end
|
||||
end
|
||||
dfhack.run_script(table.unpack(script))
|
||||
end
|
||||
if projectileExpansionFlags.matCausesSyndrome then syndromeUtil.infectWithSyndrome(unit,syndrome.id) end
|
||||
end
|
||||
--if projectileExpansionFlags.matDisappearsOnHit then dfhack.items.remove(projectile) end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if ... ~= "disable" then
|
||||
print("Enabled projectileExpansion.")
|
||||
else
|
||||
events.onProjItemCheckImpact.expansion = nil
|
||||
print("Disabled projectileExpansion.")
|
||||
end
|
||||
|
@ -0,0 +1,56 @@
|
||||
-- Resets all items in your fort to 0 wear
|
||||
-- original author: Laggy
|
||||
-- edited by expwnent
|
||||
|
||||
local args = {...}
|
||||
|
||||
if args[1] == 'help' then
|
||||
print([[removewear - this script removes wear from all items, or from individual ones
|
||||
|
||||
removewear all
|
||||
remove wear from all items
|
||||
removewear n1 n2 n3 ...
|
||||
remove wear from items with the given ids. order does not matter
|
||||
repeat enable 2 months removewear all
|
||||
remove wear from all items every 2 months. see repeat.lua for details
|
||||
]])
|
||||
do return end
|
||||
elseif args[1] == 'all' then
|
||||
local count = 0;
|
||||
for _,item in ipairs(df.global.world.items.all) do
|
||||
if (item.wear > 0) then
|
||||
item:setWear(0)
|
||||
count = count+1
|
||||
end
|
||||
end
|
||||
print('removewear removed wear from 'count' objects')
|
||||
else
|
||||
local argIndex = 1
|
||||
local isCompleted = {}
|
||||
for i,x in ipairs(args) do
|
||||
args[i] = tonumber(x)
|
||||
end
|
||||
table.sort(args)
|
||||
for _,item in ipairs(df.global.world.items.all) do
|
||||
local function loop()
|
||||
if argIndex > #args then
|
||||
return
|
||||
elseif item.id > args[argIndex] then
|
||||
argIndex = argIndex+1
|
||||
loop()
|
||||
return
|
||||
elseif item.id == args[argIndex] then
|
||||
--print('removing wear from item with id ' .. args[argIndex])
|
||||
item:setWear(0)
|
||||
isCompleted[args[argIndex]] = true
|
||||
argIndex = argIndex+1
|
||||
end
|
||||
end
|
||||
loop()
|
||||
end
|
||||
for _,arg in ipairs(args) do
|
||||
if isCompleted[arg] ~= true then
|
||||
print('failed to remove wear from item ' .. arg .. ': could not find item with that id')
|
||||
end
|
||||
end
|
||||
end
|
@ -0,0 +1,73 @@
|
||||
-- repeat.lua
|
||||
-- repeatedly calls a lua script, eg "repeat enable 1 months cleanowned"; to disable "repeat disable cleanowned"
|
||||
-- author expwnent
|
||||
-- vaguely based on a script by Putnam
|
||||
-- repeat -help for details
|
||||
|
||||
local repeatUtil = require 'plugins.repeatUtil'
|
||||
|
||||
local args = {...}
|
||||
if args[1] == '-cancel' then
|
||||
repeatUtil.cancel(args[2])
|
||||
return
|
||||
elseif args[1] == '-help' then
|
||||
print([[repeat.lua
|
||||
repeat -help
|
||||
print this help message
|
||||
repeat -cancel bob
|
||||
cancels the repetition with the name bob
|
||||
repeat -name jim -time delay timeUnits -printResult true -command printArgs 3 1 2
|
||||
except for -command, arguments can go in any order
|
||||
-name sets the name for the purposes of cancelling and making sure you don't schedule the same repeating event twice
|
||||
if not specified, it's set to the first argument after -command
|
||||
-time delay timeUnits
|
||||
delay is some positive integer
|
||||
timeUnits is some valid time unit for dfhack.timeout(delay,timeUnits,function)
|
||||
-printResult true
|
||||
print the results of the command
|
||||
-printResult false
|
||||
suppress the results of the command
|
||||
-command ...
|
||||
specify the command to be run
|
||||
]])
|
||||
end
|
||||
|
||||
local name=nil
|
||||
local time
|
||||
local timeUnits
|
||||
local i=1
|
||||
local command={}
|
||||
local printResult=true
|
||||
while i <= #args do
|
||||
if args[i] == '-name' then
|
||||
name = args[i+1]
|
||||
i = i + 2
|
||||
elseif args[i] == '-time' then
|
||||
time = tonumber(args[i+1])
|
||||
timeUnits = args[i+2]
|
||||
i = i+3
|
||||
elseif args[i] == '-command' then
|
||||
name = name or args[i+1]
|
||||
for j=i+1,#args,1 do
|
||||
table.insert(command,args[j])
|
||||
end
|
||||
break
|
||||
elseif args[i] == '-printResult' then
|
||||
if args[i+1] == "true" then
|
||||
printOutput = true
|
||||
elseif args[i+1] == "false" then
|
||||
printOutput = false
|
||||
else
|
||||
qerror("repeat -printResult " .. args[i+1] .. ": expected true or false")
|
||||
end
|
||||
i = i+2
|
||||
end
|
||||
end
|
||||
|
||||
repeatUtil.scheduleEvery(name,time,timeUnits,function()
|
||||
result = dfhack.run_command(table.unpack(command))
|
||||
if printResult then
|
||||
print(result)
|
||||
end
|
||||
end)
|
||||
|
@ -0,0 +1,97 @@
|
||||
-- shapechange.lua
|
||||
-- transforms unit (by number) into another creature, choice given to user. Syntax is: unitID tickamount maxsize namefilter. A size of 0 is ignored. A length of 0 is also ignored. If no filter, all units will be sorted. A filter of ALL will also work with all units.
|
||||
-- author Putnam
|
||||
-- edited by expwnent
|
||||
|
||||
--shapechange gui [unitId] [duration] [maxsize] [namefilter]
|
||||
--shapechange manual [unitId] [creature name] [caste name] [duration]
|
||||
|
||||
local dialog = require('gui.dialogs')
|
||||
local script = require('gui.script')
|
||||
function transform(target,race,caste,length)
|
||||
if target==nil then
|
||||
qerror("Not a valid target")
|
||||
end
|
||||
local defaultRace = target.enemy.normal_race
|
||||
local defaultCaste = target.enemy.normal_caste
|
||||
target.enemy.normal_race = race --that's it???
|
||||
target.enemy.normal_caste = caste; --that's it!
|
||||
if length and length>0 then dfhack.timeout(length,'ticks',function() target.enemy.normal_race = defaultRace target.enemy.normal_caste = defaultCaste end) end
|
||||
end
|
||||
|
||||
function getBodySize(caste)
|
||||
return caste.body_size_1[#caste.body_size_1-1]
|
||||
end
|
||||
|
||||
function selectCreature(unitID,length,size,filter) --taken straight from here, but edited so I can understand it better: https://gist.github.com/warmist/4061959/... again. Also edited for syndromeTrigger, but in a completely different way.
|
||||
size = size or 0
|
||||
filter = filter or "all"
|
||||
length = length or 2400
|
||||
local creatures=df.global.world.raws.creatures.all
|
||||
local tbl={}
|
||||
local tunit=df.unit.find(unitID)
|
||||
for cr_k,creature in ipairs(creatures) do
|
||||
for ca_k,caste in ipairs(creature.caste) do
|
||||
local name=caste.caste_name[0]
|
||||
if name=="" then name="?" end
|
||||
if (not filter or string.find(name,filter) or string.lower(filter)=="all") and (not size or size>getBodySize(caste) or size<1 and not creature.flags.DOES_NOT_EXIST) then table.insert(tbl,{name,nil,cr_k,ca_k}) end
|
||||
end
|
||||
end
|
||||
table.sort(tbl,function(a,b) return a[1]<b[1] end)
|
||||
local f=function(name,C)
|
||||
transform(tunit,C[3],C[4],length)
|
||||
end
|
||||
script.start(function()
|
||||
local ok =
|
||||
script.showYesNoPrompt(
|
||||
"Just checking","Do you want "
|
||||
.. dfhack.TranslateName(dfhack.units.getVisibleName(tunit))
|
||||
.. " to transform into a creature of size below "..NEWLINE
|
||||
.. (not not size and size>1 and size or "infinity")
|
||||
.. " ("
|
||||
.. size/(getBodySize(df.creature_raw.find(tunit.race).caste[tunit.caste]))*100
|
||||
.. "% of current size) for "
|
||||
..length
|
||||
.." ticks ("
|
||||
..length/1200
|
||||
.." days, ~"
|
||||
..length/df.global.enabler.fps
|
||||
.." seconds)?",
|
||||
COLOR_LIGHTRED
|
||||
)
|
||||
if ok then dialog.showListPrompt("Creature Selection","Choose creature:",COLOR_WHITE,tbl,f) end
|
||||
end)
|
||||
end
|
||||
|
||||
local args = {...}
|
||||
--unit id, length, size, filter
|
||||
if args[1] == 'gui' then
|
||||
selectCreature(tonumber(args[2]),tonumber(args[3]),tonumber(args[4]),args[5])
|
||||
else
|
||||
local race-- = df.creature_raw.find(args[3])
|
||||
local raceIndex
|
||||
for index,raceCandidate in ipairs(df.global.world.raws.creatures.all) do
|
||||
if raceCandidate.creature_id == args[3] then
|
||||
raceIndex = index
|
||||
race = raceCandidate
|
||||
break
|
||||
end
|
||||
end
|
||||
local caste
|
||||
local casteIndex
|
||||
if race then
|
||||
for index,casteCandidate in ipairs(race.caste) do
|
||||
if casteCandidate.caste_id == args[4] then
|
||||
caste = casteCandidate
|
||||
casteIndex = index
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if not race or not caste then
|
||||
print("shapechange error: couldn't find " .. args[3] .. " " .. args[4])
|
||||
return
|
||||
end
|
||||
transform(df.unit.find(tonumber(args[2])), raceIndex, casteIndex, args[5] and tonumber(args[5]))
|
||||
end
|
||||
|
@ -0,0 +1,47 @@
|
||||
-- skillChange.lua
|
||||
-- Allows skills to be changed with DFHack.
|
||||
-- author Putnam
|
||||
-- edited by expwnent
|
||||
|
||||
--[[
|
||||
Args are arranged like this:
|
||||
unitID SKILL level ADD/SUBTRACT/SET
|
||||
|
||||
SKILL being anything such as DETAILSTONE, level being a number that it will be set to (15 is legendary, 0 is dabbling).
|
||||
|
||||
Add will add skill levels, subtract will subtract, set will place it at exactly the level you put in.
|
||||
]]
|
||||
|
||||
local utils=require('utils')
|
||||
local args={...}
|
||||
local skills=df.unit.find(args[1]).status.current_soul.skills
|
||||
local skill=df.job_skill[tostring(args[2]):upper()]
|
||||
local level=tonumber(args[3]) or 0
|
||||
|
||||
local argfunctions={
|
||||
__index=function(t,k)
|
||||
qerror(k..' is not a valid setting! Valid settings are SET, ADD and SUBTRACT.')
|
||||
end,
|
||||
set=function(skills,skill,level)
|
||||
utils.insert_or_update(skills, {new=true, id=skill, rating=level}, 'id')
|
||||
end,
|
||||
add=function(skills,skill,level)
|
||||
local skillExists,oldSkill=utils.linear_index(skills,skill,'id')
|
||||
if skillExists then
|
||||
oldSkill.rating=level+oldSkill.rating
|
||||
else
|
||||
argfunctions.set(skills,skill,level)
|
||||
end
|
||||
end,
|
||||
subtract=function(skills,skill,level)
|
||||
local skillExists,oldSkill=utils.linear_index(skills,skill,'id')
|
||||
if skillExists then
|
||||
local newRating=oldSkill['rating'] or 0
|
||||
oldSkill.rating=oldSkill.rating-level
|
||||
if oldSkill.rating<0 or (oldSkill.rating==0 and oldSkill.experience<=0) then skills:erase(skillExists) end
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
setmetatable(argfunctions,argfunctions)
|
||||
argfunctions[args[4]:lower()](skills,skill,level)
|
@ -0,0 +1,60 @@
|
||||
-- skillroll.lua
|
||||
-- Allows skills to activate lua scripts.
|
||||
-- author Putnam
|
||||
-- edited by expwnent
|
||||
|
||||
--[[Example usage:
|
||||
...syndrome stuff...
|
||||
[SYN_CLASS:\COMMAND][SYN_CLASS:skillroll][SYN_CLASS:\WORKER_ID] For autoSyndrome/syndromeTrigger.
|
||||
[SYN_CLASS:MELEE_COMBAT] Can use any skill, including NONE (no bonus)
|
||||
[SYN_CLASS:20] Rolls uniformly from 1 to 20 inclusive. Skill will be weighted to this value.
|
||||
[SYN_CLASS:DICEROLL_1] If diceroll ends up as one...
|
||||
[SYN_CLASS:kill][SYN_CLASS:\SKILL_UNIT_ID] Theoretical kill-given-unit-id command; slayrace doesn't do so.
|
||||
[SYN_CLASS:DICEROLL_10] If diceroll is between 1 and 10 (2-10, inclusive)...
|
||||
[SYN_CLASS:force][SYN_CLASS:migrants][SYN_CLASS:player] Force migrants.
|
||||
[SYN_CLASS:DICEROLL_19] If diceroll is between 10 and 19 (11-19, inclusive)...
|
||||
[SYN_CLASS:fullheal][SYN_CLASS:\SKILL_UNIT_ID] Fully heals unit.
|
||||
[SYN_CLASS:DICEROLL_20] If diceroll is at least 20...
|
||||
[SYN_CLASS:shapechange][SYN_CLASS:\SKILL_UNIT_ID] Turns unit into any creature permanently.
|
||||
|
||||
or from the console
|
||||
skillroll workerId MELEE_COMBAT 20 DICEROLL_1 kill workerId DICEROLL_10 force migrants player DICEROLL_19 fullheal workerId DICEROLL_20 shapechange workerId
|
||||
]]
|
||||
|
||||
local args={...}
|
||||
if args[1]=='dryrun' then
|
||||
unit=df.global.world.units.all[0]
|
||||
end
|
||||
local unit = unit or df.unit.find(args[1])
|
||||
rando=rando or dfhack.random.new()
|
||||
local roll=rando:random(tonumber(args[3]))
|
||||
if args[2] ~= 'NONE' then
|
||||
local result=roll+(dfhack.units.getEffectiveSkill(unit,df.job_skill[args[2]])*(tonumber(args[3])/20))
|
||||
result = result%1<.5 and math.floor(result) or math.ceil(result)
|
||||
roll = result
|
||||
end
|
||||
|
||||
local i=4
|
||||
local command={}
|
||||
local scriptIsFinished
|
||||
repeat
|
||||
local arg=args[i]
|
||||
if arg:find('DICEROLL') then
|
||||
local dicerollnumber=tonumber(arg:match('%d+')) --yes this is truly naive as hell; I imagine if you put DICEROLL3%moa5oam3 it'll return 353.
|
||||
if dicerollnumber>=roll then
|
||||
repeat
|
||||
i=i+1
|
||||
if i<=#args and (not args[i]:find('DICEROLL')) then
|
||||
if args[i]~='\\SKILL_UNIT_ID' then table.insert(command,args[i]) else table.insert(command,args[1]) end
|
||||
end
|
||||
until i>#args or args[i]:find('DICEROLL')
|
||||
local out = dfhack.run_command(table.unpack(command))
|
||||
print(out)
|
||||
scriptIsFinished=true
|
||||
else
|
||||
i=i+1
|
||||
end
|
||||
else
|
||||
i=i+1
|
||||
end
|
||||
until i>#args or scriptIsFinished
|
@ -0,0 +1,36 @@
|
||||
-- teleport.lua
|
||||
-- teleports a unit to a location
|
||||
-- author Putnam
|
||||
-- edited by expwnent
|
||||
|
||||
local function teleport(unit,pos)
|
||||
local unitoccupancy = dfhack.maps.getTileBlock(unit.pos).occupancy[unit.pos.x%16][unit.pos.y%16]
|
||||
unit.pos.x = pos.x
|
||||
unit.pos.y = pos.y
|
||||
unit.pos.z = pos.z
|
||||
if not unit.flags1.on_ground then unitoccupancy.unit = false else unitoccupancy.unit_grounded = false end
|
||||
end
|
||||
|
||||
local function getArgsTogether(args)
|
||||
local settings={pos={}}
|
||||
for k,v in ipairs(args) do
|
||||
v=string.lower(v)
|
||||
if v=="unit" then settings.unitID=tonumber(args[k+1]) end
|
||||
if v=="x" then settings.pos['x']=tonumber(args[k+1]) end
|
||||
if v=="y" then settings.pos['y']=tonumber(args[k+1]) end
|
||||
if v=="z" then settings.pos['z']=tonumber(args[k+1]) end
|
||||
if v=="showunitid" then print(dfhack.gui.getSelectedUnit(true).id) end
|
||||
if v=="showpos" then printall(df.global.cursor) end
|
||||
end
|
||||
if not settings.pos.x or not settings.pos.y or not settings.pos.z then settings.pos=nil end
|
||||
if not settings.unitID and not settings.pos.x then qerror("Needs a position, a unit ID or both, but not neither!") end
|
||||
return settings
|
||||
end
|
||||
|
||||
local args = {...}
|
||||
local teleportSettings=getArgsTogether(args)
|
||||
local unit = teleportSettings.unitID and df.unit.find(teleportSettings.unitID) or dfhack.gui.getSelectedUnit(true)
|
||||
local pos = teleportSettings.pos and teleportSettings.pos or df.global.cursor
|
||||
|
||||
teleport(unit,pos)
|
||||
|
@ -0,0 +1,793 @@
|
||||
-- unit-info-viewer.lua
|
||||
-- Displays age, birth, maxage, shearing, milking, grazing, egg laying, body size, and death info about a unit. Recommended keybinding Alt-I
|
||||
-- version 1.04
|
||||
-- original author: Kurik Amudnil
|
||||
-- edited by expwnent
|
||||
|
||||
local gui = require 'gui'
|
||||
local widgets =require 'gui.widgets'
|
||||
local utils = require 'utils'
|
||||
|
||||
local DEBUG = false
|
||||
if DEBUG then print('-----') end
|
||||
|
||||
local pens = {
|
||||
BLACK = dfhack.pen.parse{fg=COLOR_BLACK,bg=0},
|
||||
BLUE = dfhack.pen.parse{fg=COLOR_BLUE,bg=0},
|
||||
GREEN = dfhack.pen.parse{fg=COLOR_GREEN,bg=0},
|
||||
CYAN = dfhack.pen.parse{fg=COLOR_CYAN,bg=0},
|
||||
RED = dfhack.pen.parse{fg=COLOR_RED,bg=0},
|
||||
MAGENTA = dfhack.pen.parse{fg=COLOR_MAGENTA,bg=0},
|
||||
BROWN = dfhack.pen.parse{fg=COLOR_BROWN,bg=0},
|
||||
GREY = dfhack.pen.parse{fg=COLOR_GREY,bg=0},
|
||||
DARKGREY = dfhack.pen.parse{fg=COLOR_DARKGREY,bg=0},
|
||||
LIGHTBLUE = dfhack.pen.parse{fg=COLOR_LIGHTBLUE,bg=0},
|
||||
LIGHTGREEN = dfhack.pen.parse{fg=COLOR_LIGHTGREEN,bg=0},
|
||||
LIGHTCYAN = dfhack.pen.parse{fg=COLOR_LIGHTCYAN,bg=0},
|
||||
LIGHTRED = dfhack.pen.parse{fg=COLOR_LIGHTRED,bg=0},
|
||||
LIGHTMAGENTA = dfhack.pen.parse{fg=COLOR_LIGHTMAGENTA,bg=0},
|
||||
YELLOW = dfhack.pen.parse{fg=COLOR_YELLOW,bg=0},
|
||||
WHITE = dfhack.pen.parse{fg=COLOR_WHITE,bg=0},
|
||||
}
|
||||
|
||||
function getUnit_byID(id) -- get unit by id from units.all via binsearch
|
||||
if type(id) == 'number' then
|
||||
-- (vector,key,field,cmpfun,min,max) { item/nil , found true/false , idx/insert at }
|
||||
return utils.binsearch(df.global.world.units.all,id,'id')
|
||||
end
|
||||
end
|
||||
|
||||
function getUnit_byVS(silent) -- by view screen mode
|
||||
silent = silent or false
|
||||
-- if not world loaded, return nil ?
|
||||
local u,tmp -- u: the unit to return ; tmp: temporary for intermediate tests/return values
|
||||
local v = dfhack.gui.getCurViewscreen()
|
||||
u = dfhack.gui.getSelectedUnit(true) -- supports gui scripts/plugin that provide a hook for getSelectedUnit()
|
||||
if u then
|
||||
return u
|
||||
-- else: contexts not currently supported by dfhack.gui.getSelectedUnit()
|
||||
elseif df.viewscreen_dwarfmodest:is_instance(v) then
|
||||
tmp = df.global.ui.main.mode
|
||||
if tmp == 17 or tmp == 42 or tmp == 43 then
|
||||
-- context: @dwarfmode/QueryBuiding/Some/Cage -- (q)uery cage
|
||||
-- context: @dwarfmode/ZonesPenInfo/AssignUnit -- i (zone) -> pe(N)
|
||||
-- context: @dwarfmode/ZonesPitInfo -- i (zone) -> (P)it
|
||||
u = df.global.ui_building_assign_units[df.global.ui_building_item_cursor]
|
||||
elseif tmp == 49 and df.global.ui.burrows.in_add_units_mode then
|
||||
-- @dwarfmode/Burrows/AddUnits
|
||||
u = df.global.ui.burrows.list_units[ df.global.ui.burrows.unit_cursor_pos ]
|
||||
|
||||
elseif df.global.ui.follow_unit ~= -1 then
|
||||
-- context: follow unit mode
|
||||
u = getUnit_byID(df.global.ui.follow_unit)
|
||||
end -- end viewscreen_dwarfmodest
|
||||
elseif df.viewscreen_petst:is_instance(v) then
|
||||
-- context: @pet/List/Unit -- z (status) -> animals
|
||||
if v.mode == 0 then -- List
|
||||
if not v.is_vermin[v.cursor] then
|
||||
u = v.animal[v.cursor].unit
|
||||
end
|
||||
--elseif v.mode = 1 then -- training knowledge (no unit reference)
|
||||
elseif v.mode == 2 then -- select trainer
|
||||
u = v.trainer_unit[v.trainer_cursor]
|
||||
end
|
||||
elseif df.viewscreen_layer_workshop_profilest:is_instance(v) then
|
||||
-- context: @layer_workshop_profile/Unit -- (q)uery workshop -> (P)rofile -- df.global.ui.main.mode == 17
|
||||
u = v.workers[v.layer_objects[0].cursor]
|
||||
elseif df.viewscreen_layer_overall_healthst:is_instance(v) then
|
||||
-- context @layer_overall_health/Units -- z -> health
|
||||
u = v.unit[v.layer_objects[0].cursor]
|
||||
elseif df.viewscreen_layer_militaryst:is_instance(v) then
|
||||
local PG_ASSIGNMENTS = 0
|
||||
local PG_EQUIPMENT = 2
|
||||
local TB_POSITIONS = 1
|
||||
local TB_CANDIDATES = 2
|
||||
-- layer_objects[0: squads list; 1: positions list; 2: candidates list]
|
||||
-- page 0:positions/assignments 1:alerts 2:equipment 3:uniforms 4:supplies 5:ammunition
|
||||
if v.page == PG_ASSIGNMENTS and v.layer_objects[TB_CANDIDATES].enabled and v.layer_objects[TB_CANDIDATES].active then
|
||||
-- context: @layer_military/Positions/Position/Candidates -- m -> Candidates
|
||||
u = v.positions.candidates[v.layer_objects[TB_CANDIDATES].cursor]
|
||||
elseif v.page == PG_ASSIGNMENTS and v.layer_objects[TB_POSITIONS].enabled and v.layer_objects[TB_POSITIONS].active then
|
||||
-- context: @layer_military/Positions/Position -- m -> Positions
|
||||
u = v.positions.assigned[v.layer_objects[TB_POSITIONS].cursor]
|
||||
elseif v.page == PG_EQUIPMENT and v.layer_objects[TB_POSITIONS].enabled and v.layer_objects[TB_POSITIONS].active then
|
||||
-- context: @layer_military/Equip/Customize/View/Position -- m -> (e)quip -> Positions
|
||||
-- context: @layer_military/Equip/Uniform/Positions -- m -> (e)quip -> assign (U)niforms -> Positions
|
||||
u = v.equip.units[v.layer_objects[TB_POSITIONS].cursor]
|
||||
end
|
||||
elseif df.viewscreen_layer_noblelistst:is_instance(v) then
|
||||
if v.mode == 0 then
|
||||
-- context: @layer_noblelist/List -- (n)obles
|
||||
u = v.info[v.layer_objects[v.mode].cursor].unit
|
||||
elseif v.mode == 1 then
|
||||
-- context: @layer_noblelist/Appoint -- (n)obles -> (r)eplace
|
||||
u = v.candidates[v.layer_objects[v.mode].cursor].unit
|
||||
end
|
||||
elseif df.viewscreen_unitst:is_instance(v) then
|
||||
-- @unit -- (v)unit -> z ; loo(k) -> enter ; (n)obles -> enter ; others
|
||||
u = v.unit
|
||||
elseif df.viewscreen_customize_unitst:is_instance(v) then
|
||||
-- @customize_unit -- @unit -> y
|
||||
u = v.unit
|
||||
elseif df.viewscreen_layer_unit_healthst:is_instance(v) then
|
||||
-- @layer_unit_health -- @unit -> h ; @layer_overall_health/Units -> enter
|
||||
if df.viewscreen_layer_overall_healthst:is_instance(v.parent) then
|
||||
-- context @layer_overall_health/Units -- z (status)-> health
|
||||
u = v.parent.unit[v.parent.layer_objects[0].cursor]
|
||||
elseif df.viewscreen_unitst:is_instance(v.parent) then
|
||||
-- @unit -- (v)unit -> z ; loo(k) -> enter ; (n)obles -> enter ; others
|
||||
u = v.parent.unit
|
||||
end
|
||||
elseif df.viewscreen_textviewerst:is_instance(v) then
|
||||
-- @textviewer -- @unit -> enter (thoughts and preferences)
|
||||
if df.viewscreen_unitst:is_instance(v.parent) then
|
||||
-- @unit -- @unit -> enter (thoughts and preferences)
|
||||
u = v.parent.unit
|
||||
elseif df.viewscreen_itemst:is_instance(v.parent) then
|
||||
tmp = v.parent.entry_ref[v.parent.cursor_pos]
|
||||
if df.general_ref_unit:is_instance(tmp) then -- general_ref_unit and derived ; general_ref_contains_unitst ; others?
|
||||
u = getUnit_byID(tmp.unit_id)
|
||||
end
|
||||
elseif df.viewscreen_dwarfmodest:is_instance(v.parent) then
|
||||
tmp = df.global.ui.main.mode
|
||||
if tmp == 24 then -- (v)iew units {g,i,p,w} -> z (thoughts and preferences)
|
||||
-- context: @dwarfmode/ViewUnits/...
|
||||
--if df.global.ui_selected_unit > -1 then -- -1 = 'no units nearby'
|
||||
u = df.global.world.units.active[df.global.ui_selected_unit]
|
||||
--end
|
||||
elseif tmp == 25 then -- loo(k) unit -> enter (thoughs and preferences)
|
||||
-- context: @dwarfmode/LookAround/Unit
|
||||
tmp = df.global.ui_look_list.items[df.global.ui_look_cursor]
|
||||
if tmp.type == 2 then -- 0:item 1:terrain >>2: unit<< 3:building 4:colony/vermin 7:spatter
|
||||
u = tmp.unit
|
||||
end
|
||||
end
|
||||
elseif df.viewscreen_unitlistst:is_instance(v.parent) then -- (u)nit list -> (v)iew unit (not citizen)
|
||||
-- context: @unitlist/Citizens ; @unitlist/Livestock ; @unitlist/Others ; @unitlist/Dead
|
||||
u = v.parent.units[v.parent.page][ v.parent.cursor_pos[v.parent.page] ]
|
||||
end
|
||||
end -- switch viewscreen
|
||||
if not u and not silent then
|
||||
dfhack.printerr('No unit is selected in the UI or context not supported.')
|
||||
end
|
||||
return u
|
||||
end -- getUnit_byVS()
|
||||
|
||||
--http://lua-users.org/wiki/StringRecipes ----------
|
||||
function str2FirstUpper(str)
|
||||
return str:gsub("^%l", string.upper)
|
||||
end
|
||||
|
||||
--------------------------------------------------
|
||||
--http://lua-users.org/wiki/StringRecipes ----------
|
||||
local function tchelper(first, rest)
|
||||
return first:upper()..rest:lower()
|
||||
end
|
||||
|
||||
-- Add extra characters to the pattern if you need to. _ and ' are
|
||||
-- found in the middle of identifiers and English words.
|
||||
-- We must also put %w_' into [%w_'] to make it handle normal stuff
|
||||
-- and extra stuff the same.
|
||||
-- This also turns hex numbers into, eg. 0Xa7d4
|
||||
function str2TitleCase(str)
|
||||
return str:gsub("(%a)([%w_']*)", tchelper)
|
||||
end
|
||||
|
||||
--------------------------------------------------
|
||||
--isBlank suggestion by http://stackoverflow.com/a/10330861
|
||||
function isBlank(x)
|
||||
x = tostring(x) or ""
|
||||
-- returns (not not match_begin), _ = match_end => not not true , _ => true
|
||||
-- returns not not nil => false (no match)
|
||||
return not not x:find("^%s*$")
|
||||
end
|
||||
|
||||
--http://lua-users.org/wiki/StringRecipes (removed indents since I am not using them)
|
||||
function wrap(str, limit)--, indent, indent1)
|
||||
--indent = indent or ""
|
||||
--indent1 = indent1 or indent
|
||||
local limit = limit or 72
|
||||
local here = 1 ---#indent1
|
||||
return str:gsub("(%s+)()(%S+)()", --indent1..str:gsub(
|
||||
function(sp, st, word, fi)
|
||||
if fi-here > limit then
|
||||
here = st -- - #indent
|
||||
return "\n"..word --..indent..word
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
--------------------------------------------------
|
||||
---------------------- Time ----------------------
|
||||
--------------------------------------------------
|
||||
local TU_PER_DAY = 1200
|
||||
--[[
|
||||
if advmode then TU_PER_DAY = 86400 ? or only for cur_year_tick?
|
||||
advmod_TU / 72 = ticks
|
||||
--]]
|
||||
local TU_PER_MONTH = TU_PER_DAY * 28
|
||||
local TU_PER_YEAR = TU_PER_MONTH * 12
|
||||
|
||||
local MONTHS = {
|
||||
'Granite',
|
||||
'Slate',
|
||||
'Felsite',
|
||||
'Hematite',
|
||||
'Malachite',
|
||||
'Galena',
|
||||
'Limestone',
|
||||
'Sandstone',
|
||||
'Timber',
|
||||
'Moonstone',
|
||||
'Opal',
|
||||
'Obsidian',
|
||||
}
|
||||
Time = defclass(Time)
|
||||
function Time:init(args)
|
||||
self.year = args.year or 0
|
||||
self.ticks = args.ticks or 0
|
||||
end
|
||||
function Time:getDays() -- >>float<< Days as age (including years)
|
||||
return self.year * 336 + (self.ticks / TU_PER_DAY)
|
||||
end
|
||||
function Time:getMonths() -- >>int<< Months as age (not including years)
|
||||
return math.floor (self.ticks / TU_PER_MONTH)
|
||||
end
|
||||
function Time:getMonthStr() -- Month as date
|
||||
return MONTHS[self:getMonths()+1] or 'error'
|
||||
end
|
||||
function Time:getDayStr() -- Day as date
|
||||
local d = math.floor ( (self.ticks % TU_PER_MONTH) / TU_PER_DAY ) + 1
|
||||
if d == 11 or d == 12 or d == 13 then
|
||||
d = tostring(d)..'th'
|
||||
elseif d % 10 == 1 then
|
||||
d = tostring(d)..'st'
|
||||
elseif d % 10 == 2 then
|
||||
d = tostring(d)..'nd'
|
||||
elseif d % 10 == 3 then
|
||||
d = tostring(d)..'rd'
|
||||
else
|
||||
d = tostring(d)..'th'
|
||||
end
|
||||
return d
|
||||
end
|
||||
--function Time:__add()
|
||||
--end
|
||||
function Time:__sub(other)
|
||||
if DEBUG then print(self.year,self.ticks) end
|
||||
if DEBUG then print(other.year,other.ticks) end
|
||||
if self.ticks < other.ticks then
|
||||
return Time{ year = (self.year - other.year - 1) , ticks = (TU_PER_YEAR + self.ticks - other.ticks) }
|
||||
else
|
||||
return Time{ year = (self.year - other.year) , ticks = (self.ticks - other.ticks) }
|
||||
end
|
||||
end
|
||||
--------------------------------------------------
|
||||
--------------------------------------------------
|
||||
|
||||
--------------------------------------------------
|
||||
-------------------- Identity --------------------
|
||||
--------------------------------------------------
|
||||
local SINGULAR = 0
|
||||
local PLURAL = 1
|
||||
--local POSSESSIVE = 2
|
||||
|
||||
local PRONOUNS = {
|
||||
[0]='She',
|
||||
[1]='He',
|
||||
[2]='It',
|
||||
}
|
||||
local BABY = 0
|
||||
local CHILD = 1
|
||||
local ADULT = 2
|
||||
|
||||
local TRAINING_LEVELS = {
|
||||
[0] = ' (Semi-Wild)', -- Semi-wild
|
||||
' (Trained)', -- Trained
|
||||
' (-Trained-)', -- Well-trained
|
||||
' (+Trained+)', -- Skillfully trained
|
||||
' (*Trained*)', -- Expertly trained
|
||||
' ('..string.char(240)..'Trained'..string.char(240)..')', -- Exceptionally trained
|
||||
' ('..string.char(15)..'Trained'..string.char(15)..')', -- Masterully Trained
|
||||
' (Tame)', -- Domesticated
|
||||
'', -- undefined
|
||||
'', -- wild/untameable
|
||||
}
|
||||
|
||||
local DEATH_TYPES = {
|
||||
[0] = ' died of old age', -- OLD_AGE
|
||||
' starved to death', -- HUNGER
|
||||
' died of dehydration', -- THIRST
|
||||
' was shot and killed', -- SHOT
|
||||
' bled to death', -- BLEED
|
||||
' drowned', -- DROWN
|
||||
' suffocated', -- SUFFOCATE
|
||||
' was struck down', -- STRUCK_DOWN
|
||||
' was scuttled', -- SCUTTLE
|
||||
" didn't survive a collision", -- COLLISION
|
||||
' took a magma bath', -- MAGMA
|
||||
' took a magma shower', -- MAGMA_MIST
|
||||
' was incinerated by dragon fire', -- DRAGONFIRE
|
||||
' was killed by fire', -- FIRE
|
||||
' experienced death by SCALD', -- SCALD
|
||||
' was crushed by cavein', -- CAVEIN
|
||||
' was smashed by a drawbridge', -- DRAWBRIDGE
|
||||
' was killed by falling rocks', -- FALLING_ROCKS
|
||||
' experienced death by CHASM', -- CHASM
|
||||
' experienced death by CAGE', -- CAGE
|
||||
' was murdered', -- MURDER
|
||||
' was killed by a trap', -- TRAP
|
||||
' vanished', -- VANISH
|
||||
' experienced death by QUIT', -- QUIT
|
||||
' experienced death by ABANDON', -- ABANDON
|
||||
' suffered heat stroke', -- HEAT
|
||||
' died of hypothermia', -- COLD
|
||||
' experienced death by SPIKE', -- SPIKE
|
||||
' experienced death by ENCASE_LAVA', -- ENCASE_LAVA
|
||||
' experienced death by ENCASE_MAGMA', -- ENCASE_MAGMA
|
||||
' was preserved in ice', -- ENCASE_ICE
|
||||
' became headless', -- BEHEAD
|
||||
' was crucified', -- CRUCIFY
|
||||
' experienced death by BURY_ALIVE', -- BURY_ALIVE
|
||||
' experienced death by DROWN_ALT', -- DROWN_ALT
|
||||
' experienced death by BURN_ALIVE', -- BURN_ALIVE
|
||||
' experienced death by FEED_TO_BEASTS', -- FEED_TO_BEASTS
|
||||
' experienced death by HACK_TO_PIECES', -- HACK_TO_PIECES
|
||||
' choked on air', -- LEAVE_OUT_IN_AIR
|
||||
' experienced death by BOIL', -- BOIL
|
||||
' melted', -- MELT
|
||||
' experienced death by CONDENSE', -- CONDENSE
|
||||
' experienced death by SOLIDIFY', -- SOLIDIFY
|
||||
' succumbed to infection', -- INFECTION
|
||||
"'s ghost was put to rest with a memorial", -- MEMORIALIZE
|
||||
' scared to death', -- SCARE
|
||||
' experienced death by DARKNESS', -- DARKNESS
|
||||
' experienced death by COLLAPSE', -- COLLAPSE
|
||||
' was drained of blood', -- DRAIN_BLOOD
|
||||
' was slaughtered', -- SLAUGHTER
|
||||
' became roadkill', -- VEHICLE
|
||||
' killed by a falling object', -- FALLING_OBJECT
|
||||
}
|
||||
|
||||
--GHOST_TYPES[unit.relations.ghost_info.type].." This spirit has not been properly memorialized or buried."
|
||||
local GHOST_TYPES = {
|
||||
[0]="A murderous ghost.",
|
||||
"A sadistic ghost.",
|
||||
"A secretive ghost.",
|
||||
"An energetic poltergeist.",
|
||||
"An angry ghost.",
|
||||
"A violent ghost.",
|
||||
"A moaning spirit returned from the dead. It will generally trouble one unfortunate at a time.",
|
||||
"A howling spirit. The ceaseless noise is making sleep difficult.",
|
||||
"A troublesome poltergeist.",
|
||||
"A restless haunt, generally troubling past acquaintances and relatives.",
|
||||
"A forlorn haunt, seeking out known locations or drifting around the place of death.",
|
||||
}
|
||||
|
||||
|
||||
Identity = defclass(Identity)
|
||||
function Identity:init(args)
|
||||
local u = args.unit
|
||||
self.ident = dfhack.units.getIdentity(u)
|
||||
|
||||
self.unit = u
|
||||
self.name = dfhack.TranslateName( dfhack.units.getVisibleName(u) )
|
||||
self.name_en = dfhack.TranslateName( dfhack.units.getVisibleName(u) , true)
|
||||
self.raw_prof = dfhack.units.getProfessionName(u)
|
||||
self.pronoun = PRONOUNS[u.sex] or 'It'
|
||||
|
||||
if self.ident then
|
||||
self.birth_date = Time{year = self.ident.birth_year, ticks = self.ident.birth_second}
|
||||
self.race_id = self.ident.race
|
||||
self.caste_id = self.ident.caste
|
||||
if self.ident.histfig_id > -1 then
|
||||
self.hf_id = self.ident.histfig_id
|
||||
end
|
||||
else
|
||||
self.birth_date = Time{year = self.unit.relations.birth_year, ticks = self.unit.relations.birth_time}
|
||||
self.race_id = u.race
|
||||
self.caste_id = u.caste
|
||||
if u.hist_figure_id > -1 then
|
||||
self.hf_id = u.hist_figure_id
|
||||
end
|
||||
end
|
||||
self.race = df.global.world.raws.creatures.all[self.race_id]
|
||||
self.caste = self.race.caste[self.caste_id]
|
||||
|
||||
self.isCivCitizen = (df.global.ui.civ_id == u.civ_id)
|
||||
self.isStray = u.flags1.tame --self.isCivCitizen and not u.flags1.merchant
|
||||
self.cur_date = Time{year = df.global.cur_year, ticks = df.global.cur_year_tick}
|
||||
|
||||
|
||||
------------ death ------------
|
||||
self.dead = u.flags1.dead
|
||||
self.ghostly = u.flags3.ghostly
|
||||
self.undead = u.enemy.undead
|
||||
|
||||
if self.dead and self.hf_id then -- dead-dead not undead-dead
|
||||
local events = df.global.world.history.events2
|
||||
local e
|
||||
for idx = #events - 1,0,-1 do
|
||||
e = events[idx]
|
||||
if df.history_event_hist_figure_diedst:is_instance(e) and e.victim_hf == self.hf_id then
|
||||
self.death_event = e
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if u.counters.death_id > -1 then -- if undead/ghostly dead or dead-dead
|
||||
self.death_info = df.global.world.deaths.all[u.counters.death_id]
|
||||
if not self.death_info.flags.discovered then
|
||||
self.missing = true
|
||||
end
|
||||
end
|
||||
-- slaughtered?
|
||||
if self.death_event then
|
||||
self.death_date = Time{year = self.death_event.year, ticks = self.death_event.seconds}
|
||||
elseif self.death_info then
|
||||
self.death_date = Time{year = self.death_info.event_year, ticks = self.death_info.event_time}
|
||||
end
|
||||
-- age now or age death?
|
||||
if self.dead and self.death_date then -- if cursed with no age? -- if hacked a ressurection, such that they aren't dead anymore, don't use the death date
|
||||
self.age_time = self.death_date - self.birth_date
|
||||
else
|
||||
self.age_time = self.cur_date - self.birth_date
|
||||
end
|
||||
if DEBUG then print( self.age_time.year,self.age_time.ticks,self.age_time:getMonths() ) end
|
||||
---------- ---------- ----------
|
||||
|
||||
|
||||
---------- caste_name ----------
|
||||
self.caste_name = {}
|
||||
if isBlank(self.caste.caste_name[SINGULAR]) then
|
||||
self.caste_name[SINGULAR] = self.race.name[SINGULAR]
|
||||
else
|
||||
self.caste_name[SINGULAR] = self.caste.caste_name[SINGULAR]
|
||||
end
|
||||
if isBlank(self.caste.caste_name[PLURAL]) then
|
||||
self.caste_name[PLURAL] = self.race.name[PLURAL]
|
||||
else
|
||||
self.caste_name[PLURAL] = self.caste.caste_name[PLURAL]
|
||||
end
|
||||
---------- ---------- ----------
|
||||
|
||||
--------- growth_status ---------
|
||||
-- 'baby_age' is the age the baby becomes a child
|
||||
-- 'child_age' is the age the child becomes an adult
|
||||
if self.age_time.year >= self.caste.misc.child_age then -- has child come of age becoming adult?
|
||||
self.growth_status = ADULT
|
||||
elseif self.age_time.year >= self.caste.misc.baby_age then -- has baby come of age becoming child?
|
||||
self.growth_status = CHILD
|
||||
else
|
||||
self.growth_status = BABY
|
||||
end
|
||||
---------- ---------- ----------
|
||||
|
||||
-------- aged_caste_name --------
|
||||
local caste_name, race_name
|
||||
if self.growth_status == ADULT then
|
||||
caste_name = self.caste.caste_name[SINGULAR]
|
||||
race_name = self.race.name[SINGULAR]
|
||||
elseif self.growth_status == CHILD then
|
||||
caste_name = self.caste.child_name[SINGULAR]
|
||||
race_name = self.race.general_child_name[SINGULAR]
|
||||
else --if self.growth_status == BABY then
|
||||
caste_name = self.caste.baby_name[SINGULAR]
|
||||
race_name = self.race.general_baby_name[SINGULAR]
|
||||
end
|
||||
self.aged_caste_name = {}
|
||||
if isBlank(caste_name[SINGULAR]) then
|
||||
self.aged_caste_name[SINGULAR] = race_name[SINGULAR]
|
||||
else
|
||||
self.aged_caste_name[SINGULAR] = caste_name[SINGULAR]
|
||||
end
|
||||
if isBlank(caste_name[PLURAL]) then
|
||||
self.aged_caste_name[PLURAL] = race_name[PLURAL]
|
||||
else
|
||||
self.aged_caste_name[PLURAL] = caste_name[PLURAL]
|
||||
end
|
||||
---------- ---------- ----------
|
||||
|
||||
----- Profession adjustment -----
|
||||
local prof = self.raw_prof
|
||||
if self.undead then
|
||||
prof = str2TitleCase( self.caste_name[SINGULAR] )
|
||||
if isBlank(u.enemy.undead.anon_7) then
|
||||
prof = prof..' Corpse'
|
||||
else
|
||||
prof = u.enemy.undead.anon_7 -- a reanimated body part will use this string instead
|
||||
end
|
||||
end
|
||||
--[[
|
||||
if self.ghostly then
|
||||
prof = 'Ghostly '..prof
|
||||
end
|
||||
--]]
|
||||
if u.curse.name_visible and not isBlank(u.curse.name) then
|
||||
prof = prof..' '..u.curse.name
|
||||
end
|
||||
if isBlank(self.name) then
|
||||
if self.isStray then
|
||||
prof = 'Stray '..prof --..TRAINING_LEVELS[u.training_level]
|
||||
end
|
||||
end
|
||||
self.prof = prof
|
||||
---------- ---------- ----------
|
||||
end
|
||||
--------------------------------------------------
|
||||
--------------------------------------------------
|
||||
--[[
|
||||
prof_id ?
|
||||
group_id ?
|
||||
fort_race_id
|
||||
fort_civ_id
|
||||
--fort_group_id?
|
||||
--]]
|
||||
|
||||
|
||||
UnitInfoViewer = defclass(UnitInfoViewer, gui.FramedScreen)
|
||||
UnitInfoViewer.focus_path = 'unitinfoviewer' -- -> dfhack/lua/unitinfoviewer
|
||||
UnitInfoViewer.ATTRS={
|
||||
frame_style = gui.GREY_LINE_FRAME,
|
||||
frame_inset = 2, -- used by init
|
||||
frame_outset = 1,--3, -- new, used by init; 0 = full screen, suggest 0, 1, or 3 or maybe 5
|
||||
--frame_title , -- not used
|
||||
--frame_width,frame_height calculated by frame inset and outset in init
|
||||
}
|
||||
function UnitInfoViewer:init(args) -- requires args.unit
|
||||
--if DEBUG then print('-----') end
|
||||
local x,y = dfhack.screen.getWindowSize()
|
||||
-- what if inset or outset are defined as {l,r,t,b}?
|
||||
x = x - 2*(self.frame_inset + 1 + self.frame_outset) -- 1=frame border thickness
|
||||
y = y - 2*(self.frame_inset + 1 + self.frame_outset) -- 1=frame border thickness
|
||||
self.frame_width = args.frame_width or x
|
||||
self.frame_height = args.frame_height or y
|
||||
self.text = {}
|
||||
if df.unit:is_instance(args.unit) then
|
||||
self.ident = Identity{ unit = args.unit }
|
||||
if not isBlank(self.ident.name_en) then
|
||||
self.frame_title = 'Unit: '..self.ident.name_en
|
||||
elseif not isBlank(self.ident.prof) then
|
||||
self.frame_title = 'Unit: '..self.ident.prof
|
||||
if self.ident.isStray then
|
||||
self.frame_title = self.frame_title..TRAINING_LEVELS[self.ident.unit.training_level]
|
||||
end
|
||||
end
|
||||
self:chunk_Name()
|
||||
self:chunk_Description()
|
||||
if not (self.ident.dead or self.ident.undead or self.ident.ghostly) then --not self.dead
|
||||
if self.ident.isCivCitizen then
|
||||
self:chunk_Age()
|
||||
self:chunk_MaxAge()
|
||||
end
|
||||
if self.ident.isStray then
|
||||
if self.ident.growth_status == ADULT then
|
||||
self:chunk_Milkable()
|
||||
end
|
||||
self:chunk_Grazer()
|
||||
if self.ident.growth_status == ADULT then
|
||||
self:chunk_Shearable()
|
||||
end
|
||||
if self.ident.growth_status == ADULT then
|
||||
self:chunk_EggLayer()
|
||||
end
|
||||
end
|
||||
self:chunk_BodySize()
|
||||
elseif self.ident.ghostly then
|
||||
self:chunk_Dead()
|
||||
self:chunk_Ghostly()
|
||||
elseif self.ident.undead then
|
||||
self:chunk_BodySize()
|
||||
self:chunk_Dead()
|
||||
else
|
||||
self:chunk_Dead()
|
||||
end
|
||||
else
|
||||
self:insert_chunk("No unit is selected in the UI or context not supported.",pens.LIGHTRED)
|
||||
end
|
||||
self:addviews{ widgets.Label{ frame={yalign=0}, text=self.text } }
|
||||
end
|
||||
function UnitInfoViewer:onInput(keys)
|
||||
if keys.LEAVESCREEN or keys.SELECT then
|
||||
self:dismiss()
|
||||
end
|
||||
end
|
||||
function UnitInfoViewer:onGetSelectedUnit()
|
||||
return self.ident.unit
|
||||
end
|
||||
function UnitInfoViewer:insert_chunk(str,pen)
|
||||
local lines = utils.split_string( wrap(str,self.frame_width) , NEWLINE )
|
||||
for i = 1,#lines do
|
||||
table.insert(self.text,{text=lines[i],pen=pen})
|
||||
table.insert(self.text,NEWLINE)
|
||||
end
|
||||
table.insert(self.text,NEWLINE)
|
||||
end
|
||||
function UnitInfoViewer:chunk_Name()
|
||||
local i = self.ident
|
||||
local u = i.unit
|
||||
local prof = i.prof
|
||||
local color = dfhack.units.getProfessionColor(u)
|
||||
local blurb
|
||||
if i.ghostly then
|
||||
prof = 'Ghostly '..prof
|
||||
end
|
||||
if i.isStray then
|
||||
prof = prof..TRAINING_LEVELS[u.training_level]
|
||||
end
|
||||
if isBlank(i.name) then
|
||||
if isBlank(prof) then
|
||||
blurb = 'I am a mystery'
|
||||
else
|
||||
blurb = prof
|
||||
end
|
||||
else
|
||||
if isBlank(prof) then
|
||||
blurb=i.name
|
||||
else
|
||||
blurb=i.name..', '..prof
|
||||
end
|
||||
end
|
||||
self:insert_chunk(blurb,dfhack.pen.parse{fg=color,bg=0})
|
||||
end
|
||||
function UnitInfoViewer:chunk_Description()
|
||||
local dsc = self.ident.caste.description
|
||||
if not isBlank(dsc) then
|
||||
self:insert_chunk(dsc,pens.WHITE)
|
||||
end
|
||||
end
|
||||
|
||||
function UnitInfoViewer:chunk_Age()
|
||||
local i = self.ident
|
||||
local age_str -- = ''
|
||||
if i.age_time.year > 1 then
|
||||
age_str = tostring(i.age_time.year)..' years old'
|
||||
elseif i.age_time.year > 0 then -- == 1
|
||||
age_str = '1 year old'
|
||||
else --if age_time.year == 0 then
|
||||
local age_m = i.age_time:getMonths() -- math.floor
|
||||
if age_m > 1 then
|
||||
age_str = tostring(age_m)..' months old'
|
||||
elseif age_m > 0 then -- age_m == 1
|
||||
age_str = '1 month old'
|
||||
else -- if age_m == 0 then -- and age_m < 0 which would be an error
|
||||
age_str = 'a newborn'
|
||||
end
|
||||
end
|
||||
local blurb = i.pronoun..' is '..age_str
|
||||
if i.race_id == df.global.ui.race_id then
|
||||
blurb = blurb..', born on the '..i.birth_date:getDayStr()..' of '..i.birth_date:getMonthStr()..' in the year '..tostring(i.birth_date.year)..PERIOD
|
||||
else
|
||||
blurb = blurb..PERIOD
|
||||
end
|
||||
self:insert_chunk(blurb,pens.YELLOW)
|
||||
end
|
||||
|
||||
function UnitInfoViewer:chunk_MaxAge()
|
||||
local i = self.ident
|
||||
local maxage = math.floor( (i.caste.misc.maxage_max + i.caste.misc.maxage_min)/2 )
|
||||
--or i.unit.curse.add_tags1.NO_AGING hidden ident?
|
||||
if i.caste.misc.maxage_min == -1 then
|
||||
maxage = ' die of unnatural causes.'
|
||||
elseif maxage == 0 then
|
||||
maxage = ' die at a very young age.'
|
||||
elseif maxage == 1 then
|
||||
maxage = ' live about '..tostring(maxage)..' year.'
|
||||
else
|
||||
maxage = ' live about '..tostring(maxage)..' years.'
|
||||
end
|
||||
--' is expected to '..
|
||||
local blurb = str2FirstUpper(i.caste_name[PLURAL])..maxage
|
||||
self:insert_chunk(blurb,pens.DARKGREY)
|
||||
end
|
||||
function UnitInfoViewer:chunk_Grazer()
|
||||
if self.ident.caste.flags.GRAZER then
|
||||
local blurb = 'Grazing satisfies '..tostring(self.ident.caste.misc.grazer)..' units of hunger.'
|
||||
self:insert_chunk(blurb,pens.LIGHTGREEN)
|
||||
end
|
||||
end
|
||||
function UnitInfoViewer:chunk_EggLayer()
|
||||
local caste = self.ident.caste
|
||||
if caste.flags.LAYS_EGGS then
|
||||
local clutch = math.floor( (caste.misc.clutch_size_max + caste.misc.clutch_size_min)/2 )
|
||||
local blurb = 'Lays clutches of about '..tostring(clutch)
|
||||
if clutch > 1 then
|
||||
blurb = blurb..' eggs.'
|
||||
else
|
||||
blurb = blurb..' egg.'
|
||||
end
|
||||
self:insert_chunk(blurb,pens.GREEN)
|
||||
end
|
||||
end
|
||||
function UnitInfoViewer:chunk_Milkable()
|
||||
local i = self.ident
|
||||
if i.caste.flags.MILKABLE then
|
||||
local milk = dfhack.matinfo.decode( i.caste.extracts.milkable_mat , i.caste.extracts.milkable_matidx )
|
||||
if milk then
|
||||
local days,seconds = math.modf ( i.caste.misc.milkable / TU_PER_DAY )
|
||||
days = (seconds > 0) and (tostring(days)..' to '..tostring(days + 1)) or tostring(days)
|
||||
--local blurb = pronoun..' produces '..milk:toString()..' every '..days..' days.'
|
||||
local blurb = (i.growth_status == ADULT) and (i.pronoun..' secretes ') or str2FirstUpper(i.caste_name[PLURAL])..' secrete '
|
||||
blurb = blurb..milk:toString()..' every '..days..' days.'
|
||||
self:insert_chunk(blurb,pens.LIGHTCYAN)
|
||||
end
|
||||
end
|
||||
end
|
||||
function UnitInfoViewer:chunk_Shearable()
|
||||
local i = self.ident
|
||||
local mat_types = i.caste.body_info.materials.mat_type
|
||||
local mat_idxs = i.caste.body_info.materials.mat_index
|
||||
local mat_info, blurb
|
||||
for idx,mat_type in ipairs(mat_types) do
|
||||
mat_info = dfhack.matinfo.decode(mat_type,mat_idxs[idx])
|
||||
if mat_info and mat_info.material.flags.YARN then
|
||||
blurb = (i.growth_status == ADULT) and (i.pronoun..' produces ') or str2FirstUpper(i.caste_name[PLURAL])..' produce '
|
||||
blurb = blurb..mat_info:toString()..PERIOD
|
||||
self:insert_chunk(blurb,pens.BROWN)
|
||||
end
|
||||
end
|
||||
end
|
||||
function UnitInfoViewer:chunk_BodySize()
|
||||
local i = self.ident
|
||||
local pat = i.unit.body.physical_attrs
|
||||
local blurb = i.pronoun..' appears to be about '..pat.STRENGTH.value..':'..pat.AGILITY.value..' cubic decimeters in size.'
|
||||
self:insert_chunk(blurb,pens.LIGHTBLUE)
|
||||
end
|
||||
function UnitInfoViewer:chunk_Ghostly()
|
||||
local blurb = GHOST_TYPES[self.ident.unit.relations.ghost_info.type].." This spirit has not been properly memorialized or buried."
|
||||
self:insert_chunk(blurb,pens.LIGHTMAGENTA)
|
||||
-- Arose in relations.curse_year curse_time
|
||||
end
|
||||
function UnitInfoViewer:chunk_Dead()
|
||||
local i = self.ident
|
||||
local blurb, str, pen
|
||||
if i.missing then --dfhack.units.isDead(unit)
|
||||
str = ' is missing.'
|
||||
pen = pens.WHITE
|
||||
elseif i.death_event then
|
||||
--str = "The Caste_name Unit_Name died in year #{e.year}"
|
||||
--str << " (cause: #{e.death_cause.to_s.downcase}),"
|
||||
--str << " killed by the #{e.slayer_race_tg.name[0]} #{e.slayer_hf_tg.name}" if e.slayer_hf != -1
|
||||
--str << " using a #{df.world.raws.itemdefs.weapons[e.weapon.item_subtype].name}" if e.weapon.item_type == :WEAPON
|
||||
--str << ", shot by a #{df.world.raws.itemdefs.weapons[e.weapon.bow_item_subtype].name}" if e.weapon.bow_item_type == :WEAPON
|
||||
str = DEATH_TYPES[i.death_event.death_cause]..PERIOD
|
||||
pen = pens.MAGENTA
|
||||
elseif i.death_info then
|
||||
--str = "The #{u.race_tg.name[0]}"
|
||||
--str << " #{u.name}" if u.name.has_name
|
||||
--str << " died"
|
||||
--str << " in year #{death_info.event_year}" if death_info
|
||||
--str << " (cause: #{u.counters.death_cause.to_s.downcase})," if u.counters.death_cause != -1
|
||||
--str << " killed by the #{killer.race_tg.name[0]} #{killer.name}" if killer
|
||||
str = DEATH_TYPES[i.death_info.death_cause]..PERIOD
|
||||
pen = pens.MAGENTA
|
||||
elseif i.unit.flags2.slaughter and i.unit.flags2.killed then
|
||||
str = ' was slaughtered.'
|
||||
pen = pens.MAGENTA
|
||||
else
|
||||
str = ' is dead.'
|
||||
pen = pens.MAGENTA
|
||||
end
|
||||
if i.undead or i.ghostly then
|
||||
str = ' is undead.'
|
||||
pen = pens.GREY
|
||||
end
|
||||
blurb = 'The '..i.prof -- assume prof is not blank
|
||||
if not isBlank(i.name) then
|
||||
blurb = blurb..', '..i.name
|
||||
end
|
||||
blurb = blurb..str
|
||||
self:insert_chunk(blurb,pen)
|
||||
end
|
||||
|
||||
-- only show if UnitInfoViewer isn't the current focus
|
||||
if dfhack.gui.getCurFocus() ~= 'dfhack/lua/'..UnitInfoViewer.focus_path then
|
||||
local gui_no_unit = false -- show if not found?
|
||||
local unit = getUnit_byVS(gui_no_unit) -- silent? or let the gui display
|
||||
if unit or gui_no_unit then
|
||||
local kan_viewscreen = UnitInfoViewer{unit = unit}
|
||||
kan_viewscreen:show()
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue