Skip to content

Instantly share code, notes, and snippets.

@IndigoFenix
Last active August 29, 2015 14:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save IndigoFenix/2df9cbfd95c54a954126 to your computer and use it in GitHub Desktop.
Save IndigoFenix/2df9cbfd95c54a954126 to your computer and use it in GitHub Desktop.
Enables druidic reactions, which allow the taming and manipulation of animals.
-- Enables nature merit system and reactions that utilize nature merit.
--[[
--New system:
effectiveskill(for taming) = skill
respectvalue = ((skill*5)^2)
interestvalue = merit/10
floor((1-((petvalue/2)/respectvalue))*100) = prob of success in a respect conflict
same thing with interestvalue to determine likelihood of taming
both must succeed to tame successfully
<25 - 1
<100 - 2
<225 - 3
<400 - 4
<625 - 5
<900 - 6
<1225 - 7
<2500 - 10
<4900 - 14
<5625 - 15
<10000 - 20
--impactvalue = ciel(sqrt(petvalue))/5 (1-20)
impactvalue = petvalue
trainingbonus = impactvalue*level
casualtymultiplier = 1-50
wildmultiplier = x5
childbonus = x2
adultbonus = x2 (only if not wild)
releasevalue = adultbonus*wildmultiplier
--]]
local eventful = require 'plugins.eventful'
local mo = require 'makeown'
local utils = require 'utils'
local releasedMerit = 0
if totalMerit == nil then
totalMerit = 0
end
if newMerit == nil then
newMerit = 0
end
if nextUnit == nil then
nextUnit = -1
end
args={...}
if args[1] == "see" then
print("Merit from previous count: "..totalMerit)
print("Current count: "..newMerit)
print("On unit: "..nextUnit)
elseif args[1] == "calc" then
calculateMeritAll("console")
else
totalMerit = nil
newMerit = 0
nextUnit = -1
end
function isSeen(view,p)
return p.z == view.z and view[p.y][p.x] > 0
end
function closeBy(unit,u,radius,z)
if z == nil then z = 0 end
if unit.id ~= u.id
and unit.pos.x+radius >= u.pos.x
and unit.pos.x-radius <= u.pos.x
and unit.pos.y+radius >= u.pos.y
and unit.pos.y-radius <= u.pos.y
and unit.pos.z+z >= u.pos.z
and unit.pos.z-z <= u.pos.z
then return true
else
return false
end
end
function levelUp(unit, skillId, amount)
max_skill = 20
local skill = df.unit_skill:new()
local foundSkill = false
for k, soulSkill in ipairs(unit.status.current_soul.skills) do
if soulSkill.id == skillId then
skill = soulSkill
foundSkill = true
break
end
end
if foundSkill then
-- Let's not train beyond the max skill
if skill.rating >= max_skill then
return false
end
skill.experience = skill.experience + amount
if skill.experience > 100 * skill.rating + 500 then
skill.experience = skill.experience - (100 * skill.rating + 500)
skill.rating = skill.rating + 1
end
else
skill.id = skillId
skill.experience = amount
skill.rating = 0
unit.status.current_soul.skills:insert('#',skill)
end
end
local my_entity=df.historical_entity.find(df.global.ui.civ_id)
--args={...}
function checkMerit(reaction,unit,job,input_items,input_reagents,output_items,call_native)
merit = calculateMeritAll("console")
local threshold = {-100000, -10000, -1000, 1000, 10000, 100000}
if merit < threshold[1] then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." feels absolutely abhored by nature. "..merit, COLOR_RED, true)
elseif merit >= threshold[1] and merit < threshold[2] then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." feels despised by nature. ("..merit..")", COLOR_RED)
elseif merit >= threshold[2] and merit < threshold[3] then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." feels unwelcome by nature. ("..merit..")", COLOR_RED)
elseif merit >= threshold[3] and merit < threshold[4] then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." feels all right with nature. ("..merit..")", COLOR_BROWN, true)
elseif merit >= threshold[4] and merit < threshold[5] then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." feels content with nature. ("..merit..")", COLOR_CYAN) -- capable of taming low-level animals
elseif merit >= threshold[5] and merit < threshold[6] then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." feels at peace with nature. ("..merit..")", COLOR_GREEN) -- capable of taming high-level animals
elseif merit >= threshold[6] then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." feels in absolute harmony with nature. ("..merit..")", COLOR_GREEN, true) -- capable of taming megabeasts
end
end
function isBird(u)
unitBlock = dfhack.maps.ensureTileBlock(u.pos.x,u.pos.y,u.pos.z)
if u and unitBlock then
if df.global.world.raws.creatures.all[u.race].caste[u.caste].flags.FLIER == true and unitBlock.designation[u.pos.x%16][u.pos.y%16].outside == true then return true
else return false end
else return false end
end
function getNewsFromAnimal(unit,u)
creatureName = (df.global.world.raws.creatures.all[u.race]).name[0]
if #df.global.timed_events > 0 then
dfhack.gui.showAnnouncement( "The "..creatureName.." speaks with "..dfhack.TranslateName(unit.name).."...", COLOR_WHITE, true)
for i=0, #df.global.timed_events-1, 1 do
event = df.global.timed_events[i]
distance = 0
eventseason = event.season
if eventseason < df.global.cur_season then eventseason = eventseason+3 end
distance = event.season_ticks + ((df.global.cur_season - eventseason)*10080) - df.global.cur_season_tick
entity_creature_name = {"","",""}
if event.entity ~= nil then
entity = event.entity
entity_creature_name = (df.global.world.raws.creatures.all[entity.entity_raw.creature_ids[0]]).name
end
-- 120 season ticks per day
distance_string = "somewhere"
if distance < 840 then -- 1 week
distance_string = "right nearby"
elseif distance < 1680 then -- 2 weeks
distance_string = "close by"
elseif distance < 3360 then -- 4 weeks
distance_string = "approaching the area"
elseif distance < 5040 then -- 6 weeks
distance_string = "off in the distance"
elseif distance < 6720 then -- 8 weeks
distance_string = "somewhere in the region"
elseif distance < 8400 then -- 10 weeks
distance_string = "far away"
elseif distance < 10080 then -- 12 weeks
distance_string = "a great distance away"
else -- 12 weeks
distance_string = "far, far away"
end
if event.type == 0 then
dfhack.gui.showAnnouncement( "The "..creatureName.." saw a caravan of "..entity_creature_name[2].." traders "..distance_string..".", COLOR_WHITE, true)
elseif event.type == 1 then
dfhack.gui.showAnnouncement( "The "..creatureName.." saw a group of migrating "..entity_creature_name[1].." "..distance_string..".", COLOR_WHITE, true)
elseif event.type == 2 then
dfhack.gui.showAnnouncement( "The "..creatureName.." saw a dignified-looking "..entity_creature_name[0].." "..distance_string..".", COLOR_WHITE, true)
elseif event.type == 3 then
dfhack.gui.showAnnouncement( "The "..creatureName.." saw a band of angry-looking "..entity_creature_name[1].." "..distance_string..".", COLOR_RED)
elseif event.type == 4 then
dfhack.gui.showAnnouncement( "The "..creatureName.." saw (I DON'T KNOW WHAT THIS IS, TELL ME WHAT SHOWS UP) "..distance_string..".", COLOR_RED)
elseif event.type == 5 then
dfhack.gui.showAnnouncement( "The "..creatureName.." saw a mighty beast "..distance_string..".", COLOR_RED)
elseif event.type == 6 then
dfhack.gui.showAnnouncement( "The "..creatureName.." saw a curious-looking creature "..distance_string..".", COLOR_GREEN)
elseif event.type == 7 then
dfhack.gui.showAnnouncement( "The "..creatureName.." saw a troublesome-looking creature "..distance_string..".", COLOR_GREEN)
elseif event.type == 8 then
dfhack.gui.showAnnouncement( "The "..creatureName.." saw another flying creature "..distance_string..".", COLOR_GREEN)
elseif event.type == 9 then
dfhack.gui.showAnnouncement( "The "..creatureName.." saw a strange and unnatural monster "..distance_string..".", COLOR_RED)
end
end
else
dfhack.gui.showAnnouncement( "The "..creatureName.." has nothing interesting to report.", COLOR_WHITE, true)
end
end
function nicknamePet(reaction,unit,job,input_items,input_reagents,output_items,call_native)
unitRaw = df.global.world.raws.creatures.all[unit.race]
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
allUnits = df.global.world.units.active
local animals = {}
local u
--Determine which animal is closest
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isPet(u) and closeBy(unit,u,radius) then
table.insert(animals, u)
end
end
local leader = nil
local leaderDistance = 10000
local u
for i=#animals,1,-1 do
u = animals[i]
distance = math.abs(u.pos.x - unit.pos.x) + math.abs(u.pos.y - unit.pos.y)
if distance < leaderDistance then
leader = u
leaderDistance = distance
end
end
--Nickname the animal
if leader then
local u = leader
local uSpecies = (df.global.world.raws.creatures.all[u.race]).name[0]
local script=require('gui/script')
script.start(function()
local nameok, name
name = ""
repeat nameok,name=script.showInputPrompt('Nickname','Give a nickname to the '..uSpecies..':',COLOR_LIGHTGREEN, amount) until true
if name == "" then
else
u.name.nickname = name
u.name.has_name = true
end
end)
end
end
function getName(u,capitalize)
if dfhack.TranslateName(u.name) == "" then
if u.profession == 104 then
if (df.global.world.raws.creatures.all[u.race]).caste[u.caste].baby_name[0] ~= "" then
return "the "..((df.global.world.raws.creatures.all[u.race]).caste[u.caste].baby_name[0])
else
return "the baby "..((df.global.world.raws.creatures.all[u.race]).caste[u.caste].caste_name[0])
end
elseif u.profession == 103 then
if (df.global.world.raws.creatures.all[u.race]).caste[u.caste].child_name[0] ~= "" then
return "the "..((df.global.world.raws.creatures.all[u.race]).caste[u.caste].child_name[0])
else
return "the "..((df.global.world.raws.creatures.all[u.race]).caste[u.caste].caste_name[0]).." child"
end
else
return "the "..((df.global.world.raws.creatures.all[u.race]).caste[u.caste].caste_name[0])
end
else
return dfhack.TranslateName(u.name)
end
end
function talkPet(reaction,unit,job,input_items,input_reagents,output_items,call_native)
local merit = getMerit()
if merit < 0 then
dfhack.gui.showAnnouncement( "You are too disconnected from nature to talk to animals." , COLOR_RED, true)
return false
end
unitRaw = df.global.world.raws.creatures.all[unit.race]
local skill = dfhack.units.getEffectiveSkill(unit,reaction.skill)
if skill == 0 then skill = 1 end
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
allUnits = df.global.world.units.active
local animals = {}
local u
--Determine which animal is closest
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isPet(u) and closeBy(unit,u,radius) then
table.insert(animals, u)
end
end
local leader = nil
local leaderDistance = 10000
local u
for i=#animals,1,-1 do
u = animals[i]
distance = math.abs(u.pos.x - unit.pos.x) + math.abs(u.pos.y - unit.pos.y)
if distance < leaderDistance then
leader = u
leaderDistance = distance
end
end
--
if leader then
local u = leader
local spoke = false
found = true
lastAnimalName = (df.global.world.raws.creatures.all[u.race]).name[0]
unitRaw = df.global.world.raws.creatures.all[u.race]
casteRaw = unitRaw.caste[u.caste]
petValue = casteRaw.misc.petvalue
if casteRaw.flags.PET == false and casteRaw.flags.PET_EXOTIC == false and petValue < 10000 then
petValue = 10000
end
respectvalue = ((skill*5)^2)
respectVal = math.floor((1-((petValue/2)/respectvalue))*100)
if respectVal < 0 then
dfhack.gui.showAnnouncement( getName(u,true).." has no interest in speaking with "..dfhack.TranslateName(unit.name).."." , COLOR_RED, true)
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." speaks with "..getName(u).."." , COLOR_WHITE, true)
if respectVal < 10 then dfhack.gui.showAnnouncement( getName(u,true).." has little respect for "..dfhack.TranslateName(unit.name)..", but will speak anyway." , COLOR_WHITE, true) end
for i=0,#unit.status.misc_traits-1,1 do
traitname = df.misc_trait_type[unit.status.misc_traits[i].id]
traitvalue = unit.status.misc_traits[i].value
if traitname ~= nil then
if traitname == "GroundedAnimalAnger" and traitvalue >= 10000 then
if traitvalue < 20000 then dfhack.gui.showAnnouncement( getName(u,true).." is irritated by the lack of space." , COLOR_WHITE, true)
elseif traitvalue < 30000 then dfhack.gui.showAnnouncement( getName(u,true).." is getting annoyed due to overcrowding." , COLOR_WHITE, true)
elseif traitvalue < 40000 then dfhack.gui.showAnnouncement( getName(u,true).." is upset about the crowded conditions here." , COLOR_WHITE, true)
else dfhack.gui.showAnnouncement( getName(u,true).." is angry about the crowded conditions here." , COLOR_WHITE, true)
end
spoke = true
end
if traitname == "Hardened" and traitvalue >= 1 then
end
end
end
if u.relations.pet_owner_id ~= nil then
followInterest = getFollowInterest(u,df.unit.find(u.relations.pet_owner_id))
if followInterest < 0.25 then dfhack.gui.showAnnouncement( getName(u,true).." hates being forced to follow "..getName(df.unit.find(u.relations.pet_owner_id),true).."." , COLOR_WHITE, true)
elseif followInterest < 0.5 then dfhack.gui.showAnnouncement( getName(u,true).." doesn't like being told to follow "..getName(df.unit.find(u.relations.pet_owner_id),true).."." , COLOR_WHITE, true)
elseif followInterest < 1 then dfhack.gui.showAnnouncement( getName(u,true).." is fine with following "..getName(df.unit.find(u.relations.pet_owner_id),true).."." , COLOR_WHITE, true)
elseif followInterest < 2 then dfhack.gui.showAnnouncement( getName(u,true).." enjoys following "..getName(df.unit.find(u.relations.pet_owner_id),true).."." , COLOR_WHITE, true)
elseif followInterest < 4 then dfhack.gui.showAnnouncement( getName(u,true).." is happy about following "..getName(df.unit.find(u.relations.pet_owner_id),true).."." , COLOR_WHITE, true)
else dfhack.gui.showAnnouncement( getName(u,true).." is greatly honored to accompany "..getName(df.unit.find(u.relations.pet_owner_id),true).."." , COLOR_WHITE, true)
end
spoke = true
end
huntingInterest = getHuntingInterest(u)
warInterest = getWarInterest(u)
if u.profession == 98 then
if huntingInterest < 0.3 then dfhack.gui.showAnnouncement( getName(u,true).." hates being forced to hunt animals." , COLOR_WHITE, true)
elseif huntingInterest < 0.7 then dfhack.gui.showAnnouncement( getName(u,true).." doesn't like being forced to hunt animals." , COLOR_WHITE, true)
elseif huntingInterest < 1.5 then dfhack.gui.showAnnouncement( getName(u,true).." is okay with being assigned to hunting duty." , COLOR_WHITE, true)
elseif huntingInterest < 3.5 then dfhack.gui.showAnnouncement( getName(u,true).." enjoys taking down prey." , COLOR_WHITE, true)
else dfhack.gui.showAnnouncement( getName(u,true).." is happy to be a hunter." , COLOR_WHITE, true)
end
spoke = true
elseif u.profession == 99 then
if warInterest < 0.3 then dfhack.gui.showAnnouncement( getName(u,true).." hates being forced to fight enemies." , COLOR_WHITE, true)
elseif warInterest < 0.7 then dfhack.gui.showAnnouncement( getName(u,true).." doesn't like being forced to fight enemies." , COLOR_WHITE, true)
elseif warInterest < 1.5 then dfhack.gui.showAnnouncement( getName(u,true).." is okay with being assigned to war duty." , COLOR_WHITE, true)
elseif warInterest < 3.5 then dfhack.gui.showAnnouncement( getName(u,true).." enjoys defending the settlement." , COLOR_WHITE, true)
else dfhack.gui.showAnnouncement( getName(u,true).." is happy to be a warrior." , COLOR_WHITE, true)
end
spoke = true
elseif u.profession == 103 or u.profession == 104 then
if huntingInterest > 1 then
if huntingInterest < 3 then dfhack.gui.showAnnouncement( getName(u,true).." is interested in growing up and learning how to hunt." , COLOR_WHITE, true)
else dfhack.gui.showAnnouncement( getName(u,true).." is eager to grow up and learn how to hunt." , COLOR_WHITE, true) end
spoke = true
end
if warInterest > 1 then
if warInterest < 3 then dfhack.gui.showAnnouncement( getName(u,true).." is interested in growing up and learning how to fight." , COLOR_WHITE, true)
else dfhack.gui.showAnnouncement( getName(u,true).." is eager to grow up and learn how to fight." , COLOR_WHITE, true) end
spoke = true
end
else
if huntingInterest > 1 then
if huntingInterest < 3 then dfhack.gui.showAnnouncement( getName(u,true).." is interested in hunting." , COLOR_WHITE, true)
else dfhack.gui.showAnnouncement( getName(u,true).." wants to be a hunter." , COLOR_WHITE, true) end
spoke = true
end
if warInterest > 1 then
if warInterest < 3 then dfhack.gui.showAnnouncement( getName(u,true).." is interested in fighting." , COLOR_WHITE, true)
else dfhack.gui.showAnnouncement( getName(u,true).." wants to be a fighter." , COLOR_WHITE, true) end
spoke = true
end
end
if respectVal > 25 then
if respectVal < 50 then dfhack.gui.showAnnouncement( getName(u,true).." enjoyed speaking with "..dfhack.TranslateName(unit.name).."." , COLOR_WHITE, true)
elseif respectVal < 75 then dfhack.gui.showAnnouncement( getName(u,true).." was happy to speak with "..dfhack.TranslateName(unit.name).."." , COLOR_WHITE, true)
elseif respectVal < 90 then dfhack.gui.showAnnouncement( getName(u,true).." was honored to speak with "..dfhack.TranslateName(unit.name).."." , COLOR_WHITE, true)
else dfhack.gui.showAnnouncement( getName(u,true).." was greatly honored to speak with "..dfhack.TranslateName(unit.name).."." , COLOR_WHITE, true)
end
spoke = true
end
if spoke == false then dfhack.gui.showAnnouncement( getName(u,true).." has nothing interesting to say." , COLOR_WHITE, true) end
end
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." could not find any animals to speak with." , COLOR_RED, true)
end
end
function tameRoamingAnimal(reaction,unit,job,input_items,input_reagents,output_items,call_native)
unitRaw = df.global.world.raws.creatures.all[unit.race]
local merit = getMerit()
if merit > 0 then
local probability = reaction.products[0].probability
local skill = dfhack.units.getEffectiveSkill(unit,reaction.skill)
if skill == 0 then skill = 1 end
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
local view = fov.get_fov(radius,unit.pos)
local lastAnimalName = ""
local failureType = 0
local found=false
local success=false
allUnits = df.global.world.units.other.ANY_ANIMAL
local u
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
--print((df.global.world.raws.creatures.all[u.race]).name[0])
if isRoamingAnimal(u) then
if closeBy(unit,u,radius,5) then--isSeen(view,u.pos) then
if dfhack.maps.canWalkBetween(u.pos,unit.pos) then
found = true
lastAnimalName = getName(u)
unitRaw = df.global.world.raws.creatures.all[u.race]
casteRaw = unitRaw.caste[u.caste]
petValue = casteRaw.misc.petvalue
if casteRaw.flags.PET == false and casteRaw.flags.PET_EXOTIC == false and petValue < 10000 then
petValue = 10000
end
interestvalue = merit/10
respectvalue = ((skill*5)^2)
if math.random(100) < math.floor((1-((petValue/2)/respectvalue))*100) and math.random(100) < math.floor((1-((petValue/2)/interestvalue))*100) then
u.flags1.tame = true
u.training_level = df.animal_training_level.WellTrained
--u.misc_trait_type.RevertWildTimer = 1200
mo.make_own(u)
convertInvader(u)
u.relations.following = unit
levelUp(unit,reaction.skill,petValue)
success = true
break
else
if 0 > math.floor((1-((petValue/2)/interestvalue))*100) then
failureType = 2
elseif 0 > math.floor((1-((petValue/2)/respectvalue))*100) then
failureType = 1
end
end
end
end
end
end
if not found == true then
if #input_items > 0 then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to spot a tameable animal in range." , COLOR_BROWN, true)
end
elseif success == true then
dfhack.gui.showAnnouncement( getName(u,true) .. " has joined us." , COLOR_GREEN, true)--color[,is_bright]
--now deal with any creatures that were following it
if u then
if isBird(u) then getNewsFromAnimal(unit,u) end
for i=#allUnits-1,0,-1 do -- search list in reverse
newu = allUnits[i]
if newu.relations.following == u then
newu.relations.following = nil
end
end
end
if #input_items > 0 then
input_items[0].flags.PRESERVE_REAGENT = false
end
else
if failureType == 0 then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to tame "..lastAnimalName..".", COLOR_BROWN)
elseif failureType == 1 then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." is not skilled enough to tame "..lastAnimalName..".", COLOR_BROWN)
elseif failureType == 2 then
dfhack.gui.showAnnouncement( lastAnimalName.." has no interest in joining us.", COLOR_BROWN)
end
levelUp(unit,reaction.skill,10)
if #input_items > 0 then
input_items[0].flags.PRESERVE_REAGENT = false
end
end
else
dfhack.gui.showAnnouncement( "You are too disconnected from nature to recruit animals.", COLOR_RED, true)
end
end
local function getItemValue(item)
local basevalue = 1
local matvalue = 1
local qualityvalue = 1
local improvementvalue = 0
local stack_size = 1
local wearvalue = 1
if item then
local itemname = df.item_type[item:getType()]:lower()
if itemname == "coin" then basevalue = 0.02
elseif itemname == "corpsepiece"
or itemname == "corpse"
or itemname == "remains"
or itemname == "rock"
then basevalue = 0
elseif itemname == "glob"
or itemname == "seeds"
or itemname == "drink"
or itemname == "powder_misc"
or itemname == "liquid_misc"
or itemname == "orthopedic_cast"
then basevalue = 1
elseif itemname == "fish_raw"
or itemname == "fish"
or itemname == "meat"
or itemname == "egg"
or itemname == "plant"
or itemname == "leaves"
then basevalue = 2
elseif itemname == "rough"
or itemname == "wood"
or itemname == "boulder"
then basevalue = 3
elseif itemname == "bar"
or itemname == "blocks"
or itemname == "gem"
or itemname == "skin_tanned"
then basevalue = 5
elseif itemname == "thread"
then basevalue = 6
elseif itemname == "cloth"
then basevalue = 7
elseif itemname == "siegeammo"
or itemname == "traction_bench"
then basevalue = 20
elseif itemname == "window"
or itemname == "statue"
then basevalue = 25
elseif itemname == "catapultparts"
or itemname == "ballistaparts"
or itemname == "trapparts"
then basevalue = 30
else basevalue = 10
end
if itemname == "weapon"
or itemname == "armor"
or itemname == "shoes"
or itemname == "gloves"
or itemname == "shield"
or itemname == "helm"
or itemname == "ammo"
or itemname == "pants"
or itemname == "trapcomp"
or itemname == "tool"
then basevalue = item.subtype.value
end
material = dfhack.matinfo.decode(item)
if material then
matvalue = material.material.material_value
end
quality = item:getQuality()
if quality == 0 then qualityvalue = 1
elseif quality == 1 then qualityvalue = 2
elseif quality == 2 then qualityvalue = 3
elseif quality == 3 then qualityvalue = 4
elseif quality == 4 then qualityvalue = 5
elseif quality == 5 then qualityvalue = 12
end
if item.flags.artifact == true then qualityvalue = 120 end
if item:isImprovable(nil,0,0) then
for i = 0, #item.improvements-1, 1 do
imp = item.improvements[i]
imp_mat = dfhack.matinfo.decode(imp.mat_type, imp.mat_index)
imp_mat_value = imp_mat.material.material_value
if imp.quality == 0 then imp_quality = 1
elseif imp.quality == 1 then imp_quality = 2
elseif imp.quality == 2 then imp_quality = 3
elseif imp.quality == 3 then imp_quality = 4
elseif imp.quality == 4 then imp_quality = 5
elseif imp.quality == 5 then imp_quality = 12
end
imp_value = 10 * imp_mat_value * imp_quality
improvementvalue = improvementvalue + imp_value
end
end
--improvementvalue = item:getImprovementsValue(-1) -- crashes the game
stack_size = item.stack_size
wearvalue = 1-(item:getWear()*.25)
end
return math.floor((((basevalue * matvalue * qualityvalue) + improvementvalue)*stack_size)*wearvalue)
end
function releaseRoamingAnimal(reaction,unit,job,input_items,input_reagents,output_items,call_native)
unitRaw = df.global.world.raws.creatures.all[unit.race]
local probability = reaction.products[0].probability + dfhack.units.getEffectiveSkill(unit,reaction.skill)
reaction.products[0].probability = probability
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 5 end
local view = fov.get_fov(radius,unit.pos)
local found=false
local success=false
local getItem=false
v = df.global.world.units.other.ANY_ANIMAL
for i=#v-1,0,-1 do -- search list in reverse
u = v[i]
if closeBy(unit,u,radius) then
if u.flags1.tame
--and u.flags2.roaming_wilderness_population_source==true
and not dfhack.units.isDead(u)
and u.civ_id==df.global.ui.civ_id
and u.relations.pet_owner_id==-1
--and (u.training_level == df.animal_training_level.WellTrained or u.training_level == df.animal_training_level.Trained or u.training_level == df.animal_training_level.SemiWild)
and not u.flags1.chained
and not u.flags1.caged
and not u.flags2.locked_in_for_trading then
releaseAnimal(u)
success=true
if math.random(100) < probability then
getItem=true
end
break
end
else
end
end
if success == true then
dfhack.gui.showAnnouncement( getName(u,true).. " has been released into the wild.", COLOR_GREEN)
levelUp(unit,reaction.skill,30)
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to find any stray animals to release.", COLOR_RED, true)
for _,v in ipairs(input_reagents or {}) do
v.flags.PRESERVE_REAGENT = true
end
end
if getItem ~= true then
reaction.products[0].probability = 0
end
end
function talkBird(reaction,unit,job,input_items,input_reagents,output_items,call_native)
unitRaw = df.global.world.raws.creatures.all[unit.race]
local unitBlock = dfhack.maps.ensureTileBlock(unit.pos.x,unit.pos.y,unit.pos.z)
if unitBlock.designation[unit.pos.x%16][unit.pos.y%16].outside == false then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." cannot talk to birds while inside." , COLOR_RED, true)
return false
end
local merit = getMerit()
if merit > 0 then
local found=false
local success=false
local creatureName=""
allUnits = df.global.world.units.other.ANY_ANIMAL
local u
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isRoamingAnimal(u) and isBird(u) then
found = true
break
end
end
if not found == true then
if #input_reagents > 0 then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to spot a bird in range." , COLOR_BROWN, true)
for _,v in ipairs(input_reagents or {}) do
v.flags.PRESERVE_REAGENT = true
end
end
else
getNewsFromAnimal(unit,u)
adoptPet(nil,u)
u.animal.leave_countdown=2
u.flags1.forest=true
levelUp(unit,reaction.skill,30)
end
else
dfhack.gui.showAnnouncement( "You are too disconnected from nature to commune with birds.", COLOR_RED, true)
end
end
--[[
exp+30 per successful use
(respect for trainer) * (interest in activity) * (pet training level) * general difficulty
bond bonus: respect * 100
respect = trainer skill level^3*10 / petvalue OR petvalue (leader)/petvalue (follower)
value of a historical pet is *10
interest:
following - clusternumber*value, same species*5, predator factor, respect for leader
hunting - predator*10, carnivore*5, mischeivious*2
war - likes fighting*10, prone to rage*value+1, predator*5, benign/2
for untraining: 10/interest in training
predator factor (one is predator and carnivore and other is benign grazer) * 2, /5
]]--
function getTrainingProb(trainer,u,reaction,multiplier)
skill = dfhack.units.getEffectiveSkill(trainer,reaction.skill)
respectvalue = ((skill*5)^2)
--print("respect: "..respectvalue)
local unitRaw = df.global.world.raws.creatures.all[u.race]
casteRaw = unitRaw.caste[u.caste]
petValue = casteRaw.misc.petvalue
if casteRaw.flags.PET == false and casteRaw.flags.PET_EXOTIC == false and petValue < 10000 then
petValue = 10000
end
if multiplier ~= nil then
respectvalue = respectvalue * multiplier
--print("interest in activity: "..multiplier)
end
bondBonus = 1 -- bond bonus
histBonus = 1 -- historical figure bonus
--print("training: "..u.training_level)
--print("names: "..bondBonus/histBonus)
respectvalue = respectvalue * bondBonus * u.training_level / histBonus
--print("totalRespect: "..respectvalue)
--print("self-value: "..petValue)
respectProb = ((1-((petValue/2)/respectvalue))*100)
unitRaw = df.global.world.raws.creatures.all[u.race]
--print("total prob: "..respectProb)
return respectProb
end
function getFollowInterest(u, leader)
local unitRaw = df.global.world.raws.creatures.all[u.race]
local casteRaw = unitRaw.caste[u.caste]
local leaderRaw = df.global.world.raws.creatures.all[leader.race]
local leaderCasteRaw = leaderRaw.caste[leader.caste]
currentClusterSize = 1
v = df.global.world.units.other.ANY_ANIMAL
for i=#v-1,0,-1 do
z = v[i]
if z.relations.following == leader then currentClusterSize = currentClusterSize + 1 end
end
--print(currentClusterSize)
clusterVal = ((unitRaw.cluster_number[0]+unitRaw.cluster_number[1])/2)/currentClusterSize
if u.race ~= leader.race then clusterVal = clusterVal/2 end
if unitRaw.flags.LOOSE_CLUSTERS then clusterVal = clusterVal/2 end
local unitValue = casteRaw.misc.petvalue
local leaderValue = leaderCasteRaw.misc.petvalue
if leaderCasteRaw.flags.CAN_LEARN and leaderCasteRaw.flags.CAN_SPEAK then
if u.relations.pet_owner_id == leader.id then respectVal = 5 else respectVal = 1 end
else
respectVal = leaderValue/unitValue
end
--if u.flags1.important_historical_figure then unitValue = unitValue * 10 end
--if leader.flags1.important_historical_figure then leaderValue = leaderValue * 10 end
--if casteRaw.flags.CAN_SPEAK then unitValue * 10 end
--if leaderRaw.flags.CAN_SPEAK then leaderValue * 10 end
return clusterVal * respectVal
end
function getHuntingInterest(u)
unitRaw = df.global.world.raws.creatures.all[u.race]
casteRaw = unitRaw.caste[u.caste]
local interest = 1
if (casteRaw.flags.LARGE_PREDATOR or casteRaw.flags.AMBUSHPREDATOR) then interest = interest * 2 end
if (casteRaw.flags.CARNIVORE or casteRaw.flags.BONECARN) then interest = interest * 2 end
if casteRaw.flags.GRAZER then interest = interest / 2 end
if casteRaw.flags.MEANDERER then interest = interest / 2 end
return interest
end
function getWarInterest(u)
unitRaw = df.global.world.raws.creatures.all[u.race]
casteRaw = unitRaw.caste[u.caste]
local interest = 1
if casteRaw.flags.LIKES_FIGHTING then interest = interest * 2 end
if casteRaw.misc.prone_to_rage > 0 then interest = interest * 2 end
if casteRaw.flags.LARGE_PREDATOR then interest = interest * 2 end
if casteRaw.flags.BENIGN then interest = interest / 2 end
if casteRaw.flags.FLEEQUICK then interest = interest / 2 end
if casteRaw.flags.MEANDERER then interest = interest / 2 end
return interest
end
--[[
following - clusternumber*value, same species*5, predator factor, respect for leader
hunting - predator*10, carnivore*5, mischeivious*2
war - likes fighting*10, prone to rage*value+1, predator*5, benign/2
]]--
--casteRaw.flags.GRAZER
--casteRaw.flags.CARNIVORE
--casteRaw.flags.BONECARN
--casteRaw.flags.BENIGN
--casteRaw.flags.TRAINABLE_HUNTING
--casteRaw.flags.TRAINABLE_WAR
--casteRaw.flags.LARGE_PREDATOR
--casteRaw.flags.FLEEQUICK
--casteRaw.flags.MISCHIEVOUS
--casteRaw.flags.LARGE_PREDATOR
--casteRaw.flags.AMBUSHPREDATOR
--casteRaw.flags.MEANDERER
--casteRaw.flags.LIKES_FIGHTING
--casteRaw.flags.FEATURE_BEAST
--casteRaw.flags.TITAN
function adoptPet(owner,pet)
if owner == nil then
pet.relations.following = nil
pet.relations.pet_owner_id = -1
else
pet.relations.following = owner
pet.relations.pet_owner_id = owner.id
end
end
function followUser(reaction,unit,job,input_items,input_reagents,output_items,call_native)
local merit = getMerit()
if merit < 0 then
dfhack.gui.showAnnouncement( "You are too disconnected from nature to train animals this way." , COLOR_RED, true)
return false
end
local skill = dfhack.units.getEffectiveSkill(unit,reaction.skill)
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
local found = false
local success = false
allUnits = df.global.world.units.active
local u
local failureType = 0
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
cldrid = -1
if u.relations.pet_owner_id ~= -1 then cldrid = u.relations.pet_owner_id end
if isPet(u) and closeBy(unit,u,radius) and unit.id ~= cldrid and (u.profession == 98 or u.profession == 99) then
found = true
if u.relations.pet_owner_id ~= -1 then
currentLeaderInterest = getFollowInterest(u,df.unit.find(u.relations.pet_owner_id))
else
currentLeaderInterest = 1
end
multiplier = getFollowInterest(u,unit)/currentLeaderInterest
prob = getTrainingProb(unit,u,reaction,multiplier)
if math.random(100) < prob then
adoptPet(unit,u)
success = true
break
elseif 0 < math.floor(prob) then failureType = 1 end
end
end
if not found == true then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to spot a work animal in range." , COLOR_RED, true)
for _,v in ipairs(input_reagents or {}) do
v.flags.PRESERVE_REAGENT = true
end
elseif success == true then
dfhack.gui.showAnnouncement( getName(u,true) .. " is now following " .. dfhack.TranslateName(unit.name) .. "." , COLOR_GREEN)
levelUp(unit,reaction.skill,30)
elseif failureType == 1 then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to teach any animals to follow.", COLOR_BROWN)
levelUp(unit,reaction.skill,10)
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." is not skilled enough to train the animal to follow.", COLOR_RED, true)
end
end
function cluster(reaction,unit,job,input_items,input_reagents,output_items,call_native)
local merit = getMerit()
if merit < 0 then
dfhack.gui.showAnnouncement( "You are too disconnected from nature to train animals this way." , COLOR_RED, true)
return false
end
unitRaw = df.global.world.raws.creatures.all[unit.race]
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
allUnits = df.global.world.units.active
local animals = {}
local u
local failureType = 0
--First determine the leader
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isPet(u) and closeBy(unit,u,radius) then
table.insert(animals, u)
end
end
local leader = nil
local leaderDistance = 10000
local u
for i=#animals,1,-1 do
u = animals[i]
distance = math.abs(u.pos.x - unit.pos.x) + math.abs(u.pos.y - unit.pos.y)
if distance < leaderDistance then
leader = u
leaderDistance = distance
end
end
--Now do the actual training
if leader then
local found = false
local success = false
local u
for i=#animals,1,-1 do
u = animals[i]
cldrid = -1
if u.relations.pet_owner_id ~= -1 then cldrid = u.relations.pet_owner_id end
if u.id ~= leader.id and leader.id ~= cldrid and leader.relations.pet_owner_id ~= u.id and (u.profession == 98 or u.profession == 99) then
found = true
if u.relations.pet_owner_id ~= -1 then
currentLeaderInterest = getFollowInterest(u,df.unit.find(u.relations.pet_owner_id))
else
currentLeaderInterest = 1
end
multiplier = getFollowInterest(u,leader)/currentLeaderInterest
prob = getTrainingProb(unit,u,reaction,multiplier)
if math.random(100) < prob then
adoptPet(leader,u)
success = true
break
elseif 0 < math.floor(prob) then failureType = 1 end
end
end
if not found == true then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." could not find a work animal to accompany "..getName(leader).."." , COLOR_RED, true)
for _,v in ipairs(input_reagents or {}) do
v.flags.PRESERVE_REAGENT = true
end
elseif success == true then
dfhack.gui.showAnnouncement( getName(u,true) .. " is now following "..getName(leader).. ".", COLOR_GREEN)
levelUp(unit,reaction.skill,30)
elseif failureType == 1 then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to train any animals to follow "..getName(leader).. ".", COLOR_BROWN)
levelUp(unit,reaction.skill,10)
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." is not skilled enough to train the animal to follow "..getName(leader).. ".", COLOR_RED, true)
end
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to spot an animal to lead." , COLOR_RED, true)
end
end
function unFollow(reaction,unit,job,input_items,input_reagents,output_items,call_native)
local merit = getMerit()
if merit < 0 then
dfhack.gui.showAnnouncement( "You are too disconnected from nature to train animals this way." , COLOR_RED, true)
return false
end
unitRaw = df.global.world.raws.creatures.all[unit.race]
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
allUnits = df.global.world.units.active
local found = false
local success = false
local u
local failureType = 0
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
local leader = df.unit.find(u.relations.pet_owner_id)
if isPet(u) and closeBy(unit,u,radius) and leader ~= nil then
found = true
multiplier = 1/getFollowInterest(u,leader)
prob = getTrainingProb(unit,u,reaction,multiplier)
if math.random(100) < prob then
adoptPet(nil,u)
success = true
break
elseif 0 < math.floor(prob) then failureType = 1 end
end
end
if not found == true then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." could not find any animals to dismiss." , COLOR_RED, true)
for _,v in ipairs(input_reagents or {}) do
v.flags.PRESERVE_REAGENT = true
end
elseif success == true then
dfhack.gui.showAnnouncement( getName(u,true) .. " has been dismissed.", COLOR_GREEN)
levelUp(unit,reaction.skill,30)
elseif failureType == 1 then
dfhack.gui.showAnnouncement( "The animal is not listening to "..dfhack.TranslateName(unit.name)..".", COLOR_BROWN)
levelUp(unit,reaction.skill,10)
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." is not skilled enough to dismiss the animal.", COLOR_RED, true)
end
end
function trainHunting(reaction,unit,job,input_items,input_reagents,output_items,call_native)
local merit = getMerit()
if merit < 0 then
dfhack.gui.showAnnouncement( "You are too disconnected from nature to train animals this way." , COLOR_RED, true)
return false
end
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
local found = false
local success = false
allUnits = df.global.world.units.active
local u
local failureType = 0
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isPet(u) and closeBy(unit,u,radius) and u.profession == 102 then
found = true
multiplier = getHuntingInterest(u)
prob = getTrainingProb(unit,u,reaction,multiplier)
if math.random(100) < prob or casteRaw.flags.TRAINABLE_HUNTING then
u.profession = 98
success = true
fixList(u)
break
elseif 0 < math.floor(prob) then failureType = 1 end
end
end
if not found == true then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to spot a trainable animal in range." , COLOR_RED, true)
for _,v in ipairs(input_reagents or {}) do
v.flags.PRESERVE_REAGENT = true
end
elseif success == true then
dfhack.gui.showAnnouncement( getName(u,true) .. " has been trained for hunting." , COLOR_GREEN)
levelUp(unit,reaction.skill,30)
elseif failureType == 1 then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to train any animals to hunt.", COLOR_BROWN)
levelUp(unit,reaction.skill,10)
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." is not skilled enough to train the animal to hunt.", COLOR_RED, true)
end
end
function trainWar(reaction,unit,job,input_items,input_reagents,output_items,call_native)
local merit = getMerit()
if merit < 0 then
dfhack.gui.showAnnouncement( "You are too disconnected from nature to train animals this way." , COLOR_RED, true)
return false
end
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
local found = false
local success = false
allUnits = df.global.world.units.active
local u
local failureType = 0
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isPet(u) and closeBy(unit,u,radius) and u.profession == 102 then
found = true
multiplier = getWarInterest(u)
prob = getTrainingProb(unit,u,reaction,multiplier)
if math.random(100) < prob or casteRaw.flags.TRAINABLE_WAR then
u.profession = 99
success = true
fixList(u)
break
elseif 0 < math.floor(prob) then failureType = 1 end
end
end
if not found == true then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to spot a trainable animal in range." , COLOR_RED, true)
for _,v in ipairs(input_reagents or {}) do
v.flags.PRESERVE_REAGENT = true
end
elseif success == true then
dfhack.gui.showAnnouncement( getName(u,true) .. " has been trained for war." , COLOR_GREEN)
levelUp(unit,reaction.skill,30)
elseif failureType == 1 then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to train any animals for combat.", COLOR_BROWN)
levelUp(unit,reaction.skill,10)
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." is not skilled enough to train the animal for combat.", COLOR_RED, true)
end
end
function fixList(u)
found = false
animals = df.global.world.units.other.ANY_ANIMAL
for v=#animals-1, 0, -1 do
li = animals[v]
if animals[v].id == u.id then
if found == false then
found = true
end
animals:erase(v)
end
end
if found == false then
animals:insert("#",u)
end
end
function unTrain(reaction,unit,job,input_items,input_reagents,output_items,call_native)
local merit = getMerit()
if merit < 0 then
dfhack.gui.showAnnouncement( "You are too disconnected from nature to train animals this way." , COLOR_RED, true)
return false
end
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
local found = false
local success = false
allUnits = df.global.world.units.active
local u
local failureType = 0
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isPet(u) and closeBy(unit,u,radius) and u.profession ~= 103 and u.profession ~= 104 and u.profession ~= 102 and u.relations.pet_owner_id == -1 then
found = true
currentJobInterest = 1
if u.profession == 98 then
multiplier = 1/getHuntingInterest(u)
elseif u.profession == 99 then
multiplier = 1/getWarInterest(u)
end
prob = getTrainingProb(unit,u,reaction,multiplier)
if math.random(100) < prob then
u.profession = 102
success = true
fixList(u)
break
elseif 0 < math.floor(prob) then failureType = 1 end
end
end
if not found == true then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to spot an unassigned work animal in range." , COLOR_RED, true)
for _,v in ipairs(input_reagents or {}) do
v.flags.PRESERVE_REAGENT = true
end
elseif success == true then
dfhack.gui.showAnnouncement( getName(u,true) .. " has been unconditioned." , COLOR_GREEN)
levelUp(unit,reaction.skill,30)
elseif failureType == 1 then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to untrain any animals.", COLOR_BROWN)
levelUp(unit,reaction.skill,10)
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." is not skilled enough to untrain the animal.", COLOR_RED, true)
end
end
function replenishFish(reaction,unit,job,input_items,input_reagents,output_items,call_native)
local merit = getMerit()
if merit > 0 then
local success = false
local found = false
local needed = false
local name = ""
for i=0, #df.global.world.populations-1, 1 do
if df.global.world.populations[i].known and df.global.world.populations[i].type==1 then -- a type of vermin on the map
local race = df.global.world.populations[i].race
local raw = df.global.world.raws.creatures.all[race]
if raw.flags.VERMIN_FISH == true then
found = true
if (df.global.world.populations[i].quantity < 100) then
success = true
df.global.world.populations[i].quantity = df.global.world.populations[i].quantity + 100
name = raw.name
end
end
end
end
if found == false then
dfhack.gui.showAnnouncement( "No fish can live in this area.", COLOR_RED, true)
elseif success == true then
dfhack.gui.showAnnouncement( "The waters have been filled with "..name[1]..".", COLOR_GREEN, true)
else
dfhack.gui.showAnnouncement( "There are already plenty of fish living here.", COLOR_YELLOW, true)
end
else
dfhack.gui.showAnnouncement( "You are too out of touch with nature to summon fish.", COLOR_RED, true)
end
end
function combatTraining(reaction,unit,job,input_items,input_reagents,output_items,call_native)
local merit = getMerit()
if merit < 0 then
dfhack.gui.showAnnouncement( "You are too disconnected from nature to train animals this way." , COLOR_RED, true)
return false
end
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
local found = false
local success = false
allUnits = df.global.world.units.active
local u
local animals = {}
local combatSkills = {"BITE","GRASP_STRIKE","STANCE_STRIKE","SITUATIONAL_AWARENESS","MELEE_COMBAT","WRESTLING","DODGING","ARMOR"}
local trainerSkill = dfhack.units.getEffectiveSkill(unit,reaction.skill)
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isPet(u) and closeBy(unit,u,radius) and (u.profession == 98 or u.profession == 99) then
found = true
for s=1,#combatSkills,1 do
local skillId = df.job_skill[combatSkills[s]]
trainerLevel = dfhack.units.getEffectiveSkill(unit,skillId)
traineeLevel = dfhack.units.getEffectiveSkill(u,skillId)
if trainerLevel > traineeLevel then
table.insert(animals, u)
break
end
end
end
end
if #animals > 0 then
u = animals[math.random(#animals)-1]
for s=1,#combatSkills,1 do
local skillId = df.job_skill[combatSkills[s]]
trainerLevel = dfhack.units.getEffectiveSkill(unit,skillId)
traineeLevel = dfhack.units.getEffectiveSkill(u,skillId)
if trainerLevel > traineeLevel then
quality = 0
if math.random(5) < trainerSkill then quality = quality + 1 end
if math.random(10) < trainerSkill then quality = quality + 1 end
if math.random(15) < trainerSkill then quality = quality + 1 end
if math.random(20) < trainerSkill then quality = quality + 1 end
if math.random(25) < trainerSkill and math.random(3) == 1 then quality = quality + 1 end
levelUp(u,skillId,quality*30)
levelUp(unit,skillId,10)
success = true
end
end
end
if not found == true then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." needs a work animal to train." , COLOR_RED, true)
for _,v in ipairs(input_reagents or {}) do
v.flags.PRESERVE_REAGENT = true
end
elseif success == true then
levelUp(unit,reaction.skill,30)
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." has nothing left to teach." , COLOR_RED, true)
end
end
--Doesn't work
function setIdleArea(reaction,unit,job,input_items,input_reagents,output_items,call_native)
unitRaw = df.global.world.raws.creatures.all[unit.race]
local probability = reaction.products[0].probability + dfhack.units.getEffectiveSkill(unit,reaction.skill)
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
allUnits = df.global.world.units.active
local u
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isPet(u) and closeBy(unit,u,radius) and math.random(100) < probability then
u.idle_area.x = unit.pos.x
u.idle_area.y = unit.pos.y
u.idle_area.z = unit.pos.z
end
end
end
--Doesn't work
function unsetIdleArea(reaction,unit,job,input_items,input_reagents,output_items,call_native)
unitRaw = df.global.world.raws.creatures.all[unit.race]
local probability = reaction.products[0].probability + dfhack.units.getEffectiveSkill(unit,reaction.skill)
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
allUnits = df.global.world.units.active
local u
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isPet(u) and closeBy(unit,u,radius) and math.random(100) < probability then
u.idle_area.x = -30000
u.idle_area.y = -30000
u.idle_area.z = -30000
end
end
end
function tameAllRoamingAnimals(reaction,unit,job,input_items,input_reagents,output_items,call_native)
v = df.global.world.units.active -- To include sentient creatures
--v = df.global.world.units.other.ANY_ANIMAL
unitRaw = df.global.world.raws.creatures.all[unit.race]
local merit = getMerit()
if merit > 0 then
local probability = reaction.products[0].probability
local skill = dfhack.units.getEffectiveSkill(unit,reaction.skill)
if skill == 0 then skill = 1 end
local success = false
for i=#v-1,0,-1 do -- search list in reverse
u = v[i]
if isTameable(u) then
if dfhack.maps.canWalkBetween(u.pos,unit.pos) then
unitRaw = df.global.world.raws.creatures.all[u.race]
casteRaw = unitRaw.caste[u.caste]
petValue = casteRaw.misc.petvalue
if casteRaw.flags.PET == false and casteRaw.flags.PET_EXOTIC == false and petValue < 10000 then
petValue = 10000
end
interestvalue = merit/10
--respectvalue = ((skill*5)^2)
if math.random(100) < math.floor((1-((petValue/2)/interestvalue))*100) then
u.flags1.tame = true
u.training_level = df.animal_training_level.WellTrained
--u.misc_trait_type.RevertWildTimer = 1200
mo.make_own(u)
convertInvader(u)
u.relations.following = -1
levelUp(unit,reaction.skill,petValue)
success = true
end
end
end
end
if success == true then
dfhack.gui.showAnnouncement( "Animals have come in response to " .. dfhack.TranslateName(unit.name) .. "." , COLOR_GREEN, true)--color[,is_bright]
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name) .. " called out to the creatures of the wild, but there was no response..." , COLOR_RED)
end
else
dfhack.gui.showAnnouncement( "You are too out of touch with nature to tame animals this way." , COLOR_RED, true)
end
end
function releaseAllRoamingAnimals(reaction,unit,job,input_items,input_reagents,output_items,call_native)
--v = df.global.world.units.active -- To include sentient creatures
v = df.global.world.units.other.ANY_ANIMAL
for i=#v-1,0,-1 do -- search list in reverse
u = v[i]
--unitRaw = df.global.world.raws.creatures.all[u.race]
if u.flags2.roaming_wilderness_population_source==true
and not dfhack.units.isDead(u)
and u.flags1.tame
and u.civ_id==df.global.ui.civ_id
and u.relations.pet_owner_id==-1
and (u.training_level == df.animal_training_level.WellTrained or u.training_level == df.animal_training_level.Trained or u.training_level == df.animal_training_level.SemiWild)
and not u.flags1.chained
and not u.flags1.caged
and not u.flags2.locked_in_for_trading then
releaseAnimal(u)
dfhack.gui.showAnnouncement( "The " .. (df.global.world.raws.creatures.all[u.race]).name[0] .. " has been released into the wild.", COLOR_RED)
end
end
end
function exileCivMember(reaction,unit,job,input_items,input_reagents,output_items,call_native)
if unit.civ_id==df.global.ui.civ_id then
unit.civ_id=-1
unit.flags1.tame=false
unit.animal.leave_countdown=2
unit.flags1.forest=true
unit.relations.following = nil
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." has left your civilization.", COLOR_WHITE)
merit = 0
for inv_id,item_inv in ipairs(unit.inventory) do
itemcheck = item_inv.item
merit = merit + getItemValue(itemcheck)
end
druidMerit(merit)
end
end
function isRoamingAnimal(u)
unitRaw = df.global.world.raws.creatures.all[u.race]
casteRaw = unitRaw.caste[u.caste]
if u.flags2.roaming_wilderness_population_source
and not dfhack.units.isDead(u)
and not u.flags1.caged
and not u.flags2.locked_in_for_trading
and not u.flags1.tame
and not dfhack.units.isOpposedToLife(u)
and u.civ_id==-1
and not u.flags1.merchant
and not u.flags1.diplomat
and unitRaw.flags.LARGE_ROAMING
and (casteRaw.flags.NATURAL or casteRaw.flags.PET or casteRaw.flags.PET_EXOTIC)
and (u.animal.leave_countdown > 0 or u.flags2.roaming_wilderness_population_source_not_a_map_feature == false) then
return true
end
return false
end
function isTameable(u)
unitRaw = df.global.world.raws.creatures.all[u.race]
casteRaw = unitRaw.caste[u.caste]
if not dfhack.units.isDead(u)
and not u.flags2.locked_in_for_trading
and u.civ_id~=df.global.ui.civ_id
and not dfhack.units.isOpposedToLife(u)
and unitRaw.flags.LARGE_ROAMING
and (casteRaw.flags.NATURAL or casteRaw.flags.PET or casteRaw.flags.PET_EXOTIC) then
return true
end
return false
end
function isPet(u,allowDead)
--unitRaw = df.global.world.raws.creatures.all[u.race]
--casteRaw = unitRaw.caste[u.caste]
if u.flags1.tame
and not (dfhack.units.isDead(u) and allowDead ~= true)
and u.civ_id==df.global.ui.civ_id
and not dfhack.units.isOpposedToLife(u)
and not u.flags1.merchant
and not u.flags1.diplomat
and not u.flags2.locked_in_for_trading
then
return true
end
return false
end
function druidMerit(value)
if df.global.world.world_data.next_site_id ~= 1 then
local gCode = df.global.world.world_data.active_site[0].id
local druidMerit = dfhack.persistent.get(gCode..'_druid-merit')
if druidMerit == nil then
dfhack.persistent.save({key=gCode..'_druid-merit'})
druidMerit = dfhack.persistent.get(gCode..'_druid-merit')
druidMerit.ints[1] = 0
end
if value ~= nil then
druidMerit.ints[1] = druidMerit.ints[1] + value
druidMerit:save()
end
return druidMerit.ints[1]
end
return 0
end
--Gets merit instantly (but may cause a pause, so use it only in special situations)
function calculateMeritAll(respond)
allUnits = df.global.world.units.all
local u
local merit = 0
if respond=="console" then print("Passing judgement on behavior towards animals...") end
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
merit = merit + calculateUnitMerit(u,respond)
end
releasedMerit = druidMerit()
if respond=="console" then print('Accumulated merit from released animals and item offerings: +'..releasedMerit) end
merit = merit + releasedMerit
if respond=="console" then print("Total merit: "..merit) end
totalMerit = merit
newMerit = 0
nextUnit = -1
return(merit)
end
function calculateUnitMerit(u,respond)
local merit = 0
unitRaw = df.global.world.raws.creatures.all[u.race]
casteRaw = unitRaw.caste[u.caste]
name=casteRaw.caste_name[0]
petValue = casteRaw.misc.petvalue
local wildOriginMultiplier = 1
if u.profession == 103 then childMultiplier = 2
else childMultiplier = 1 end
local deathWeight = 1
local casualty = 0
local timeSinceDeath = 0
local line = ""
--if respond=="console" then print(casteRaw.flags[158]) end
--if respond=="console" then print(casteRaw.flags[52]) end -- Both are unknown, and both seem to pop up on nonliving creatures. I'm going to guess that one is NOT_LIVING and one is CANNOT_UNDEAD, which are identical.
if casteRaw.flags[158] ~= true then
--Pets
if isPet(u, true) then
if u.flags2.roaming_wilderness_population_source==true then wildOriginMultiplier = 5 end
--first pass judgement on deaths
if dfhack.units.isDead(u) then
casualty = 5
local accountedFor = false
for _, d in ipairs(df.global.world.deaths.all) do
death_id=d.victim
if u.id==death_id then
timeSinceDeath = (df.global.cur_year*403200 + df.global.cur_year_tick) - (d.event_year*403200 + d.event_time)
if timeSinceDeath > 403200 then
return 0 -- If it's been dead for a year, it doesn't matter.
end
accountedFor = true
if d.death_cause==df.death_type.STRUCK_DOWN then
if respond=="console" then line = ('Pet '..name..' was struck down.') end
elseif d.death_cause==df.death_type.BLEED then
if respond=="console" then line = ('Pet '..name..' bled to death.') end
elseif d.death_cause==df.death_type.COLLISION then
if respond=="console" then line = ('Pet '..name..' was killed in a collision.') end
elseif d.death_cause==df.death_type.HUNGER then
if respond=="console" then line = ('Pet '..name..' starved to death.') end
casualty = 20
elseif d.death_cause==df.death_type.THIRST then
if respond=="console" then line = ('Pet '..name..' died of thirst.') end
casualty = 20
elseif d.death_cause==df.death_type.SHOT then
if respond=="console" then line = ('Pet '..name..' was shot.') end
elseif d.death_cause==df.death_type.DROWN then
if respond=="console" then line = ('Pet '..name..' drowned.') end
elseif d.death_cause==df.death_type.SUFFOCATE then
if respond=="console" then line = ('Pet '..name..' suffocated.') end
elseif d.death_cause==df.death_type.FIRE then
if respond=="console" then line = ('Pet '..name..' died in a fire.') end
elseif d.death_cause==df.death_type.DRAGONFIRE then
if respond=="console" then line = ('Pet '..name..' was killed by dragonfire.') end
elseif d.death_cause==df.death_type.CAVEIN then
if respond=="console" then line = ('Pet '..name..' was killed in a cave in.') end
elseif d.death_cause==df.death_type.DRAWBRIDGE then
if respond=="console" then line = ('Pet '..name..' was killed by a drawbridge.') end
elseif d.death_cause==df.death_type.CAGE then
if respond=="console" then line = ('Pet '..name..' died in a cage.') end
casualty = 20
elseif d.death_cause==df.death_type.MURDER then
if respond=="console" then line = ('Pet '..name..' was murdered.') end
elseif d.death_cause==df.death_type.TRAP then
if respond=="console" then line = ('Pet '..name..' was killed by a trap.') end
elseif d.death_cause==df.death_type.ABANDON then
if respond=="console" then line = ('Pet '..name..' was abandoned.') end
elseif d.death_cause==df.death_type.HEAT then
if respond=="console" then line = ('Pet '..name..' died from heat.') end
elseif d.death_cause==df.death_type.COLD then
if respond=="console" then line = ('Pet '..name..' died from cold.') end
elseif d.death_cause==df.death_type.SPIKE then
if respond=="console" then line = ('Pet '..name..' was killed by a spike.') end
elseif d.death_cause==df.death_type.ENCASE_LAVA then
if respond=="console" then line = ('Pet '..name..' was encased in cooling lava.') end
elseif d.death_cause==df.death_type.ENCASE_MAGMA then
if respond=="console" then line = ('Pet '..name..' was encased in cooling magma.') end
elseif d.death_cause==df.death_type.ENCASE_ICE then
if respond=="console" then line = ('Pet '..name..' was encased in ice.') end
elseif d.death_cause==df.death_type.INFECTION then
if respond=="console" then line = ('Pet '..name..' succumbed to an infection.') end
elseif d.death_cause==df.death_type.VEHICLE then
if respond=="console" then line = ('Pet '..name..' was killed by a vehicle.') end
elseif d.death_cause==df.death_type.FALLING_OBJECT then
if respond=="console" then line = ('Pet '..name..' was killed by a falling object.') end
elseif d.death_cause==df.death_type.DRAIN_BLOOD then
if respond=="console" then line = ('Pet '..name..' was killed by the draining of blood.') end
elseif d.death_cause==df.death_type.SLAUGHTER or d.death_cause==df.death_type.SCUTTLE then
if respond=="console" then line = ('Pet '..name..' was slaughtered.') end
casualty = 50
elseif d.death_cause==df.death_type.OLD_AGE then
if respond=="console" then line = ('Pet '..name..' died of old age.') end
casualty = 0
merit = merit + (wildOriginMultiplier*petValue*10)
else
if respond=="console" then line = ('Pet '..name..' died by method '..d.death_cause..'.') end
end
break
end
end
if accountedFor == false then
if u.flags2.slaughter == true then
if respond=="console" then line = ('Pet '..name..' was slaughtered.') end
casualty = 50
else
if respond=="console" then line = ('Pet '..name..' died of unknown causes.') end
end
end
else
--Now pass judgement on living pets
if respond=="console" then line = ('Taking care of '..name..'.') end
merit = merit + (wildOriginMultiplier*petValue*childMultiplier) --Keeping a pet
end
elseif u.civ_id==-1
and not u.flags1.tame
and not dfhack.units.isOpposedToLife(u)
and not u.flags1.merchant
and not u.flags1.diplomat
and unitRaw.flags.LARGE_ROAMING
and casteRaw.flags.NATURAL then
--Wild animals (isRoamingAnimal)
if dfhack.units.isDead(u) and u.relations.last_attacker_id ~= -1 then
local killer = df.unit.find(u.relations.last_attacker_id)
if killer ~= nil then
if killer.civ_id == df.global.ui.civ_id and not killer.flags1.merchant and not killer.flags1.diplomat and not dfhack.units.isDead(killer) then
if u.training_level == df.animal_training_level.WildUntamed then
if isPet(killer, true) then
if respond=="console" then line = ('Allowed a pet to kill a wild '..name..'.') end
casualty = 1
else
if respond=="console" then line = ('Killed a wild '..name..'.') end
casualty = 2
end
else
if isPet(killer, true) then
if respond=="console" then line = ('Set loose '..name..' and then set animals on it.') end
casualty = 5
else
if respond=="console" then line = ('Set loose '..name..' and then killed it.') end
casualty = 10
end
end
end
end
end
end
--Restrained animal penalty
if ((u.civ_id==-1 and u.training_level ~= df.animal_training_level.WildUntamed) or u.civ_id==df.global.ui.civ_id)
and not u.flags1.merchant
and not u.flags1.diplomat
and not u.flags2.locked_in_for_trading then
if u.flags1.caged then
merit = merit - (10*wildOriginMultiplier*childMultiplier*petValue) --Keeping an animal caged
if respond=="console" then line = ('Keeping '..name..' caged.') end
elseif u.flags1.chained then merit = merit - (5*wildOriginMultiplier*childMultiplier*petValue) --Keeping an animal chained
if respond=="console" then line = ('Keeping '..name..' chained.') end
end
end
--Training bonus (affects all creatures that originated in the wild)
if u.flags2.roaming_wilderness_population_source==true
and u.training_level ~= df.animal_training_level.WildUntamed
and u.training_level ~= df.animal_training_level.SemiWild then
merit = merit + (petValue * u.training_level * 7) -- Add bonus for training
end
merit = merit - (casualty*petValue*wildOriginMultiplier*childMultiplier)
--Reduce intensity for creatures that died a long time ago
if (timeSinceDeath >= 100800) then
merit = math.floor(merit / (timeSinceDeath / 100800))
end
local meritLine = ""
if merit > 0 then meritLine = "+"..merit
else meritLine = ""..merit end
if respond=="console" and merit ~= 0 then print(line ..' '.. meritLine) end
return merit
else
return 0
end
end
function releaseAnimal(u)
u.flags1.tame = false
u.civ_id=-1
adoptPet(nil,u)
u.animal.leave_countdown=2
u.flags1.forest=true
-- remove animal from pastures
for i = #u.general_refs - 1, 0, -1 do
if getmetatable(u.general_refs[i]) == 'general_ref_building_civzone_assignedst' then
local zone = df.building.find(u.general_refs[i].building_id)
if zone then
for j = #zone.assigned_creature - 1, 0, -1 do
local u_id = zone.assigned_creature[j]
if u_id == u.id then
zone.assigned_creature:erase(j)
end
end
end
u.general_refs:erase(i)
end
end
-- cancel dragging
local dragger_id = u.relations.dragger_id
if dragger_id and dragger_id >= 0 then
u.relations.dragger_id = -1
local dragger = df.u.find(dragger_id)
dragger.relations.draggee_id = -1
local job = dragger.job.current_job
if job then
dragger.job.current_job = nil
for i = #job.general_refs - 1, 0, -1 do
if getmetatable(job.general_refs[i]) == 'general_ref_unit_workerst' then
job.general_refs:erase(i)
end
end
end
end
merit = 0
unitRaw = df.global.world.raws.creatures.all[u.race]
casteRaw = unitRaw.caste[u.caste]
petValue = casteRaw.misc.petvalue
adultMultiplier = 1
if u.flags2.roaming_wilderness_population_source==true then wildOriginMultiplier = 5
else
wildOriginMultiplier = 1
if u.profession ~= 103 then adultMultiplier = 2 end
end
local casualty = 0
-- Add merit for release (same as keeping)
merit = merit + (wildOriginMultiplier*adultMultiplier*petValue)
--Training bonus (affects all creatures that originated in the wild)
if u.flags2.roaming_wilderness_population_source==true
and u.training_level ~= df.animal_training_level.WildUntamed
and u.training_level ~= df.animal_training_level.SemiWild then
merit = merit + (petValue * u.training_level * 7) -- Add bonus for training
end
merit = merit - (casualty*petValue*wildOriginMultiplier)
for inv_id,item_inv in ipairs(u.inventory) do
itemcheck = item_inv.item
merit = merit + getItemValue(itemcheck)
end
druidMerit(merit)
end
function callBird(reaction,unit,job,input_items,input_reagents,output_items,call_native)
season = df.global.cur_season
season_ticks = df.global.cur_season_tick
if (df.global.timed_events:insert('#', { new = df.timed_event, type = 8, season = season, season_ticks = season_ticks, entity = nil} )) then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." has summoned a bird.", COLOR_WHITE, true)
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." tried to summon a bird, but nothing happened...", COLOR_RED, true)
end
end
function callCurious(reaction,unit,job,input_items,input_reagents,output_items,call_native)
season = df.global.cur_season
season_ticks = df.global.cur_season_tick
if (df.global.timed_events:insert('#', { new = df.timed_event, type = 8, season = season, season_ticks = season_ticks, entity = nil} )) then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." has made an offering to the wild. Animals are coming to collect it.", COLOR_WHITE, true)
else
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." tried to make an offering to the wild, but nothing happened...", COLOR_RED, true)
end
end
function convertInvader(u)
u.flags1.marauder = false
u.flags1.active_invader = false
u.flags1.hidden_in_ambush = false
u.flags1.hidden_ambusher = false
u.flags1.invades = false
u.flags1.coward = false
u.flags1.invader_origin = false
u.flags2.underworld = false
u.flags2.visitor_uninvited = false
u.invasion_id = -1
end
function ejectItem(building,item,pos)
if item then
itemid = item.id
item.pos.x=pos.x
item.pos.y=pos.y
item.pos.z=pos.z
item.flags.on_ground=true
--dfhack.items.moveToGround(item,pos)
item:moveToGround(pos.x,pos.y,pos.z)
item.flags.in_building=false
if building ~= nil then
for i = 0, #item.general_refs - 1, 1 do
if getmetatable(item.general_refs[i]) == 'general_ref_building_holderst' then
item.general_refs:erase(i)
end
end
for i = 0, #building.contained_items - 1, 1 do
ic = building.contained_items[i].item
if ic.id == itemid then
building.contained_items:erase(i)
break
end
end
end
end
end
function armorPet(reaction,unit,job,input_items,input_reagents,output_items,call_native)
local building = dfhack.buildings.findAtTile(unit.pos)
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
local found = false
local success = false
allUnits = df.global.world.units.active
local u
local failureType = 0
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isPet(u) and closeBy(unit,u,radius) and (u.profession == 98 or u.profession == 99) then
for ji=0, #job - 1, 1 do
item = job[ji]
local itemname = df.item_type[item:getType()]:lower()
bodyparts = u.body.body_plan.body_parts
inventory = u.inventory
for p=0, #bodyparts-1, 1 do
if (bodyparts[p].flags.UPPERBODY == true and itemname == 'armor')
or (bodyparts[p].flags.LOWERBODY == true and itemname == 'pants')
or (bodyparts[p].flags.HEAD == true and itemname == 'helm')
or (bodyparts[p].flags.STANCE == true and itemname == 'shoes')
or (bodyparts[p].flags.GRASP == true and itemname == 'gloves') then
hasarmor = false
for v=0, #inventory-1, 1 do
if inventory[v].mode == 2 and inventory[v].body_part_id == p then hasarmor = true break end
end
if hasarmor == false then
--equip the item to this bodypart
ejectItem(building,item,u.pos)
holder_ref = df.general_ref_unit_holderst:new()
holder_ref.unit_id = u.id
item.general_refs:insert('#',holder_ref)
item.flags.on_ground = false
item.flags.in_inventory = true
invitem = df.unit_inventory_item:new()
invitem.item = item
invitem.mode = 2
invitem.body_part_id = p
invitem.wound_id = -1
inventory:insert('#',invitem)
found = true
success = true
break
end
end
end
if success == true then break end
end
if success == true then break end
end
end
if not found == true then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to spot an unarmored work animal in range." , COLOR_RED, true)
for _,v in ipairs(input_reagents or {}) do
v.flags.PRESERVE_REAGENT = true
end
elseif success == true then
dfhack.gui.showAnnouncement( getName(u,true) .. " has been equipped." , COLOR_GREEN)
levelUp(unit,reaction.skill,30)
end
end
function unarmorPet(reaction,unit,job,input_items,input_reagents,output_items,call_native)
local building = dfhack.buildings.findAtTile(unit.pos)
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
local found = false
local success = false
allUnits = df.global.world.units.active
local u
local failureType = 0
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isPet(u) and closeBy(unit,u,radius) then
bodyparts = u.body.body_plan.body_parts
inventory = u.inventory
for p=#inventory-1, 0, -1 do
item = inventory[p].item
for g = #item.general_refs-1, 0, -1 do
if getmetatable(item.general_refs[g]) == 'general_ref_unit_holderst' then
item.general_refs:erase(g)
end
end
inventory:erase(p)
item.flags.in_inventory = false
item.flags.on_ground = true
dfhack.items.moveToGround(item,u.pos)
found = true
success = true
end
if success == true then break end
end
end
if not found == true then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to spot an armored animal in range." , COLOR_RED, true)
for _,v in ipairs(input_reagents or {}) do
v.flags.PRESERVE_REAGENT = true
end
elseif success == true then
dfhack.gui.showAnnouncement( getName(u,true) .. " has been unequipped." , COLOR_GREEN)
levelUp(unit,reaction.skill,30)
end
end
function healPet(reaction,unit,job,input_items,input_reagents,output_items,call_native)
local building = dfhack.buildings.findAtTile(unit.pos)
local radius = reaction.products[0].product_dimension
if radius < 1 then radius = 10 end
local found = false
local success = false
allUnits = df.global.world.units.active
local u
local failureType = 0
for i=#allUnits-1,0,-1 do -- search list in reverse
u = allUnits[i]
if isPet(u) and closeBy(unit,u,radius) then
while #u.body.wounds > 0 do
u.body.wounds:erase(#u.body.wounds-1)
found = true
success = true
end
inventory = u.inventory
u.body.wound_next_id=1
if success == true then
for p=#inventory-1, 0, -1 do
item = inventory[p].item
for g = #item.general_refs-1, 0, -1 do
if getmetatable(item.general_refs[g]) == 'general_ref_unit_holderst' then
item.general_refs:erase(g)
end
end
inventory:erase(p)
item.flags.in_inventory = false
item.flags.on_ground = true
dfhack.items.moveToGround(item,u.pos)
end
u.body.blood_count=u.body.blood_max
u.body.infection_level = 0
--print("Resetting grasp/stand status...")
--u.status2.limbs_stand_count=u.status2.limbs_stand_max
--u.status2.limbs_grasp_count=u.status2.limbs_grasp_max
u.flags2.has_breaks=false
u.flags2.gutted=false
u.flags2.circulatory_spray=false
u.flags2.breathing_good=true
u.flags2.breathing_problem=false
u.flags2.calculated_nerves=false
u.flags2.calculated_bodyparts=false
u.flags2.calculated_insulation=false
u.flags3.compute_health=true
v=u.body.components
--for i=0,#v.body_layer_328 - 1,1 do
-- v.body_layer_328[i] = 100 -- percent remaining of fluid layers (Urist Da Vinci)
--end
v=u.body.components
--for i=0,#v.body_layer_338 - 1,1 do
-- v.body_layer_338[i] = 0 -- severed, leaking layers (Urist Da Vinci)
-- v.body_layer_348[i] = 0 -- wound contact areas (Urist Da Vinci)
-- v.body_layer_358[i] = 0 -- 100*surface percentage of cuts/fractures on the body part layer (Urist Da Vinci)
-- v.body_layer_368[i] = 0 -- 100*surface percentage of dents on the body part layer (Urist Da Vinci)
-- v.body_layer_378[i] = 0 -- 100*surface percentage of "effects" on the body part layer (Urist Da Vinci)
--end
v=u.body.components.body_part_status
for i=0,#v-1,1 do
v[i].on_fire = false
v[i].severed_or_jammed = 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
end
if success == true then break end
end
end
if not found == true then
dfhack.gui.showAnnouncement( dfhack.TranslateName(unit.name).." was unable to spot a wounded animal in range." , COLOR_RED, true)
for _,v in ipairs(input_reagents or {}) do
v.flags.PRESERVE_REAGENT = true
end
elseif success == true then
dfhack.gui.showAnnouncement( getName(u,true) .. " has been treated." , COLOR_GREEN)
levelUp(unit,reaction.skill,30)
end
end
local function checkCuriousbeastSteal(unit_id,new_equip,item_id)
local item = df.item.find(item_id)
if not item then return false end
local unit = df.unit.find(unit_id)
if not unit then return false end
local unitRaw = df.global.world.raws.creatures.all[unit.race]
local casteRaw = unitRaw.caste[unit.caste]
if unit.civ_id == -1 then
itemInInventory = false
for inv_id,item_inv in ipairs(unit.inventory) do
itemchanged = item_inv.item
if itemchanged.id == item_id then
itemInInventory = true
break
end
end
if itemInInventory == true then
itemvalue = getItemValue(item)
druidMerit(itemvalue)
else
itemvalue = -(getItemValue(item))
druidMerit(itemvalue)
end
end
end
eventful.enableEvent(eventful.eventType.INVENTORY_CHANGE,5)
eventful.onInventoryChange.itemsyndrome=function(unit_id,item_id,old_equip,new_equip)
checkCuriousbeastSteal(unit_id,new_equip,item_id)
end
--------------------------------------------------
--------------------------------------------------
--http://lua-users.org/wiki/StringRecipes
function string.starts(String,Start)
return string.sub(String,1,string.len(Start))==Start
end
--------------------------------------------------
--------------------------------------------------
dfhack.onStateChange.loadDruidism = function(code)
local registered_reactions
if code==SC_MAP_LOADED then
--registered_reactions = {}
for i,reaction in ipairs(df.global.world.raws.reactions) do
if string.starts(reaction.code,'LUA_HOOK_DRUID_TAME_ANIMAL') then
eventful.registerReaction(reaction.code,tameRoamingAnimal)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_RELEASE_ANIMAL') then
eventful.registerReaction(reaction.code,releaseRoamingAnimal)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_NICKNAME') then
eventful.registerReaction(reaction.code,nicknamePet)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_TAME_ALL') then
eventful.registerReaction(reaction.code,tameAllRoamingAnimals)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_RELEASE_ALL') then
eventful.registerReaction(reaction.code,releaseAllRoamingAnimals)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_EXILE') then
eventful.registerReaction(reaction.code,exileCivMember)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_REPLENISH_FISH') then
eventful.registerReaction(reaction.code,replenishFish)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_FOLLOW') then
eventful.registerReaction(reaction.code,followUser)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_TALK_PET') then
eventful.registerReaction(reaction.code,talkPet)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_CLUSTER') then
eventful.registerReaction(reaction.code,cluster)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_UNFOLLOW') then
eventful.registerReaction(reaction.code,unFollow)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_SETIDLEAREA') then
eventful.registerReaction(reaction.code,setIdleArea)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_UNSETIDLEAREA') then
eventful.registerReaction(reaction.code,unsetIdleArea)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_TRAIN_HUNTING') then
eventful.registerReaction(reaction.code,trainHunting)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_TRAIN_WAR') then
eventful.registerReaction(reaction.code,trainWar)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_TRAIN_COMBAT') then
eventful.registerReaction(reaction.code,combatTraining)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_UNTRAIN') then
eventful.registerReaction(reaction.code,unTrain)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_CHECK') then
eventful.registerReaction(reaction.code,checkMerit)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_CALL_BIRD') then
eventful.registerReaction(reaction.code,callBird)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_TALK_BIRD') then
eventful.registerReaction(reaction.code,talkBird)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_DRUID_CALL_CURIOUS') then
eventful.registerReaction(reaction.code,callCurious)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_ANIMALCARE_ARMOR') then
eventful.registerReaction(reaction.code,armorPet)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_ANIMALCARE_UNARMOR') then
eventful.registerReaction(reaction.code,unarmorPet)
registered_reactions = true
elseif string.starts(reaction.code,'LUA_HOOK_ANIMALCARE_HEAL') then
eventful.registerReaction(reaction.code,healPet)
registered_reactions = true
end
end
--if #registered_reactions > 0 then print('Construct Creature: Loaded') end
if registered_reactions then
print('Druidism: Loaded.')
releasedMerit = druidMerit()
dfhack.timeout(1,"ticks",function() update() end)
end
elseif code==SC_MAP_UNLOADED then
end
end
-- if dfhack.init has already been run, force it to think SC_WORLD_LOADED to that reactions get refreshed
if dfhack.isMapLoaded() then dfhack.onStateChange.loadDruidism(SC_MAP_LOADED) end
function getMerit()
if totalMerit == nil then
return calculateMeritAll("")
else
return totalMerit
end
end
function createItem(mat,itemType,quality,pos)
pname = itemType[1]
local item=df['item_'..pname..'st']:new() --incredible
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
if itemType[2]~=-1 then
item:setSubtype(itemType[2])
end
item:setMaterial(mat.type)
item:setMaterialIndex(mat.index)
item:categorize(true)
item.flags.removed=true
item:setSharpness(1,0)
item:setQuality(quality)
dfhack.items.moveToGround(item,{x=pos.x,y=pos.y,z=pos.z})
return item
end
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
--Capture creatures
eventful.onProjItemCheckImpact.capture=function(projectile)
if projectile then
if getmetatable(projectile.item) == 'item_ammost' then
if string.starts(projectile.item.subtype.ammo_class,'DFHACK_PROJCAGE') then
local material = dfhack.matinfo.decode(projectile.item)
if not material then return nil end
local unit = nil
for uid,u in ipairs(df.global.world.units.active) do
if posIsEqual(u.pos,projectile.cur_pos) then unit = u end
end
local cage = createItem(material,{'cage',-1},0,projectile.cur_pos)
if unit ~= nil then
local damagefactor = 1
if unit.counters.winded > 0 then damagefactor = damagefactor/1.5 end
if unit.counters.stunned > 0 then damagefactor = damagefactor/1.5 end
if unit.counters.unconscious > 0 then damagefactor = damagefactor/2 end
if unit.counters.webbed > 0 then damagefactor = damagefactor/1.5 end
if unit.counters.nausea > 0 then damagefactor = damagefactor/1.5 end
local bloodfrac = 1
blood_max = unit.body.blood_max
if blood_max == 0 then bloodfrac = 1
else bloodfrac = unit.body.blood_count/unit.body.blood_max end
local prob = 100 - (math.pow(unit.body.size_info.size_cur*10,1/3)*bloodfrac*damagefactor)
if math.random(100) > prob then
cage_ref = df.general_ref_contained_in_itemst:new()
cage_ref.item_id = cage.id
unit.general_refs:insert('#',cage_ref)
unit.flags1.caged = true
--make other units stop following it
for i=#allUnits-1,0,-1 do -- search list in reverse
newu = allUnits[i]
if newu.relations.following == unit then
newu.relations.following = nil
end
end
unit_ref = df.general_ref_contains_unitst:new()
unit_ref.unit_id = unit.id
cage.general_refs:insert('#',unit_ref)
end
projectile.flags.to_be_deleted=true
end
end
end
end
return true
end
local last_check = -1
local nextUnit = -1
function update()
if last_check ~= df.global.cur_season_tick then
last_check = df.global.cur_season_tick
if releasedMerit > 120 then
if math.floor(df.global.cur_season_tick/120)==df.global.cur_season_tick/120 then --daily
druidMerit(-120)
end
end
end
eventFound = false
if nextUnit == -1 or nextUnit == nil then
--Finish the old count
releasedMerit = druidMerit()
newMerit = newMerit + releasedMerit
totalMerit = newMerit
--Start a new count
newMerit = 0
allUnits = df.global.world.units.all
nextUnit = #df.global.world.units.all-1
else
newMerit = newMerit + calculateUnitMerit(df.global.world.units.all[nextUnit],"")
nextUnit = nextUnit - 1
end
dfhack.timeout(1,"ticks",function() update() end)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment