Magic.
> Odd question, but would "Text Will Be Text" (I think that's what it's called...) allow this mod to have proper pony tiles?
I don't really know, haven't looked into tilesets yet. Right now the priority is implementing DFHack to allow majority of features to work and trying to rectify any crashes/error thingies that may occur. While we are at it, has anyone apart from Destyvirago downloaded and played the .40 version yet?
Oh yes, in another mod, called The Earth Strikes Back, there is a custom script to spawn a creature when a stone is mined:
-- create Awakened Stone creature
-- modtools/syndrome-trigger -syndrome "wake andesite" -command [tesb-spawn -caste ANDESITE -miner \\UNIT_ID -location [ \\LOCATION ] ]
-- Script will be responsible for determining if the miner is "favor"ed. May have to search for the syndrome itself since SYN_CLASS
-- really only applies to interactions.
--[=[
Only the caste ID is required, all others have defaults (though default x,y,z exists only when cursor is visible)
Made by warmist, but edited by Putnam for the dragon ball mod to be used in reactions, then customized by Dirst for The Earth Strikes Back mod
Dirst also included some parts from Rubble's announce.lua and expwent's unit-info-viewer.lua and spawn-flow.lua
--]=]
local utils=require 'utils'
validArgs = validArgs or utils.invert({
'help',
'caste',
'miner',
'location',
'friendly',
'unfriendly',
})
local args = utils.processArgs({...}, validArgs)
if args.help then
print([[scripts/tesb-spawn.lua
arguments:
-help
Print this help message
-caste
Specify the type of awakened stone
This parameter is required
-location [ x y z ]
The location to spawn the awakened stone
Will default to the cursor location, if the cursor is displayed
-miner
Unit ID of creature causing the spawn
If miner has the caste's favor syndrome, the creature spawns Tame
If miner is omitted, "Something" without favor awakens the creature
-friendly
If flag is present, the awakened stone will be Tame
-unfriendly
If flag is present, the awakened stone will be a wild animal
Note: -friendly and -unfriendly override the miner's favor status
]])
return
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: 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
-- WATCH: This block caused errors in the previous version of tesb-spawn.
for k,v in pairs(tmp_soul.personality.traits) do
local min,mean,max
min=caste.personality.a[k]
mean=caste.personality.b[k]
max=caste.personality.c[k]
tmp_soul.personality.traits[k]=clampedNormal(min,mean,max)
end
--[[natural skill fix]]
for k, skill in ipairs(caste.natural_skill_id) do
local rating = caste.natural_skill_lvl[k]
utils.insert_or_update(tmp_soul.skills,
{new=true,id=skill,experience=caste.natural_skill_exp[k],rating=rating}, 'id')
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:assign{
race=race_id,
caste=caste_id,
sex=caste.gender,
}
unit.relations.birth_year=df.global.cur_year-0 --AGE is set here, Awakened Stones are mature at age 0
if caste.misc.maxage_max==-1 then
unit.relations.old_year=-1
else
unit.relations.old_year=df.global.cur_year+math.random(caste.misc.maxage_min,caste.misc.maxage_max)
end
-- WATCH: Not sure about the next line.
unit.relations.birth_time=df.global.cur_year_tick
--unit.relations.old_time=?? --TODO add normal age
--[[ interataction stuff, probably timers ]]--
local num_inter=#caste.body_info.interactions -- new for interactions
unit.curse.own_interaction:resize(num_inter) -- new for interactions
unit.curse.own_interaction_delay:resize(num_inter) -- new for interactions
--[[ body stuff ]]
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
--[[ body components ]]
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}
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
--[[ largely unknown stuff ]]
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
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)
--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)
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)
--finally set the id
unit.id=df.global.unit_next_id
df.global.unit_next_id=df.global.unit_next_id+1
df.global.world.units.all:insert("#",unit)
df.global.world.units.active:insert("#",unit)
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 createFigure(trgunit,he)
local hf=df.historical_figure:new()
hf.id=df.global.hist_figure_next_id
hf.race=trgunit.race
hf.caste=trgunit.caste
hf.profession = trgunit.profession
hf.sex = trgunit.sex
df.global.hist_figure_next_id=df.global.hist_figure_next_id+1
hf.appeared_year = df.global.cur_year
hf.born_year = trgunit.relations.birth_year
hf.born_seconds = trgunit.relations.birth_time
hf.curse_year = trgunit.relations.curse_year
hf.curse_seconds = trgunit.relations.curse_time
hf.birth_year_bias = trgunit.relations.birth_year_bias
hf.birth_time_bias = trgunit.relations.birth_time_bias
hf.old_year = trgunit.relations.old_year
hf.old_seconds = trgunit.relations.old_time
hf.died_year = -1
hf.died_seconds = -1
hf.name:assign(trgunit.name)
hf.civ_id = trgunit.civ_id
hf.population_id = trgunit.population_id
hf.breed_id = -1
hf.unit_id = trgunit.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 state?
--unk_14.region_id = -1; unk_14.beast_id = -1; unk_14.unk_14 = 0
hf.info.unk_14.unk_18 = -1; hf.info.unk_14.unk_1c = -1
-- set values that seem related to state and do event
--change_state(hf, dfg.ui.site_id, region_pos)
--lets skip skills for now
--local skills = df.historical_figure_info.T_skills:new() -- skills snap shot
-- ...
--info.skills = skills
he.histfig_ids:insert('#', hf.id)
he.hist_figures:insert('#', hf)
trgunit.flags1.important_historical_figure = true
trgunit.flags2.important_historical_figure = true
trgunit.hist_figure_id = hf.id
trgunit.hist_figure_id2 = hf.id
hf.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=trgunit.civ_id,link_strength=100})
--add entity event
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=trgunit.relations.birth_year,
seconds=trgunit.relations.birth_time,id=hf_event_id,civ=hf.civ_id,histfig=hf.id,link_type=0})
return hf
end
function createNemesis(trgunit,civ_id)
local id=df.global.nemesis_next_id
local nem=df.nemesis_record:new()
local he=df.historical_entity.find(civ_id)
nem.id=id
nem.unit_id=trgunit.id
nem.unit=trgunit
nem.flags:resize(1)
--not sure about these flags...
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
nem.save_file_id=he.save_file_id
he.nemesis_ids:insert("#",id)
he.nemesis:insert("#",nem)
nem.member_idx=he.next_member_idx
he.next_member_idx=he.next_member_idx+1
--[[ local gen=df.global.world.worldgen
gen.next_unit_chunk_id
gen.next_unit_chunk_offset
]]
nem.figure=createFigure(trgunit,he)
end
function PlaceAwakenedStone(caste,position,is_friendly)-- Stripped-down version of PlaceUnit(race,caste,name,position,civ_id)
local pos=position or copyall(df.global.cursor)
if pos.x==-30000 then
qerror("Point your pointy thing somewhere")
end
race=findRace("AWAKENED_STONE") -- Hard-coded race
-- WATCH: Originally local u=CreateUnit(race,tonumber(caste) or 0)
local u=CreateUnit(race,caste)
u.pos:assign(pos)
u.name.has_name=false -- These creatures do not inherently have names
-- Sets the creature's civ
if is_friendly == true then
u.civ_id = df.global.ui.civ_id
else
u.civ_id = -1
end
local desig,ocupan=dfhack.maps.getTileFlags(pos)
if ocupan.unit then
ocupan.unit_grounded=true
u.flags1.on_ground=true
else
ocupan.unit=true
end
if df.historical_entity.find(u.civ_id) ~= nil then
createNemesis(u,u.civ_id)
end
end
function Announce(caste_id,unit_id,is_friendly) -- Create an in-game announcement that a stone has awakened
-- Rubble's Announcement command, customized by Dirst for The Earth Strikes Back mod
--[[
Rubble Announcement DFHack Command
Copyright 2013-2014 Milo Christiansen
This software is provided 'as-is', without any express or implied warranty. In
no event will the authors be held liable for any damages arising from the use of
this software.
Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim
that you wrote the original software. If you use this software in a product, an
acknowledgment in the product documentation would be appreciated but is not
required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
]]
if not dfhack.isMapLoaded() then
dfhack.printerr('Error: Map is not loaded.')
return
end
local caste_name = string.upper(string.sub(caste_id,1,1))..string.lower(string.sub(caste_id,2,-1))
if caste_name == "Rocksalt" then -- Special handling for two-word stone
caste_name = "Rock Salt"
end
local unit_name
if not unit_id then
unit_name = "Something" -- The player activated the script in an interactive window.
else
unit_name = dfhack.TranslateName(dfhack.units.getVisibleName(df.unit.find(unit_id)))
end
if is_friendly == true then
text = unit_name.." has awakened a creature of Living "..caste_name
color_id = "COLOR_WHITE"
else
text = unit_name.." has incurred the wrath of an Awakened "..caste_name
color_id = "COLOR_RED"
end
local color = _G[color_id]
dfhack.gui.showAnnouncement(text, color)
print(text)
end
function IsItFriendly(caste,miner,is_friendly,is_unfriendly)
-- is_unfriendly overrides is_friendly which overrides miner's status
-- The default is false if nothing was specified
local verdict = false
if is_unfriendly then -- Command line flag
verdict = false
elseif is_friendly then -- Command line flag
verdict = true
elseif miner then
local miner_unit = df.unit.find(miner)
local syndrome_list = miner_unit.syndromes.active
if caste == "ROCKSALT" then caste = "rock salt" end
local favor_syndrome_name = string.lower(caste).." favor"
for index,syndrome in ipairs(syndrome_list) do -- No need to check for 0 syndromes because "wake" is a syndrome
local syndrome_info = df.syndrome.find(syndrome.type)
if syndrome_info.syn_name == favor_syndrome_name then -- Unit has the appropriate "favor" syndrome
verdict = true
break
end
end
end
return verdict
end
-- Conversion table of CASTE_IDs to caste indices
local caste_table = {
["ANDESITE"] = 0,
["BASALT"] = 1,
["CHALK"] = 2,
["CHERT"] = 3,
["CLAYSTONE"] = 4,
["CONGLOMERATE"] = 5,
["DACITE"] = 6,
["DIORITE"] = 7,
["DOLOMITE"] = 8,
["GABBRO"] = 9,
["GNEISS"] = 10,
["GRANITE"] = 11,
["LIMESTONE"] = 12,
["MARBLE"] = 13,
["MUDSTONE"] = 14,
["PHYLLITE"] = 15,
["QUARTZITE"] = 16,
["RHYOLITE"] = 17,
["ROCKSALT"] = 18,
["SANDSTONE"] = 19,
["SCHIST"] = 20,
["SHALE"] = 21,
["SILTSTONE"] = 22,
["SLATE"] = 23
}
local argPos
local argFriendly
if args.location then
argPos={}
argPos.x=args.location[1]
argPos.y=args.location[2]
argPos.z=args.location[3]
end
argFriendly = IsItFriendly(args.caste,args.miner,args.friendly,args.unfriendly)
Announce(args.caste,args.miner,argFriendly)
PlaceAwakenedStone(caste_table[args.caste],argPos,argFriendly)
I think this can be modified to spawn a creature from a workshop reaction inside of using chickens. Since I am only a competent programmer at best, this may or may not be true, do you think it's possible to do so?