From bb553a014cf72fda011cd50980f2fa8c76ba2b19 Mon Sep 17 00:00:00 2001 From: Myk Taylor Date: Sun, 27 Apr 2025 14:10:52 -0700 Subject: [PATCH] update moddable-gods --- changelog.txt | 1 + docs/modtools/moddable-gods.rst | 62 ++++++++--- modtools/moddable-gods.lua | 192 ++++++++++++++++++++------------ 3 files changed, 163 insertions(+), 92 deletions(-) diff --git a/changelog.txt b/changelog.txt index 7a74140c52..49a241e338 100644 --- a/changelog.txt +++ b/changelog.txt @@ -29,6 +29,7 @@ Template for new versions: # Future ## New Tools +- `modtools/moddable-gods`: (reinstated) create new deities from scratch ## New Features - `gui/mod-manager`: when run in a loaded world, shows a list of active mods -- click to export the list to the clipboard for easy sharing or posting diff --git a/docs/modtools/moddable-gods.rst b/docs/modtools/moddable-gods.rst index 1a575c8325..22b890e6c5 100644 --- a/docs/modtools/moddable-gods.rst +++ b/docs/modtools/moddable-gods.rst @@ -3,21 +3,47 @@ modtools/moddable-gods .. dfhack-tool:: :summary: Create deities. - :tags: unavailable - -This is a standardized version of Putnam's moddableGods script. It allows you -to create gods on the command-line. - -Arguments:: - - -name godName - sets the name of the god to godName - if there's already a god of that name, the script halts - -spheres [ sphereList ] - define a space-separated list of spheres of influence of the god - -gender male|female|neuter - sets the gender of the god - -depictedAs str - often depicted as a str - -verbose - if specified, prints details about the created god + :tags: dev + +This script allows you to create new gods in an existing world. + +Usage +----- + +:: + + moddable-gods --name --spheres [] + +Examples +-------- + +``modtools/moddable-gods --name "Slarty Bog" --spheres FATE,WEATHER`` + Create a new god named "Slarty Bog" with spheres of influence of FATE and + WEATHER. The god will have a random gender and will be depicted as a dwarf. + +``modtools/moddable-gods -n Og -s SPEECH,SALT,SACRIFICE -g neuter -d emu`` + Create a new god named "Og" with spheres of influence of SPEECH, SALT, and + SACRIFICE. The god will be genderless and will be depicted as an emu. + +Options +------- + +``-n``, ``--name `` + The name of the god to create. This is a required argument. The name must + be unique in the world. If the name is already taken, the script will exit + without action. +``-s``, ``--spheres `` + A comma-separated list of spheres of influence for the god. This is a + required argument. To see the available spheres, run this command:: + + lua @df.sphere_type + +``-g``, ``--gender (male|female|neuter)`` + The gender of the god. If not specified, a random gender will be chosen. +``-d``, ``--depicted-as `` + When the deity is referenced in-game, it will be described as "often + depicted as a ". The string must match the token ID or descriptive + name of a race that exists in the world. You can also specify a numeric + race ID. If not specified, it defaults to "dwarf". +``-q``, ``--quiet`` + If specified, suppresses all non-error output. diff --git a/modtools/moddable-gods.lua b/modtools/moddable-gods.lua index 3d1726ba10..f852992faf 100644 --- a/modtools/moddable-gods.lua +++ b/modtools/moddable-gods.lua @@ -1,92 +1,136 @@ -local utils = require('utils') - -local validArgs = utils.invert{ - 'help', - 'name', - 'spheres', - 'gender', - 'depictedAs', - 'verbose', --- 'entities', -} -local args = utils.processArgs({...}, validArgs) +local argparse = require('argparse') + +local function get_spheres(arg) + local spheres = {} + for _, sphere in ipairs(argparse.stringList(arg, 'spheres')) do + local sphereType = df.sphere_type[sphere] + if not sphereType then + qerror('invalid sphere: ' .. sphere) + end + table.insert(spheres, sphereType) + end + return spheres +end -if args.help then - print(dfhack.script_help()) - return +local function get_gender(arg) + if arg == 'male' then + return df.pronoun_type.he + elseif arg == 'female' then + return df.pronoun_type.she + elseif arg == 'neuter' then + return df.pronoun_type.it + else + qerror('invalid gender: ' .. arg) + end end -if not args.name or not args.depictedAs or not args.spheres or not args.gender then - error('All arguments must be specified.') +local function get_race(arg) + local int_arg = tonumber(arg) + if int_arg then + local raw = df.creature_raw.find(int_arg) + if not raw then + qerror('race id ' .. int_arg .. ' does not exist') + end + return int_arg + end + for k, raw in ipairs(df.global.world.raws.creatures.all) do + if raw.creature_id == arg or raw.name[0] == arg then + return k + end + end + qerror('race ' .. arg .. ' does not exist') +end + +local function do_god(opts) + local godFig = df.historical_figure:new() + godFig.race = opts.race + godFig.caste = 0 + godFig.sex = opts.gender + + godFig.appeared_year = -1 + godFig.born_year = -1 + godFig.born_seconds = -1 + godFig.curse_year = -1 + godFig.curse_seconds = -1 + godFig.old_year = -1 + godFig.old_seconds = -1 + godFig.died_year = -1 + godFig.died_seconds = -1 + + godFig.name.has_name = true + godFig.name.first_name = opts.name + + godFig.breed_id = -1 + godFig.flags.deity = true + godFig.flags.brag_on_kill = true + godFig.flags.kill_quest = true + godFig.flags.chatworthy = true + godFig.flags.flashes = true + godFig.flags.never_cull = true + + godFig.info = df.historical_figure_info:new() + godFig.info.metaphysical = {new=true} + godFig.info.known_info = {new=true} + for _,sphere in ipairs(opts.spheres) do + godFig.info.metaphysical.spheres:insert('#', sphere) + end + + godFig.pool_id = -1 -- will get a pool_id when game is saved and reloaded + godFig.id = df.global.hist_figure_next_id + df.global.hist_figure_next_id = 1 + df.global.hist_figure_next_id + df.global.world.history.figures:insert('#', godFig) + + return godFig end -local templateGod -for _,fig in ipairs(df.global.world.history.figures) do - if fig.flags.deity then - templateGod = fig - break - end +if not dfhack.isWorldLoaded() then + qerror('This script requires a loaded world.') end -if not templateGod then - error 'Could not find template god.' + +local opts = { + name=nil, + spheres=nil, + gender=nil, + race=nil, + quiet=false, + help=false, +} + +local _ = argparse.processArgsGetopt({ ... }, { + {'n', 'name', hasArg=true, handler=function(arg) opts.name = arg end}, + {'s', 'spheres', hasArg=true, handler=function(arg) opts.spheres = get_spheres(arg) end}, + {'g', 'gender', hasArg=true, handler=function(arg) opts.gender = get_gender(arg) end}, + {'d', 'depicted-as', hasArg=true, handler=function(arg) opts.race = get_race(arg) end}, + {'h', 'help', handler=function() opts.help = true end}, + {'q', 'quiet', handler=function() opts.quiet = true end}, +}) + +if opts.help then + print(dfhack.script_help()) + return end -local gender -if args.gender == 'male' then - gender = 1 -elseif args.gender == 'female' then - gender = 0 -elseif args.gender == "neuter" then - gender = -1 -else - error 'invalid gender' +if not opts.name or not opts.spheres or #opts.name == 0 or #opts.spheres == 0 then + qerror('name and spheres must be specified.') end -local race -for k,v in ipairs(df.global.world.raws.creatures.all) do - if v.creature_id == args.depictedAs or v.name[0] == args.depictedAs then - race = k - break +for _, fig in ipairs(df.global.world.history.figures) do + if fig.name.first_name == opts.name then + print('god "' .. opts.name .. '" already exists.') + return end end -if not race then - error('invalid race: ' .. args.depictedAs) -end -for _,fig in ipairs(df.global.world.history.figures) do - if fig.name.first_name == args.name then - print('god ' .. args.name .. ' already exists. Skipping') - return - end +if not opts.gender then + opts.gender = math.random(-1, 1) end -local godFig = df.historical_figure:new() -godFig.appeared_year = -1 -godFig.born_year = -1 -godFig.born_seconds = -1 -godFig.curse_year = -1 -godFig.curse_seconds = -1 -godFig.old_year = -1 -godFig.old_seconds = -1 -godFig.died_year = -1 -godFig.died_seconds = -1 -godFig.name.has_name = true -godFig.breed_id = -1 -godFig.flags:assign(templateGod.flags) -godFig.id = df.global.hist_figure_next_id -df.global.hist_figure_next_id = 1+df.global.hist_figure_next_id -godFig.info = df.historical_figure_info:new() -godFig.info.spheres = {new=true} -godFig.info.known_info = df.knowledge_profilest:new() -godFig.race = race -godFig.caste = 0 -godFig.sex = gender -godFig.name.first_name = args.name -for _,sphere in ipairs(args.spheres) do - godFig.info.metaphysical.spheres:insert('#',df.sphere_type[sphere]) +if not opts.race then + opts.race = get_race('dwarf') end -df.global.world.history.figures:insert('#',godFig) -if args.verbose then - print(godFig.name.first_name .. " created as historical figure " .. tostring(godFig.id)) +local godFig = do_god(opts) + +if not opts.quiet then + print(godFig.name.first_name .. " created as historical figure " .. tostring(godFig.id)) end