-- Magic for the magic god, I guess.
--[[
Magic is, in the Drow Underdark mod, a bit different from what you'd be used to.
As usual, there are experience points and levels. These are kept track of with this script (drow/magic.lua). The script gives all drow on the map a second soul; this is where the information is stored. A reaction is used to start a drow on his or her journey through magic; this reaction assigns the drow a particular school.
Yes, schools. Schools are probably a familiar concept to you; however, the schools in Drow Underdark are not like many other schools.
First, you have divine magic. This is magic that uses the favor of the gods to work. This appears entirely in the form of reactions and won't be covered here; it'll use the "Priest" skill, a renamed strand extractor skill. Lolth will be the primary goddess prayed to.
Second, you have body magic, which has two sub-schools: destructive body magic and constructive body magic. Destructive body magic is such things as slowing down the opponent, decreasing his, her or its physical attributes, causing nausea, and, at higher levels, outright draining all of their blood. Constructive body magic is the healing of wounds, buffing of abilities, speeding up, the perfect inverse of destructive body magic. You want your healers to be constructive body mages.
Third, you have soul magic, which has the same two sub-schools. Changing mental attributes, skill rolls and, at higher levels, the skills themselves permenantly.
Fourth, you have sorcery. This is things like fire magic (drow don't really care for the above-ground, so burning it all down is just fine by them), paralysis, shooting webs, and other such generic things.
All of these things start out weak, but grow very strong over time. A long time. Unlike battle, magic is learned not by experience, but by study; Drow will study in their free time, gaining experience points while idle. You may notice a bit of slowdown every once in a while; this is an unavoidable consequence. I'll try to make it as fast as possible.
--]]
local function findAllMagicInorganics()
local inorganics =
{
sorcery = 0,
destructive body = 0,
destructive soul = 0,
constructive body = 0,
constructive soul = 0
}
for matID,material in ipairs(df.global.world.raws.inorganics) do
if string.find(material.id,"DFHACK_SORCERY_DROWFORT") then inorganics.sorcery = matID end
if string.find(material.id,"DFHACK_BODY_DESTRUCTIVE_DROWFORT") then inorganics.body_destructive = matID end
if string.find(material.id,"DFHACK_SOUL_DESTRUCTIVE_DROWFORT") then inorganics.soul_destructive = matID end
if string.find(material.id,"DFHACK_BODY_CONSTRUCTIVE_DROWFORT") then inorganics.body_constructive = matID end
if string.find(material.id,"DFHACK_SOUL_CONSTRUCTIVE_DROWFORT") then inorganics.soul_constructive = matID end
end
return inorganics
end
local function alreadyHasSyndrome(unit,syn_id)
for _,syndrome in ipairs(unit.syndromes.active) do
if syndrome.type == syn_id then return true end
end
return false
end
local function assignSyndrome(target,syn_id) --taken straight from here, but edited so I can understand it better: https://gist.github.com/warmist/4061959/. Also implemented expwnent's changes for compatibility with syndromeTrigger.
if target==nil then
return nil
end
if alreadyHasSyndrome(target,syn_id) then
return true
end
local newSyndrome=df.unit_syndrome:new()
local target_syndrome=df.syndrome.find(syn_id)
newSyndrome.type=target_syndrome.id
newSyndrome.year=df.global.cur_year
newSyndrome.year_time=df.global.cur_year_tick
newSyndrome.ticks=1
newSyndrome.unk1=1
for k,v in ipairs(target_syndrome.ce) do
local sympt=df.unit_syndrome.T_symptoms:new()
sympt.ticks=1
sympt.flags=2
newSyndrome.symptoms:insert("#",sympt)
end
target.syndromes.active:insert("#",newSyndrome)
return true
end
local function unitHasMagicSkill(unit)
if #unit.status.souls>1 then return true else return false end
end
local function iterateSkill(unit)
local realSoul = unit.status.current_soul
local fakeSoul = unit.status.souls[1]
local initialExperienceIncrease = 100
local mentalAttributeModifier = 0
for k,attribute in ipairs(realsoul.mental_attrs) do
if not(k == 9 or k == 11 or k == 12) then
mentalAttributeModifier = mentalAttributeModifier+(attribute.value/1000)
end
end
mentalAttributeModifier = mentalAttributeModifier/10
local personalityModifier=0
for k,attribute in ipairs(realSoul.traits) do
if k == 8 or k == 9 or k == 11 or k == 12 or k == 14 or k == 15 or (k>22 and k<29) then
personalityModifier = personalityModifier+(attribute/50)
end
end
personalityModifier = personalityModifier/12
finalExperienceIncrease = math.floor(initialExperienceIncrease*personalityModifier*mentalAttributeModifier)
fakeSoul.unk1=fakeSoul.unk1+finalExperienceIncrease--unk1 is being used as an experience points counter; the second soul shouldn't be checked for anything at this point at all, so there's no need to worry about it, I should hope.
local experiencePoints = fakeSoul.unk1
fakeSoul.unk2=math.floor((1/10)*math.sqrt(2*experiencePoints+9025)-(19/2))
end
magicInorganics = findAllMagicInorganics()
local function assignSpells(unit)
local school = df.global.world.raws.inorganics[magicInorganics[unit.status.souls[1].name.first_name]].material --jebus
for i=0,unit.status.souls[1].unk2 do --unk2 is level of magic skill
assignSyndrome(unit,material.syndrome[i].id)
end
end
local function checkAllUnitsForIdleMagicExperienceAndGiveThemMagicSpells() --long-ass function name
local delayCounter = 1
for k,unit in ipairs(df.global.world.units.active) do
if unitHasMagicSkill(unit) then
dfhack.timeout(delayCounter,"ticks",function() iterateSkill(unit) end)
dfhack.timeout(delayCounter+1,"ticks",function() assignSpells(unit) end)
delayCounter = delayCounter+2
end
end
end
dfhack.onStateChange.drowMagic = function(code)
if code==SC_WORLD_LOADED then
dfhack.timeout(1,'months',mainloop) --disables if map/world is unloaded automatically
end
end
local function mainloop()
checkAllUnitsForIdleMagicExperienceAndGiveThemMagicSpells()
dfhack.timeout(1,'months',mainloop) --a check every "eight hours" in-game; eight hours seems like a good "study" interval.
end
AHAHAHAHAHAHAHA
--For autosyndrome.
local args = {...}
local function createFakeSoul(unit)
unit.status.souls:insert('#',{new = df.unit_soul})
end
local function setSchool(unit,school)
unit.status.souls[1].name.first_name = school
end
local unitToWorkOn = df.global.world.units.all[args[1]]
local magicSchool = args[2]
createFakeSoul(unitToWorkOn)
setSchool(unitToWorkOn,magicSchool)