What do you mean, "there are no anchors"?

Adventurer Freedom + AdvTweaks - A DFhack command-prompt + light raw mod that allows you to adventure as any creature, from any civilization. Now with added automaticness!

- A DFhack command-prompt + light raw mod that allows you to adventure as any creature, from any civilization. Now with added automaticness! View Allegiance - A DFhack script that gives a readable report about a unit's entity links, past and present

- A DFhack script that gives a readable report about a unit's entity links, past and present Make Companion - A DFhack script that makes the selected unit an adventuring companion.

- A DFhack script that makes the selected unit an adventuring companion. Clothing Optional - A DFhack script that you can use to prevent creatures from generating bad thoughts for lacking shirts/pants/shoes. Useful for playing modded creatures designed to lack such things.

Adventurer Freedom

Play as any creature, including world-unique generated creatures (contains spoilers)

Start as a member of any Civilization

Start as an Outsider of any race

Begin with any natural skills you should have (bugfix of vanilla bug)

Edit the size of your attribute and skill pool

At the moment it runs through the DFhack command-prompt but I hope one day (when I actually figure GUI stuff out) it'll have a swanky GUI interface, or at the very least use the Dwarf Fortress window

View Allegiance

Spoiler (click to show/hide) Code: (view-allegiance.lua) [Select] --Reports the entities a selected unit is linked to.



--I appreciate that there's a lot of inefficiency and redundancy in this script, but I'm a hack of a programmer so screw it.



--[[

List of needed changes:

- assignment name doesn't account for gender

- Need more information form 'real world' testing

]]



local unit = dfhack.gui.getSelectedUnit()



if not unit then

print("Select a unit")

return

end



--Find historical figure given historical figure id

local function findHistFig(id)

local out = df.historical_figure.find(id) or false

if out then

return out

else

dfhack.printerr("No historical figure found with id: ".. id)

end

end



--Find site given site id

local function findSite(id)

local out = df.world_site.find(id) or false

if out then

return out

else

dfhack.printerr("No site found with id: ".. id)

end

end



local function getSiteGoverned(links)

for i = 1, #links, 1 do

local alti = i-1 --Program conventions, yadda yadda

local current = links[alti]



if current.flags.residence == true or current.flags.capital == true then --This is the site the Site Government governs! (Confusing, I know)

return findSite(current.target)

end

end

return false

end



local function getRaceName(id) --Probably short enough to not need a function but screw it :P

return df.global.world.raws.creatures.all[id].name[0]

end



--Doesn't really need to be a function but oh well. Returns the entity with the given id.

local function getEntity(entityId)

return df.global.world.entities.all[entityId]

end





local function getAssignment(linkEntry) --Gets the actual position data of an assignment from entity_link, using entity_link entry information

local entity = getEntity(linkEntry.entity_id)

local assignmentId = linkEntry.assignment_id



local positionId --One part needed from this convoluted system

for i = 1, #entity.positions.assignments, 1 do --While the table identifiers and the position_ids seem to match up in gm-editor, I'm not going to trust the game to be sensible.

local alti = i-1

local current = entity.positions.assignments[alti]



if current.id == assignmentId then

positionId = current.position_id --The pointer to the ACTUAL position information

break

end

end

for i = 1, #entity.positions.own, 1 do

local alti = i-1

local current = entity.positions.own[alti]



if current.id == positionId then

--Finally found the damn thing

return current

end

end

end



local function workaround(testTable, testKey) --A hacky workaround to test if a key exists in a table. In normal Lua you can just call a variable and it'll be false if it doesn't exist, but C++ doesn't like that. If you're reading this and there's a better way of doing this, please tell me :P

--Returns true if key is present, false if not

for k, v in pairs(testTable) do

if k == testKey then

return true

end

end

return false

end



--[[

For my own future reference, here are all the types of histfig_entity_links

histfig_entity_link_mercenaryst

histfig_entity_link_former_slavest

histfig_entity_link_criminalst

histfig_entity_link_positionst

histfig_entity_link_memberst

histfig_entity_link_enemyst

histfig_entity_link_former_prisonerst

histfig_entity_link_former_squadst

histfig_entity_link_former_mercenaryst

histfig_entity_link_slavest

histfig_entity_link_squadst

histfig_entity_link_former_occupationst

histfig_entity_link_position_claimst

histfig_entity_link_former_memberst

histfig_entity_link_prisonerst

histfig_entity_link_former_positionst

histfig_entity_link_occupationst

]]

