-- Inject new raw definitions into the world
--[====[

devel/inject-raws
=================
WARNING: THIS SCRIPT CAN PERMANENLY DAMAGE YOUR SAVE.

This script attempts to inject new raw objects into your
world. If the injected references do not match the actual
edited raws, your save will refuse to load, or load but crash.

This script can handle reaction, item and building definitions.

The savegame contains a list of the relevant definition tokens in
the right order, but all details are read from raws every time.
This allows just adding stub definitions, and simply saving and
reloading the game.

This is useful enough for modders and some users to justify the danger.

Usage example::

    devel/inject-raws trapcomp ITEM_TRAPCOMP_STEAM_PISTON workshop STEAM_ENGINE MAGMA_STEAM_ENGINE reaction STOKE_BOILER

]====]

local utils = require 'utils'

local raws = df.global.world.raws

print[[
WARNING: THIS SCRIPT CAN PERMANENLY DAMAGE YOUR SAVE.

This script attempts to inject new raw objects into your
world. If the injected references do not match the actual
edited raws, your save will refuse to load, or load but crash.
]]

if not utils.prompt_yes_no('Did you make a backup?') then
    qerror('Not backed up.')
end

df.global.pause_state = true

local changed = false

--luacheck: in=string
function inject_reaction(name)
    for _,v in ipairs(raws.reactions.reactions) do
        if v.code == name then
            print('Reaction '..name..' already exists.')
            return
        end
    end

    print('Injecting reaction '..name)
    changed = true

    raws.reactions.reactions:insert('#', {
        new = true,
        code = name,
        name = 'Dummy reaction '..name,
        index = #raws.reactions.reactions,
    })
end

local building_types = {
    workshop = { df.building_def_workshopst, raws.buildings.workshops },
    furnace = { df.building_def_furnacest, raws.buildings.furnaces },
}

--luacheck: in=string,string
function inject_building(btype, name)
    for _,v in ipairs(raws.buildings.all) do
        if v.code == name then
            print('Building '..name..' already exists.')
            return
        end
    end

    print('Injecting building '..name)
    changed = true

    local typeinfo = building_types[btype]

    local id = raws.buildings.next_id
    raws.buildings.next_id = id+1

    raws.buildings.all:insert('#', {
        new = typeinfo[1],
        code = name,
        name = 'Dummy '..btype..' '..name,
        id = id,
    })

    typeinfo[2]:insert('#', raws.buildings.all[#raws.buildings.all-1])
end

local itemdefs = raws.itemdefs
local item_types = { --as:{1:df.itemdef,2:'df.itemdef[]',3:string}[]
    weapon = { df.itemdef_weaponst, itemdefs.weapons, 'weapon_type' },
    trainweapon = { df.itemdef_weaponst, itemdefs.weapons, 'training_weapon_type' },
    pick = { df.itemdef_weaponst, itemdefs.weapons, 'digger_type' },
    trapcomp = { df.itemdef_trapcompst, itemdefs.trapcomps, 'trapcomp_type' },
    toy = { df.itemdef_toyst, itemdefs.toys, 'toy_type' },
    tool = { df.itemdef_toolst, itemdefs.tools, 'tool_type' },
    instrument = { df.itemdef_instrumentst, itemdefs.instruments, 'instrument_type' },
    armor = { df.itemdef_armorst, itemdefs.armor, 'armor_type' },
    ammo = { df.itemdef_ammost, itemdefs.ammo, 'ammo_type' },
    siegeammo = { df.itemdef_siegeammost, itemdefs.siege_ammo, 'siegeammo_type' },
    gloves = { df.itemdef_glovesst, itemdefs.gloves, 'gloves_type' },
    shoes = { df.itemdef_shoesst, itemdefs.shoes, 'shoes_type' },
    shield = { df.itemdef_shieldst, itemdefs.shields, 'shield_type' },
    helm = { df.itemdef_helmst, itemdefs.helms, 'helm_type' },
    pants = { df.itemdef_pantsst, itemdefs.pants, 'pants_type' },
    food = { df.itemdef_foodst, itemdefs.food },
}

function add_to_civ(entity, bvec, id)
    local resources = entity.resources --as:number[][]
    for _,v in ipairs(resources[bvec]) do
        if v == id then
            return
        end
    end

    resources[bvec]:insert('#', id)
end

function add_to_dwarf_civs(btype, id)
    local typeinfo = item_types[btype]
    if not typeinfo[3] then
        print('Not adding to civs.')
    end

    for _,entity in ipairs(df.global.world.entities.all) do
        if entity.race == df.global.ui.race_id then
            local bvec = typeinfo[3] --as:string
            add_to_civ(entity, bvec, id)
        end
    end
end

--luacheck: in=string,string
function inject_item(btype, name)
    for _,v in ipairs(itemdefs.all) do
        if v.id == name then
            print('Itemdef '..name..' already exists.')
            return
        end
    end

    print('Injecting item '..name)
    changed = true

    local typeinfo = item_types[btype]
    local vec = typeinfo[2]
    local id = #vec

    vec:insert('#', {
        new = typeinfo[1],
        id = name,
        subtype = id,
        name = name,
        name_plural = name,
    })

    itemdefs.all:insert('#', vec[id])

    add_to_dwarf_civs(btype, id)
end

local args = {...}
local mode = nil
local ops = {} --as:{_type:function,_node:none}[]

for _,kv in ipairs(args) do
    if mode and string.match(kv, '^[%u_ ]+$') then
        table.insert(ops, curry(mode, kv))
    elseif kv == 'reaction' then
        mode = inject_reaction
    elseif building_types[kv] then
        mode = curry(inject_building, kv)
    elseif item_types[kv] then
        mode = curry(inject_item, kv)
    else
        qerror('Invalid option: '..kv)
    end
end

if #ops > 0 then
    print('')
    for _,v in ipairs(ops) do
        v()
    end
end

if changed then
    print('\nNow without unpausing save and reload the game to re-read raws.')
else
    print('\nNo changes made.')
end
