v50 Steam/Premium information for editors
  • v50 information can now be added to pages in the main namespace. v0.47 information can still be found in the DF2014 namespace. See here for more details on the new versioning policy.
  • Use this page to report any issues related to the migration.
This notice may be cached—the current version can be found here.

User:Fleeting Frames/listobituary

From Dwarf Fortress Wiki
< User:Fleeting Frames
Revision as of 12:01, 19 November 2019 by Fleeting Frames (talk | contribs) (Correction of link, correction of typo, more description)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Requires gui/indicator-screen, run when viewing dead units list, outputs deadcounts.csv into the folder that sorts number of deaths by category and date

--Death listing shower
--[===[
listobituary
============

Displays the death date and cause in unitlist/Dead

]===]

local saveToFileEnabled = false
local indipresent, indi = pcall(function() return dfhack.script_environment('gui/indicator_screen') end)
if not indipresent then qerror("this script required gui/indicator_screen") end

function categorizeUnit(unit)
	if(df.global.world.raws.creatures.all[unit.race].flags.GENERATED) then return "Procedurals" end
	--in case of procedural invaders
	if(unit.flags1.invader_origin or unit.flags1.invades) then return "Invaders" end
	if(dfhack.units.isMerchant(unit) or (unit.flags3.unk31 and unit.flags2.visitor) ) then return "Visitors" end
	--unk31 is "guest" toggle.
	if(dfhack.units.isOwnCiv(unit) and 
	( (unit.profession>74 and unit.profession<98) or 
	   unit.profession == 110 
	or unit.profession == 65)) then return "Military" end
	--Profession is military profession or Mercenary or Administrator; not ideal but what is better?
	if(dfhack.units.isOwnCiv(unit) and (dfhack.units.isTamable(unit) or dfhack.units.isDomesticated(unit))) then return "Livestock" end
	--higher specific matches above less specific ones 
	if(dfhack.units.isOwnCiv(unit)) then return "Fort" end
	--For covering both workers and residents here. 
	if( (dfhack.units.isTamable(unit) or not df.global.world.raws.creatures.all[unit.race].caste[unit.caste].flags.CAN_LEARN)) then return "Animals" end
	--Not learning but not tamable: Unicorns, some evil and cavern creatures. 
	if(df.global.world.raws.creatures.all[unit.race].flags.CASTE_CAN_LEARN) then return "Savages" end
	--Covers animalmen, gremlins, current resident cave people and camp goblins
end

function saveToFile(deadlist, deathdates, cumulative)
	if saveToFileEnabled then
    local file, err = io.open((dfhack.getDFPath() .. "/deadcounts.csv"), "wb")
    
    local outT,outTi = {}
    outTb = {Date =0, in_years =0, Total =0, Fort =0, Military =0, Livestock =0, Animals =0, Procedurals =0, Invaders =0, Visitors =0, Savages=0}
    outTi = copyall(outTb)
    for index = 0, #deadlist-1 do 
		if index == 0 or outTi["Date"] ~= deathdates[index+1].text then
			if index ~= 0 then table.insert(outT,outTi)
			outTi = cumulative and copyall(outTi) or copyall(outTb) end
			outTi["Date"] = deathdates[index+1].text
			outTi["in_years"] = outTi["Date"]:find("Unknown") and "Unknown" or (outTi["Date"]:sub(1,4)+(outTi["Date"]:sub(6,7)-1)/12+(outTi["Date"]:sub(9,10)/(12*28)))
		end
		outTi["Total"] = outTi["Total"] + 1
		if categorizeUnit(deadlist[index]) then
		outTi[categorizeUnit(deadlist[index])] = outTi[categorizeUnit(deadlist[index])] + 1
		else
			print("No category for " .. df.global.world.raws.creatures.all[unit.race].creature_id .. " " .. dfhack.TranslateName(unit.name,true) " id " .. unit.id)
		end
    end
	table.insert(outT,outTi) --last element
	file:write("Date;in years;Total;Fort;Military;Livestock;Animals;Procedurals;Invaders;Visitors;Savages")
	file:write(NEWLINE)
	local optionorder= {}
	table.insert(optionorder,"Date")
	table.insert(optionorder,"in_years")
	table.insert(optionorder,"Total")
	table.insert(optionorder,"Fort")
	table.insert(optionorder,"Military")
	table.insert(optionorder,"Livestock")
	table.insert(optionorder,"Animals")
	table.insert(optionorder,"Procedurals")
	table.insert(optionorder,"Invaders")
	table.insert(optionorder,"Visitors")
	table.insert(optionorder,"Savages")
			
	for index = 1, #outT do
		for oindex=1,#optionorder do
			file:write((tostring(outT[index][optionorder[oindex]]) .. ";"))
		end
		file:write(NEWLINE)
	end	
	file:flush()
	file:close()
	end
end

function getBottomMostViewscreenWithFocus(text, targetscreeni)
    --Finds and returns the screen closest to root screen whose path includes text
 local targetscreen = targetscreeni or df.global.gview.view.child
 --if the target screen wasn't included, starts looking from the bottom
 if targetscreen and
    dfhack.gui.getFocusString(targetscreen):find(text) then
    return targetscreen
 elseif targetscreen and targetscreen.child then --Attempting to call nil.child will crash this
    return getBottomMostViewscreenWithFocus(text, targetscreen.child)
 end
 -- if there's no child, it didn't find a screen with text in focus and returns nil
end
orbituarylist = orbituarylist or {}
local unitscreen = getBottomMostViewscreenWithFocus("unitlist/Dead")
if not unitscreen then
qerror("This script requires you to view dead units list")
else
	print(#unitscreen.units.Dead)
	local pagelength = df.global.gps.dimy - 9
	local list_rect = {x = 86, y = 4, width = 13, height = pagelength}
	local currentpage = math.floor(unitscreen.cursor_pos.Dead / pagelength)
	orbituarylist.color=6
	
	if #orbituarylist<#unitscreen.units.Dead then --refreshing things
		for index=#orbituarylist, #unitscreen.units.Dead-1 do 
			table.insert(orbituarylist,{text = dfhack.run_command_silent("deathcause " .. unitscreen.units.Dead[index].id):sub(4,16):match('%d+-%d+-%d+') or "Unknown"})
			dfhack.println(index, orbituarylist[index].text)
		--[[if(categorizeUnit(unitscreen.units.Dead[index])) then
				print("unit's category is " .. categorizeUnit(unitscreen.units.Dead[index]))
			else
				print("No category for " .. df.global.world.raws.creatures.all[unitscreen.units.Dead[index].race].creature_id .. " " .. dfhack.TranslateName(unitscreen.units.Dead[index].name,true) .. " id " .. unitscreen.units.Dead[index].id)
			end--]]
		end
	end

	saveToFile(unitscreen.units.Dead, orbituarylist, false)
	local newscreen = indi.getScreen(orbituarylist, list_rect, nil)
	local baseonInput = newscreen.onInput
	newscreen.onInput = function(self, keys)
	baseonInput(self,keys)
		if ((keys._MOUSE_L or keys._MOUSE_L_DOWN) or
			(keys._MOUSE_R or keys._MOUSE_R_DOWN)) then
			df.global.gview.view.child.child.cursor_pos = (df.global.gps.mouse_y-2+currentpage*pagelength)
		end
	if currentpage ~= math.floor(df.global.gview.view.child.child.cursor_pos.Dead / pagelength) then
	currentpage = math.floor(df.global.gview.view.child.child.cursor_pos.Dead / pagelength)
	self.frame_body.y1 = 4-currentpage*pagelength --Hacky, but directly setting rect won't work due rollover. Might want to update indicator-screen for map scrolling.
	end
	end

	local baseOnResize = newscreen.onResize
	newscreen.onResize = function(self)
		baseOnResize(self)
		if pagelength ~= (df.global.gps.dimy - 7) then
			--If window length changed, better refresh the data.
			pagelength = df.global.gps.dimy - 7
			self:adjustDims(true,list_rect.x, list_rect.y,(df.global.gps.dimx-4), pagelength)
			--Not adjusting height here would result in situation where making screen shorter works, but taller not.
			self.frame_body.y1 = 4-currentpage*pagelength
		end
	end

	newscreen:show()
end