local function getLinkType(link) --Returns a nicely-formatted string for the type of link provided. I've ordered these in a general order of what I think is most likely to least likely

if df.histfig_entity_link_memberst:is_instance(link) then

--A regular member

return "Member"

elseif df.histfig_entity_link_former_memberst:is_instance(link) then

--A former member

return "Former Member"

elseif df.histfig_entity_link_enemyst:is_instance(link) then

--An enemy

return "Enemy" --?

elseif df.histfig_entity_link_criminalst:is_instance(link) then

--A criminal

return "Criminal" --?

--Does it say something about DF societies that there's no "former enemy"s or "former criminal"s?

elseif df.histfig_entity_link_positionst:is_instance(link) then

--Occupying a position

return "Position Holder"

elseif df.histfig_entity_link_former_positionst:is_instance(link) then

--Former position holder

return "Former Position Holder"

elseif df.histfig_entity_link_slavest:is_instance(link) then

--Slave

return "Slave"

elseif df.histfig_entity_link_former_slavest:is_instance(link) then

--Former Slave

return "Former Slave"

elseif df.histfig_entity_link_squadst:is_instance(link) then

--Squad member. There's probably some way to find out what the civ calls a member of that squad via finding the assignment their leader is linked to but I can't be bothered :P

return "Soldier"

elseif df.histfig_entity_link_former_squadst:is_instance(link) then

--Former squad member

return "Former Soldier"

elseif df.histfig_entity_link_prisonerst:is_instance(link) then

--Prisoner

return "Prisoner"

elseif df.histfig_entity_link_former_prisonerst:is_instance(link) then

--Former Prisoner

return "Former Prisoner"

elseif df.histfig_entity_link_occupationst:is_instance(link) then

--Occupation holder?

return "Occupation Holder"

elseif df.histfig_entity_link_former_occupationst:is_instance(link) then

--Former Occupation holder?

return "Former Occupation Holder"

elseif df.histfig_entity_link_mercenaryst:is_instance(link) then

--Mercenary

return "Mercenary"

elseif df.histfig_entity_link_former_mercenaryst:is_instance(link) then

--Former Mercenary

return "Former Mercenary"

elseif df.histfig_entity_link_position_claimst:is_instance(link) then

--I have no idea what this is

return "Position Claimant" --?

else

return "Unknown"

end

end





local function process(histFig) --Get all entity data. Add it to a table. Get data that's missing (race names, site names, link strength from Histfig, etc.). Make sure there are no duplicate Entity entries

local out = {}



local links = histFig.entity_links

for i = 1, #links, 1 do

local alti = i - 1

local currentEntity = getEntity(links[alti].entity_id)

--Hoo boy. Here we go

if out[currentEntity.id] ~= nil then --There's already been an entity with the same id

--Check if there's relevant position data to add, if not it'll just be disregarded entirely

if workaround(links[alti],"assignment_id") or workaround(links[alti],"squad_id") then --Has assignment data/squad data.

--Following is just copypasta'd from code later on. If you want explainations, go there.

--Checks to allow squad data has been messily hacked on.

local ass = {}



ass.startDate = links[alti].start_year

if workaround(links[alti],"end_year") then

ass.endDate = links[alti].end_year

else

ass.endDate = -1

end



if workaround(links[alti],"assignment_id") then --An assignment

ass.assignmentName = getAssignment(links[alti]).name[0]

ass.assignmentId = links[alti].assignment_id

else --A squad

ass.assignmentId = links[alti].squad_id

ass.assignmentName = "A Soldier" --TODO: allow it to actually get squad soldier name

end



--Make sure the place we're adding this to actually exists, and if not, make it

if out[currentEntity.id].assignment == nil then

out[currentEntity.id].assignment = {}

end

table.insert(out[currentEntity.id].assignment, ass)

end

else

--No entry using this id, so we'll create one using the Entity's ID as a key

out[currentEntity.id] = {}



--First, the simple things:

out[currentEntity.id].name = currentEntity.name

out[currentEntity.id].id = currentEntity.id

out[currentEntity.id].nameNice = dfhack.TranslateName(currentEntity.name, true)



--Then on to the bits that are slightly more complicated:

--Getting the type and the name of that type

if currentEntity.type == 0 then

out[currentEntity.id].typeName = "Civilization"

out[currentEntity.id].type = 0

elseif currentEntity.type == 1 then --Screw figuring out switch statements or whatever

out[currentEntity.id].typeName = "Site Government"

out[currentEntity.id].type = 1

elseif currentEntity.type == 2 then

