diff --git a/scripts/modtools/reaction-trigger-transition.lua b/scripts/modtools/reaction-trigger-transition.lua new file mode 100644 index 000000000..7193d2d0c --- /dev/null +++ b/scripts/modtools/reaction-trigger-transition.lua @@ -0,0 +1,137 @@ +-- reaction-trigger-transition.lua +-- author expwnent +-- prints useful things to the console and a file to help modders transition from autoSyndrome to reaction-trigger +-- this script is basically an apology for breaking backward compatibiility + +local function maybeQuote(str) + if str == '' or string.find(str,' ') then + return ('"' .. str .. '"') + else + return str + end +end + +warnings = '' +output = '' +for _,reaction in ipairs(df.global.world.raws.reactions) do + local function foreachProduct(product) + local prodType = product:getType() + if prodType ~= df.reaction_product_type.item then + return + end + if product.item_type ~= df.item_type.BOULDER then + return + end + if product.mat_index < 0 then + return + end + local inorganic = df.global.world.raws.inorganics[product.mat_index] + local didInorganicName + for _,syndrome in ipairs(inorganic.material.syndrome) do + local workerOnly = true + local allowMultipleTargets = false; + local command + local commandStr + local destroyRock = true; + local foundAutoSyndrome = false; + local resetPolicy; + for i,synclass in ipairs(syndrome.syn_class) do + synclass = synclass.value + if false then + elseif synclass == '\\AUTO_SYNDROME' then + foundAutoSyndrome = true + elseif synclass == '\\ALLOW_NONWORKER_TARGETS' then + workerOnly = false + elseif synclass == '\\ALLOW_MULTIPLE_TARGETS' then + allowMultipleTargets = true + elseif synclass == '\\PRESERVE_ROCK' then + destroyRock = false + elseif synclass == '\\RESET_POLICY DoNothing' then + resetPolicy = 'DoNothing' + elseif synclass == '\\RESET_POLICY ResetDuration' then + resetPolicy = 'ResetDuration' + elseif synclass == '\\RESET_POLICY AddDuration' then + resetPolicy = 'AddDuration' + elseif synclass == '\\RESET_POLICY NewInstance' then + resetPolicy = 'NewInstance' + elseif synclass == '\\COMMAND' then + command = '' + elseif command then + if synclass == '\\LOCATION' then + command = command .. '\\LOCATION ' + elseif synclass == '\\WORKER_ID' then + command = command .. '\\WORKER_ID ' + elseif synclass == '\\REACTION_INDEX' then + warnings = warnings .. ('Warning: \\REACTION_INDEX is deprecated. Use \\REACTION_NAME instead.\n') + command = command .. '\\REACTION_NAME ' + else + commandStr = true + command = command .. maybeQuote(synclass) .. ' ' + end + end + end + if foundAutoSyndrome then + if destroyRock then + warnings = warnings .. ('Warning: instead of destroying the rock, do not produce it in the first place.\n') + end + if workerOnly then + workerOnly = 'true' + else + workerOnly = 'false' + end + if allowMultipleTargets then + allowMultipleTargets = 'true' + else + allowMultipleTargets = 'false' + end + local reactionTriggerStr = 'modtools/reaction-trigger -reactionName ' .. maybeQuote(reaction.code) --.. '"' + if workerOnly ~= 'true' then + reactionTriggerStr = reactionTriggerStr .. ' -workerOnly ' .. workerOnly + end + if allowMultipleTargets ~= 'false' then + reactionTriggerStr = reactionTriggerStr .. ' -allowMultipleTargets ' .. allowMultipleTargets + end + if resetPolicy and resetPolicy ~= 'NewInstance' then + reactionTriggerStr = reactionTriggerStr .. ' -resetPolicy ' .. resetPolicy + end + if #syndrome.ce > 0 then + if syndrome.syn_name == '' then + warnings = warnings .. ('Warning: give this syndrome a name!\n') + end + reactionTriggerStr = reactionTriggerStr .. ' -syndrome ' .. maybeQuote(syndrome.syn_name) .. '' + end + if command and commandStr then + reactionTriggerStr = reactionTriggerStr .. ' -command ' .. command + end + if (not command or command == '') and (not syndrome.syn_name or syndrome.syn_name == '') then + --output = output .. '#' + else + if not didInorganicName then +-- output = output .. '# ' .. (inorganic.id) .. '\n' + didInorganicName = true + end + output = output .. (reactionTriggerStr) .. '\n' + end + end + end + end + for _,product in ipairs(reaction.products) do + foreachProduct(product) + end +end + +print(warnings) +print('\n\n\n\n') +print(output) +local file = io.open('reaction-trigger-transition.txt', 'w+') +--io.output(file) +--file:write(warnings) +--file:write('\n\n\n\n') +file:write(output) +file:flush() +--io.flush(file) +io.close(file) +--io.output() +print('transition information written to reaction-trigger-transition.txt') + + diff --git a/scripts/modtools/reaction-trigger.lua b/scripts/modtools/reaction-trigger.lua new file mode 100644 index 000000000..3fb64e168 --- /dev/null +++ b/scripts/modtools/reaction-trigger.lua @@ -0,0 +1,211 @@ +-- reaction-trigger.lua +-- author expwnent +-- replaces autoSyndrome: trigger commands when custom reactions are completed + +local eventful = require 'plugins.eventful' +local syndromeUtil = require 'syndromeUtil' + +reactionHooks = reactionHooks or {} + +local function getWorkerAndBuilding(job) + local workerId = -1 + local buildingId = -1 + for _,generalRef in ipairs(job.general_refs) do + if generalRef:getType() == df.general_ref_type.UNIT_WORKER then + if workerId ~= -1 then + print(job) + printall(job) + error('reaction-trigger: two workers on same job: ' .. workerId .. ', ' .. generalRef.unit_id) + else + workerId = generalRef.unit_id + if workerId == -1 then + print(job) + printall(job) + error('reaction-trigger: invalid worker') + end + end + elseif generalRef:getType() == df.general_ref_type.BUILDING_HOLDER then + if buildingId ~= -1 then + print(job) + printall(job) + error('reaction-trigger: two buildings same job: ' .. buildingId .. ', ' .. generalRef.building_id) + else + buildingId = generalRef.building_id + if buildingId == -1 then + print(job) + printall(job) + error('reaction-trigger: invalid building') + end + end + end + end + return workerId,buildingId +end + +local function processCommand(job, worker, target, building, command) + local result = {} + for _,arg in ipairs(command) do + if arg == '\\WORKER_ID' then + table.insert(result,''..worker.id) + elseif arg == '\\TARGET_ID' then + table.insert(restul,''..target.id) + elseif arg == '\\BUILDING_ID' then + table.insert(result,''..building.id) + elseif arg == '\\LOCATION' then + table.insert(result,''..job.pos.x) + table.insert(result,''..job.pos.y) + table.insert(result,''..job.pos.z) + elseif arg == '\\REACTION_NAME' then + table.insert(result,''..job.reaction_name) + else + table.insert(result,arg) + end + end + return result +end + +eventful.onJobCompleted.reactionTrigger = function(job) + if job.completion_timer > 0 then + return + end + +-- if job.job_type ~= df.job_type.CustomReaction then +-- --TODO: support builtin reaction triggers if someone asks +-- return +-- end + + if not job.reaction_name or job.reaction_name == '' then + return + end +-- print('reaction name: ' .. job.reaction_name) + if not job.reaction_name or not reactionHooks[job.reaction_name] then + return + end + + local worker,building = getWorkerAndBuilding(job) + worker = df.unit.find(worker) + building = df.building.find(building) + if not worker or not building then + --this probably means that it finished before EventManager could get a copy of the job while the job was running + --TODO: consider printing a warning once + return + end + + local function doAction(action) + local didSomething + if action.args then + local processed = processCommand(job, worker, worker, building, action.args) + print(dfhack.run_command(table.unpack(processed))) + end + if action.syndrome then + didSomething = syndromeUtil.infectWithSyndromeIfValidTarget(worker, action.syndrome, action.resetPolicy) or didSomething + end + if action.workerOnly then + return + end + if didSomething and not action.allowMultipleTargets then + return + end + local function foreach(unit) + if unit == worker then + return false + elseif unit.pos.z ~= building.z then + return false + elseif unit.pos.x < building.x1 or unit.pos.x > building.x2 then + return false + elseif unit.pos.y < building.y1 or unit.pos.y > building.y2 then + return false + else + if action.args then + processCommand(job, worker, unit, building, action.args) + end + if action.syndrome then + didSomething = syndrome.infectWithSyndromeIfValidTarget(unit,action.syndrome,action.resetPolicy) or didSomething + end + if didSomething and not action.allowMultipleTargets then + return true + end + return false + end + end + for _,unit in ipairs(df.global.world.units.all) do + if foreach(unit) then + break + end + end + end + for _,action in ipairs(reactionHooks[job.reaction_name]) do + doAction(action) + end +end + +eventful.enableEvent(eventful.eventType.JOB_COMPLETED,0) + +local args = {...} +local i=1 +local reactionName +local action = {} +action.workerOnly = true +action.allowMultipleTargets = false +--action.args = {} +--action.syndrome, action.resetPolicy, action.workerOnly, action.allowMultipleTargets, action.args +while i <= #args do + if action.args then + table.insert(action.args, args[i]) + i = i+1 + else + if args[i] == '-clear' then + reactionHooks = {} + i = i+1 + elseif args[i] == '-reactionName' then + reactionName = args[i+1] + i = i+2 + elseif args[i] == '-syndrome' then + if action.syndrome then + error('only one syndrome at a time permitted') + end + for _,syndrome in ipairs(df.global.world.raws.syndromes.all) do + if syndrome.syn_name == args[i+1] then + action.syndrome = syndrome + break + end + end + if not action.syndrome then + error('could not find syndrome ' .. args[i+1]) + end + i = i+2 + elseif args[i] == '-workerOnly' then + action.workerOnly = args[i+1] == 'true' + i = i+2 + elseif args[i] == '-allowMultipleTargets' then + action.allowMultipleTargets = args[i+1] == 'true' + i = i+2 + elseif args[i] == '-command' then + action.args = {} + i = i+1 + elseif args[i] == '-resetPolicy' then + action.resetPolicy = syndromeUtil.ResetPolicy[args[i+1]] + if not action.resetPolicy then + error('invalid reset policy: ' .. args[i+1]) + end + i = i+2 + else + error('invalid arguments') + end + end +end + +if not reactionName then + return +end + +if action.args then + print(action.args) + printall(action.args) +end + +if not reactionHooks[reactionName] then + reactionHooks[reactionName] = {} +end +table.insert(reactionHooks[reactionName], action) +