I just recently had similar problem, and I found a solution! I tried to add wild caveswallow man as my citizen. At first I tried ‘tweak makeown’ command and it kinda worked, but not really. Caveswallow man became some sort of long-term resident, which means that I couldn't turn on any labors for him. Oh, man, how long I was looking for a solution, and it end up being rather simple one. I used gui/gm-editor on unit in question (press 'v' to select unit, then type gui/gm-editor in dfhack). First, his civ_id should match you fortress civilization id (you could look it up from one of your citizens).
(http://i.imgur.com/fZL99ts.png)
Also, you should uncheck some flags: flags2.visitor, flags3.unk31 definitely should be false (unk31 is scolar, some other unks are probably responsible for other types of visitors. To be sure, check one of your dwarfs and set all flags the same way), for invaders other flags should be unchecked, but it still didn't work (while invader became fully controllable, they remained hostile)
(http://i.imgur.com/AxCQMVW.png)
(http://i.imgur.com/5MJKXIN.png)
Your soon to be a citizen also should be a historical figure, it's not a problem for visitors, they always are, but for wild animal people you have to make them historical figures (being involved in combat, killing someone etc.). For next step find hist_figure_id or hist_figure_id2. To open hist_figure entry highlight it and press 'i' and in a list choose 'historical_figure' (would be first in a list).
(http://i.imgur.com/IqobwZq.png)
There go to entity_links, you will see links to all of the entities.
(http://i.imgur.com/QTTobEM.png)
To add another link press alt-i then write histfig_entity_link_memberst and enter.
(http://i.imgur.com/zJXRSKW.png)
New entry will appear at the bottom, go into it, there would be two values. For id write your civ_id, for strength - 100.
(http://i.imgur.com/jUEAK2t.png)
Now go back to entity_links and add another histfig_entity_link_memberst. For this one you have to look up the id of your local government. To do this just find it in entity_links of your citizen. Use this id for new citizen and strength to 100.
(http://i.imgur.com/40J42g8.png)
This way you should get fully controllable citizen
(http://i.imgur.com/PduYawQ.png)
The most important thing in all of this is him being a member of you local government entity as well as having civ_id set to you civ id. (some flags may also mess up him being controllable)
You could also add id of your historical figure to both historical_entity's in histfig_ids and hist_figures but last time I tried, I skipped this and it worked fine.
Somebody with lua knowledge could probably make script for all of this. (I could do it myself, but it will require me learning lua, and I'm not a programmer at all).
Preferences were not a big deal for me, and that's probably the simpliest method of making someone controllabe.
Ps. Sorry for my English, haven’t properly checked for mistakes.
Edit: I have to confess, I haven't read whole thread when I posted. From what I'm understanding now, you want him to undergo whole process of petitioning, not just make him controllable. Would love to see your results.
If your visitor entered with everything in the meeting set up except state, then i would assume that everything else required for petitioning was also set up. Look for specific_refs with type 4 (activity), if it is there when your visitor entered, then it seems like he had been set up for petitioning by a game. I tried adding same specific_refs for both mayor and visitor and it still didn't work. But i'll post my script anyway:local utils = require 'gui'
function petition()
local newcitizen
newcitizen=dfhack.gui.getSelectedUnit()
if newcitizen==nil then
qerror("No unit selected")
end
local govId=df.global.ui.group_id
newcitizen.meeting.target_entity=govId
newcitizen.meeting.state=1
newcitizen.meeting.target_role=3
newcitizen.idle_area_type=18
local govEntity=df.historical_entity.find(govId)
local mayorHistfigId
for i,j in pairs(govEntity.positions.own) do
if j.responsibilities.MEET_WORKERS==true then
for k,l in pairs(govEntity.positions.assignments) do
if l.position_id==j.id then
if l.histfig~=-1 then
mayorHistfigId=l.histfig
break
else
break
end
end
end
if mayorHistfigId~=nil then
break
end
end
end
local mayorHistfig=df.historical_figure.find(mayorHistfigId)
local mayorUnitId=mayorHistfig.unit_id
local mayorUnit=df.unit.find(mayorUnitId)
local actId=df.global.activity_next_id
df.global.activity_next_id=df.global.activity_next_id+1
df.global.ui.activities:insert("#",{new=true,id=actId,unit_actor=newcitizen,unit_noble=mayorUnit,flags={add_delay=true},unk3=3,delay=0,tree_quota=-1})
local actNum=#df.global.ui.activities-1
newcitizen.specific_refs:insert("#",{new=true,type=4,activity=df.global.ui.activities[actNum]})
local specNum=#newcitizen.specific_refs-1
mayorUnit.specific_refs:insert("#",newcitizen.specific_refs[specNum])
mayorUnit.idle_area_type=19
end
petition()
This script changes meeting state for visitor (and everything else in meeting), adds specific_ref and activity to both mayor and visitor. Visitor would be 'attending meeting', but wouldn't really do anything. On a side note, I used df.global.activity_next_id for next id when created activity for specific_ref, but i’m not sure if it’s right id, I think that one used for activity_entrys. Also I almost never coded and that’s my first attempt at lua, so I am open to hear what I’ve done wrong in this script.
Other things that I found: When visitor is looking for meeting, in df.global.ui.meeting_requests will appear some value (I think it's some sort of id for meeting request, though gm-editor doesn't have a list of meeting requests). When visitor and mayor met and petition appeared, in df.global.ui.petitions will appear another entry (again, I think it's an id for petition). It would be great if it was possible just to add petition itself, but at this point I don't think it's possible.
Some visitors also petition not just for themselves, but for a troup, and this information also have to be somewhere (my guess that it's stored in meeting requests and later in petitions, but df.global.ui only lists their ids).
So it’s easier just to add them by my first method, I’m pretty sure that stuff required to force visitors to petition is not indexed by dfhack yet.
No, the specific ref activity is set up as the meeting is set up. It's not present on visitors entering, but set up later. The stuff in df.global.ui.meeting_requests is likewise set up as the meeting is scheduled (and is messed up on blocked meetings). The df.global.ui.petitions list has been empty when I have looked at it, but I have assumed it is populated as a meeting has been conducted (i.e. contains the stuff that go into the P menu).
I think looking at the unblockmeetings script -- Work around for blocked meetings where a change of position holder causes the meeting activities to
-- stall. The somewhat heavy handed approch wipes all meeting activities from the participants and the
-- internal store and relies on DF's ability to rebuild the ones that should actually be there.
--
function unblockmeetings ()
if not dfhack.isWorldLoaded () or not dfhack.isMapLoaded () then
dfhack.printerr ("Error: This script requires an embark to be loaded.")
return
end
dfhack.println ("Deleting " .. tostring (#df.global.ui.meeting_requests) .. " meeting requests")
for i = #df.global.ui.meeting_requests - 1, 0, -1 do
df.global.ui.meeting_requests:erase (i)
end
dfhack.println ("Deleting " .. tostring (#df.global.ui.activities) .. " activities")
for i = #df.global.ui.activities - 1, 0, -1 do
for k = #df.global.ui.activities [i].unit_actor.specific_refs - 1, 0, -1 do
if df.global.ui.activities [i].unit_actor.specific_refs [k].type == 4 then -- ACTIVITY
dfhack.println ("Removing actor ACTIVITY from " .. dfhack.TranslateName (df.global.ui.activities [i].unit_actor.name, true))
df.global.ui.activities [i].unit_actor.specific_refs:erase (k)
end
end
for k = #df.global.ui.activities [i].unit_noble.specific_refs - 1, 0, -1 do
if df.global.ui.activities [i].unit_noble.specific_refs [k].type == 4 then -- ACTIVITY
dfhack.println ("Removing noble ACTIVITY from " .. dfhack.TranslateName (df.global.ui.activities [i].unit_noble.name, true))
df.global.ui.activities [i].unit_noble.specific_refs:erase (k)
end
end
df.global.ui.activities:erase (i)
end
end
unblockmeetings ()
should give you an idea of the territory I've covered.
Even if it's possible, we don't really know all of the secondary changes and I don't really know how to find them.
Because of that I made a script just to add units to controllable citizens, without petitioning process. It's probably isn't perfect, it won't work for hostile creatures, I'm not sure how it would work for merchants, caravan guards and diplomats, but it works fine for visitors (and probably for wild animal people). If you'll use it on a visitor who already decided to petition, even if he's not indicating it in any way, then he still going to petition eventually. In that case, it's better to reject him to avoid any mess. Here's the script:local utils = require 'gui'
function addcitizen()
local citizen
citizen=dfhack.gui.getSelectedUnit()
if citizen==nil then
qerror("No unit selected")
end
if citizen.hist_figure_id==-1 then
makeHistoricalFigure(citizen)
end
local civId=df.global.ui.civ_id
local govId=df.global.ui.group_id
citizen.flags1.merchant=false
citizen.flags1.forest=false
citizen.flags1.diplomat=false
citizen.flags2.visitor=false
citizen.flags3.unk31=false
citizen.civ_id=civId
local histfig=df.historical_figure.find(citizen.hist_figure_id)
local isCiv
local isGov
for i=#histfig.entity_links - 1, 0, -1 do
if histfig.entity_links[i].entity_id==civId then
histfig.entity_links[i].link_strength=100
print("Civ entity link is already exist")
isCiv=1
end
if histfig.entity_links[i].entity_id==govId then
histfig.entity_links[i].link_strength=100
print("Gov entity link is already exist")
isGov=1
end
end
if isCiv==nil then
histfig.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=civId,link_strength=100})
--print("Civ entity link added")
end
if isGov==nil then
histfig.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=govId,link_strength=100})
--print("Gov entity link added")
end
histfig.civ_id=civId
local hf_event_id=df.global.hist_event_next_id
df.global.hist_event_next_id=df.global.hist_event_next_id+1
df.global.world.history.events:insert("#",{new=df.history_event_add_hf_entity_linkst,year=citizen.birth_year,
seconds=citizen.birth_time,id=hf_event_id,civ=civId,histfig=histfig.id,link_type=0})
local color=_G["COLOR_YELLOW"]
dfhack.gui.showAnnouncement(dfhack.TranslateName(dfhack.units.getVisibleName(citizen)).." joined your fortress.",
color)
end
function makeHistoricalFigure(unit)
local hf=df.historical_figure:new()
hf.id=df.global.hist_figure_next_id
hf.race=unit.race
hf.caste=unit.caste
hf.profession = unit.profession
hf.sex = unit.sex
hf.orientation_flags:assign(unit.status.current_soul.orientation_flags)
df.global.hist_figure_next_id=df.global.hist_figure_next_id+1
hf.appeared_year = df.global.cur_year
hf.born_year = unit.birth_year
hf.born_seconds = unit.birth_time
hf.curse_year = unit.curse_year
hf.curse_seconds = unit.curse_time
hf.birth_year_bias = unit.birth_year_bias
hf.birth_time_bias = unit.birth_time_bias
hf.old_year = unit.old_year
hf.old_seconds = unit.old_time
hf.died_year = -1
hf.died_seconds = -1
hf.name:assign(unit.name)
hf.civ_id = unit.civ_id
hf.population_id = unit.population_id
hf.breed_id = -1
hf.unit_id = unit.id
df.global.world.history.figures:insert("#",hf)
hf.info = df.historical_figure_info:new()
hf.info.unk_14 = df.historical_figure_info.T_unk_14:new()
hf.info.unk_14.unk_18 = -1; hf.info.unk_14.unk_1c = -1
hf.info.skills = {new=true}
unit.flags1.important_historical_figure = true
unit.flags2.important_historical_figure = true
unit.hist_figure_id = hf.id
unit.hist_figure_id2 = hf.id
end
addcitizen()
I will probably try to improve it later.