out[currentEntity.id].typeName = "Vessel Crew"

--I couldn't find any examples of an entity of this type in my save

out[currentEntity.id].type = 2

elseif currentEntity.type == 3 then

out[currentEntity.id].typeName = "Migrating Group"

out[currentEntity.id].type = 3

elseif currentEntity.type == 4 then

out[currentEntity.id].typeName = "Nomadic Group"

out[currentEntity.id].type = 4

elseif currentEntity.type == 5 then

out[currentEntity.id].typeName = "Religion"

out[currentEntity.id].type = 5

elseif currentEntity.type == 6 then

out[currentEntity.id].typeName = "Military Unit"

out[currentEntity.id].type = 6

--I couldn't find any examples of an entity of this type in my save

elseif currentEntity.type == 7 then

out[currentEntity.id].typeName = "Outcast"

out[currentEntity.id].type = 7

elseif currentEntity.type == 8 then

out[currentEntity.id].typeName = "Performance Troupe"

out[currentEntity.id].type = 8

end



--Record the Histfig's link strength to the entity.

out[currentEntity.id].strength = links[alti].link_strength



--Record the type of entity link. Long enough that I'm putting it in its own function

out[currentEntity.id].linkType = getLinkType(links[alti])



--Check if entity is a Civilization to add the additional raceName data. Race might be applicable for other entity types, but the ultimate output will only care if it's a Civ

if currentEntity.type == 0 then

out[currentEntity.id].raceName = getRaceName(currentEntity.race)

end



--Check if entity is a Site Government to add the additional siteName data

if out[currentEntity.id].type == 1 then

--Surprisingly, it's possible to have a Site Government without a Site, so gotta do a check here

local site = getSiteGoverned(currentEntity.site_links)

if site ~= false then

out[currentEntity.id].siteName = dfhack.TranslateName(getSiteGoverned(currentEntity.site_links).name, true)

else

out[currentEntity.id].siteName = "an unknown place"

end

end



--Check if entity has a parent to add the parent + parent's name as data

if #currentEntity.entity_links >= 1 then

for j = 1, #currentEntity.entity_links, 1 do --I'm using J because I don't want to have to work out how the locals will interact ;P

local altj = j-1

if currentEntity.entity_links[altj].type == 0 then --it has a parent

local parent = getEntity(currentEntity.entity_links[altj].target)

out[currentEntity.id].parent = parent

out[currentEntity.id].parentName = dfhack.TranslateName(parent.name, true)

end

end

end



--Check if the HistFig had any assignments related to the entity, and if so create an entry with the data.

if workaround(links[alti],"assignment_id") or workaround(links[alti],"squad_id") then --There's a (hackily-found) assignment

--Check if there's a preexisting assignment data holder and, if there isn't, create one

if out[currentEntity.id].assignment == nil then

out[currentEntity.id].assignment = {}

end

--Make a temp table to store the assignment data into before adding it

local ass = {} --I know, I'm hilarious

ass.startDate = links[alti].start_year



if workaround(links[alti],"end_year") then

ass.endDate = links[alti].end_year

else

ass.endDate = -1

end



if workaround(links[alti],"assignment_id") then --An assignment

--Blimey, getting the name for an assignment is convoluted. Enough so that I'm putting it in its own function

ass.assignmentName = getAssignment(links[alti]).name[0]

ass.assignmentId = links[alti].assignment_id

else --A squad

ass.assignmentId = links[alti].squad_id

ass.assignmentName = "A Soldier" --TODO: allow it to actually get squad soldier name

end



--Then time to add the data

out[currentEntity.id].assignment[#out[currentEntity.id].assignment+1] = ass

-- NOTE: This table is actually a table that starts at 1! (THE HORROR). I might forget that later and wonder why everything's going wrong.

end



--Aaand I think that's it

end

end

return out

end



local function doTheThing()

local messyData = process(findHistFig(unit.hist_figure_id))

local finalOut = {}

for k, v in pairs(messyData) do

local stringAdd

stringAdd = v.nameNice

stringAdd = (stringAdd .. " - ")

if v.type == 0 then --A civ

stringAdd = stringAdd .. ("A " .. tostring(v.raceName):gsub("^%l", string.upper) .. " " .. v.typeName)

elseif v.type == 1 then --A Site Government

stringAdd = stringAdd .. ("A Government related to " .. v.siteName)

else --Those other things without extra information

stringAdd = stringAdd .. ("A " .. v.typeName)

end



--Add extra information if it has a parent

if v.parent ~= nil then

stringAdd = stringAdd .. (" - Part of " .. v.parentName)

end



--Now mention the nature of their membership. For now, I'm leaving out the link_strength % since I don't fully understand their significance

stringAdd = stringAdd .. (" (" .. v.linkType .. ")")



--Add a few extra bits of info if the HistFig had any related assignments

if v.assignment ~= nil then

--Just for prettiness' sake, have different formatting depending on whether there's only one, or multiple assignment entries

if #v.assignment == 1 then

stringAdd = stringAdd .. ("

Serving as ")

else

stringAdd = stringAdd .. ("

Serving as:

\t")

end



for k2, v2 in pairs(v.assignment) do

--Quick check to find out what to say for the End Date

local theEnd

if v2.endDate == -1 then

theEnd = "the present day"

else

theEnd = v2.endDate

end



--Just going to capitalize the first letter of the assignment name here so it doesn't make the final output assignment look so cluttered

local niceTitle = tostring(v2.assignmentName):gsub("^%l", string.upper)



--check if there are more entries to see if there should be a newline at the end... this might break based on me using pairs to iterate through this... BUG POTENTIAL

local endLine = ""

if v.assignment[k2+1] ~= nil then

endLine = "

\t"

end



stringAdd = (stringAdd .. niceTitle .. " from " .. v2.startDate .. " until " .. theEnd .. endLine)

end

end



finalOut[#finalOut+1] = stringAdd

end



--And now to FINALLY output all the pretty information to the console:

print(dfhack.TranslateName(dfhack.units.getVisibleName(unit), true) .. " belongs to:")

for k, v in pairs(finalOut) do

print(v)

end

end



--ZHU LI

doTheThing()



Make Companion

The game will usually generate a nemesis record and historical figure data after you first talk to that person.

Because the unit didn't join as part of an agreement, you'll have to use dfhack's gui/companion-order to get them to leave. If you're using dfhack 0.44.12-r2, you'll need to download an updated version of companion-order since the one that shipped with the release is broken.

While animals are smart enough to obey your request via the talk menu for them to wait in a spot, they aren't smart enough to follow you again if you ask them. They'll just stand in place, forever. So maybe don't do that.

Clothing Optional

While playing adventure mode I've often made scripts to improve my adventuring experience, and since I've now reached the skill level that I'm capable of making somewhat presentable scripts, I figured I'd share the ones I've made that might be of use to some people.I was delayed long enough before uploading the mod that there's now a whole new addition. First, there's the original command-prompt version, and now there's an additional new script that performs some of its features automatically, as well as some new features. The new automatic version automatically adds every creature to the Intelligent Wilderness Animal section, automatically makes every civ available to start from, automatically patches in any natural skills and automatically unlocks the skill list to make every skill available to purchase. Automatically automatically automatically. It also has a bunch of settings that can be altered to toggle certain features on/off, among other things. With all the extra stuff added to the menus, it's worth remembering that Page Up and Page Down can be used to help navigateThe download also contains a small tweak script to allow for playing as non-standard race to be a more enjoyable experience, like giving your adventurer the ability to open doors, learn skills, and speak. The features of the tweak script are fully customizable and can be toggled on/off.I'd include the readme in this post but it sets me over the character limit ^_^;One of the things that always nags me is that there's generally no way to ask characters what groups they belong to - you usually only end up finding out when the citizens of the nearby hamlet start calling you a murderer for killing the person you thought was a bandit, or when your new companion suddenly decides to start a massacre in the town market because it turns out they're a sworn enemy of the people who live there. So I made this script. It prints all the entities a person has links to and the nature of those links, so used in conjunction with information gathered ingame and perhaps a bit of legends viewer it allows you to make informed decisions on how to act.Tired of having to actually do a bunch of good deeds to cement your reputation as a legend before you can recruit a bunch of peasants to build a nice house for you? Use this script and you can turn anyone into a travelling companion!(See script's help entry for usage)Notes:Mainly intended for modded creatures that should be fine not wearing particular types of clothes, this script allows you to register creatures or entities to no longer get upset if they're lacking shirts, pants, shoes, or any combination of the three. This means you no longer have to set their bashfulness to minimum(See script's help entry for usage)Shoutouts to Max for teaching me some of the stuff I needed to know for making Adventurer Freedom, as well as letting me steal his codeFeel free to post if you have any questions/know how I could be doing things better/run into problems - I do my best to make sure my scripts work properly before I share them, but they could all definitely benefit from some decent field testing.