From 6258870e2c8c65b9bdb620af243270cea391b69e Mon Sep 17 00:00:00 2001 From: lethosor Date: Tue, 14 Jun 2016 19:40:48 -0400 Subject: [PATCH] Remove scripts/ folder --- .gitmodules | 18 - scripts/3rdparty/dscorbett | 1 - scripts/3rdparty/kane-t | 1 - scripts/3rdparty/lethosor | 1 - scripts/3rdparty/maienm | 1 - scripts/3rdparty/maxthyme | 1 - scripts/3rdparty/roses | 1 - scripts/CMakeLists.txt | 14 - scripts/Scripts.cmake | 19 - scripts/about.txt | 2 - scripts/adaptation.rb | 104 - scripts/add-thought.lua | 93 - scripts/armoks-blessing.lua | 202 -- scripts/autofarm.rb | 192 -- scripts/autolabor-artisans.lua | 79 - scripts/autounsuspend.rb | 49 - scripts/ban-cooking.rb | 132 -- scripts/binpatch.lua | 51 - scripts/brainwash.lua | 70 - scripts/burial.lua | 27 - scripts/catsplosion.lua | 97 - scripts/colonies.lua | 82 - scripts/create-items.rb | 207 -- scripts/deathcause.rb | 75 - scripts/deteriorateclothes.rb | 81 - scripts/deterioratecorpses.rb | 106 - scripts/deterioratefood.rb | 96 - scripts/devel/about.txt | 6 - scripts/devel/all-bob.lua | 15 - scripts/devel/check-release.lua | 59 - scripts/devel/clear-script-env.lua | 26 - scripts/devel/cmptiles.lua | 50 - scripts/devel/export-dt-ini.lua | 515 ----- scripts/devel/find-offsets.lua | 1936 ----------------- scripts/devel/inject-raws.lua | 197 -- scripts/devel/inspect-screen.lua | 110 - scripts/devel/light.lua | 388 ---- scripts/devel/list-filters.lua | 78 - scripts/devel/lsmem.lua | 21 - scripts/devel/lua-example.lua | 14 - scripts/devel/make-dt.pl | 492 ----- scripts/devel/nuke-items.lua | 22 - scripts/devel/pop-screen.lua | 10 - scripts/devel/prepare-save.lua | 98 - scripts/devel/print-args.lua | 15 - scripts/devel/print-args2.lua | 17 - scripts/devel/save-version.lua | 154 -- scripts/devel/scanitemother.rb | 16 - scripts/devel/spawn-unit-helper.rb | 35 - scripts/devel/test-perlin.lua | 193 -- scripts/devel/unforbidall.rb | 10 - scripts/devel/unit-path.lua | 225 -- scripts/devel/watch-minecarts.lua | 83 - scripts/digfort.rb | 94 - scripts/drain-aquifer.lua | 40 - scripts/elevate-mental.lua | 52 - scripts/elevate-physical.lua | 51 - scripts/emigration.lua | 132 -- scripts/exportlegends.lua | 836 ------- scripts/exterminate.rb | 192 -- scripts/feature.lua | 75 - scripts/fix-ster.lua | 122 -- scripts/fix/about.txt | 1 - scripts/fix/blood-del.lua | 62 - scripts/fix/build-location.lua | 45 - scripts/fix/dead-units.lua | 37 - scripts/fix/diplomats.lua | 106 - scripts/fix/dry-buckets.lua | 28 - scripts/fix/fat-dwarves.lua | 33 - scripts/fix/feeding-timers.lua | 43 - scripts/fix/item-occupancy.lua | 131 -- scripts/fix/loyaltycascade.rb | 70 - scripts/fix/merchants.lua | 104 - scripts/fix/population-cap.lua | 47 - scripts/fix/stable-temp.lua | 71 - scripts/fix/stuckdoors.rb | 32 - scripts/fixnaked.lua | 50 - scripts/forum-dwarves.lua | 133 -- scripts/full-heal.lua | 153 -- scripts/gaydar.lua | 208 -- scripts/growcrops.rb | 64 - scripts/gui/about.txt | 6 - scripts/gui/advfort.lua | 1894 ---------------- scripts/gui/advfort_items.lua | 226 -- scripts/gui/assign-rack.lua | 209 -- scripts/gui/autobutcher.lua | 662 ------ scripts/gui/choose-weapons.lua | 169 -- scripts/gui/clone-uniform.lua | 58 - scripts/gui/companion-order.lua | 490 ----- scripts/gui/confirm-opts.lua | 74 - scripts/gui/create-item.lua | 260 --- scripts/gui/dfstatus.lua | 229 -- scripts/gui/family-affairs.lua | 292 --- scripts/gui/gm-editor.lua | 536 ----- scripts/gui/gm-unit.lua | 604 ----- scripts/gui/guide-path.lua | 205 -- scripts/gui/hack-wish.lua | 8 - scripts/gui/hello-world.lua | 31 - scripts/gui/liquids.lua | 326 --- scripts/gui/mechanisms.lua | 146 -- scripts/gui/mod-manager.lua | 361 --- scripts/gui/no-dfhack-init.lua | 37 - scripts/gui/power-meter.lua | 135 -- scripts/gui/prerelease-warning.lua | 141 -- scripts/gui/rename.lua | 93 - scripts/gui/room-list.lua | 259 --- scripts/gui/siege-engine.lua | 536 ----- scripts/gui/stockpiles.lua | 78 - scripts/gui/unit-info-viewer.lua | 800 ------- scripts/gui/workflow.lua | 1099 ---------- scripts/gui/workshop-job.lua | 332 --- scripts/hfs-pit.lua | 105 - scripts/hotkey-notes.lua | 16 - scripts/item-descriptions.lua | 663 ------ scripts/lever.rb | 152 -- scripts/locate-ore.rb | 102 - scripts/lua.lua | 91 - scripts/make-legendary.lua | 141 -- scripts/make-monarch.lua | 39 - scripts/markdown.lua | 239 -- scripts/masspit.rb | 51 - scripts/migrants-now.lua | 16 - scripts/modtools/about.txt | 17 - scripts/modtools/add-syndrome.lua | 114 - scripts/modtools/anonymous-script.lua | 27 - scripts/modtools/create-item.lua | 140 -- scripts/modtools/create-unit.lua | 575 ----- scripts/modtools/equip-item.lua | 105 - scripts/modtools/extra-gamelog.lua | 192 -- scripts/modtools/force.lua | 90 - scripts/modtools/interaction-trigger.lua | 176 -- scripts/modtools/invader-item-destroyer.lua | 184 -- scripts/modtools/item-trigger.lua | 294 --- scripts/modtools/moddable-gods.lua | 102 - scripts/modtools/outside-only.lua | 141 -- scripts/modtools/projectile-trigger.lua | 108 - scripts/modtools/random-trigger.lua | 179 -- scripts/modtools/reaction-product-trigger.lua | 132 -- .../modtools/reaction-trigger-transition.lua | 147 -- scripts/modtools/reaction-trigger.lua | 241 -- scripts/modtools/skill-change.lua | 115 - scripts/modtools/spawn-flow.lua | 91 - scripts/modtools/syndrome-trigger.lua | 122 -- scripts/modtools/transform-unit.lua | 164 -- scripts/multicmd.rb | 16 - scripts/points.lua | 13 - scripts/position.lua | 51 - scripts/pref-adjust.lua | 106 - scripts/putontable.lua | 36 - scripts/quicksave.lua | 39 - scripts/region-pops.lua | 199 -- scripts/rejuvenate.lua | 30 - scripts/remove-stress.lua | 41 - scripts/remove-wear.lua | 62 - scripts/repeat.lua | 85 - scripts/setfps.lua | 18 - scripts/show-unit-syndromes.rb | 1035 --------- scripts/siren.lua | 136 -- scripts/source.rb | 114 - scripts/spawnunit.lua | 65 - scripts/startdwarf.rb | 20 - scripts/starvingdead.rb | 92 - scripts/stripcaged.rb | 227 -- scripts/superdwarf.rb | 91 - scripts/teleport.lua | 58 - scripts/tidlers.lua | 10 - scripts/twaterlvl.lua | 11 - scripts/undump-buildings.lua | 36 - scripts/unsuspend.rb | 28 - scripts/view-item-info.lua | 407 ---- scripts/warn-starving.lua | 140 -- scripts/weather.lua | 46 - 172 files changed, 28178 deletions(-) delete mode 160000 scripts/3rdparty/dscorbett delete mode 160000 scripts/3rdparty/kane-t delete mode 160000 scripts/3rdparty/lethosor delete mode 160000 scripts/3rdparty/maienm delete mode 160000 scripts/3rdparty/maxthyme delete mode 160000 scripts/3rdparty/roses delete mode 100644 scripts/CMakeLists.txt delete mode 100644 scripts/Scripts.cmake delete mode 100644 scripts/about.txt delete mode 100644 scripts/adaptation.rb delete mode 100644 scripts/add-thought.lua delete mode 100644 scripts/armoks-blessing.lua delete mode 100644 scripts/autofarm.rb delete mode 100644 scripts/autolabor-artisans.lua delete mode 100644 scripts/autounsuspend.rb delete mode 100644 scripts/ban-cooking.rb delete mode 100644 scripts/binpatch.lua delete mode 100644 scripts/brainwash.lua delete mode 100644 scripts/burial.lua delete mode 100644 scripts/catsplosion.lua delete mode 100644 scripts/colonies.lua delete mode 100644 scripts/create-items.rb delete mode 100644 scripts/deathcause.rb delete mode 100644 scripts/deteriorateclothes.rb delete mode 100644 scripts/deterioratecorpses.rb delete mode 100644 scripts/deterioratefood.rb delete mode 100644 scripts/devel/about.txt delete mode 100644 scripts/devel/all-bob.lua delete mode 100644 scripts/devel/check-release.lua delete mode 100644 scripts/devel/clear-script-env.lua delete mode 100644 scripts/devel/cmptiles.lua delete mode 100644 scripts/devel/export-dt-ini.lua delete mode 100644 scripts/devel/find-offsets.lua delete mode 100644 scripts/devel/inject-raws.lua delete mode 100644 scripts/devel/inspect-screen.lua delete mode 100644 scripts/devel/light.lua delete mode 100644 scripts/devel/list-filters.lua delete mode 100644 scripts/devel/lsmem.lua delete mode 100644 scripts/devel/lua-example.lua delete mode 100644 scripts/devel/make-dt.pl delete mode 100644 scripts/devel/nuke-items.lua delete mode 100644 scripts/devel/pop-screen.lua delete mode 100644 scripts/devel/prepare-save.lua delete mode 100644 scripts/devel/print-args.lua delete mode 100644 scripts/devel/print-args2.lua delete mode 100644 scripts/devel/save-version.lua delete mode 100644 scripts/devel/scanitemother.rb delete mode 100644 scripts/devel/spawn-unit-helper.rb delete mode 100644 scripts/devel/test-perlin.lua delete mode 100644 scripts/devel/unforbidall.rb delete mode 100644 scripts/devel/unit-path.lua delete mode 100644 scripts/devel/watch-minecarts.lua delete mode 100644 scripts/digfort.rb delete mode 100644 scripts/drain-aquifer.lua delete mode 100644 scripts/elevate-mental.lua delete mode 100644 scripts/elevate-physical.lua delete mode 100644 scripts/emigration.lua delete mode 100644 scripts/exportlegends.lua delete mode 100644 scripts/exterminate.rb delete mode 100644 scripts/feature.lua delete mode 100644 scripts/fix-ster.lua delete mode 100644 scripts/fix/about.txt delete mode 100644 scripts/fix/blood-del.lua delete mode 100644 scripts/fix/build-location.lua delete mode 100644 scripts/fix/dead-units.lua delete mode 100644 scripts/fix/diplomats.lua delete mode 100644 scripts/fix/dry-buckets.lua delete mode 100644 scripts/fix/fat-dwarves.lua delete mode 100644 scripts/fix/feeding-timers.lua delete mode 100644 scripts/fix/item-occupancy.lua delete mode 100644 scripts/fix/loyaltycascade.rb delete mode 100644 scripts/fix/merchants.lua delete mode 100644 scripts/fix/population-cap.lua delete mode 100644 scripts/fix/stable-temp.lua delete mode 100644 scripts/fix/stuckdoors.rb delete mode 100644 scripts/fixnaked.lua delete mode 100644 scripts/forum-dwarves.lua delete mode 100644 scripts/full-heal.lua delete mode 100644 scripts/gaydar.lua delete mode 100644 scripts/growcrops.rb delete mode 100644 scripts/gui/about.txt delete mode 100644 scripts/gui/advfort.lua delete mode 100644 scripts/gui/advfort_items.lua delete mode 100644 scripts/gui/assign-rack.lua delete mode 100644 scripts/gui/autobutcher.lua delete mode 100644 scripts/gui/choose-weapons.lua delete mode 100644 scripts/gui/clone-uniform.lua delete mode 100644 scripts/gui/companion-order.lua delete mode 100644 scripts/gui/confirm-opts.lua delete mode 100644 scripts/gui/create-item.lua delete mode 100644 scripts/gui/dfstatus.lua delete mode 100644 scripts/gui/family-affairs.lua delete mode 100644 scripts/gui/gm-editor.lua delete mode 100644 scripts/gui/gm-unit.lua delete mode 100644 scripts/gui/guide-path.lua delete mode 100644 scripts/gui/hack-wish.lua delete mode 100644 scripts/gui/hello-world.lua delete mode 100644 scripts/gui/liquids.lua delete mode 100644 scripts/gui/mechanisms.lua delete mode 100644 scripts/gui/mod-manager.lua delete mode 100644 scripts/gui/no-dfhack-init.lua delete mode 100644 scripts/gui/power-meter.lua delete mode 100644 scripts/gui/prerelease-warning.lua delete mode 100644 scripts/gui/rename.lua delete mode 100644 scripts/gui/room-list.lua delete mode 100644 scripts/gui/siege-engine.lua delete mode 100644 scripts/gui/stockpiles.lua delete mode 100644 scripts/gui/unit-info-viewer.lua delete mode 100644 scripts/gui/workflow.lua delete mode 100644 scripts/gui/workshop-job.lua delete mode 100644 scripts/hfs-pit.lua delete mode 100644 scripts/hotkey-notes.lua delete mode 100644 scripts/item-descriptions.lua delete mode 100644 scripts/lever.rb delete mode 100644 scripts/locate-ore.rb delete mode 100644 scripts/lua.lua delete mode 100644 scripts/make-legendary.lua delete mode 100644 scripts/make-monarch.lua delete mode 100644 scripts/markdown.lua delete mode 100644 scripts/masspit.rb delete mode 100644 scripts/migrants-now.lua delete mode 100644 scripts/modtools/about.txt delete mode 100644 scripts/modtools/add-syndrome.lua delete mode 100644 scripts/modtools/anonymous-script.lua delete mode 100644 scripts/modtools/create-item.lua delete mode 100644 scripts/modtools/create-unit.lua delete mode 100644 scripts/modtools/equip-item.lua delete mode 100644 scripts/modtools/extra-gamelog.lua delete mode 100644 scripts/modtools/force.lua delete mode 100644 scripts/modtools/interaction-trigger.lua delete mode 100644 scripts/modtools/invader-item-destroyer.lua delete mode 100644 scripts/modtools/item-trigger.lua delete mode 100644 scripts/modtools/moddable-gods.lua delete mode 100644 scripts/modtools/outside-only.lua delete mode 100644 scripts/modtools/projectile-trigger.lua delete mode 100644 scripts/modtools/random-trigger.lua delete mode 100644 scripts/modtools/reaction-product-trigger.lua delete mode 100644 scripts/modtools/reaction-trigger-transition.lua delete mode 100644 scripts/modtools/reaction-trigger.lua delete mode 100644 scripts/modtools/skill-change.lua delete mode 100644 scripts/modtools/spawn-flow.lua delete mode 100644 scripts/modtools/syndrome-trigger.lua delete mode 100644 scripts/modtools/transform-unit.lua delete mode 100644 scripts/multicmd.rb delete mode 100644 scripts/points.lua delete mode 100644 scripts/position.lua delete mode 100644 scripts/pref-adjust.lua delete mode 100644 scripts/putontable.lua delete mode 100644 scripts/quicksave.lua delete mode 100644 scripts/region-pops.lua delete mode 100644 scripts/rejuvenate.lua delete mode 100644 scripts/remove-stress.lua delete mode 100644 scripts/remove-wear.lua delete mode 100644 scripts/repeat.lua delete mode 100644 scripts/setfps.lua delete mode 100644 scripts/show-unit-syndromes.rb delete mode 100644 scripts/siren.lua delete mode 100644 scripts/source.rb delete mode 100644 scripts/spawnunit.lua delete mode 100644 scripts/startdwarf.rb delete mode 100644 scripts/starvingdead.rb delete mode 100644 scripts/stripcaged.rb delete mode 100644 scripts/superdwarf.rb delete mode 100644 scripts/teleport.lua delete mode 100644 scripts/tidlers.lua delete mode 100644 scripts/twaterlvl.lua delete mode 100644 scripts/undump-buildings.lua delete mode 100644 scripts/unsuspend.rb delete mode 100644 scripts/view-item-info.lua delete mode 100644 scripts/warn-starving.lua delete mode 100644 scripts/weather.lua diff --git a/.gitmodules b/.gitmodules index 6085402ac..9f1b48395 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,21 +10,3 @@ [submodule "depends/clsocket"] path = depends/clsocket url = git://github.com/DFHack/clsocket.git -[submodule "scripts/3rdparty/lethosor"] - path = scripts/3rdparty/lethosor - url = git://github.com/DFHack/lethosor-scripts -[submodule "scripts/3rdparty/roses"] - path = scripts/3rdparty/roses - url = git://github.com/DFHack/roses-scripts.git -[submodule "scripts/3rdparty/maxthyme"] - path = scripts/3rdparty/maxthyme - url = git://github.com/DFHack/maxthyme-scripts.git -[submodule "scripts/3rdparty/dscorbett"] - path = scripts/3rdparty/dscorbett - url = git://github.com/DFHack/dscorbett-scripts.git -[submodule "scripts/3rdparty/kane-t"] - path = scripts/3rdparty/kane-t - url = git://github.com/DFHack/kane-t-scripts.git -[submodule "scripts/3rdparty/maienm"] - path = scripts/3rdparty/maienm - url = git://github.com/DFHack/maienm-scripts.git diff --git a/scripts/3rdparty/dscorbett b/scripts/3rdparty/dscorbett deleted file mode 160000 index 4353c1040..000000000 --- a/scripts/3rdparty/dscorbett +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4353c10401ced7aec89f002947d1252f30237789 diff --git a/scripts/3rdparty/kane-t b/scripts/3rdparty/kane-t deleted file mode 160000 index 0a75d5ff6..000000000 --- a/scripts/3rdparty/kane-t +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0a75d5ff69916cf9b3739f4b20d36ab4cfdcf824 diff --git a/scripts/3rdparty/lethosor b/scripts/3rdparty/lethosor deleted file mode 160000 index 704aed444..000000000 --- a/scripts/3rdparty/lethosor +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 704aed4447f27ae802dae6994479ebc9c46568cc diff --git a/scripts/3rdparty/maienm b/scripts/3rdparty/maienm deleted file mode 160000 index 45c78449e..000000000 --- a/scripts/3rdparty/maienm +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 45c78449e71d1ba263044fb00108509088ad0026 diff --git a/scripts/3rdparty/maxthyme b/scripts/3rdparty/maxthyme deleted file mode 160000 index b337e931b..000000000 --- a/scripts/3rdparty/maxthyme +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b337e931b8b7a167ee5ce1ac6b5c3155c291f260 diff --git a/scripts/3rdparty/roses b/scripts/3rdparty/roses deleted file mode 160000 index 4b6e77265..000000000 --- a/scripts/3rdparty/roses +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4b6e772654df6805b66f77900a4618bbf9b54dab diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt deleted file mode 100644 index e9d899970..000000000 --- a/scripts/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -include(Scripts.cmake) -DFHACK_3RDPARTY_SCRIPT_REPO(dscorbett) -DFHACK_3RDPARTY_SCRIPT_REPO(kane-t) -DFHACK_3RDPARTY_SCRIPT_REPO(lethosor) -DFHACK_3RDPARTY_SCRIPT_REPO(maienm) -DFHACK_3RDPARTY_SCRIPT_REPO(maxthyme) -# DFHACK_3RDPARTY_SCRIPT_REPO(roses) - -install(DIRECTORY ${dfhack_SOURCE_DIR}/scripts - DESTINATION ${DFHACK_DATA_DESTINATION} - FILES_MATCHING PATTERN "*.lua" - PATTERN "*.rb" - PATTERN "3rdparty" EXCLUDE - ) diff --git a/scripts/Scripts.cmake b/scripts/Scripts.cmake deleted file mode 100644 index 3e7de5424..000000000 --- a/scripts/Scripts.cmake +++ /dev/null @@ -1,19 +0,0 @@ -include(../plugins/Plugins.cmake) - -MACRO(DFHACK_SCRIPTS) - PARSE_ARGUMENTS(SCRIPT - "SUBDIRECTORY" - "SOME_OPT" - ${ARGN} - ) - CAR(SCRIPT_SUBDIRECTORY ${SCRIPT_SUBDIRECTORY}) - install(FILES ${SCRIPT_DEFAULT_ARGS} - DESTINATION ${DFHACK_DATA_DESTINATION}/scripts/${SCRIPT_SUBDIRECTORY}) -ENDMACRO() - -MACRO(DFHACK_3RDPARTY_SCRIPT_REPO repo_path) - if(NOT EXISTS ${dfhack_SOURCE_DIR}/scripts/3rdparty/${repo_path}/CMakeLists.txt) - MESSAGE(SEND_ERROR "Script submodule scripts/3rdparty/${repo_path} does not exist - run `git submodule update --init`.") - endif() - add_subdirectory(3rdparty/${repo_path}) -ENDMACRO() diff --git a/scripts/about.txt b/scripts/about.txt deleted file mode 100644 index 818891af5..000000000 --- a/scripts/about.txt +++ /dev/null @@ -1,2 +0,0 @@ -Basic scripts are not stored in any subdirectory, and can be invoked directly. -They are generally useful tools for any player. diff --git a/scripts/adaptation.rb b/scripts/adaptation.rb deleted file mode 100644 index 50cf80a86..000000000 --- a/scripts/adaptation.rb +++ /dev/null @@ -1,104 +0,0 @@ -# View or set cavern adaptation levels -# based on removebadthoughts.rb -=begin - -adaptation -========== -View or set level of cavern adaptation for the selected unit or the whole fort. -Usage: ``adaptation [value]``. The ``value`` must be -between 0 and 800,000 inclusive. - -=end - -# Color constants, values mapped to color_value enum in include/ColorText.h -COLOR_GREEN = 2 -COLOR_RED = 4 -COLOR_YELLOW = 14 -COLOR_WHITE = 15 - -def usage(s) - if nil != s - puts(s) - end - puts "Usage: adaptation [value]" - throw :script_finished -end - -mode = $script_args[0] || 'help' -who = $script_args[1] -value = $script_args[2] - -if 'help' == mode - usage(nil) -elsif 'show' != mode && 'set' != mode - usage("Invalid mode '#{mode}': must be either 'show' or 'set'") -end - -if nil == who - usage("Target not specified") -elsif 'him' != who && 'all' != who - usage("Invalid target '#{who}'") -end - -if 'set' == mode - if nil == value - usage("Value not specified") - elsif !/[[:digit:]]/.match(value) - usage("Invalid value '#{value}'") - end - - if 0 > value.to_i || 800000 < value.to_i - usage("Value must be between 0 and 800000") - end - value = value.to_i -end - -num_set = 0 - -set_adaptation_value = lambda { |u,v| - next if !df.unit_iscitizen(u) - next if u.flags1.dead - u.status.misc_traits.each { |t| - if t.id == :CaveAdapt - # TBD: expose the color_ostream console and color values of - # t.value based on adaptation level - if mode == 'show' - if df.respond_to?(:print_color) - print "Unit #{u.id} (#{u.name}) has an adaptation of " - case t.value - when 0..399999 - #df.print_color(COLOR_GREEN, "#{t.value}\n") - print "#{t.value}\n" - when 400000..599999 - df.print_color(COLOR_YELLOW, "#{t.value}\n") - else - df.print_color(COLOR_RED, "#{t.value}\n") - end - else - puts "Unit #{u.id} (#{u.name}) has an adaptation of #{t.value}" - end - elsif mode == 'set' - puts "Unit #{u.id} (#{u.name}) changed from #{t.value} to #{v}" - t.value = v - num_set += 1 - end - end - } -} - -case who -when 'him' - if u = df.unit_find - set_adaptation_value[u,value] - else - puts 'Please select a dwarf ingame' - end -when 'all' - df.unit_citizens.each { |uu| - set_adaptation_value[uu,value] - } -end - -if 'set' == mode - puts "#{num_set} unit#{'s' if num_set != 1} updated." -end diff --git a/scripts/add-thought.lua b/scripts/add-thought.lua deleted file mode 100644 index 0f82b555c..000000000 --- a/scripts/add-thought.lua +++ /dev/null @@ -1,93 +0,0 @@ --- Adds emotions to creatures. ---@ module = true - ---[[=begin - -add-thought -=========== -Adds a thought or emotion to the selected unit. Can be used by other scripts, -or the gui invoked by running ``add-thought gui`` with a unit selected. - -=end]] - -local utils=require('utils') - -function addEmotionToUnit(unit,thought,emotion,severity,strength,subthought) - local emotions=unit.status.current_soul.personality.emotions - if not (type(emotion)=='number') then emotion=df.emotion_type[emotion] end - if not (type(thought)=='number') then thought=df.unit_thought_type[thought] end - emotions:insert('#',{new=df.unit_personality.T_emotions, - type=emotion, - unk2=1, - strength=strength, - thought=thought, - subthought=subthought, - severity=severity, - flags=0, - unk7=0, - year=df.global.cur_year, - year_tick=df.global.cur_year_tick - }) - local divider=df.emotion_type.attrs[emotion].divider - if divider~=0 then - unit.status.current_soul.personality.stress_level=unit.status.current_soul.personality.stress_level+math.ceil(severity/df.emotion_type.attrs[emotion].divider) - end -end - -validArgs = validArgs or utils.invert({ - 'unit', - 'thought', - 'emotion', - 'severity', - 'strength', - 'subthought', - 'gui' -}) - -function tablify(iterableObject) - t={} - for k,v in ipairs(iterableObject) do - t[k] = v~=nil and v or 'nil' - end - return t -end - -if moduleMode then - return -end - -local args = utils.processArgs({...}, validArgs) - -local unit = args.unit and df.unit.find(args.unit) or dfhack.gui.getSelectedUnit(true) - -if not unit then qerror('A unit must be specified or selected.') end -if args.gui then - local script=require('gui.script') - script.start(function() - local tok,thought=script.showListPrompt('emotions','Which thought?',COLOR_WHITE,tablify(df.unit_thought_type),10,true) - if tok then - local eok,emotion=script.showListPrompt('emotions','Which emotion?',COLOR_WHITE,tablify(df.emotion_type),10,true) - if eok then - local sok,severity=script.showInputPrompt('emotions','At what severity?',COLOR_WHITE,'0') - if sok then - local stok,strength=script.showInputPrompt('emotions','At what strength?',COLOR_WHITE,'0') - if stok then - addEmotionToUnit(unit,thought,emotion,severity,strength,0) - end - end - end - end - end) -else - local thought = args.thought or 180 - - local emotion = args.emotion or -1 - - local severity = args.severity or 0 - - local subthought = args.subthought or 0 - - local strength = args.strength or 0 - - addEmotionToUnit(unit,thought,emotion,severity,strength,subthought) -end diff --git a/scripts/armoks-blessing.lua b/scripts/armoks-blessing.lua deleted file mode 100644 index dfa99daad..000000000 --- a/scripts/armoks-blessing.lua +++ /dev/null @@ -1,202 +0,0 @@ --- Adjust all attributes of all dwarves to an ideal --- by vjek ---[[=begin - -armoks-blessing -=============== -Runs the equivalent of `rejuvenate`, `elevate-physical`, `elevate-mental`, and -`brainwash` on all dwarves currently on the map. This is an extreme change, -which sets every stat to an ideal - legendary skills, great traits, and -easy-to-satisfy preferences. - -Without arguments, all attributes, age & personalities are adjusted. -Arguments allow for skills to be adjusted as well. - -=end]] -function rejuvenate(unit) - if unit==nil then - print ("No unit available! Aborting with extreme prejudice.") - return - end - - local current_year=df.global.cur_year - local newbirthyear=current_year - 20 - if unit.relations.birth_year < newbirthyear then - unit.relations.birth_year=newbirthyear - end - if unit.relations.old_year < current_year+100 then - unit.relations.old_year=current_year+100 - end - -end --- --------------------------------------------------------------------------- -function brainwash_unit(unit) - if unit==nil then - print ("No unit available! Aborting with extreme prejudice.") - return - end - - local profile ={75,25,25,75,25,25,25,99,25,25,25,50,75,50,25,75,75,50,75,75,25,75,75,50,75,25,50,25,75,75,75,25,75,75,25,75,25,25,75,75,25,75,75,75,25,75,75,25,25,50} - local i - - for i=1, #profile do - unit.status.current_soul.personality.traits[i-1]=profile[i] - end - -end --- --------------------------------------------------------------------------- -function elevate_attributes(unit) - if unit==nil then - print ("No unit available! Aborting with extreme prejudice.") - return - end - - local ok,f,t,k = pcall(pairs,unit.status.current_soul.mental_attrs) - if ok then - for k,v in f,t,k do - v.value=v.max_value - end - end - - local ok,f,t,k = pcall(pairs,unit.body.physical_attrs) - if ok then - for k,v in f,t,k do - v.value=v.max_value - end - end -end --- --------------------------------------------------------------------------- --- this function will return the number of elements, starting at zero. --- useful for counting things where #foo doesn't work -function count_this(to_be_counted) - local count = -1 - local var1 = "" - while var1 ~= nil do - count = count + 1 - var1 = (to_be_counted[count]) - end - count=count-1 - return count -end --- --------------------------------------------------------------------------- -function make_legendary(skillname,unit) - local skillnamenoun,skillnum - - if unit==nil then - print ("No unit available! Aborting with extreme prejudice.") - return - end - - if (df.job_skill[skillname]) then - skillnamenoun = df.job_skill.attrs[df.job_skill[skillname]].caption_noun - else - print ("The skill name provided is not in the list.") - return - end - - if skillnamenoun ~= nil then - utils = require 'utils' - skillnum = df.job_skill[skillname] - utils.insert_or_update(unit.status.current_soul.skills, { new = true, id = skillnum, rating = 20 }, 'id') - print (unit.name.first_name.." is now a Legendary "..skillnamenoun) - else - print ("Empty skill name noun, bailing out!") - return - end -end --- --------------------------------------------------------------------------- -function BreathOfArmok(unit) - - if unit==nil then - print ("No unit available! Aborting with extreme prejudice.") - return - end - local i - - local count_max = count_this(df.job_skill) - utils = require 'utils' - for i=0, count_max do - utils.insert_or_update(unit.status.current_soul.skills, { new = true, id = i, rating = 20 }, 'id') - end - print ("The breath of Armok has engulfed "..unit.name.first_name) -end --- --------------------------------------------------------------------------- -function LegendaryByClass(skilltype,v) - unit=v - if unit==nil then - print ("No unit available! Aborting with extreme prejudice.") - return - end - - utils = require 'utils' - local i - local skillclass - local count_max = count_this(df.job_skill) - for i=0, count_max do - skillclass = df.job_skill_class[df.job_skill.attrs[i].type] - if skilltype == skillclass then - print ("Skill "..df.job_skill.attrs[i].caption.." is type: "..skillclass.." and is now Legendary for "..unit.name.first_name) - utils.insert_or_update(unit.status.current_soul.skills, { new = true, id = i, rating = 20 }, 'id') - end - end -end --- --------------------------------------------------------------------------- -function PrintSkillList() - local count_max = count_this(df.job_skill) - local i - for i=0, count_max do - print("'"..df.job_skill.attrs[i].caption.."' "..df.job_skill[i].." Type: "..df.job_skill_class[df.job_skill.attrs[i].type]) - end - print ("Provide the UPPER CASE argument, for example: PROCESSPLANTS rather than Threshing") -end --- --------------------------------------------------------------------------- -function PrintSkillClassList() - local i - local count_max = count_this(df.job_skill_class) - for i=0, count_max do - print(df.job_skill_class[i]) - end - print ("Provide one of these arguments, and all skills of that type will be made Legendary") - print ("For example: Medical will make all medical skills legendary") -end --- --------------------------------------------------------------------------- -function adjust_all_dwarves(skillname) - for _,v in ipairs(df.global.world.units.all) do - if v.race == df.global.ui.race_id then - print("Adjusting "..dfhack.TranslateName(dfhack.units.getVisibleName(v))) - brainwash_unit(v) - elevate_attributes(v) - rejuvenate(v) - if skillname then - if skillname=="Normal" or skillname=="Medical" or skillname=="Personal" or skillname=="Social" or skillname=="Cultural" or skillname=="MilitaryWeapon" or skillname=="MilitaryAttack" or skillname=="MilitaryDefense" or skillname=="MilitaryMisc" then - LegendaryByClass(skillname,v) - elseif skillname=="all" then - BreathOfArmok(v) - else - make_legendary(skillname,v) - end - end - end - end -end --- --------------------------------------------------------------------------- --- main script operation starts here --- --------------------------------------------------------------------------- -local opt = ... -local skillname - -if opt then - if opt=="list" then - PrintSkillList() - return - end - if opt=="classes" then - PrintSkillClassList() - return - end - skillname = opt -else - print ("No skillname supplied, no skills will be adjusted. Pass argument 'list' to see a skill list, 'classes' to show skill classes, or use 'all' if you want all skills legendary.") -end - -adjust_all_dwarves(skillname) diff --git a/scripts/autofarm.rb b/scripts/autofarm.rb deleted file mode 100644 index 1bf07c599..000000000 --- a/scripts/autofarm.rb +++ /dev/null @@ -1,192 +0,0 @@ -# Select crops to plant based on current stocks -=begin - -autofarm -======== -Automatically handle crop selection in farm plots based on current plant stocks. -Selects a crop for planting if current stock is below a threshold. -Selected crops are dispatched on all farmplots. - -Usage:: - - autofarm start - autofarm default 30 - autofarm threshold 150 helmet_plump tail_pig - -=end -class AutoFarm - - def initialize - @thresholds = Hash.new(50) - @lastcounts = Hash.new(0) - end - - def setthreshold(id, v) - list = df.world.raws.plants.all.find_all { |plt| plt.flags[:SEED] }.map { |plt| plt.id } - if tok = df.match_rawname(id, list) - @thresholds[tok] = v.to_i - else - puts "No plant with id #{id}, try one of " + - list.map { |w| w =~ /[^\w]/ ? w.inspect : w }.sort.join(' ') - end - end - - def setdefault(v) - @thresholds.default = v.to_i - end - - def is_plantable(plant) - has_seed = plant.flags[:SEED] - season = df.cur_season - harvest = df.cur_season_tick + plant.growdur * 10 - will_finish = harvest < 10080 - can_plant = has_seed && plant.flags[season] - can_plant = can_plant && (will_finish || plant.flags[(season+1)%4]) - can_plant - end - - def find_plantable_plants - plantable = {} - counts = Hash.new(0) - - df.world.items.other[:SEEDS].each { |i| - if (!i.flags.dump && !i.flags.forbid && !i.flags.garbage_collect && - !i.flags.hostile && !i.flags.on_fire && !i.flags.rotten && - !i.flags.trader && !i.flags.in_building && !i.flags.construction && - !i.flags.artifact) - counts[i.mat_index] += i.stack_size - end - } - - counts.keys.each { |i| - if df.ui.tasks.discovered_plants[i] - plant = df.world.raws.plants.all[i] - if is_plantable(plant) - plantable[i] = :Surface if (plant.underground_depth_min == 0 || plant.underground_depth_max == 0) - plantable[i] = :Underground if (plant.underground_depth_min > 0 || plant.underground_depth_max > 0) - end - end - } - - return plantable - end - - def set_farms(plants, farms) - return if farms.length == 0 - if plants.length == 0 - plants = [-1] - end - - season = df.cur_season - - farms.each_with_index { |f, idx| - f.plant_id[season] = plants[idx % plants.length] - } - end - - def process - plantable = find_plantable_plants - @lastcounts = Hash.new(0) - - df.world.items.other[:PLANT].each { |i| - if (!i.flags.dump && !i.flags.forbid && !i.flags.garbage_collect && - !i.flags.hostile && !i.flags.on_fire && !i.flags.rotten && - !i.flags.trader && !i.flags.in_building && !i.flags.construction && - !i.flags.artifact && plantable.has_key?(i.mat_index)) - id = df.world.raws.plants.all[i.mat_index].id - @lastcounts[id] += i.stack_size - end - } - - return unless @running - - plants_s = [] - plants_u = [] - - plantable.each_key { |k| - plant = df.world.raws.plants.all[k] - if (@lastcounts[plant.id] < @thresholds[plant.id]) - plants_s.push(k) if plantable[k] == :Surface - plants_u.push(k) if plantable[k] == :Underground - end - } - - farms_s = [] - farms_u = [] - df.world.buildings.other[:FARM_PLOT].each { |f| - if (f.flags.exists) - underground = df.map_designation_at(f.centerx,f.centery,f.z).subterranean - farms_s.push(f) unless underground - farms_u.push(f) if underground - end - } - - set_farms(plants_s, farms_s) - set_farms(plants_u, farms_u) - end - - def start - return if @running - @onupdate = df.onupdate_register('autofarm', 1200) { process } - @running = true - end - - def stop - df.onupdate_unregister(@onupdate) - @running = false - end - - def status - stat = @running ? "Running." : "Stopped." - @lastcounts.each { |k,v| - stat << "\n#{k} limit #{@thresholds.fetch(k, 'default')} current #{v}" - } - @thresholds.each { |k,v| - stat << "\n#{k} limit #{v} current 0" unless @lastcounts.has_key?(k) - } - stat << "\nDefault: #{@thresholds.default}" - stat - end - -end - -$AutoFarm ||= AutoFarm.new - -case $script_args[0] -when 'start', 'enable' - $AutoFarm.start - puts $AutoFarm.status - -when 'end', 'stop', 'disable' - $AutoFarm.stop - puts 'Stopped.' - -when 'default' - $AutoFarm.setdefault($script_args[1]) - -when 'threshold' - t = $script_args[1] - $script_args[2..-1].each {|i| - $AutoFarm.setthreshold(i, t) - } - -when 'delete' - $AutoFarm.stop - $AutoFarm = nil - -when 'help', '?' - puts < 0 - puts "Unsuspended #{count} job(s)." - df.process_jobs = true - end - end - - def start - @running = true - @onupdate = df.onupdate_register('autounsuspend', 5) { process if @running } - end - - def stop - @running = false - df.onupdate_unregister(@onupdate) - end -end - -case $script_args[0] -when 'start' - $AutoUnsuspend ||= AutoUnsuspend.new - $AutoUnsuspend.start - -when 'end', 'stop' - $AutoUnsuspend.stop - -else - puts $AutoUnsuspend && $AutoUnsuspend.running ? 'Running.' : 'Stopped.' -end diff --git a/scripts/ban-cooking.rb b/scripts/ban-cooking.rb deleted file mode 100644 index 535f39126..000000000 --- a/scripts/ban-cooking.rb +++ /dev/null @@ -1,132 +0,0 @@ -# convenient way to ban cooking categories of food -=begin - -ban-cooking -=========== -A more convenient way to ban cooking various categories of foods than the -kitchen interface. Usage: ``ban-cooking ``. Valid types are ``booze``, -``honey``, ``tallow``, ``oil``, ``seeds`` (non-tree plants with seeds), -``brew``, ``mill``, ``thread``, and ``milk``. - -=end - -already_banned = {} -kitchen = df.ui.kitchen -kitchen.item_types.length.times { |i| - already_banned[[kitchen.mat_types[i], kitchen.mat_indices[i], kitchen.item_types[i], kitchen.item_subtypes[i]]] = kitchen.exc_types[i] & 1 -} -ban_cooking = lambda { |mat_type, mat_index, type| - subtype = -1 - key = [mat_type, mat_index, type, subtype] - if already_banned[key] - next if already_banned[key] == 1 - - index = kitchen.mat_types.zip(kitchen.mat_indices, kitchen.item_types, kitchen.item_subtypes) - kitchen.exc_types[index] |= 1 - already_banned[key] = 1 - next - end - df.ui.kitchen.mat_types << mat_type - df.ui.kitchen.mat_indices << mat_index - df.ui.kitchen.item_types << type - df.ui.kitchen.item_subtypes << subtype - df.ui.kitchen.exc_types << 1 - already_banned[key] = 1 -} - -$script_args.each do |arg| - case arg - when 'booze' - df.world.raws.plants.all.each_with_index do |p, i| - p.material.each_with_index do |m, j| - if m.flags[:ALCOHOL] - ban_cooking[j + DFHack::MaterialInfo::PLANT_BASE, i, :DRINK] - end - end - end - df.world.raws.creatures.all.each_with_index do |c, i| - c.material.each_with_index do |m, j| - if m.flags[:ALCOHOL] - ban_cooking[j + DFHack::MaterialInfo::CREATURE_BASE, i, :DRINK] - end - end - end - - when 'honey' - # hard-coded in the raws of the mead reaction - honey = df.decode_mat('CREATURE:HONEY_BEE:HONEY') - ban_cooking[honey.mat_type, honey.mat_index, :LIQUID_MISC] - - when 'tallow' - df.world.raws.creatures.all.each_with_index do |c, i| - c.material.each_with_index do |m, j| - if m.reaction_product and m.reaction_product.id and m.reaction_product.id.include?('SOAP_MAT') - ban_cooking[j + DFHack::MaterialInfo::CREATURE_BASE, i, :GLOB] - end - end - end - - when 'oil' - df.world.raws.plants.all.each_with_index do |p, i| - p.material.each_with_index do |m, j| - if m.reaction_product and m.reaction_product.id and m.reaction_product.id.include?('SOAP_MAT') - ban_cooking[j + DFHack::MaterialInfo::PLANT_BASE, i, :LIQUID_MISC] - end - end - end - - when 'seeds' - df.world.raws.plants.all.each do |p| - m = df.decode_mat(p.material_defs.type_basic_mat, p.material_defs.idx_basic_mat).material - ban_cooking[p.material_defs.type_basic_mat, p.material_defs.idx_basic_mat, :PLANT] if m.reaction_product and m.reaction_product.id and m.reaction_product.id.include?('SEED_MAT') - - if not p.flags[:TREE] - p.growths.each do |g| - m = df.decode_mat(g).material - ban_cooking[g.mat_type, g.mat_index, :PLANT_GROWTH] if m.reaction_product and m.reaction_product.id and m.reaction_product.id.include?('SEED_MAT') - end - end - end - - when 'brew' - df.world.raws.plants.all.each do |p| - m = df.decode_mat(p.material_defs.type_basic_mat, p.material_defs.idx_basic_mat).material - ban_cooking[p.material_defs.type_basic_mat, p.material_defs.idx_basic_mat, :PLANT] if m.reaction_product and m.reaction_product.id and m.reaction_product.id.include?('DRINK_MAT') - - p.growths.each do |g| - m = df.decode_mat(g).material - ban_cooking[g.mat_type, g.mat_index, :PLANT_GROWTH] if m.reaction_product and m.reaction_product.id and m.reaction_product.id.include?('DRINK_MAT') - end - end - - when 'mill' - df.world.raws.plants.all.each do |p| - ban_cooking[p.material_defs.type_basic_mat, p.material_defs.idx_basic_mat, :PLANT] if m.flags[:MILL] - end - - when 'thread' - df.world.raws.plants.all.each do |p| - ban_cooking[p.material_defs.type_basic_mat, p.material_defs.idx_basic_mat, :PLANT] if m.flags[:THREAD] - end - - when 'milk' - df.world.raws.creatures.all.each_with_index do |c, i| - c.material.each_with_index do |m, j| - if m.reaction_product and m.reaction_product.id and m.reaction_product.id.include?('CHEESE_MAT') - ban_cooking[j + DFHack::MaterialInfo::CREATURE_BASE, i, :LIQUID_MISC] - end - end - end - - else - puts "ban-cooking booze - bans cooking of drinks" - puts "ban-cooking honey - bans cooking of honey bee honey" - puts "ban-cooking tallow - bans cooking of tallow" - puts "ban-cooking oil - bans cooking of oil" - puts "ban-cooking seeds - bans cooking of plants that have seeds (tree seeds don't count)" - puts "ban-cooking brew - bans cooking of plants that can be brewed into alcohol" - puts "ban-cooking mill - bans cooking of plants that can be milled into powder" - puts "ban-cooking thread - bans cooking of plants that can be turned into thread" - puts "ban-cooking milk - bans cooking of creature liquids that can be turned into cheese" - end -end diff --git a/scripts/binpatch.lua b/scripts/binpatch.lua deleted file mode 100644 index ac7705250..000000000 --- a/scripts/binpatch.lua +++ /dev/null @@ -1,51 +0,0 @@ --- Apply or remove binary patches at runtime. ---[[=begin - -binpatch -======== -Implements functions for in-memory binpatches. See `binpatches`. - -=end]] - -local bp = require('binpatch') - -function run_command(cmd,name) - local pfix = name..': ' - - local patch, err = bp.load_dif_file(name) - if not patch then - dfhack.printerr(pfix..err) - return - end - - if cmd == 'check' then - local status, addr = patch:status() - if status == 'conflict' then - dfhack.printerr(string.format('%sconflict at address %x', pfix, addr)) - else - print(pfix..'patch is '..status) - end - elseif cmd == 'apply' then - local ok, msg = patch:apply() - if ok then - print(pfix..msg) - else - dfhack.printerr(pfix..msg) - end - elseif cmd == 'remove' then - local ok, msg = patch:remove() - if ok then - print(pfix..msg) - else - dfhack.printerr(pfix..msg) - end - else - qerror('Invalid command: '..cmd) - end -end - -local cmd,name = ... -if not cmd or not name then - qerror('Usage: binpatch check/apply/remove ') -end -run_command(cmd, name) diff --git a/scripts/brainwash.lua b/scripts/brainwash.lua deleted file mode 100644 index b28f648f7..000000000 --- a/scripts/brainwash.lua +++ /dev/null @@ -1,70 +0,0 @@ --- Brainwash a dwarf, modifying their personality --- usage is: target a unit in DF, and execute this script in dfhack --- by vjek ---[[=begin - -brainwash -========= -Modify the personality traits of the selected dwarf to match an 'ideal' -personality - as stable and reliable as possible. This makes dwarves very -stable, preventing tantrums even after months of misery. - -=end]] - -function brainwash_unit(profile) - local i,unit_name - - unit=dfhack.gui.getSelectedUnit() - if unit==nil then - print ("No unit under cursor! Aborting with extreme prejudice.") - return - end - - unit_name=dfhack.TranslateName(dfhack.units.getVisibleName(unit)) - - print("Previous personality values for "..unit_name) - printall(unit.status.current_soul.personality.traits) - - --now set new personality - for i=1, #profile do - unit.status.current_soul.personality.traits[i-1]=profile[i] - end - - print("New personality values for "..unit_name) - printall(unit.status.current_soul.personality.traits) - print(unit_name.." has been brainwashed, praise Armok!") -end - --- main script starts here --- profiles are listed here and passed to the brainwash function --- -local baseline={50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50} -local ideal={75,25,25,75,25,25,25,99,25,25,25,50,75,50,25,75,75,50,75,75,25,75,75,50,75,25,50,25,75,75,75,25,75,75,25,75,25,25,75,75,25,75,75,75,25,75,75,25,25,50} -local stepford={99,1,1,99,1,1,1,99,1,1,1,1,50,50,1,99,99,50,50,50,1,1,99,50,50,50,50,50,50,99,50,1,1,99,1,99,1,1,99,99,1,99,99,99,1,50,50,1,1,1} -local wrecked={1,99,99,1,99,99,99,1,99,99,99,1,1,99,99,1,1,1,1,1,99,1,1,99,1,99,99,99,1,1,1,99,1,1,99,1,99,99,1,1,99,1,1,1,99,1,1,99,99,99} -local opt = ... - -if opt then - if opt=="ideal" then - brainwash_unit(ideal) - return - end - if opt=="baseline" then - brainwash_unit(baseline) - return - end - if opt=="stepford" then - brainwash_unit(stepford) - return - end - if opt=="wrecked" then - brainwash_unit(wrecked) - return - end -else - print ("Invalid or missing personality argument.\nValid choices are ideal , baseline , stepford, and wrecked.") - print ("ideal will create a reliable dwarf with generally positive personality traits.") - print ("baseline will reset all personality traits to a default / the average.") - print ("stepford amplifies all good qualities to an excessive degree.") - print ("wrecked amplifies all bad qualities to an excessive degree.") -end diff --git a/scripts/burial.lua b/scripts/burial.lua deleted file mode 100644 index 3023f9128..000000000 --- a/scripts/burial.lua +++ /dev/null @@ -1,27 +0,0 @@ --- allows burial in unowned coffins --- by Putnam https://gist.github.com/Putnam3145/e7031588f4d9b24b9dda ---[[=begin - -burial -====== -Sets all unowned coffins to allow burial. ``burial -pets`` also allows burial -of pets. - -=end]] - -local utils=require('utils') - -validArgs = validArgs or utils.invert({ - 'pets' -}) - -local args = utils.processArgs({...}, validArgs) - -for k,v in ipairs(df.global.world.buildings.other.COFFIN) do - if v.owner_id==-1 then - v.burial_mode.allow_burial=true - if not args.pets then - v.burial_mode.no_pets=true - end - end -end \ No newline at end of file diff --git a/scripts/catsplosion.lua b/scripts/catsplosion.lua deleted file mode 100644 index bd3728b9c..000000000 --- a/scripts/catsplosion.lua +++ /dev/null @@ -1,97 +0,0 @@ --- Make cats just /multiply/. ---[[=begin - -catsplosion -=========== -Makes cats (and other animals) just *multiply*. It is not a good idea to run this -more than once or twice. - -Usage: - -:catsplosion: Make all cats pregnant -:catsplosion list: List IDs of all animals on the map -:catsplosion ID ...: Make animals with given ID(s) pregnant - -Animals will give birth within two in-game hours (100 ticks or fewer). - -=end]] - -world = df.global.world - -if not dfhack.isWorldLoaded() then - qerror('World not loaded.') -end - -args = {...} -list_only = false -creatures = {} - -if #args > 0 then - for _, arg in pairs(args) do - if arg == 'list' then - list_only = true - else - creatures[arg:upper()] = true - end - end -else - creatures.CAT = true -end - -total = 0 -total_changed = 0 -total_created = 0 - -males = {} -females = {} - -for _, unit in pairs(world.units.all) do - local id = world.raws.creatures.all[unit.race].creature_id - males[id] = males[id] or {} - females[id] = females[id] or {} - table.insert((dfhack.units.isFemale(unit) and females or males)[id], unit) -end - -if list_only then - print("Type Male # Female #") - -- sort IDs alphabetically - local ids = {} - for id in pairs(males) do - table.insert(ids, id) - end - table.sort(ids) - for _, id in pairs(ids) do - print(("%22s %6d %8d"):format(id, #males[id], #females[id])) - end - return -end - -for id in pairs(creatures) do - local females = females[id] or {} - total = total + #females - for _, female in pairs(females) do - if female.relations.pregnancy_timer ~= 0 then - female.relations.pregnancy_timer = math.random(1, 100) - total_changed = total_changed + 1 - elseif not female.relations.pregnancy_genes then - local preg = df.unit_genes:new() - preg.appearance:assign(female.appearance.genes.appearance) - preg.colors:assign(female.appearance.genes.colors) - female.relations.pregnancy_genes = preg - female.relations.pregnancy_timer = math.random(1, 100) - female.relations.pregnancy_caste = 1 - total_created = total_created + 1 - end - end -end - -if total_changed ~= 0 then - print(("%d pregnancies accelerated."):format(total_changed)) -end -if total_created ~= 0 then - print(("%d pregnancies created."):format(total_created)) -end -if total == 0 then - qerror("No creatures matched.") -end -print(("Total creatures checked: %d"):format(total)) diff --git a/scripts/colonies.lua b/scripts/colonies.lua deleted file mode 100644 index 2e755ea18..000000000 --- a/scripts/colonies.lua +++ /dev/null @@ -1,82 +0,0 @@ --- List, create, or change wild colonies (eg honey bees) --- By PeridexisErrant and Warmist - -local help = [[=begin - -colonies -======== -List vermin colonies, place honey bees, or convert all vermin -to honey bees. Usage: - -:colonies: List all vermin colonies on the map. -:colonies place: Place a honey bee colony under the cursor. -:colonies convert: Convert all existing colonies to honey bees. - -The ``place`` and ``convert`` subcommands by default create or -convert to honey bees, as this is the most commonly useful. -However both accept an optional flag to use a different vermin -type, for example ``colonies place ANT`` creates an ant colony -and ``colonies convert TERMITE`` ends your beekeeping industry. - -=end]] - -function findVermin(target_verm) - for k,v in pairs(df.global.world.raws.creatures.all) do - if v.creature_id == target_verm then - return k - end - end - qerror("No vermin found with name: "..target_verm) -end - -function list_colonies() - for idx, col in pairs(df.global.world.vermin.colonies) do - race = df.global.world.raws.creatures.all[col.race].creature_id - print(race..' at '..col.pos.x..', '..col.pos.y..', '..col.pos.z) - end -end - -function convert_vermin_to(target_verm) - local vermin_id = findVermin(target_verm) - local changed = 0 - for _, verm in pairs(df.global.world.vermin.colonies) do - verm.race = vermin_id - verm.caste = -1 -- check for queen bee? - verm.amount = 18826 - verm.visible = true - changed = changed + 1 - end - print('Converted '..changed..' colonies to '..target_verm) -end - -function place_vermin(target_verm) - local pos = copyall(df.global.cursor) - if pos.x == -30000 then - qerror("Cursor must be pointing somewhere") - end - local verm = df.vermin:new() - verm.race = findVermin(target_verm) - verm.flags.is_colony = true - verm.caste = -1 -- check for queen bee? - verm.amount = 18826 - verm.visible = true - verm.pos:assign(pos) - df.global.world.vermin.colonies:insert("#", verm) - df.global.world.vermin.all:insert("#", verm) -end - -local args = {...} -local target_verm = args[2] or "HONEY_BEE" - -if args[1] == 'help' or args[1] == '?' then - print(help) -elseif args[1] == 'convert' then - convert_vermin_to(target_verm) -elseif args[1] == 'place' then - place_vermin(target_verm) -else - if #df.global.world.vermin.colonies < 1 then - dfhack.printerr('There are no colonies on the map.') - end - list_colonies() -end diff --git a/scripts/create-items.rb b/scripts/create-items.rb deleted file mode 100644 index 544bd8ce0..000000000 --- a/scripts/create-items.rb +++ /dev/null @@ -1,207 +0,0 @@ -# create first necessity items under cursor -=begin - -create-items -============ -Spawn items under the cursor, to get your fortress started. - -The first argument gives the item category, the second gives the material, -and the optionnal third gives the number of items to create (defaults to 20). - -Currently supported item categories: ``boulder``, ``bar``, ``plant``, ``log``, -``web``. - -Instead of material, using ``list`` makes the script list eligible materials. - -The ``web`` item category will create an uncollected cobweb on the floor. - -Note that the script does not enforce anything, and will let you create -boulders of toad blood and stuff like that. -However the ``list`` mode will only show 'normal' materials. - -Examples:: - - create-items boulders COAL_BITUMINOUS 12 - create-items plant tail_pig - create-items log list - create-items web CREATURE:SPIDER_CAVE_GIANT:SILK - create-items bar CREATURE:CAT:SOAP - create-items bar adamantine - -=end - -category = $script_args[0] || 'help' -mat_raw = $script_args[1] || 'list' -count = $script_args[2] - - -category = df.match_rawname(category, ['help', 'bars', 'boulders', 'plants', 'logs', 'webs', 'anvils']) || 'help' - -if category == 'help' - puts < 5 - df.curview.feed_keys(:CURSOR_DOWN_Z) - df.curview.feed_keys(:CURSOR_UP_Z) -else - df.curview.feed_keys(:CURSOR_UP_Z) - df.curview.feed_keys(:CURSOR_DOWN_Z) -end diff --git a/scripts/deathcause.rb b/scripts/deathcause.rb deleted file mode 100644 index 6b2f252c0..000000000 --- a/scripts/deathcause.rb +++ /dev/null @@ -1,75 +0,0 @@ -# show death cause of a creature -=begin - -deathcause -========== -Select a body part ingame, or a unit from the :kbd:`u` unit list, and this -script will display the cause of death of the creature. - -=end - -def display_death_event(e) - str = "The #{e.victim_hf_tg.race_tg.name[0]} #{e.victim_hf_tg.name} died in year #{e.year}" - str << " (cause: #{e.death_cause.to_s.downcase})," - str << " killed by the #{e.slayer_race_tg.name[0]} #{e.slayer_hf_tg.name}" if e.slayer_hf != -1 - str << " using a #{df.world.raws.itemdefs.weapons[e.weapon.item_subtype].name}" if e.weapon.item_type == :WEAPON - str << ", shot by a #{df.world.raws.itemdefs.weapons[e.weapon.shooter_item_subtype].name}" if e.weapon.shooter_item_type == :WEAPON - - puts str.chomp(',') + '.' -end - -def display_death_unit(u) - death_info = u.counters.death_tg - killer = death_info.killer_tg if death_info - - str = "The #{u.race_tg.name[0]}" - str << " #{u.name}" if u.name.has_name - str << " died" - str << " in year #{death_info.event_year}" if death_info - str << " (cause: #{u.counters.death_cause.to_s.downcase})," if u.counters.death_cause != -1 - str << " killed by the #{killer.race_tg.name[0]} #{killer.name}" if killer - - puts str.chomp(',') + '.' -end - -item = df.item_find(:selected) -unit = df.unit_find(:selected) - -if !item or !item.kind_of?(DFHack::ItemBodyComponent) - item = df.world.items.other[:ANY_CORPSE].find { |i| df.at_cursor?(i) } -end - -if item and item.kind_of?(DFHack::ItemBodyComponent) - hf = item.hist_figure_id -elsif unit - hf = unit.hist_figure_id -end - -if not hf - puts "Please select a corpse in the loo'k' menu, or an unit in the 'u'nitlist screen" - -elsif hf == -1 - if unit ||= item.unit_tg - display_death_unit(unit) - else - puts "Not a historical figure, cannot death find info" - end - -else - histfig = df.world.history.figures.binsearch(hf) - unit = histfig ? df.unit_find(histfig.unit_id) : nil - if unit and not unit.flags1.dead and not unit.flags3.ghostly - puts "#{unit.name} is not dead yet !" - - else - events = df.world.history.events - (0...events.length).reverse_each { |i| - e = events[i] - if e.kind_of?(DFHack::HistoryEventHistFigureDiedst) and e.victim_hf == hf - display_death_event(e) - break - end - } - end -end - diff --git a/scripts/deteriorateclothes.rb b/scripts/deteriorateclothes.rb deleted file mode 100644 index 33084185e..000000000 --- a/scripts/deteriorateclothes.rb +++ /dev/null @@ -1,81 +0,0 @@ -# Increase the rate at which clothes wear out -=begin - -deteriorateclothes -================== -Somewhere between a "mod" and a "fps booster", with a small impact on -vanilla gameplay. All of those slightly worn wool shoes that dwarves -scatter all over the place will deteriorate at a greatly increased rate, -and eventually just crumble into nothing. As warm and fuzzy as a dining -room full of used socks makes your dwarves feel, your FPS does not like it. - -Usage: ``deteriorateclothes (start|stop)`` - -=end - -class DeteriorateClothes - - def initialize - end - - def process - return false unless @running - - items = [df.world.items.other[:GLOVES], - df.world.items.other[:ARMOR], - df.world.items.other[:SHOES], - df.world.items.other[:PANTS], - df.world.items.other[:HELM]] - - items.each { |type| - type.each { |i| - if (i.subtype.armorlevel == 0 and i.flags.on_ground == true and i.wear > 0) - i.wear_timer *= i.wear + 0.5 - if (i.wear > 2) - i.flags.garbage_collect = true - end - end - } - } - - - end - - def start - @onupdate = df.onupdate_register('deteriorateclothes', 1200, 1200) { process } - @running = true - - puts "Deterioration of old clothes commencing..." - - end - - def stop - df.onupdate_unregister(@onupdate) - @running = false - end - - def status - @running ? 'Running.' : 'Stopped.' - end - -end - -case $script_args[0] -when 'start' - if ($DeteriorateClothes) - $DeteriorateClothes.stop - end - - $DeteriorateClothes = DeteriorateClothes.new - $DeteriorateClothes.start - -when 'end', 'stop' - $DeteriorateClothes.stop - -else - if $DeteriorateClothes - puts $DeteriorateClothes.status - else - puts 'Not loaded.' - end -end \ No newline at end of file diff --git a/scripts/deterioratecorpses.rb b/scripts/deterioratecorpses.rb deleted file mode 100644 index 56003f6d0..000000000 --- a/scripts/deterioratecorpses.rb +++ /dev/null @@ -1,106 +0,0 @@ -# Make corpse parts decay and vanish over time -=begin - -deterioratecorpses -================== -Somewhere between a "mod" and a "fps booster", with a small impact on -vanilla gameplay. - -In long running forts, especially evil biomes, you end up with a lot -of toes, teeth, fingers, and limbs scattered all over the place. -Various corpses from various sieges, stray kitten corpses, probably -some heads. Basically, your map will look like a giant pile of -assorted body parts, all of which individually eat up a small part -of your FPS, which collectively eat up quite a bit. - -In addition, this script also targets various butchery byproducts. -Enjoying your thriving animal industry? Your FPS does not. Those -thousands of skulls, bones, hooves, and wool eat up precious FPS -that could be used to kill goblins and elves. Whose corpses will -also get destroyed by the script to kill more goblins and elves. - -This script causes all of those to rot away into nothing after -several months. - -Usage: ``deterioratecorpses (start|stop)`` - -=end - -class DeteriorateCorpses - - def initialize - end - - def process - return false unless @running - - df.world.items.other[:ANY_CORPSE].each { |i| - if (i.flags.dead_dwarf == false) - i.wear_timer += 1 - if (i.wear_timer > 24 + rand(8)) - i.wear_timer = 0 - i.wear += 1 - end - if (i.wear > 3) - i.flags.garbage_collect = true - end - - end - - } - - df.world.items.other[:REMAINS].each { |i| - if (i.flags.dead_dwarf == false) - i.wear_timer += 1 - if (i.wear_timer > 6) - i.wear_timer = 0 - i.wear += 1 - end - if (i.wear > 3) - i.flags.garbage_collect = true - end - - end - - } - - end - - def start - @onupdate = df.onupdate_register('deterioratecorpses', 1200, 1200) { process } - @running = true - - puts "Deterioration of body parts commencing..." - - end - - def stop - df.onupdate_unregister(@onupdate) - @running = false - end - - def status - @running ? 'Running.' : 'Stopped.' - end - -end - -case $script_args[0] -when 'start' - if ($DeteriorateCorpses) - $DeteriorateCorpses.stop - end - - $DeteriorateCorpses = DeteriorateCorpses.new - $DeteriorateCorpses.start - -when 'end', 'stop' - $DeteriorateCorpses.stop - -else - if $DeteriorateCorpses - puts $DeteriorateCorpses.status - else - puts 'Not loaded.' - end -end \ No newline at end of file diff --git a/scripts/deterioratefood.rb b/scripts/deterioratefood.rb deleted file mode 100644 index c59171ea5..000000000 --- a/scripts/deterioratefood.rb +++ /dev/null @@ -1,96 +0,0 @@ -# Food and plants decay, and vanish after a few months -=begin - -deterioratefood -=============== -Somewhere between a "mod" and a "fps booster", with a small impact on -vanilla gameplay. - -With this script running, all food and plants wear out and disappear -after several months. Barrels and stockpiles will keep them from -rotting, but it won't keep them from decaying. No more sitting on a -hundred years worth of food. No more keeping barrels of pig tails -sitting around until you decide to use them. Either use it, eat it, -or lose it. Seeds, are excluded from this, if you aren't planning on -using your pig tails, hold onto the seeds for a rainy day. - -This script is...pretty far reaching. However, almost all long -running forts I've had end up sitting on thousands and thousands of -food items. Several thousand cooked meals, three thousand plump -helmets, just as many fish and meat. It gets pretty absurd. And your -FPS doesn't like it. - -Usage: ``deterioratefood (start|stop)`` - -=end - -class DeteriorateFood - - def initialize - end - - def process - return false unless @running - - items = [df.world.items.other[:FISH], - df.world.items.other[:FISH_RAW], - df.world.items.other[:EGG], - df.world.items.other[:CHEESE], - df.world.items.other[:PLANT], - df.world.items.other[:PLANT_GROWTH], - df.world.items.other[:FOOD]] - - items.each { |type| - type.each { |i| - i.wear_timer += 1 - if (i.wear_timer > 24 + rand(8)) - i.wear_timer = 0 - i.wear += 1 - end - if (i.wear > 3) - i.flags.garbage_collect = true - end - } - } - - - end - - def start - @onupdate = df.onupdate_register('deterioratefood', 1200, 1200) { process } - @running = true - - puts "Deterioration of food commencing..." - - end - - def stop - df.onupdate_unregister(@onupdate) - @running = false - end - - def status - @running ? 'Running.' : 'Stopped.' - end - -end - -case $script_args[0] -when 'start' - if ($DeteriorateFood) - $DeteriorateFood.stop - end - - $DeteriorateFood = DeteriorateFood.new - $DeteriorateFood.start - -when 'end', 'stop' - $DeteriorateFood.stop - -else - if $DeteriorateFood - puts $DeteriorateFood.status - else - puts 'Not loaded.' - end -end \ No newline at end of file diff --git a/scripts/devel/about.txt b/scripts/devel/about.txt deleted file mode 100644 index 12b1f1853..000000000 --- a/scripts/devel/about.txt +++ /dev/null @@ -1,6 +0,0 @@ -``devel/*`` scripts are intended for developer use, but many may -be of interest to anyone investigating odd phenomema or just messing -around. They are documented to encourage such inquiry. - -Some can PERMANENTLY DAMAGE YOUR SAVE if misused, so please be careful. -The warnings are real; if in doubt make backups before running the command. diff --git a/scripts/devel/all-bob.lua b/scripts/devel/all-bob.lua deleted file mode 100644 index 163f708dd..000000000 --- a/scripts/devel/all-bob.lua +++ /dev/null @@ -1,15 +0,0 @@ --- Changes the first name of all units to "Bob" ---author expwnent --- ---[[=begin - -devel/all-bob -============= -Changes the first name of all units to "Bob". -Useful for testing `modtools/interaction-trigger` events. - -=end]] - -for _,v in ipairs(df.global.world.units.all) do - v.name.first_name = "Bob" -end diff --git a/scripts/devel/check-release.lua b/scripts/devel/check-release.lua deleted file mode 100644 index fcc50642e..000000000 --- a/scripts/devel/check-release.lua +++ /dev/null @@ -1,59 +0,0 @@ --- basic check for release readiness ---[[=begin -devel/check-release -=================== -Basic checks for release readiness -=end]] - -ok = true -function err(s) - dfhack.printerr(s) - ok = false -end -function warn(s) - dfhack.color(COLOR_YELLOW) - dfhack.print(s .. '\n') - dfhack.color(nil) -end - -dfhack_ver = dfhack.getDFHackVersion() -git_desc = dfhack.getGitDescription() -git_commit = dfhack.getGitCommit() -if not dfhack.isRelease() then - err('This build is not tagged as a release') - print[[ -This is probably due to missing git tags. -Try running `git fetch origin --tags` in the DFHack source tree. -]] -end - -expected_git_desc = ('%s-0-g%s'):format(dfhack_ver, git_commit:sub(1, 7)) -if git_desc:sub(1, #expected_git_desc) ~= expected_git_desc then - err(([[Bad git description: -Expected %s, got %s]]):format(expected_git_desc, git_desc)) - print[[ -Ensure that the DFHack source tree is up-to-date (`git pull`) and CMake is -installing DFHack to this DF folder. -]] -end - -if not dfhack.gitXmlMatch() then - err('library/xml submodule commit does not match tracked commit\n' .. - ('Expected %s, got %s'):format( - dfhack.getGitXmlCommit():sub(1, 7), - dfhack.getGitXmlExpectedCommit():sub(1, 7) - )) - print('Try running `git submodule update` in the DFHack source tree.') -end - -if dfhack.isPrerelease() then - warn('This build is marked as a prerelease.') - print('If this is not intentional, be sure your DFHack tree is up-to-date\n' .. - '(`git pull`) and try again.') -end - -if not ok then - err('This build is not release-ready!') -else - print('Release checks succeeded') -end diff --git a/scripts/devel/clear-script-env.lua b/scripts/devel/clear-script-env.lua deleted file mode 100644 index 90c8db0ed..000000000 --- a/scripts/devel/clear-script-env.lua +++ /dev/null @@ -1,26 +0,0 @@ --- Clear script environment ---[[=begin - -devel/clear-script-env -====================== -Clears the environment of the specified lua script(s). - -=end]] -args = {...} -if #args < 1 then qerror("script name(s) required") end -for _, name in pairs(args) do - local file = dfhack.findScript(name) - if file then - local script = dfhack.internal.scripts[file] - if script then - local env = script.env - while next(env) do - env[next(env)] = nil - end - else - dfhack.printerr("Script not loaded: " .. name) - end - else - dfhack.printerr("Can't find script: " .. name) - end -end diff --git a/scripts/devel/cmptiles.lua b/scripts/devel/cmptiles.lua deleted file mode 100644 index bb0871051..000000000 --- a/scripts/devel/cmptiles.lua +++ /dev/null @@ -1,50 +0,0 @@ --- Lists and/or compares two tiletype material groups. ---[[=begin - -devel/cmptiles -============== -Lists and/or compares two tiletype material groups. - -Usage: ``devel/cmptiles material1 [material2]`` - -=end]] - -local nmat1,nmat2=... -local mat1 = df.tiletype_material[nmat1] -local mat2 = df.tiletype_material[nmat2] - -local tmat1 = {} -local tmat2 = {} - -local attrs = df.tiletype.attrs - -for i=df.tiletype._first_item,df.tiletype._last_item do - local shape = df.tiletype_shape[attrs[i].shape] or '' - local variant = df.tiletype_variant[attrs[i].variant] or '' - local special = df.tiletype_special[attrs[i].special] or '' - local direction = attrs[i].direction or '' - local code = shape..':'..variant..':'..special..':'..direction - - if attrs[i].material == mat1 then - tmat1[code] = true - end - if attrs[i].material == mat2 then - tmat2[code] = true - end -end - -local function list_diff(n, t1, t2) - local lst = {} - for k,v in pairs(t1) do - if not t2[k] then - lst[#lst+1] = k - end - end - table.sort(lst) - for k,v in ipairs(lst) do - print(n, v) - end -end - -list_diff(nmat1,tmat1,tmat2) -list_diff(nmat2,tmat2,tmat1) diff --git a/scripts/devel/export-dt-ini.lua b/scripts/devel/export-dt-ini.lua deleted file mode 100644 index b6a2989eb..000000000 --- a/scripts/devel/export-dt-ini.lua +++ /dev/null @@ -1,515 +0,0 @@ --- Exports an ini file for Dwarf Therapist. ---[[=begin -devel/export-dt-ini -=================== -Exports an ini file containing memory addresses for Dwarf Therapist. -=end]] - -local utils = require 'utils' -local ms = require 'memscan' - --- Utility functions - -local globals = df.global -local global_addr = dfhack.internal.getAddress -local os_type = dfhack.getOSType() -local rdelta = dfhack.internal.getRebaseDelta() -local lines = {} -local complete = true - -local function header(name) - table.insert(lines, '') - table.insert(lines, '['..name..']') -end - -local function value(name,addr) - local line - - if not addr then - complete = false - line = name..'=0x0' - elseif addr < 0x10000 then - line = string.format('%s=0x%04x',name,addr) - else - line = string.format('%s=0x%08x',name,addr) - end - - table.insert(lines, line) -end -local function address(name,base,field,...) - local addr - - if base == globals then - addr = global_addr(field) - if addr and select('#',...) > 0 then - _,addr = df.sizeof(ms.field_ref(base,field,...)) - end - elseif base._kind == 'class-type' then - -- field_offset crashes with classes due to vtable problems, - -- so we have to create a real temporary object here. - local obj = df.new(base) - if obj then - local _,a1 = df.sizeof(obj) - local _,a2 = df.sizeof(ms.field_ref(obj,field,...)) - addr = a2-a1 - obj:delete() - end - else - addr = ms.field_offset(base,field,...) - end - - value(name, addr) -end - - --- List of actual values -header('addresses') -address('cur_year_tick',globals,'cur_year_tick') -address('current_year',globals,'cur_year') -address('dwarf_civ_index',globals,'ui','civ_id') -address('dwarf_race_index',globals,'ui','race_id') -address('fortress_entity',globals,'ui','main','fortress_entity') -address('historical_entities_vector',globals,'world','entities','all') -address('creature_vector',globals,'world','units','all') -address('active_creature_vector',globals,'world','units','active') -address('weapons_vector',globals,'world','items','other','WEAPON') -address('shields_vector',globals,'world','items','other', 'SHIELD') -address('quivers_vector',globals,'world','items','other', 'QUIVER') -address('crutches_vector',globals,'world','items','other', 'CRUTCH') -address('backpacks_vector',globals,'world','items','other', 'BACKPACK') -address('ammo_vector',globals,'world','items','other', 'AMMO') -address('flasks_vector',globals,'world','items','other', 'FLASK') -address('pants_vector',globals,'world','items','other', 'PANTS') -address('armor_vector',globals,'world','items','other', 'ARMOR') -address('shoes_vector',globals,'world','items','other', 'SHOES') -address('helms_vector',globals,'world','items','other', 'HELM') -address('gloves_vector',globals,'world','items','other', 'GLOVES') -address('artifacts_vector',globals,'world','artifacts','all') -address('squad_vector',globals,'world','squads','all') -address('activities_vector',globals,'world','activities','all') -address('fake_identities_vector',globals,'world','identities','all') -address('poetic_forms_vector',globals,'world','poetic_forms','all') -address('musical_forms_vector',globals,'world','musical_forms','all') -address('dance_forms_vector',globals,'world','dance_forms','all') -address('occupations_vector',globals,'world','occupations','all') -address('world_data',globals,'world','world_data') -address('material_templates_vector',globals,'world','raws','material_templates') -address('inorganics_vector',globals,'world','raws','inorganics') -address('plants_vector',globals,'world','raws','plants','all') -address('races_vector',globals,'world','raws','creatures','all') -address('itemdef_weapons_vector',globals,'world','raws','itemdefs','weapons') -address('itemdef_trap_vector',globals,'world','raws','itemdefs','trapcomps') -address('itemdef_toy_vector',globals,'world','raws','itemdefs','toys') -address('itemdef_tool_vector',globals,'world','raws','itemdefs','tools') -address('itemdef_instrument_vector',globals,'world','raws','itemdefs','instruments') -address('itemdef_armor_vector',globals,'world','raws','itemdefs','armor') -address('itemdef_ammo_vector',globals,'world','raws','itemdefs','ammo') -address('itemdef_siegeammo_vector',globals,'world','raws','itemdefs','siege_ammo') -address('itemdef_glove_vector',globals,'world','raws','itemdefs','gloves') -address('itemdef_shoe_vector',globals,'world','raws','itemdefs','shoes') -address('itemdef_shield_vector',globals,'world','raws','itemdefs','shields') -address('itemdef_helm_vector',globals,'world','raws','itemdefs','helms') -address('itemdef_pant_vector',globals,'world','raws','itemdefs','pants') -address('itemdef_food_vector',globals,'world','raws','itemdefs','food') -address('language_vector',globals,'world','raws','language','words') -address('translation_vector',globals,'world','raws','language','translations') -address('colors_vector',globals,'world','raws','language','colors') -address('shapes_vector',globals,'world','raws','language','shapes') -address('reactions_vector',globals,'world','raws','reactions') -address('base_materials',globals,'world','raws','mat_table','builtin') -address('all_syndromes_vector',globals,'world','raws','syndromes','all') -address('events_vector',globals,'world','history','events') -address('historical_figures_vector',globals,'world','history','figures') -address('world_site_type',df.world_site,'type') -address('active_sites_vector',df.world_data,'active_site') - -header('offsets') -address('word_table',df.language_translation,'words') -value('string_buffer_offset', 0x0000) - -header('word_offsets') -address('base',df.language_word,'word') -address('noun_singular',df.language_word,'forms','Noun') -address('noun_plural',df.language_word,'forms','NounPlural') -address('adjective',df.language_word,'forms','Adjective') -address('verb',df.language_word,'forms','Verb') -address('present_simple_verb',df.language_word,'forms','Verb3rdPerson') -address('past_simple_verb',df.language_word,'forms','VerbPast') -address('past_participle_verb',df.language_word,'forms','VerbPassive') -address('present_participle_verb',df.language_word,'forms','VerbGerund') -address('words',df.language_name,'words') -address('word_type',df.language_name,'parts_of_speech') -address('language_id',df.language_name,'language') - -header('general_ref_offsets') ---WARNING below value should be: "general_ref::vtable","1","0x8","0x4","vmethod","getType","general_ref_type","" -value('ref_type',0x8) -address('artifact_id',df.general_ref_artifact,'artifact_id') -address('item_id',df.general_ref_item,'item_id') - -header('race_offsets') -address('name_singular',df.creature_raw,'name',0) -address('name_plural',df.creature_raw,'name',1) -address('adjective',df.creature_raw,'name',2) -address('baby_name_singular',df.creature_raw,'general_baby_name',0) -address('baby_name_plural',df.creature_raw,'general_baby_name',1) -address('child_name_singular',df.creature_raw,'general_child_name',0) -address('child_name_plural',df.creature_raw,'general_child_name',1) -address('pref_string_vector',df.creature_raw,'prefstring') -address('castes_vector',df.creature_raw,'caste') -address('pop_ratio_vector',df.creature_raw,'pop_ratio') -address('materials_vector',df.creature_raw,'material') -address('flags',df.creature_raw,'flags') -address('tissues_vector',df.creature_raw,'tissue') - -header('caste_offsets') -address('caste_name',df.caste_raw,'caste_name') -address('caste_descr',df.caste_raw,'description') -address('caste_trait_ranges',df.caste_raw,'personality','a') -address('caste_phys_att_ranges',df.caste_raw,'attributes','phys_att_range') -address('baby_age',df.caste_raw,'misc','baby_age') -address('child_age',df.caste_raw,'misc','child_age') -address('adult_size',df.caste_raw,'misc','adult_size') -address('flags',df.caste_raw,'flags') -address('body_info',df.caste_raw,'body_info') -address('skill_rates',df.caste_raw,'skill_rates') -address('caste_att_rates',df.caste_raw,'attributes','phys_att_rates') -address('caste_att_caps',df.caste_raw,'attributes','phys_att_cap_perc') -address('shearable_tissues_vector',df.caste_raw,'shearable_tissue_layer') -address('extracts',df.caste_raw,'extracts','extract_matidx') - -header('hist_entity_offsets') -address('histfigs',df.historical_entity,'histfig_ids') -address('beliefs',df.historical_entity,'resources','values') -address('squads',df.historical_entity,'squads') -address('positions',df.historical_entity,'positions','own') -address('assignments',df.historical_entity,'positions','assignments') -address('assign_hist_id',df.entity_position_assignment,'histfig') -address('assign_position_id',df.entity_position_assignment,'position_id') -address('position_id',df.entity_position,'id') -address('position_name',df.entity_position,'name') -address('position_female_name',df.entity_position,'name_female') -address('position_male_name',df.entity_position,'name_male') - -header('hist_figure_offsets') -address('hist_race',df.historical_figure,'race') -address('hist_name',df.historical_figure,'name') -address('id',df.historical_figure,'id') -address('hist_fig_info',df.historical_figure,'info') -address('reputation',df.historical_figure_info,'reputation') -address('current_ident',df.historical_figure_info.T_reputation,'cur_identity') -address('fake_name',df.identity,'name') -address('fake_birth_year',df.identity,'birth_year') -address('fake_birth_time',df.identity,'birth_second') -address('kills',df.historical_figure_info,'kills') -address('killed_race_vector',df.historical_kills,'killed_race') -address('killed_undead_vector',df.historical_kills,'killed_undead') -address('killed_counts_vector',df.historical_kills,'killed_count') - -header('hist_event_offsets') -address('event_year',df.history_event,'year') -address('id',df.history_event,'id') -address('killed_hist_id',df.history_event_hist_figure_diedst,'victim_hf') - -header('item_offsets') -if os_type == 'darwin' then - value('item_type',0x4) -else - value('item_type',0x1) -end -address('item_def',df.item_ammost,'subtype') --currently same for all -address('id',df.item,'id') -address('general_refs',df.item,'general_refs') -address('stack_size',df.item_actual,'stack_size') -address('wear',df.item_actual,'wear') -address('mat_type',df.item_crafted,'mat_type') -address('mat_index',df.item_crafted,'mat_index') -address('maker_race',df.item_crafted,'maker_race') -address('quality',df.item_crafted,'quality') - -header('item_subtype_offsets') -address('sub_type',df.itemdef,'subtype') -address('name',df.itemdef_armorst,'name') -address('name_plural',df.itemdef_armorst,'name_plural') -address('adjective',df.itemdef_armorst,'name_preplural') - -header('item_filter_offsets') -address('item_subtype',df.item_filter_spec,'item_subtype') -address('mat_class',df.item_filter_spec,'material_class') -address('mat_type',df.item_filter_spec,'mattype') -address('mat_index',df.item_filter_spec,'matindex') - -header('weapon_subtype_offsets') -address('single_size',df.itemdef_weaponst,'two_handed') -address('multi_size',df.itemdef_weaponst,'minimum_size') -address('ammo',df.itemdef_weaponst,'ranged_ammo') -address('melee_skill',df.itemdef_weaponst,'skill_melee') -address('ranged_skill',df.itemdef_weaponst,'skill_ranged') - -header('armor_subtype_offsets') -address('layer',df.armor_properties,'layer') -address('mat_name',df.itemdef_armorst,'material_placeholder') -address('other_armor_level',df.itemdef_helmst,'armorlevel') -address('armor_adjective',df.itemdef_armorst,'adjective') -address('armor_level',df.itemdef_armorst,'armorlevel') -address('chest_armor_properties',df.itemdef_armorst,'props') -address('pants_armor_properties',df.itemdef_pantsst,'props') -address('other_armor_properties',df.itemdef_helmst,'props') - -header('material_offsets') -address('solid_name',df.material_common,'state_name','Solid') -address('liquid_name',df.material_common,'state_name','Liquid') -address('gas_name',df.material_common,'state_name','Gas') -address('powder_name',df.material_common,'state_name','Powder') -address('paste_name',df.material_common,'state_name','Paste') -address('pressed_name',df.material_common,'state_name','Pressed') -address('flags',df.material_common,'flags') -address('inorganic_materials_vector',df.inorganic_raw,'material') -address('inorganic_flags',df.inorganic_raw,'flags') - -header('plant_offsets') -address('name',df.plant_raw,'name') -address('name_plural',df.plant_raw,'name_plural') -address('name_leaf_plural',df.plant_raw,'leaves_plural') -address('name_seed_plural',df.plant_raw,'seed_plural') -address('materials_vector',df.plant_raw,'material') -address('flags',df.plant_raw,'flags') - -header('descriptor_offsets') -address('color_name',df.descriptor_color,'name') -address('shape_name_plural',df.descriptor_shape,'name_plural') - -header('health_offsets') -address('parent_id',df.body_part_raw,'con_part_id') -address('body_part_flags',df.body_part_raw,'flags') -address('layers_vector',df.body_part_raw,'layers') -address('number',df.body_part_raw,'number') -address('names_vector',df.body_part_raw,'name_singular') -address('names_plural_vector',df.body_part_raw,'name_plural') -address('layer_tissue',df.body_part_layer_raw,'tissue_id') -address('layer_global_id',df.body_part_layer_raw,'layer_id') -address('tissue_name',df.tissue_template,'tissue_name_singular') -address('tissue_flags',df.tissue_template,'flags') - -header('dwarf_offsets') -address('first_name',df.unit,'name','first_name') -address('nick_name',df.unit,'name','nickname') -address('last_name',df.unit,'name','words') -address('custom_profession',df.unit,'custom_profession') -address('profession',df.unit,'profession') -address('race',df.unit,'race') -address('flags1',df.unit,'flags1') -address('flags2',df.unit,'flags2') -address('flags3',df.unit,'flags3') -address('meeting',df.unit,'meeting') -address('caste',df.unit,'caste') -address('sex',df.unit,'sex') -address('id',df.unit,'id') -address('animal_type',df.unit,'training_level') -address('civ',df.unit,'civ_id') -address('specific_refs',df.unit,'specific_refs') -address('squad_id',df.unit,'military','squad_id') -address('squad_position',df.unit,'military','squad_position') -address('recheck_equipment',df.unit,'military','pickup_flags') -address('mood',df.unit,'mood') -address('birth_year',df.unit,'relations','birth_year') -address('birth_time',df.unit,'relations','birth_time') -address('pet_owner_id',df.unit,'relations','pet_owner_id') -address('current_job',df.unit,'job','current_job') -address('physical_attrs',df.unit,'body','physical_attrs') -address('body_size',df.unit,'appearance','body_modifiers') -address('size_info',df.unit,'body','size_info') -address('curse',df.unit,'curse','name') -address('curse_add_flags1',df.unit,'curse','add_tags1') -address('turn_count',df.unit,'curse','time_on_site') -address('souls',df.unit,'status','souls') -address('states',df.unit,'status','misc_traits') -address('labors',df.unit,'status','labors') -address('hist_id',df.unit,'hist_figure_id') -address('artifact_name',df.unit,'status','artifact_name') -address('active_syndrome_vector',df.unit,'syndromes','active') -address('syn_sick_flag',df.unit_syndrome,'flags') -address('unit_health_info',df.unit,'health') -address('temp_mood',df.unit,'counters','soldier_mood') -address('counters1',df.unit,'counters','winded') -address('counters2',df.unit, 'counters','pain') -address('counters3',df.unit, 'counters2','paralysis') -address('limb_counters',df.unit,'status2','limbs_stand_max') -address('blood',df.unit,'body','blood_max') -address('body_component_info',df.unit,'body','components') -address('layer_status_vector',df.body_component_info,'layer_status') -address('wounds_vector',df.unit,'body','wounds') -address('mood_skill',df.unit,'job','mood_skill') -address('used_items_vector',df.unit,'used_items') -address('affection_level',df.unit_item_use,'affection_level') -address('inventory',df.unit,'inventory') -address('inventory_item_mode',df.unit_inventory_item,'mode') -address('inventory_item_bodypart',df.unit_inventory_item,'body_part_id') - -header('syndrome_offsets') -address('cie_effects',df.syndrome,'ce') -address('cie_end',df.creature_interaction_effect,'end') -address('cie_first_perc',df.creature_interaction_effect_phys_att_changest,'phys_att_perc') --same for mental -address('cie_phys',df.creature_interaction_effect_phys_att_changest,'phys_att_add') -address('cie_ment',df.creature_interaction_effect_ment_att_changest,'ment_att_add') -address('syn_classes_vector',df.syndrome,'syn_class') -address('trans_race_id',df.creature_interaction_effect_body_transformationst,'race') - -header('unit_wound_offsets') -address('parts',df.unit_wound,'parts') -address('id',df.unit_wound.T_parts,'body_part_id') -address('layer',df.unit_wound.T_parts,'layer_idx') -address('general_flags',df.unit_wound,'flags') -address('flags1',df.unit_wound.T_parts,'flags1') -address('flags2',df.unit_wound.T_parts,'flags2') -address('effects_vector',df.unit_wound.T_parts,'effect_type') -address('bleeding',df.unit_wound.T_parts,'bleeding') -address('pain',df.unit_wound.T_parts,'pain') -address('cur_pen',df.unit_wound.T_parts,'cur_penetration_perc') -address('max_pen',df.unit_wound.T_parts,'max_penetration_perc') - -header('soul_details') -address('name',df.unit_soul,'name') -address('orientation',df.unit_soul,'orientation_flags') -address('mental_attrs',df.unit_soul,'mental_attrs') -address('skills',df.unit_soul,'skills') -address('preferences',df.unit_soul,'preferences') -address('personality',df.unit_soul,'personality') -address('beliefs',df.unit_personality,'values') -address('emotions',df.unit_personality,'emotions') -address('goals',df.unit_personality,'dreams') -address('goal_realized',df.unit_personality.T_dreams,'unk8') -address('traits',df.unit_personality,'traits') -address('stress_level',df.unit_personality,'stress_level') - -header('emotion_offsets') -address('emotion_type',df.unit_personality.T_emotions,'type') -address('strength',df.unit_personality.T_emotions,'strength') -address('thought_id',df.unit_personality.T_emotions,'thought') -address('sub_id',df.unit_personality.T_emotions,'subthought') -address('level',df.unit_personality.T_emotions,'severity') -address('year',df.unit_personality.T_emotions,'year') -address('year_tick',df.unit_personality.T_emotions,'year_tick') - -header('job_details') -address('id',df.job,'job_type') -address('mat_type',df.job,'mat_type') -address('mat_index',df.job,'mat_index') -address('mat_category',df.job,'material_category') -value('on_break_flag',df.misc_trait_type.OnBreak) -address('sub_job_id',df.job,'reaction_name') -address('reaction',df.reaction,'name') -address('reaction_skill',df.reaction,'skill') - -header('squad_offsets') -address('id',df.squad,'id') -address('name',df.squad,'name') -address('alias',df.squad,'alias') -address('members',df.squad,'positions') -address('orders',df.squad,'orders') -address('schedules',df.squad,'schedule') -if os_type ~= 'windows' then --squad_schedule_entry size - value('sched_size',0x20) -else - value('sched_size',0x40) -end -address('sched_orders',df.squad_schedule_entry,'orders') -address('sched_assign',df.squad_schedule_entry,'order_assignments') -address('alert',df.squad,'cur_alert_idx') -address('carry_food',df.squad,'carry_food') -address('carry_water',df.squad,'carry_water') -address('ammunition',df.squad,'ammunition') -address('ammunition_qty',df.squad_ammo_spec,'amount') -address('quiver',df.squad_position,'quiver') -address('backpack',df.squad_position,'backpack') -address('flask',df.squad_position,'flask') -address('armor_vector',df.squad_position,'uniform','body') -address('helm_vector',df.squad_position,'uniform','head') -address('pants_vector',df.squad_position,'uniform','pants') -address('gloves_vector',df.squad_position,'uniform','gloves') -address('shoes_vector',df.squad_position,'uniform','shoes') -address('shield_vector',df.squad_position,'uniform','shield') -address('weapon_vector',df.squad_position,'uniform','weapon') -address('uniform_item_filter',df.squad_uniform_spec,'item_filter') -address('uniform_indiv_choice',df.squad_uniform_spec,'indiv_choice') - -header('activity_offsets') -address('activity_type',df.activity_entry,'type') -address('events',df.activity_entry,'events') -address('participants',df.activity_event_combat_trainingst,'participants') -address('sq_lead',df.activity_event_skill_demonstrationst,'hist_figure_id') -address('sq_skill',df.activity_event_skill_demonstrationst,'skill') -address('sq_train_rounds',df.activity_event_skill_demonstrationst,'train_rounds') -address('pray_deity',df.activity_event_prayerst,'histfig_id') -address('pray_sphere',df.activity_event_prayerst,'topic') -address('knowledge_category',df.activity_event_ponder_topicst,'knowledge_category') -address('knowledge_flag',df.activity_event_ponder_topicst,'knowledge_flag') -address('perf_type',df.activity_event_performancest,'type') -address('perf_participants',df.activity_event_performancest,'participant_actions') -address('perf_histfig',df.activity_event_performancest.T_participant_actions,'histfig_id') - --- Final creation of the file - -local out = io.open('therapist.ini', 'w') - -out:write('[info]\n') -if dfhack.getOSType() == 'windows' and dfhack.internal.getPE then - out:write(('checksum=0x%x\n'):format(dfhack.internal.getPE())) -elseif dfhack.getOSType() ~= 'windows' and dfhack.internal.getMD5 then - out:write(('checksum=0x%s\n'):format(dfhack.internal.getMD5():sub(1, 8))) -else - out:write('checksum=<>\n') -end -out:write('version_name='..dfhack.getDFVersion()..'\n') -out:write('complete='..(complete and 'true' or 'false')..'\n') - -for i,v in ipairs(lines) do - out:write(v..'\n') -end - -out:write[[ - -[valid_flags_2] -size=0 - -[invalid_flags_1] -size=9 -1\name=a skeleton -1\value=0x00002000 -2\name=a merchant -2\value=0x00000040 -3\name=outpost liason or diplomat -3\value=0x00000800 -4\name=an invader or hostile -4\value=0x00020000 -5\name=an invader or hostile -5\value=0x00080000 -6\name=resident, invader or ambusher -6\value=0x00600000 -7\name=part of a merchant caravan -7\value=0x00000080 -8\name="Dead, Jim." -8\value=0x00000002 -9\name=marauder -9\value=0x00000010 - -[invalid_flags_2] -size=5 -1\name="killed, Jim." -1\value=0x00000080 -2\name=from the Underworld. SPOOKY! -2\value=0x00040000 -3\name=resident -3\value=0x00080000 -4\name=uninvited visitor -4\value=0x00400000 -5\name=visitor -5\value=0x00800000 - -[invalid_flags_3] -size=1 -1\name=a ghost -1\value=0x00001000 -]] - -out:close() diff --git a/scripts/devel/find-offsets.lua b/scripts/devel/find-offsets.lua deleted file mode 100644 index 9cfacece0..000000000 --- a/scripts/devel/find-offsets.lua +++ /dev/null @@ -1,1936 +0,0 @@ --- Find some offsets for linux. ---[[=begin - -devel/find-offsets -================== -WARNING: THIS SCRIPT IS STRICTLY FOR DFHACK DEVELOPERS. - -Running this script on a new DF version will NOT -MAKE IT RUN CORRECTLY if any data structures -changed, thus possibly leading to CRASHES AND/OR -PERMANENT SAVE CORRUPTION. - -Finding the first few globals requires this script to be -started immediately after loading the game, WITHOUT -first loading a world. The rest expect a loaded save, -not a fresh embark. Finding current_weather requires -a special save previously processed with `devel/prepare-save` -on a DF version with working dfhack. - -The script expects vanilla game configuration, without -any custom tilesets or init file changes. Never unpause -the game unless instructed. When done, quit the game -without saving using 'die'. - -Arguments: - -* global names to force finding them -* ``all`` to force all globals -* ``nofeed`` to block automated fake input searches -* ``nozoom`` to disable neighboring object heuristics - -=end]] - -local utils = require 'utils' -local ms = require 'memscan' -local gui = require 'gui' - -local is_known = dfhack.internal.getAddress - -local os_type = dfhack.getOSType() - -local force_scan = {} -for _,v in ipairs({...}) do - force_scan[v] = true -end - -collectgarbage() - -function prompt_proceed(indent) - if not indent then indent = 0 end - return utils.prompt_yes_no(string.rep(' ', indent) .. 'Proceed?', true) -end - -print[[ -WARNING: THIS SCRIPT IS STRICTLY FOR DFHACK DEVELOPERS. - -Running this script on a new DF version will NOT -MAKE IT RUN CORRECTLY if any data structures -changed, thus possibly leading to CRASHES AND/OR -PERMANENT SAVE CORRUPTION. - -Finding the first few globals requires this script to be -started immediately after loading the game, WITHOUT -first loading a world. The rest expect a loaded save, -not a fresh embark. Finding current_weather requires -a special save previously processed with devel/prepare-save -on a DF version with working dfhack. - -The script expects vanilla game configuration, without -any custom tilesets or init file changes. Never unpause -the game unless instructed. When done, quit the game -without saving using 'die'. -]] - -if not utils.prompt_yes_no('Proceed?') then - return -end - --- Data segment location - -local data = ms.get_data_segment() -if not data then - qerror('Could not find data segment') -end - -print('\nData section: '..tostring(data)) -if data.size < 5000000 then - qerror('Data segment too short.') -end - -local searcher = ms.DiffSearcher.new(data) - -local function get_screen(class, prompt) - if not is_known('gview') then - print('Please navigate to '..prompt) - if not prompt_proceed() then - return nil - end - return true - end - - while true do - local cs = dfhack.gui.getCurViewscreen(true) - if not df.is_instance(class, cs) then - print('Please navigate to '..prompt) - if not prompt_proceed() then - return nil - end - else - return cs - end - end -end - -local function screen_title() - return get_screen(df.viewscreen_titlest, 'the title screen') -end -local function screen_dwarfmode() - return get_screen(df.viewscreen_dwarfmodest, 'the main dwarf mode screen') -end - -local function validate_offset(name,validator,addr,tname,...) - local obj = data:object_by_field(addr,tname,...) - if obj and not validator(obj) then - obj = nil - end - ms.found_offset(name,obj) -end - -local function zoomed_searcher(startn, end_or_sz) - if force_scan.nozoom then - return nil - end - local sv = is_known(startn) - if not sv then - return nil - end - local ev - if type(end_or_sz) == 'number' then - ev = sv + end_or_sz - if end_or_sz < 0 then - sv, ev = ev, sv - end - else - ev = is_known(end_or_sz) - if not ev then - return nil - end - end - sv = sv - (sv % 4) - ev = ev + 3 - ev = ev - (ev % 4) - if data:contains_range(sv, ev-sv) then - return ms.DiffSearcher.new(ms.MemoryArea.new(sv,ev)) - end -end - -local finder_searches = {} -local function exec_finder(finder, names, validators) - if type(names) ~= 'table' then - names = { names } - end - if type(validators) ~= 'table' then - validators = { validators } - end - local search = force_scan['all'] - for k,v in ipairs(names) do - if force_scan[v] or not is_known(v) then - table.insert(finder_searches, v) - search = true - elseif validators[k] then - if not validators[k](df.global[v]) then - dfhack.printerr('Validation failed for '..v..', will try to find again') - table.insert(finder_searches, v) - search = true - end - end - end - if search then - local ok, err = dfhack.safecall(finder) - if not ok then - if tostring(err):find('abort') or not utils.prompt_yes_no('Proceed with the rest of the script?') then - searcher:reset() - qerror('Quit') - end - end - else - print('Already known: '..table.concat(names,', ')) - end -end - -local ordinal_names = { - [0] = '1st entry', - [1] = '2nd entry', - [2] = '3rd entry' -} -setmetatable(ordinal_names, { - __index = function(self,idx) return (idx+1)..'th entry' end -}) - -local function list_index_choices(length_func) - return function(id) - if id > 0 then - local ok, len = pcall(length_func) - if not ok then - len = 5 - elseif len > 10 then - len = 10 - end - return id % len - else - return 0 - end - end -end - -local function can_feed() - return not force_scan['nofeed'] and is_known 'gview' -end - -local function dwarfmode_feed_input(...) - local screen = screen_dwarfmode() - if not df.isvalid(screen) then - qerror('could not retrieve dwarfmode screen') - end - try_save_cursor() - for _,v in ipairs({...}) do - gui.simulateInput(screen, v) - end -end - -local function dwarfmode_step_frames(count) - local screen = screen_dwarfmode() - if not df.isvalid(screen) then - qerror('could not retrieve dwarfmode screen') - end - - for i = 1,(count or 1) do - gui.simulateInput(screen, 'D_ONESTEP') - if screen.keyRepeat ~= 1 then - qerror('Could not step one frame: . did not work') - end - screen:logic() - end -end - -local function dwarfmode_to_top() - if not can_feed() then - return false - end - - local screen = screen_dwarfmode() - if not df.isvalid(screen) then - return false - end - - for i=0,10 do - if is_known 'ui' and df.global.ui.main.mode == df.ui_sidebar_mode.Default then - break - end - gui.simulateInput(screen, 'LEAVESCREEN') - end - - -- force pause just in case - screen.keyRepeat = 1 - return true -end - -local prev_cursor = df.global.T_cursor:new() -prev_cursor.x = -30000 -function try_save_cursor() - if not dfhack.internal.getAddress('cursor') then return end - for _, v in pairs(df.global.cursor) do - if v < 0 then - return - end - end - prev_cursor:assign(df.global.cursor) -end - -function try_restore_cursor() - if not dfhack.internal.getAddress('cursor') then return end - if prev_cursor.x >= 0 then - df.global.cursor:assign(prev_cursor) - dwarfmode_feed_input('CURSOR_DOWN_Z', 'CURSOR_UP_Z') - end -end - -local function feed_menu_choice(catnames,catkeys,enum,enter_seq,exit_seq,prompt) - return function (idx) - if idx == 0 and prompt and not prompt_proceed(2) then - return false - end - if idx > 0 then - dwarfmode_feed_input(table.unpack(exit_seq or {})) - end - idx = idx % #catnames + 1 - dwarfmode_feed_input(table.unpack(enter_seq or {})) - dwarfmode_feed_input(catkeys[idx]) - if enum then - return true, enum[catnames[idx]] - else - return true, catnames[idx] - end - end -end - -local function feed_list_choice(count,upkey,downkey) - return function(idx) - if idx > 0 then - local ok, len - if type(count) == 'number' then - ok, len = true, count - else - ok, len = pcall(count) - end - if not ok then - len = 5 - elseif len > 10 then - len = 10 - end - - local hcnt = len-1 - local rix = 1 + (idx-1) % (hcnt*2) - - if rix >= hcnt then - dwarfmode_feed_input(upkey or 'SECONDSCROLL_UP') - return true, hcnt*2 - rix - else - dwarfmode_feed_input(donwkey or 'SECONDSCROLL_DOWN') - return true, rix - end - else - print(' Please select the first list item.') - if not prompt_proceed(2) then - return false - end - return true, 0 - end - end -end - -local function feed_menu_bool(enter_seq, exit_seq) - return function(idx) - if idx == 0 then - if not prompt_proceed(2) then - return false - end - return true, 0 - end - if idx == 5 then - print(' Please resize the game window.') - if not prompt_proceed(2) then - return false - end - end - if idx%2 == 1 then - dwarfmode_feed_input(table.unpack(enter_seq)) - return true, 1 - else - dwarfmode_feed_input(table.unpack(exit_seq)) - return true, 0 - end - end -end - --- --- Cursor group --- - -local function find_cursor() - if not screen_title() then - return false - end - - -- Unpadded version - local idx, addr = data.int32_t:find_one{ - -30000, -30000, -30000, - -30000, -30000, -30000, -30000, -30000, -30000, - df.game_mode.NONE, df.game_type.NONE - } - if idx then - ms.found_offset('cursor', addr) - ms.found_offset('selection_rect', addr + 12) - ms.found_offset('gamemode', addr + 12 + 24) - ms.found_offset('gametype', addr + 12 + 24 + 4) - return true - end - - -- Padded version - idx, addr = data.int32_t:find_one{ - -30000, -30000, -30000, 0, - -30000, -30000, -30000, -30000, -30000, -30000, 0, 0, - df.game_mode.NONE, 0, 0, 0, df.game_type.NONE - } - if idx then - ms.found_offset('cursor', addr) - ms.found_offset('selection_rect', addr + 0x10) - ms.found_offset('gamemode', addr + 0x30) - ms.found_offset('gametype', addr + 0x40) - return true - end - - dfhack.printerr('Could not find cursor.') - return false -end - --- --- Announcements --- - -local function find_announcements() - local idx, addr = data.int32_t:find_one{ - 25, 25, 31, 31, 24, 24, 40, 40, 40, 40, 40, 40, 40 - } - if idx then - ms.found_offset('announcements', addr) - return - end - - dfhack.printerr('Could not find announcements.') -end - --- --- d_init --- - -local function is_valid_d_init(di) - if di.sky_tile ~= 178 then - print('Sky tile expected 178, found: '..di.sky_tile) - if not utils.prompt_yes_no('Ignore?') then - return false - end - end - - local ann = is_known 'announcements' - local size,ptr = di:sizeof() - if ann and ptr+size ~= ann then - print('Announcements not immediately after d_init.') - if not utils.prompt_yes_no('Ignore?') then - return false - end - end - - return true -end - -local function find_d_init() - local idx, addr = data.int16_t:find_one{ - 1,0, 2,0, 5,0, 25,0, -- path_cost - 4,4, -- embark_rect - 20,1000,1000,1000,1000 -- store_dist - } - if idx then - validate_offset('d_init', is_valid_d_init, addr, df.d_init, 'path_cost') - return - end - - dfhack.printerr('Could not find d_init') -end - --- --- gview --- - -local function find_gview() - local vs_vtable = dfhack.internal.getVTable('viewscreenst') - if not vs_vtable then - dfhack.printerr('Cannot search for gview - no viewscreenst vtable.') - return - end - - local idx, addr = data.uint32_t:find_one{0, vs_vtable} - if idx then - ms.found_offset('gview', addr) - return - end - - idx, addr = data.uint32_t:find_one{100, vs_vtable} - if idx then - ms.found_offset('gview', addr) - return - end - - dfhack.printerr('Could not find gview') -end - --- --- enabler --- - -local function lookup_colors() - local f = io.open('data/init/colors.txt', 'r') or error('failed to open file') - local text = f:read('*all') - f:close() - local colors = {} - for _, color in pairs({'BLACK', 'BLUE', 'GREEN', 'CYAN', 'RED', 'MAGENTA', - 'BROWN', 'LGRAY', 'DGRAY', 'LBLUE', 'LGREEN', 'LCYAN', 'LRED', - 'LMAGENTA', 'YELLOW', 'WHITE'}) do - for _, part in pairs({'R', 'G', 'B'}) do - local opt = color .. '_' .. part - table.insert(colors, tonumber(text:match(opt .. ':(%d+)') or error('missing from colors.txt: ' .. opt))) - end - end - return colors -end - -local function is_valid_enabler(e) - if not ms.is_valid_vector(e.textures.raws, 4) - or not ms.is_valid_vector(e.text_system, 4) - then - dfhack.printerr('Vector layout check failed.') - return false - end - - return true -end - -local function find_enabler() - -- Data from data/init/colors.txt - local default_colors = { - 0, 0, 0, 0, 0, 128, 0, 128, 0, - 0, 128, 128, 128, 0, 0, 128, 0, 128, - 128, 128, 0, 192, 192, 192, 128, 128, 128, - 0, 0, 255, 0, 255, 0, 0, 255, 255, - 255, 0, 0, 255, 0, 255, 255, 255, 0, - 255, 255, 255 - } - local colors - local ok, ret = pcall(lookup_colors) - if not ok then - dfhack.printerr('Failed to look up colors, using defaults: \n' .. ret) - colors = default_colors - else - colors = ret - end - - for i = 1,#colors do colors[i] = colors[i]/255 end - - local idx, addr = data.float:find_one(colors) - if not idx then - idx, addr = data.float:find_one(default_colors) - end - if idx then - validate_offset('enabler', is_valid_enabler, addr, df.enabler, 'ccolor') - return - end - - dfhack.printerr('Could not find enabler') -end - --- --- gps --- - -local function is_valid_gps(g) - if g.clipx[0] < 0 or g.clipx[0] > g.clipx[1] or g.clipx[1] >= g.dimx then - dfhack.printerr('Invalid clipx: ', g.clipx[0], g.clipx[1], g.dimx) - end - if g.clipy[0] < 0 or g.clipy[0] > g.clipy[1] or g.clipy[1] >= g.dimy then - dfhack.printerr('Invalid clipy: ', g.clipy[0], g.clipy[1], g.dimy) - end - - return true -end - -local function find_gps() - print('\nPlease ensure the mouse cursor is not over the game window.') - if not prompt_proceed() then - return - end - - local zone - if os_type == 'windows' or os_type == 'linux' then - zone = zoomed_searcher('cursor', 0x1000) - elseif os_type == 'darwin' then - zone = zoomed_searcher('enabler', 0x1000) - end - zone = zone or searcher - - local w,h = ms.get_screen_size() - - local idx, addr = zone.area.int32_t:find_one{w, h, -1, -1} - if not idx then - idx, addr = data.int32_t:find_one{w, h, -1, -1} - end - if idx then - validate_offset('gps', is_valid_gps, addr, df.graphic, 'dimx') - return - end - - dfhack.printerr('Could not find gps') -end - --- --- World --- - -local function is_valid_world(world) - if not ms.is_valid_vector(world.units.all, 4) - or not ms.is_valid_vector(world.units.active, 4) - or not ms.is_valid_vector(world.units.bad, 4) - or not ms.is_valid_vector(world.history.figures, 4) - or not ms.is_valid_vector(world.features.map_features, 4) - then - dfhack.printerr('Vector layout check failed.') - return false - end - - if #world.units.all == 0 or #world.units.all ~= #world.units.bad then - print('Different or zero size of units.all and units.bad:'..#world.units.all..' vs '..#world.units.bad) - if not utils.prompt_yes_no('Ignore?') then - return false - end - end - - return true -end - -local function find_world() - local catnames = { - 'Corpses', 'Refuse', 'Stone', 'Wood', 'Gems', 'Bars', 'Cloth', 'Leather', 'Ammo', 'Coins' - } - local catkeys = { - 'STOCKPILE_GRAVEYARD', 'STOCKPILE_REFUSE', 'STOCKPILE_STONE', 'STOCKPILE_WOOD', - 'STOCKPILE_GEM', 'STOCKPILE_BARBLOCK', 'STOCKPILE_CLOTH', 'STOCKPILE_LEATHER', - 'STOCKPILE_AMMO', 'STOCKPILE_COINS' - } - local addr - - if dwarfmode_to_top() then - dwarfmode_feed_input('D_STOCKPILES') - - addr = searcher:find_interactive( - 'Auto-searching for world.', - 'int32_t', - feed_menu_choice(catnames, catkeys, df.stockpile_category), - 20 - ) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for world. Please open the stockpile creation -menu, and select different types as instructed below:]], - 'int32_t', catnames, df.stockpile_category - ) - end - - validate_offset('world', is_valid_world, addr, df.world, 'selected_stockpile_type') -end - --- --- UI --- - -local function is_valid_ui(ui) - if not ms.is_valid_vector(ui.economic_stone, 1) - or not ms.is_valid_vector(ui.dipscripts, 4) - then - dfhack.printerr('Vector layout check failed.') - return false - end - - if ui.follow_item ~= -1 or ui.follow_unit ~= -1 then - print('Invalid follow state: '..ui.follow_item..', '..ui.follow_unit) - return false - end - - return true -end - -local function find_ui() - local catnames = { - 'DesignateMine', 'DesignateChannel', 'DesignateRemoveRamps', - 'DesignateUpStair', 'DesignateDownStair', 'DesignateUpDownStair', - 'DesignateUpRamp', 'DesignateChopTrees' - } - local catkeys = { - 'DESIGNATE_DIG', 'DESIGNATE_CHANNEL', 'DESIGNATE_DIG_REMOVE_STAIRS_RAMPS', - 'DESIGNATE_STAIR_UP', 'DESIGNATE_STAIR_DOWN', 'DESIGNATE_STAIR_UPDOWN', - 'DESIGNATE_RAMP', 'DESIGNATE_CHOP' - } - local addr - - if dwarfmode_to_top() then - dwarfmode_feed_input('D_DESIGNATE') - - addr = searcher:find_interactive( - 'Auto-searching for ui.', - 'int16_t', - feed_menu_choice(catnames, catkeys, df.ui_sidebar_mode), - 20 - ) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for ui. Please open the designation -menu, and switch modes as instructed below:]], - 'int16_t', catnames, df.ui_sidebar_mode - ) - end - - validate_offset('ui', is_valid_ui, addr, df.ui, 'main', 'mode') -end - --- --- ui_sidebar_menus --- - -local function is_valid_ui_sidebar_menus(usm) - if not ms.is_valid_vector(usm.workshop_job.choices_all, 4) - or not ms.is_valid_vector(usm.workshop_job.choices_visible, 4) - then - dfhack.printerr('Vector layout check failed.') - return false - end - - if #usm.workshop_job.choices_all == 0 - or #usm.workshop_job.choices_all ~= #usm.workshop_job.choices_visible then - print('Different or zero size of visible and all choices:'.. - #usm.workshop_job.choices_all..' vs '..#usm.workshop_job.choices_visible) - if not utils.prompt_yes_no('Ignore?') then - return false - end - end - - return true -end - -local function find_ui_sidebar_menus() - local addr - - if dwarfmode_to_top() then - dwarfmode_feed_input('D_BUILDJOB') - - addr = searcher:find_interactive([[ -Auto-searching for ui_sidebar_menus. Please select a Mason's, -Craftsdwarf's, or Carpenter's workshop:]], - 'int32_t', - function(idx) - if idx == 0 then - prompt_proceed(2) - -- ensure that the job list isn't full - dwarfmode_feed_input('BUILDJOB_CANCEL', 'BUILDJOB_ADD') - return true, 0 - end - return feed_list_choice(7)(idx) - end, - 20 - ) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for ui_sidebar_menus. Please switch to 'q' mode, -select a Mason, Craftsdwarfs, or Carpenters workshop, open -the Add Job menu, and move the cursor within:]], - 'int32_t', - { 0, 1, 2, 3, 4, 5, 6 }, - ordinal_names - ) - end - - validate_offset('ui_sidebar_menus', is_valid_ui_sidebar_menus, - addr, df.ui_sidebar_menus, 'workshop_job', 'cursor') -end - --- --- ui_build_selector --- - -local function is_valid_ui_build_selector(ubs) - if not ms.is_valid_vector(ubs.requirements, 4) - or not ms.is_valid_vector(ubs.choices, 4) - then - dfhack.printerr('Vector layout check failed.') - return false - end - - if ubs.building_type ~= df.building_type.Trap - or ubs.building_subtype ~= df.trap_type.PressurePlate then - print('Invalid building type and subtype:'..ubs.building_type..','..ubs.building_subtype) - return false - end - - return true -end - -local function find_ui_build_selector() - local addr - - if dwarfmode_to_top() then - addr = searcher:find_interactive([[ -Auto-searching for ui_build_selector. This requires mechanisms.]], - 'int32_t', - function(idx) - if idx == 0 then - dwarfmode_to_top() - dwarfmode_feed_input( - 'D_BUILDING', - 'HOTKEY_BUILDING_TRAP', - 'HOTKEY_BUILDING_TRAP_TRIGGER', - 'BUILDING_TRIGGER_ENABLE_CREATURE' - ) - else - dwarfmode_feed_input('BUILDING_TRIGGER_MIN_SIZE_UP') - end - return true, 5000 + 1000*idx - end, - 20 - ) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for ui_build_selector. Please start constructing -a pressure plate, and enable creatures. Then change the min -weight as requested, knowing that the ui shows 5000 as 5K:]], - 'int32_t', - { 5000, 6000, 7000, 8000, 9000, 10000, 11000 } - ) - end - - validate_offset('ui_build_selector', is_valid_ui_build_selector, - addr, df.ui_build_selector, 'plate_info', 'unit_min') -end - --- --- init --- - -local function is_valid_init(i) - -- derived from curses_*.png image sizes presumably - if i.font.small_font_dispx ~= 8 or i.font.small_font_dispy ~= 12 or - i.font.large_font_dispx ~= 10 or i.font.large_font_dispy ~= 12 then - print('Unexpected font sizes: ', - i.font.small_font_dispx, i.font.small_font_dispy, - i.font.large_font_dispx, i.font.large_font_dispy) - if not utils.prompt_yes_no('Ignore?') then - return false - end - end - - return true -end - -local function find_init() - local zone - if os_type == 'windows' then - zone = zoomed_searcher('ui_build_selector', 0x3000) - elseif os_type == 'linux' or os_type == 'darwin' then - zone = zoomed_searcher('d_init', -0x2000) - end - zone = zone or searcher - - local idx, addr = zone.area.int32_t:find_one{250, 150, 15, 0} - if idx then - validate_offset('init', is_valid_init, addr, df.init, 'input', 'hold_time') - return - end - - local w,h = ms.get_screen_size() - - local idx, addr = zone.area.int32_t:find_one{w, h} - if idx then - validate_offset('init', is_valid_init, addr, df.init, 'display', 'grid_x') - return - end - - dfhack.printerr('Could not find init') -end - --- --- current_weather --- - -local function find_current_weather() - local zone - if os_type == 'windows' then - zone = zoomed_searcher('crime_next_id', 512) - elseif os_type == 'darwin' then - zone = zoomed_searcher('cursor', -64) - elseif os_type == 'linux' then - zone = zoomed_searcher('ui_building_assign_type', -512) - end - zone = zone or searcher - - local wbytes = { - 2, 1, 0, 2, 0, - 1, 2, 1, 0, 0, - 2, 0, 2, 1, 2, - 1, 2, 0, 1, 1, - 2, 0, 1, 0, 2 - } - - local idx, addr = zone.area.int8_t:find_one(wbytes) - if idx then - ms.found_offset('current_weather', addr) - return - end - - dfhack.printerr('Could not find current_weather - must be a wrong save.') -end - --- --- ui_menu_width --- - -local function find_ui_menu_width() - local addr - - if dwarfmode_to_top() then - addr = searcher:find_interactive('Auto-searching for ui_menu_width', 'int8_t', function(idx) - local val = (idx % 3) + 1 - if idx == 0 then - print('Switch to the default [map][menu][map] layout (with Tab)') - if not prompt_proceed(2) then return false end - else - dwarfmode_feed_input('CHANGETAB', val ~= 3 and 'CHANGETAB') - end - return true, val - end) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for ui_menu_width. Please exit to the main -dwarfmode menu, then use Tab to do as instructed below:]], - 'int8_t', - { 2, 3, 1 }, - { [2] = 'switch to the most usual [mapmap][menu] layout', - [3] = 'hide the menu completely', - [1] = 'switch to the default [map][menu][map] layout' } - ) - end - - ms.found_offset('ui_menu_width', addr) - - -- NOTE: Assume that the vars are adjacent, as always - ms.found_offset('ui_area_map_width', addr+1) - - -- reset to make sure view is small enough for window_x/y scan on small maps - df.global.ui_menu_width = 2 - df.global.ui_area_map_width = 3 -end - --- --- ui_selected_unit --- - -local function find_ui_selected_unit() - if not is_known 'world' then - dfhack.printerr('Cannot search for ui_selected_unit: no world') - return - end - - for i,unit in ipairs(df.global.world.units.active) do - -- This function does a lot of things and accesses histfigs, souls and so on: - --dfhack.units.setNickname(unit, i) - - -- Instead use just a simple bit of code that only requires the start of the - -- unit to be valid. It may not work properly with vampires or reset later - -- if unpaused, but is sufficient for this script and won't crash: - unit.name.nickname = tostring(i) - unit.name.has_name = true - end - - local addr = searcher:find_menu_cursor([[ -Searching for ui_selected_unit. Please activate the 'v' -mode, point it at units, and enter their numeric nickname -into the prompts below:]], - 'int32_t', - function() - return utils.prompt_input(' Enter index: ', utils.check_number) - end, - 'noprompt' - ) - ms.found_offset('ui_selected_unit', addr) -end - --- --- ui_unit_view_mode --- - -local function find_ui_unit_view_mode() - local catnames = { 'General', 'Inventory', 'Preferences', 'Wounds' } - local catkeys = { 'UNITVIEW_GEN', 'UNITVIEW_INV', 'UNITVIEW_PRF', 'UNITVIEW_WND' } - local addr - - if dwarfmode_to_top() and is_known('ui_selected_unit') then - dwarfmode_feed_input('D_VIEWUNIT') - - if df.global.ui_selected_unit < 0 then - df.global.ui_selected_unit = 0 - end - - addr = searcher:find_interactive( - 'Auto-searching for ui_unit_view_mode.', - 'int32_t', - feed_menu_choice(catnames, catkeys, df.ui_unit_view_mode.T_value), - 10 - ) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for ui_unit_view_mode. Having selected a unit -with 'v', switch the pages as requested:]], - 'int32_t', catnames, df.ui_unit_view_mode.T_value - ) - end - - ms.found_offset('ui_unit_view_mode', addr) -end - --- --- ui_look_cursor --- - -local function look_item_list_count() - return #df.global.ui_look_list.items -end - -local function find_ui_look_cursor() - local addr - - if dwarfmode_to_top() then - dwarfmode_feed_input('D_LOOK') - - addr = searcher:find_interactive([[ -Auto-searching for ui_look_cursor. Please select a tile -with at least 5 items or units on the ground, and move -the cursor as instructed:]], - 'int32_t', - feed_list_choice(look_item_list_count), - 20 - ) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for ui_look_cursor. Please activate the 'k' -mode, find a tile with many items or units on the ground, -and select list entries as instructed:]], - 'int32_t', - list_index_choices(look_item_list_count), - ordinal_names - ) - end - - ms.found_offset('ui_look_cursor', addr) -end - --- --- ui_building_item_cursor --- - -local function building_item_list_count() - return #df.global.world.selected_building.contained_items -end - -local function find_ui_building_item_cursor() - local addr - - if dwarfmode_to_top() then - dwarfmode_feed_input('D_BUILDITEM') - - addr = searcher:find_interactive([[ -Auto-searching for ui_building_item_cursor. Please highlight a -workshop, trade depot or other building with at least 5 contained -items, and select as instructed:]], - 'int32_t', - feed_list_choice(building_item_list_count), - 20 - ) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for ui_building_item_cursor. Please activate the 't' -mode, find a cluttered workshop, trade depot, or other building -with many contained items, and select as instructed:]], - 'int32_t', - list_index_choices(building_item_list_count), - ordinal_names - ) - end - - ms.found_offset('ui_building_item_cursor', addr) -end - --- --- ui_workshop_in_add --- - -local function find_ui_workshop_in_add() - local addr - - if dwarfmode_to_top() then - dwarfmode_feed_input('D_BUILDJOB') - - addr = searcher:find_interactive([[ -Auto-searching for ui_workshop_in_add. Please select a -workshop, e.g. Carpenters or Masons.]], - 'int8_t', - feed_menu_bool( - { 'BUILDJOB_CANCEL', 'BUILDJOB_ADD' }, - { 'SELECT', 'SELECT', 'SELECT', 'SELECT', 'SELECT' } - ), - 20 - ) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for ui_workshop_in_add. Please activate the 'q' -mode, find a workshop without jobs (or delete jobs), -and do as instructed below. - -NOTE: If not done after first 3-4 steps, resize the game window.]], - 'int8_t', - { 1, 0 }, - { [1] = 'enter the add job menu', - [0] = 'add job, thus exiting the menu' } - ) - end - - ms.found_offset('ui_workshop_in_add', addr) -end - --- --- ui_workshop_job_cursor --- - -local function workshop_job_list_count() - return #df.global.world.selected_building.jobs -end - -local function find_ui_workshop_job_cursor() - local addr - - if dwarfmode_to_top() then - dwarfmode_feed_input('D_BUILDJOB') - addr = searcher:find_interactive([[ -Auto-searching for ui_workshop_job_cursor. Please highlight a -Mason's or Carpenter's workshop, or any building with a job -selection interface navigable with just "Enter":]], - 'int32_t', - function(idx) - if idx == 0 then prompt_proceed(2) end - for i = 1, 10 - workshop_job_list_count() do - dwarfmode_feed_input('BUILDJOB_ADD', 'SELECT', 'SELECT', 'SELECT', 'SELECT', 'SELECT') - end - dwarfmode_feed_input('SECONDSCROLL_DOWN') - -- adding jobs resets the cursor position, so it is difficult to determine here - return true - end, - 20 - ) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for ui_workshop_job_cursor. Please activate the 'q' -mode, find a workshop with many jobs, and select as instructed:]], - 'int32_t', - list_index_choices(workshop_job_list_count), - ordinal_names - ) - end - - ms.found_offset('ui_workshop_job_cursor', addr) -end - --- --- ui_building_in_assign --- - -local function find_ui_building_in_assign() - local addr - - if dwarfmode_to_top() then - dwarfmode_feed_input('D_BUILDJOB') - try_restore_cursor() - - addr = searcher:find_interactive([[ -Auto-searching for ui_building_in_assign. Please select a room, -i.e. a bedroom, tomb, office, dining room or statue garden.]], - 'int8_t', - feed_menu_bool( - { { 'BUILDJOB_STATUE_ASSIGN', 'BUILDJOB_COFFIN_ASSIGN', - 'BUILDJOB_CHAIR_ASSIGN', 'BUILDJOB_TABLE_ASSIGN', - 'BUILDJOB_BED_ASSIGN' } }, - { 'LEAVESCREEN' } - ), - 20 - ) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for ui_building_in_assign. Please activate -the 'q' mode, select a room building (e.g. a bedroom) -and do as instructed below. - -NOTE: If not done after first 3-4 steps, resize the game window.]], - 'int8_t', - { 1, 0 }, - { [1] = 'enter the Assign owner menu', - [0] = 'press Esc to exit assign' } - ) - end - - ms.found_offset('ui_building_in_assign', addr) -end - --- --- ui_building_in_resize --- - -local function find_ui_building_in_resize() - local addr - - if dwarfmode_to_top() then - dwarfmode_feed_input('D_BUILDJOB') - try_restore_cursor() - - addr = searcher:find_interactive([[ -Auto-searching for ui_building_in_resize. Please select a room, -i.e. a bedroom, tomb, office, dining room or statue garden.]], - 'int8_t', - feed_menu_bool( - { { 'BUILDJOB_STATUE_SIZE', 'BUILDJOB_COFFIN_SIZE', - 'BUILDJOB_CHAIR_SIZE', 'BUILDJOB_TABLE_SIZE', - 'BUILDJOB_BED_SIZE' } }, - { 'LEAVESCREEN' } - ), - 20 - ) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for ui_building_in_resize. Please activate -the 'q' mode, select a room building (e.g. a bedroom) -and do as instructed below. - -NOTE: If not done after first 3-4 steps, resize the game window.]], - 'int8_t', - { 1, 0 }, - { [1] = 'enter the Resize room mode', - [0] = 'press Esc to exit resize' } - ) - end - - ms.found_offset('ui_building_in_resize', addr) -end - --- --- ui_lever_target_type --- -local function find_ui_lever_target_type() - local catnames = { - 'Bridge', 'Door', 'Floodgate', - 'Cage', 'Chain', 'TrackStop', - 'GearAssembly', - } - local catkeys = { - 'HOTKEY_TRAP_BRIDGE', 'HOTKEY_TRAP_DOOR', 'HOTKEY_TRAP_FLOODGATE', - 'HOTKEY_TRAP_CAGE', 'HOTKEY_TRAP_CHAIN', 'HOTKEY_TRAP_TRACK_STOP', - 'HOTKEY_TRAP_GEAR_ASSEMBLY', - } - local addr - - if dwarfmode_to_top() then - dwarfmode_feed_input('D_BUILDJOB') - - addr = searcher:find_interactive( - 'Auto-searching for ui_lever_target_type. Please select a lever:', - 'int8_t', - feed_menu_choice(catnames, catkeys, df.lever_target_type, - {'BUILDJOB_ADD'}, - {'LEAVESCREEN', 'LEAVESCREEN'}, - true -- prompt - ), - 20 - ) - end - - if not addr then - addr = searcher:find_menu_cursor([[ -Searching for ui_lever_target_type. Please select a lever with -'q' and enter the "add task" menu with 'a':]], - 'int8_t', catnames, df.lever_target_type - ) - end - - ms.found_offset('ui_lever_target_type', addr) -end - --- --- window_x --- - -local function feed_window_xyz(dec,inc,step) - return function(idx) - if idx == 0 then - for i = 1,30 do dwarfmode_feed_input(dec) end - else - dwarfmode_feed_input(inc) - end - return true, nil, step - end -end - -local function find_window_x() - local addr - - if dwarfmode_to_top() then - addr = searcher:find_interactive( - 'Auto-searching for window_x.', - 'int32_t', - feed_window_xyz('CURSOR_LEFT_FAST', 'CURSOR_RIGHT', 10), - 20 - ) - - dwarfmode_feed_input('D_HOTKEY1') - end - - if not addr then - addr = searcher:find_counter([[ -Searching for window_x. Please exit to main dwarfmode menu, -scroll to the LEFT edge, then do as instructed:]], - 'int32_t', 10, - 'Please press Right to scroll right one step.' - ) - end - - ms.found_offset('window_x', addr) -end - --- --- window_y --- - -local function find_window_y() - local addr - - if dwarfmode_to_top() then - addr = searcher:find_interactive( - 'Auto-searching for window_y.', - 'int32_t', - feed_window_xyz('CURSOR_UP_FAST', 'CURSOR_DOWN', 10), - 20 - ) - - dwarfmode_feed_input('D_HOTKEY1') - end - - if not addr then - addr = searcher:find_counter([[ -Searching for window_y. Please exit to main dwarfmode menu, -scroll to the TOP edge, then do as instructed:]], - 'int32_t', 10, - 'Please press Down to scroll down one step.' - ) - end - - ms.found_offset('window_y', addr) -end - --- --- window_z --- - -local function find_window_z() - local addr - - if dwarfmode_to_top() then - addr = searcher:find_interactive( - 'Auto-searching for window_z.', - 'int32_t', - feed_window_xyz('CURSOR_UP_Z', 'CURSOR_DOWN_Z', -1), - 30 - ) - - dwarfmode_feed_input('D_HOTKEY1') - end - - if not addr then - addr = searcher:find_counter([[ -Searching for window_z. Please exit to main dwarfmode menu, -scroll to a Z level near surface, then do as instructed below. - -NOTE: If not done after first 3-4 steps, resize the game window.]], - 'int32_t', -1, - "Please press '>' to scroll one Z level down." - ) - end - - ms.found_offset('window_z', addr) -end - --- --- cur_year --- - -local function find_cur_year() - local zone - if os_type == 'windows' then - zone = zoomed_searcher('formation_next_id', 32) - elseif os_type == 'darwin' then - zone = zoomed_searcher('cursor', -32) - elseif os_type == 'linux' then - zone = zoomed_searcher('ui_building_assign_type', -512) - end - if not zone then - dfhack.printerr('Cannot search for cur_year - prerequisites missing.') - return - end - - local yvalue = utils.prompt_input('Please enter current in-game year: ', utils.check_number) - local idx, addr = zone.area.int32_t:find_one{yvalue} - if idx then - ms.found_offset('cur_year', addr) - return - end - - dfhack.printerr('Could not find cur_year') -end - --- --- cur_year_tick --- - -function stop_autosave() - if is_known 'd_init' then - local f = df.global.d_init.flags4 - if f.AUTOSAVE_SEASONAL or f.AUTOSAVE_YEARLY then - f.AUTOSAVE_SEASONAL = false - f.AUTOSAVE_YEARLY = false - print('Disabled seasonal and yearly autosave.') - end - else - dfhack.printerr('Could not disable autosave!') - end -end - -function step_n_frames(cnt, feed) - local world = df.global.world - local ctick = world.frame_counter - - if feed then - print(" Auto-stepping "..cnt.." frames.") - dwarfmode_step_frames(cnt) - return world.frame_counter-ctick - end - - local more = '' - while world.frame_counter-ctick < cnt do - print(" Please step the game "..(cnt-world.frame_counter+ctick)..more.." frames.") - more = ' more' - if not prompt_proceed(2) then - return nil - end - end - return world.frame_counter-ctick -end - -local function find_cur_year_tick() - local zone - if os_type == 'windows' then - zone = zoomed_searcher('ui_unit_view_mode', 0x200) - else - zone = zoomed_searcher('cur_year', 128) - end - if not zone then - dfhack.printerr('Cannot search for cur_year_tick - prerequisites missing.') - return - end - - stop_autosave() - - local feed = dwarfmode_to_top() - local addr = zone:find_interactive( - 'Searching for cur_year_tick.', - 'int32_t', - function(idx) - if idx > 0 then - if not step_n_frames(1, feed) then - return false - end - end - return true, nil, 1 - end, - 20 - ) - - ms.found_offset('cur_year_tick', addr) -end - -local function find_cur_year_tick_advmode() - stop_autosave() - - local feed = dwarfmode_to_top() - local addr = searcher:find_interactive( - 'Searching for cur_year_tick_advmode.', - 'int32_t', - function(idx) - if idx > 0 then - if not step_n_frames(1, feed) then - return false - end - end - return true, nil, 144 - end, - 20 - ) - - ms.found_offset('cur_year_tick_advmode', addr) -end - --- --- cur_season_tick --- - -local function find_cur_season_tick() - if not (is_known 'cur_year_tick') then - dfhack.printerr('Cannot search for cur_season_tick - prerequisites missing.') - return - end - - stop_autosave() - - local feed = dwarfmode_to_top() - local addr = searcher:find_interactive([[ -Searching for cur_season_tick. Please exit to main dwarfmode -menu, then do as instructed below:]], - 'int32_t', - function(ccursor) - if ccursor > 0 then - if not step_n_frames(10, feed) then - return false - end - end - return true, math.floor((df.global.cur_year_tick%100800)/10) - end - ) - ms.found_offset('cur_season_tick', addr) -end - --- --- cur_season --- - -local function find_cur_season() - if not (is_known 'cur_year_tick' and is_known 'cur_season_tick') then - dfhack.printerr('Cannot search for cur_season - prerequisites missing.') - return - end - - stop_autosave() - - local feed = dwarfmode_to_top() - local addr = searcher:find_interactive([[ -Searching for cur_season. Please exit to main dwarfmode -menu, then do as instructed below:]], - 'int8_t', - function(ccursor) - if ccursor > 0 then - local cst = df.global.cur_season_tick - df.global.cur_season_tick = 10079 - df.global.cur_year_tick = df.global.cur_year_tick + (10079-cst)*10 - if not step_n_frames(10, feed) then - return false - end - end - return true, math.floor(df.global.cur_year_tick/100800)%4 - end - ) - ms.found_offset('cur_season', addr) -end - --- --- process_jobs --- - -local function get_process_zone() - if os_type == 'windows' then - return zoomed_searcher('ui_workshop_job_cursor', 'ui_building_in_resize') - elseif os_type == 'linux' or os_type == 'darwin' then - return zoomed_searcher('cur_year', 'cur_year_tick') - end -end - -local function find_process_jobs() - local zone = get_process_zone() or searcher - local addr - - stop_autosave() - - if dwarfmode_to_top() and dfhack.internal.getAddress('cursor') then - local cursor = df.global.T_cursor:new() - addr = zone:find_interactive([[ -Searching for process_jobs. Please position the cursor to the left -of at least 10 vacant natural floor tiles.]], - 'int8_t', - function(idx) - if idx == 0 then - dwarfmode_feed_input('D_LOOK') - if not prompt_proceed(2) then return false end - cursor:assign(df.global.cursor) - elseif idx == 6 then - print(' Please resize the game window.') - if not prompt_proceed(2) then return false end - end - dwarfmode_to_top() - dwarfmode_step_frames(1) - if idx % 2 == 0 then - dwarfmode_feed_input( - 'D_BUILDING', - 'HOTKEY_BUILDING_CONSTRUCTION', - 'HOTKEY_BUILDING_CONSTRUCTION_WALL' - ) - df.global.cursor:assign(cursor) - df.global.cursor.x = df.global.cursor.x + (idx / 2) - dwarfmode_feed_input('CURSOR_RIGHT', 'CURSOR_LEFT', 'SELECT', 'SELECT') - return true, 1 - else - return true, 0 - end - end, - 20) - end - - if not addr then - local addr = zone:find_menu_cursor([[ -Searching for process_jobs. Please do as instructed below:]], - 'int8_t', - { 1, 0 }, - { [1] = 'designate a building to be constructed, e.g a bed or a wall', - [0] = 'step or unpause the game to reset the flag' } - ) - end - ms.found_offset('process_jobs', addr) -end - --- --- process_dig --- - -local function find_process_dig() - local zone = get_process_zone() or searcher - local addr - - stop_autosave() - - if dwarfmode_to_top() and dfhack.internal.getAddress('cursor') then - local cursor = df.global.T_cursor:new() - addr = zone:find_interactive([[ -Searching for process_dig. Please position the cursor to the left -of at least 10 unmined, unrevealed tiles.]], - 'int8_t', - function(idx) - if idx == 0 then - dwarfmode_feed_input('D_LOOK') - if not prompt_proceed(2) then return false end - cursor:assign(df.global.cursor) - elseif idx == 6 then - print(' Please resize the game window.') - if not prompt_proceed(2) then return false end - end - dwarfmode_to_top() - dwarfmode_step_frames(1) - if idx % 2 == 0 then - dwarfmode_feed_input('D_DESIGNATE', 'DESIGNATE_DIG') - df.global.cursor:assign(cursor) - df.global.cursor.x = df.global.cursor.x + (idx / 2) - dwarfmode_feed_input('SELECT', 'SELECT') - return true, 1 - else - return true, 0 - end - end, - 20) - end - - if not addr then - addr = zone:find_menu_cursor([[ -Searching for process_dig. Please do as instructed below:]], - 'int8_t', - { 1, 0 }, - { [1] = 'designate a tile to be mined out', - [0] = 'step or unpause the game to reset the flag' } - ) - end - ms.found_offset('process_dig', addr) -end - --- --- pause_state --- - -local function find_pause_state() - local zone, addr - if os_type == 'linux' or os_type == 'darwin' then - zone = zoomed_searcher('ui_look_cursor', 32) - elseif os_type == 'windows' then - zone = zoomed_searcher('ui_workshop_job_cursor', 80) - end - zone = zone or searcher - - stop_autosave() - - if dwarfmode_to_top() then - addr = zone:find_interactive( - 'Auto-searching for pause_state', - 'int8_t', - function(idx) - if idx%2 == 0 then - dwarfmode_feed_input('D_ONESTEP') - return true, 0 - else - screen_dwarfmode():logic() - return true, 1 - end - end, - 20 - ) - end - - if not addr then - addr = zone:find_menu_cursor([[ -Searching for pause_state. Please do as instructed below:]], - 'int8_t', - { 1, 0 }, - { [1] = 'PAUSE the game', - [0] = 'UNPAUSE the game' } - ) - end - - ms.found_offset('pause_state', addr) -end - --- --- standing orders --- - -local function find_standing_orders(gname, seq, depends) - if type(seq) ~= 'table' then seq = {seq} end - for k, v in pairs(depends) do - if not dfhack.internal.getAddress(k) then - qerror(('Cannot locate %s: %s not found'):format(gname, k)) - end - df.global[k] = v - end - local addr - if dwarfmode_to_top() then - addr = searcher:find_interactive( - 'Auto-searching for ' .. gname, - 'uint8_t', - function(idx) - dwarfmode_feed_input('D_ORDERS') - dwarfmode_feed_input(table.unpack(seq)) - return true - end - ) - else - dfhack.printerr("Won't scan for standing orders global manually: " .. gname) - return - end - - ms.found_offset(gname, addr) -end - -local function exec_finder_so(gname, seq, _depends) - local depends = {} - for k, v in pairs(_depends or {}) do - if k:find('standing_orders_') ~= 1 then - k = 'standing_orders_' .. k - end - depends[k] = v - end - if force_scan['standing_orders'] then - force_scan[gname] = true - end - exec_finder(function() - return find_standing_orders(gname, seq, depends) - end, gname) -end - --- --- MAIN FLOW --- - -print('\nInitial globals (need title screen):\n') - -exec_finder(find_gview, 'gview') -exec_finder(find_cursor, { 'cursor', 'selection_rect', 'gamemode', 'gametype' }) -exec_finder(find_announcements, 'announcements') -exec_finder(find_d_init, 'd_init', is_valid_d_init) -exec_finder(find_enabler, 'enabler', is_valid_enabler) -exec_finder(find_gps, 'gps', is_valid_gps) - -print('\nCompound globals (need loaded world):\n') - -print('\nPlease load the save previously processed with prepare-save.') -if not prompt_proceed() then - searcher:reset() - return -end - -exec_finder(find_world, 'world', is_valid_world) -exec_finder(find_ui, 'ui', is_valid_ui) -exec_finder(find_ui_sidebar_menus, 'ui_sidebar_menus') -exec_finder(find_ui_build_selector, 'ui_build_selector') -exec_finder(find_init, 'init', is_valid_init) - -print('\nPrimitive globals:\n') - -exec_finder(find_current_weather, 'current_weather') -exec_finder(find_ui_menu_width, { 'ui_menu_width', 'ui_area_map_width' }) -exec_finder(find_ui_selected_unit, 'ui_selected_unit') -exec_finder(find_ui_unit_view_mode, 'ui_unit_view_mode') -exec_finder(find_ui_look_cursor, 'ui_look_cursor') -exec_finder(find_ui_building_item_cursor, 'ui_building_item_cursor') -exec_finder(find_ui_workshop_in_add, 'ui_workshop_in_add') -exec_finder(find_ui_workshop_job_cursor, 'ui_workshop_job_cursor') -exec_finder(find_ui_building_in_assign, 'ui_building_in_assign') -exec_finder(find_ui_building_in_resize, 'ui_building_in_resize') -exec_finder(find_ui_lever_target_type, 'ui_lever_target_type') -exec_finder(find_window_x, 'window_x') -exec_finder(find_window_y, 'window_y') -exec_finder(find_window_z, 'window_z') - -print('\nUnpausing globals:\n') - -exec_finder(find_cur_year, 'cur_year') -exec_finder(find_cur_year_tick, 'cur_year_tick') -exec_finder(find_cur_year_tick_advmode, 'cur_year_tick_advmode') -exec_finder(find_cur_season_tick, 'cur_season_tick') -exec_finder(find_cur_season, 'cur_season') -exec_finder(find_process_jobs, 'process_jobs') -exec_finder(find_process_dig, 'process_dig') -exec_finder(find_pause_state, 'pause_state') - -print('\nStanding orders:\n') - -exec_finder_so('standing_orders_gather_animals', 'ORDERS_GATHER_ANIMALS') -exec_finder_so('standing_orders_gather_bodies', 'ORDERS_GATHER_BODIES') -exec_finder_so('standing_orders_gather_food', 'ORDERS_GATHER_FOOD') -exec_finder_so('standing_orders_gather_furniture', 'ORDERS_GATHER_FURNITURE') -exec_finder_so('standing_orders_gather_minerals', 'ORDERS_GATHER_STONE') -exec_finder_so('standing_orders_gather_wood', 'ORDERS_GATHER_WOOD') - -exec_finder_so('standing_orders_gather_refuse', - {'ORDERS_REFUSE', 'ORDERS_REFUSE_GATHER'}) -exec_finder_so('standing_orders_gather_refuse_outside', - {'ORDERS_REFUSE', 'ORDERS_REFUSE_OUTSIDE'}, {gather_refuse=1}) -exec_finder_so('standing_orders_gather_vermin_remains', - {'ORDERS_REFUSE', 'ORDERS_REFUSE_OUTSIDE_VERMIN'}, {gather_refuse=1, gather_refuse_outside=1}) -exec_finder_so('standing_orders_dump_bones', - {'ORDERS_REFUSE', 'ORDERS_REFUSE_DUMP_BONE'}, {gather_refuse=1}) -exec_finder_so('standing_orders_dump_corpses', - {'ORDERS_REFUSE', 'ORDERS_REFUSE_DUMP_CORPSE'}, {gather_refuse=1}) -exec_finder_so('standing_orders_dump_hair', - {'ORDERS_REFUSE', 'ORDERS_REFUSE_DUMP_STRAND_TISSUE'}, {gather_refuse=1}) -exec_finder_so('standing_orders_dump_other', - {'ORDERS_REFUSE', 'ORDERS_REFUSE_DUMP_OTHER'}, {gather_refuse=1}) -exec_finder_so('standing_orders_dump_shells', - {'ORDERS_REFUSE', 'ORDERS_REFUSE_DUMP_SHELL'}, {gather_refuse=1}) -exec_finder_so('standing_orders_dump_skins', - {'ORDERS_REFUSE', 'ORDERS_REFUSE_DUMP_SKIN'}, {gather_refuse=1}) -exec_finder_so('standing_orders_dump_skulls', - {'ORDERS_REFUSE', 'ORDERS_REFUSE_DUMP_SKULL'}, {gather_refuse=1}) - - -exec_finder_so('standing_orders_auto_butcher', - {'ORDERS_WORKSHOP', 'ORDERS_BUTCHER'}) -exec_finder_so('standing_orders_auto_collect_webs', - {'ORDERS_WORKSHOP', 'ORDERS_COLLECT_WEB'}) -exec_finder_so('standing_orders_auto_fishery', - {'ORDERS_WORKSHOP', 'ORDERS_AUTO_FISHERY'}) -exec_finder_so('standing_orders_auto_kiln', - {'ORDERS_WORKSHOP', 'ORDERS_AUTO_KILN'}) -exec_finder_so('standing_orders_auto_kitchen', - {'ORDERS_WORKSHOP', 'ORDERS_AUTO_KITCHEN'}) -exec_finder_so('standing_orders_auto_loom', - {'ORDERS_WORKSHOP', 'ORDERS_LOOM'}) -exec_finder_so('standing_orders_auto_other', - {'ORDERS_WORKSHOP', 'ORDERS_AUTO_OTHER'}) -exec_finder_so('standing_orders_auto_slaughter', - {'ORDERS_WORKSHOP', 'ORDERS_SLAUGHTER'}) -exec_finder_so('standing_orders_auto_smelter', - {'ORDERS_WORKSHOP', 'ORDERS_AUTO_SMELTER'}) -exec_finder_so('standing_orders_auto_tan', - {'ORDERS_WORKSHOP', 'ORDERS_TAN'}) -exec_finder_so('standing_orders_use_dyed_cloth', - {'ORDERS_WORKSHOP', 'ORDERS_DYED_CLOTH'}) - -exec_finder_so('standing_orders_forbid_other_dead_items', - {'ORDERS_AUTOFORBID', 'ORDERS_FORBID_OTHER_ITEMS'}) -exec_finder_so('standing_orders_forbid_other_nohunt', - {'ORDERS_AUTOFORBID', 'ORDERS_FORBID_OTHER_CORPSE'}) -exec_finder_so('standing_orders_forbid_own_dead', - {'ORDERS_AUTOFORBID', 'ORDERS_FORBID_YOUR_CORPSE'}) -exec_finder_so('standing_orders_forbid_own_dead_items', - {'ORDERS_AUTOFORBID', 'ORDERS_FORBID_YOUR_ITEMS'}) -exec_finder_so('standing_orders_forbid_used_ammo', - {'ORDERS_AUTOFORBID', 'ORDERS_FORBID_PROJECTILE'}) - -exec_finder_so('standing_orders_farmer_harvest', 'ORDERS_ALL_HARVEST') -exec_finder_so('standing_orders_job_cancel_announce', 'ORDERS_EXCEPTIONS') -exec_finder_so('standing_orders_mix_food', 'ORDERS_MIXFOODS') - -exec_finder_so('standing_orders_zoneonly_drink', - {'ORDERS_ZONE', 'ORDERS_ZONE_DRINKING'}) -exec_finder_so('standing_orders_zoneonly_fish', - {'ORDERS_ZONE', 'ORDERS_ZONE_FISHING'}) - -dwarfmode_to_top() -print('\nDone. Now exit the game with the die command and add\n'.. - 'the newly-found globals to symbols.xml. You can find them\n'.. - 'in stdout.log or here:\n') - -for _, global in ipairs(finder_searches) do - local addr = dfhack.internal.getAddress(global) - if addr ~= nil then - local ival = addr - dfhack.internal.getRebaseDelta() - print(string.format("", global, ival)) - end -end - -searcher:reset() diff --git a/scripts/devel/inject-raws.lua b/scripts/devel/inject-raws.lua deleted file mode 100644 index 048d4b560..000000000 --- a/scripts/devel/inject-raws.lua +++ /dev/null @@ -1,197 +0,0 @@ --- Inject new raw definitions into the world ---[[=begin - -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 - -=end]] - -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 - -function inject_reaction(name) - for _,v in ipairs(raws.reactions) do - if v.code == name then - print('Reaction '..name..' already exists.') - return - end - end - - print('Injecting reaction '..name) - changed = true - - raws.reactions:insert('#', { - new = true, - code = name, - name = 'Dummy reaction '..name, - index = #raws.reactions, - }) -end - -local building_types = { - workshop = { df.building_def_workshopst, raws.buildings.workshops }, - furnace = { df.building_def_furnacest, raws.buildings.furnaces }, -} - -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 = { - 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_glovest, itemdefs.gloves, 'gloves_type' }, - shoes = { df.itemdef_shoest, 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) - for _,v in ipairs(entity.resources[bvec]) do - if v == id then - return - end - end - - entity.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 - add_to_civ(entity, typeinfo[3], id) - end - end -end - -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 = {} - -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 diff --git a/scripts/devel/inspect-screen.lua b/scripts/devel/inspect-screen.lua deleted file mode 100644 index 8f622756f..000000000 --- a/scripts/devel/inspect-screen.lua +++ /dev/null @@ -1,110 +0,0 @@ --- Read from the screen and display info about the tiles ---[[=begin - -devel/inspect-screen -==================== -Read the tiles from the screen and display info about them. - -=end]] - -local utils = require 'utils' -local gui = require 'gui' - -InspectScreen = defclass(InspectScreen, gui.Screen) - -function InspectScreen:init(args) - local w,h = dfhack.screen.getWindowSize() - self.cursor_x = math.floor(w/2) - self.cursor_y = math.floor(h/2) -end - -function InspectScreen:computeFrame(parent_rect) - local sw, sh = parent_rect.width, parent_rect.height - self.cursor_x = math.max(0, math.min(self.cursor_x, sw-1)) - self.cursor_y = math.max(0, math.min(self.cursor_y, sh-1)) - - local frame = { w = 14, r = 1, h = 10, t = 1 } - if self.cursor_x > sw/2 then - frame = { w = 14, l = 1, h = 10, t = 1 } - end - - return gui.compute_frame_body(sw, sh, frame, 1, 0, false) -end - -function InspectScreen:onRenderFrame(dc, rect) - self:renderParent() - self.cursor_pen = dfhack.screen.readTile(self.cursor_x, self.cursor_y) - if gui.blink_visible(100) then - dfhack.screen.paintTile({ch='X',fg=COLOR_LIGHTGREEN}, self.cursor_x, self.cursor_y) - end - dc:fill(rect, {ch=' ',fg=COLOR_WHITE,bg=COLOR_CYAN}) -end - -local FG_PEN = {fg=COLOR_WHITE,bg=COLOR_BLACK,tile_color=true} -local BG_PEN = {fg=COLOR_BLACK,bg=COLOR_WHITE,tile_color=true} -local TXT_PEN = {fg=COLOR_WHITE} - -function InspectScreen:onRenderBody(dc) - dc:pen(COLOR_WHITE, COLOR_CYAN) - if self.cursor_pen then - local info = self.cursor_pen - dc:string('CH: '):char(info.ch, FG_PEN):char(info.ch, BG_PEN):string(' '):string(''..info.ch,TXT_PEN):newline() - local fgcolor = info.fg - local fgstr = info.fg - if info.bold then - fgcolor = (fgcolor+8)%16 - fgstr = fgstr..'+8' - end - dc:string('FG: '):string('NN',{fg=fgcolor}):string(' '):string(''..fgstr,TXT_PEN) - dc:seek(dc.width-1):char(info.ch,{fg=info.fg,bold=info.bold}):newline() - dc:string('BG: '):string('NN',{fg=info.bg}):string(' '):string(''..info.bg,TXT_PEN) - dc:seek(dc.width-1):char(info.ch,{fg=COLOR_BLACK,bg=info.bg}):newline() - local bstring = 'false' - if info.bold then bstring = 'true' end - dc:string('Bold: '..bstring):newline():newline() - - if info.tile and gui.USE_GRAPHICS then - dc:string('TL: '):tile(' ', info.tile, FG_PEN):tile(' ', info.tile, BG_PEN):string(' '..info.tile):newline() - if info.tile_color then - dc:string('Color: true') - elseif info.tile_fg then - dc:string('FG: '):string('NN',{fg=info.tile_fg}):string(' '):string(''..info.tile_fg,TXT_PEN):newline() - dc:string('BG: '):string('NN',{fg=info.tile_bg}):string(' '):string(''..info.tile_bg,TXT_PEN):newline() - end - end - else - dc:string('Invalid', COLOR_LIGHTRED) - end -end - -local MOVEMENT_KEYS = { - CURSOR_UP = { 0, -1, 0 }, CURSOR_DOWN = { 0, 1, 0 }, - CURSOR_LEFT = { -1, 0, 0 }, CURSOR_RIGHT = { 1, 0, 0 }, - CURSOR_UPLEFT = { -1, -1, 0 }, CURSOR_UPRIGHT = { 1, -1, 0 }, - CURSOR_DOWNLEFT = { -1, 1, 0 }, CURSOR_DOWNRIGHT = { 1, 1, 0 }, - CURSOR_UP_FAST = { 0, -1, 0, true }, CURSOR_DOWN_FAST = { 0, 1, 0, true }, - CURSOR_LEFT_FAST = { -1, 0, 0, true }, CURSOR_RIGHT_FAST = { 1, 0, 0, true }, - CURSOR_UPLEFT_FAST = { -1, -1, 0, true }, CURSOR_UPRIGHT_FAST = { 1, -1, 0, true }, - CURSOR_DOWNLEFT_FAST = { -1, 1, 0, true }, CURSOR_DOWNRIGHT_FAST = { 1, 1, 0, true }, -} - -function InspectScreen:onInput(keys) - if keys.LEAVESCREEN then - self:dismiss() - else - for k,v in pairs(MOVEMENT_KEYS) do - if keys[k] then - local delta = 1 - if v[4] then - delta = 10 - end - self.cursor_x = self.cursor_x + delta*v[1] - self.cursor_y = self.cursor_y + delta*v[2] - self:updateLayout() - return - end - end - end -end - -InspectScreen{}:show() diff --git a/scripts/devel/light.lua b/scripts/devel/light.lua deleted file mode 100644 index e19591e11..000000000 --- a/scripts/devel/light.lua +++ /dev/null @@ -1,388 +0,0 @@ --- an experimental lighting engine ---[[=begin - -devel/light -=========== -An experimental lighting engine for DF, using the `rendermax` plugin. - -Call ``devel/light static`` to not recalculate lighting when in game. -Press :kbd:`~` to recalculate lighting. Press :kbd:`\`` to exit. - -=end]] - -local gui = require 'gui' -local guidm = require 'gui.dwarfmode' -local render = require 'plugins.rendermax' - -local levelDim=0.05 -local tile_attrs = df.tiletype.attrs - -local args={...} - -function setCell(x,y,cell) - cell=cell or {} - cell.fm=cell.fm or {r=1,g=1,b=1} - cell.bm=cell.bm or {r=1,g=1,b=1} - cell.fo=cell.fo or {r=0,g=0,b=0} - cell.bo=cell.bo or {r=0,g=0,b=0} - render.setCell(x,y,cell) -end -function getCursorPos() - local g_cursor=df.global.cursor - if g_cursor.x ~= -30000 then - return copyall(g_cursor) - end -end -function falloff(color,sqDist,maxdist) - local v1=1/(sqDist/maxdist+1) - local v2=v1-1/(1+maxdist*maxdist) - local v=v2/(1-1/(1+maxdist*maxdist)) - return {r=v*color.r,g=v*color.g,b=v*color.b} -end -function blend(c1,c2) -return {r=math.max(c1.r,c2.r), - g=math.max(c1.g,c2.g), - b=math.max(c1.b,c2.b)} -end -LightOverlay=defclass(LightOverlay,guidm.DwarfOverlay) -LightOverlay.ATTRS { - lightMap={}, - dynamic=true, - dirty=false, -} -function LightOverlay:init(args) - - self.tick=df.global.cur_year_tick_advmode -end - -function lightPassable(shape) - if shape==df.tiletype_shape.WALL or - shape==df.tiletype_shape.BROOK_BED or - shape==df.tiletype_shape.TREE then - return false - else - return true - end -end -function circle(xm, ym,r,plot) - local x = -r - local y = 0 - local err = 2-2*r -- /* II. Quadrant */ - repeat - plot(xm-x, ym+y);--/* I. Quadrant */ - plot(xm-y, ym-x);--/* II. Quadrant */ - plot(xm+x, ym-y);--/* III. Quadrant */ - plot(xm+y, ym+x);--/* IV. Quadrant */ - r = err; - if (r <= y) then - y=y+1 - err =err+y*2+1; --/* e_xy+e_y < 0 */ - end - if (r > x or err > y) then - x=x+1 - err =err+x*2+1; --/* e_xy+e_x > 0 or no 2nd y-step */ - end - until (x >= 0); -end -function line(x0, y0, x1, y1,plot) - local dx = math.abs(x1-x0) - local dy = math.abs(y1-y0) - local sx,sy - if x0 < x1 then sx = 1 else sx = -1 end - if y0 < y1 then sy = 1 else sy = -1 end - local err = dx-dy - - while true do - if not plot(x0,y0) then - return - end - if x0 == x1 and y0 == y1 then - break - end - local e2 = 2*err - if e2 > -dy then - err = err - dy - x0 = x0 + sx - end - if x0 == x1 and y0 == y1 then - if not plot(x0,y0) then - return - end - break - end - if e2 < dx then - err = err + dx - y0 = y0 + sy - end - end -end -function LightOverlay:calculateFovs() - self.fovs=self.fovs or {} - self.precalc=self.precalc or {} - for k,v in ipairs(self.fovs) do - self:calculateFov(v.pos,v.radius,v.color) - end -end -function LightOverlay:calculateFov(pos,radius,color) - local vp=self:getViewport() - local map = self.df_layout.map - local ray=function(tx,ty) - local power=copyall(color) - local lx=pos.x - local ly=pos.y - local setTile=function(x,y) - if x>0 and y>0 and x<=map.width and y<=map.height then - local dtsq=(lx-x)*(lx-x)+(ly-y)*(ly-y) - local dt=math.sqrt(dtsq) - local tile=x+y*map.width - if self.precalc[tile] then - local tcol=blend(self.precalc[tile],power) - if tcol.r==self.precalc[tile].r and tcol.g==self.precalc[tile].g and self.precalc[tile].b==self.precalc[tile].b - and dtsq>0 then - return false - end - end - local ocol=self.lightMap[tile] or {r=0,g=0,b=0} - local ncol=blend(power,ocol) - - self.lightMap[tile]=ncol - local v=self.ocupancy[tile] - if dtsq>0 then - power.r=power.r*(v.r^dt) - power.g=power.g*(v.g^dt) - power.b=power.b*(v.b^dt) - end - lx=x - ly=y - local pwsq=power.r*power.r+power.g*power.g+power.b*power.b - return pwsq>levelDim*levelDim - end - return false - end - line(pos.x,pos.y,tx,ty,setTile) - end - circle(pos.x,pos.y,radius,ray) -end -function LightOverlay:placeLightFov(pos,radius,color) - local map = self.df_layout.map - local tile=pos.x+pos.y*map.width - local ocol=self.precalc[tile] or {r=0,g=0,b=0} - local ncol=blend(color,ocol) - self.precalc[tile]=ncol - local ocol=self.lightMap[tile] or {r=0,g=0,b=0} - local ncol=blend(color,ocol) - self.lightMap[tile]=ncol - table.insert(self.fovs,{pos=pos,radius=radius,color=color}) -end -function LightOverlay:placeLightFov2(pos,radius,color,f,rays) - f=f or falloff - local raycount=rays or 25 - local vp=self:getViewport() - local map = self.df_layout.map - local off=math.random(0,math.pi) - local done={} - for d=0,math.pi*2,math.pi*2/raycount do - local dx,dy - dx=math.cos(d+off) - dy=math.sin(d+off) - local cx=0 - local cy=0 - - for dt=0,radius,0.01 do - if math.abs(math.floor(dt*dx)-cx)>0 or math.abs(math.floor(dt*dy)-cy)> 0 then - local x=cx+pos.x - local y=cy+pos.y - - if x>0 and y>0 and x<=map.width and y<=map.height and not done[tile] then - local tile=x+y*map.width - done[tile]=true - local ncol=f(color,dt*dt,radius) - local ocol=self.lightMap[tile] or {r=0,g=0,b=0} - ncol=blend(ncol,ocol) - self.lightMap[tile]=ncol - - - if --(ncol.r==ocol.r and ncol.g==ocol.g and ncol.b==ocol.b) or - not self.ocupancy[tile] then - break - end - end - cx=math.floor(dt*dx) - cy=math.floor(dt*dy) - end - end - end -end -function LightOverlay:placeLight(pos,radius,color,f) - f=f or falloff - local vp=self:getViewport() - local map = self.df_layout.map - - for i=-radius,radius do - for j=-radius,radius do - local x=pos.x+i+1 - local y=pos.y+j+1 - if x>0 and y>0 and x<=map.width and y<=map.height then - local tile=x+y*map.width - local ncol=f(color,(i*i+j*j),radius) - local ocol=self.lightMap[tile] or {r=0,g=0,b=0} - self.lightMap[tile]=blend(ncol,ocol) - end - end - end -end -function LightOverlay:calculateLightLava() - local vp=self:getViewport() - local map = self.df_layout.map - for i=map.x1,map.x2 do - for j=map.y1,map.y2 do - local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z} - local pos2={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z-1} - local t1=dfhack.maps.getTileFlags(pos) - local tt=dfhack.maps.getTileType(pos) - if tt then - local shape=tile_attrs[tt].shape - local t2=dfhack.maps.getTileFlags(pos2) - if (t1 and t1.liquid_type and t1.flow_size>0) or - (shape==df.tiletype_shape.EMPTY and t2 and t2.liquid_type and t2.flow_size>0) then - --self:placeLight({x=i,y=j},5,{r=0.8,g=0.2,b=0.2}) - self:placeLightFov({x=i,y=j},5,{r=0.8,g=0.2,b=0.2},nil) - end - end - end - end -end -function LightOverlay:calculateLightSun() - local vp=self:getViewport() - local map = self.df_layout.map - for i=map.x1,map.x2+1 do - for j=map.y1,map.y2+1 do - local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z} - - local t1=dfhack.maps.getTileFlags(pos) - - if (t1 and t1.outside ) then - - self:placeLightFov({x=i,y=j},15,{r=1,g=1,b=1},nil) - end - end - end -end -function LightOverlay:calculateLightCursor() - local c=getCursorPos() - - if c then - - local vp=self:getViewport() - local pos=vp:tileToScreen(c) - --self:placeLight(pos,11,{r=0.96,g=0.84,b=0.03}) - self:placeLightFov({x=pos.x+1,y=pos.y+1},11,{r=0.96,g=0.84,b=0.03}) - - end -end -function LightOverlay:buildOcupancy() - self.ocupancy={} - local vp=self:getViewport() - local map = self.df_layout.map - for i=map.x1,map.x2+1 do - for j=map.y1,map.y2+1 do - local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z} - local tile=i+j*map.width - local tt=dfhack.maps.getTileType(pos) - local t1=dfhack.maps.getTileFlags(pos) - if tt then - local shape=tile_attrs[tt].shape - if not lightPassable(shape) then - self.ocupancy[tile]={r=0,g=0,b=0} - else - if t1 and not t1.liquid_type and t1.flow_size>2 then - self.ocupancy[tile]={r=0.5,g=0.5,b=0.7} - else - self.ocupancy[tile]={r=0.8,g=0.8,b=0.8} - end - end - end - end - end -end -function LightOverlay:changed() - if self.dirty or self.tick~=df.global.cur_year_tick_advmode then - self.dirty=false - self.tick=df.global.cur_year_tick_advmode - return true - end - return false -end -function LightOverlay:makeLightMap() - if not self:changed() then - return - end - self.fovs={} - self.precalc={} - self.lightMap={} - - self:buildOcupancy() - self:calculateLightCursor() - self:calculateLightLava() - self:calculateLightSun() - - self:calculateFovs() -end -function LightOverlay:onIdle() - self._native.parent:logic() -end -function LightOverlay:render(dc) - if self.dynamic then - self:makeLightMap() - end - self:renderParent() - local vp=self:getViewport() - local map = self.df_layout.map - - self.lightMap=self.lightMap or {} - render.lockGrids() - render.invalidate({x=map.x1,y=map.y1,w=map.width,h=map.height}) - render.resetGrids() - for i=map.x1,map.x2 do - for j=map.y1,map.y2 do - local v=self.lightMap[i+j*map.width] - if v then - setCell(i,j,{fm=v,bm=v}) - else - local dimRgb={r=levelDim,g=levelDim,b=levelDim} - setCell(i,j,{fm=dimRgb,bm=dimRgb}) - end - end - end - render.unlockGrids() - -end -function LightOverlay:onDismiss() - render.lockGrids() - render.resetGrids() - render.invalidate() - render.unlockGrids() - -end -function LightOverlay:onInput(keys) - if keys.STRING_A096 then - self:dismiss() - else - self:sendInputToParent(keys) - - if keys.CHANGETAB then - self:updateLayout() - end - if keys.STRING_A126 and not self.dynamic then - self:makeLightMap() - end - self.dirty=true - end -end -if not render.isEnabled() then - qerror("Lua rendermode not enabled!") -end -local dyn=true -if #args>0 and args[1]=="static" then dyn=false end -local lview = LightOverlay{ dynamic=dyn} -lview:show() diff --git a/scripts/devel/list-filters.lua b/scripts/devel/list-filters.lua deleted file mode 100644 index 7eb2210a9..000000000 --- a/scripts/devel/list-filters.lua +++ /dev/null @@ -1,78 +0,0 @@ --- List input items for the building being built. ---[[=begin - -devel/list-filters -================== -List input items for the building currently being built. -This is where the filters in lua/dfhack/buildings.lua come from. - -=end]] - -local dumper = require 'dumper' -local utils = require 'utils' -local buildings = require 'dfhack.buildings' - -local function name_enum(tgt,name,ename,enum) - if tgt[name] ~= nil then - tgt[name] = ename..'.'..enum[tgt[name]] - end -end - -local lookup = {} -local items = df.global.world.items - -for i=df.job_item_vector_id._first_item,df.job_item_vector_id._last_item do - local id = df.job_item_vector_id.attrs[i].other - local ptr - if id == df.items_other_id.ANY then - ptr = items.all - elseif id == df.items_other_id.BAD then - ptr = items.bad - else - ptr = items.other[id] - end - if ptr then - local _,addr = df.sizeof(ptr) - lookup[addr] = 'df.job_item_vector_id.'..df.job_item_vector_id[i] - end -end - -local function clone_filter(src,quantity) - local tgt = utils.clone_with_default(src, buildings.input_filter_defaults, true) - if quantity ~= 1 then - tgt.quantity = quantity - end - name_enum(tgt, 'item_type', 'df.item_type', df.item_type) - name_enum(tgt, 'has_tool_use', 'df.tool_uses', df.tool_uses) - local ptr = src.item_vector - if ptr and ptr ~= df.global.world.items.other[0] then - local _,addr = df.sizeof(ptr) - tgt.vector_id = lookup[addr] - end - return tgt -end - -local function dump(name) - local out = {} - for i,v in ipairs(df.global.ui_build_selector.requirements) do - out[#out+1] = clone_filter(v.filter, v.count_required) - end - - local fmt = dumper.DataDumper(out,name,false,1,4) - fmt = string.gsub(fmt, '"(df%.[^"]+)"','%1') - fmt = string.gsub(fmt, '%s+$', '') - print(fmt) -end - -local itype = df.global.ui_build_selector.building_type -local stype = df.global.ui_build_selector.building_subtype - -if itype == df.building_type.Workshop then - dump(' [df.workshop_type.'..df.workshop_type[stype]..'] = ') -elseif itype == df.building_type.Furnace then - dump(' [df.furnace_type.'..df.furnace_type[stype]..'] = ') -elseif itype == df.building_type.Trap then - dump(' [df.trap_type.'..df.trap_type[stype]..'] = ') -else - dump(' [df.building_type.'..df.building_type[itype]..'] = ') -end diff --git a/scripts/devel/lsmem.lua b/scripts/devel/lsmem.lua deleted file mode 100644 index b390adc5c..000000000 --- a/scripts/devel/lsmem.lua +++ /dev/null @@ -1,21 +0,0 @@ --- Prints memory ranges of the process. ---[[=begin - -devel/lsmem -=========== -Prints memory ranges of the process. - -=end]] - -for _,v in ipairs(dfhack.internal.getMemRanges()) do - local access = { '-', '-', '-', 'p' } - if v.read then access[1] = 'r' end - if v.write then access[2] = 'w' end - if v.execute then access[3] = 'x' end - if not v.valid then - access[4] = '?' - elseif v.shared then - access[4] = 's' - end - print(string.format('%08x-%08x %s %s', v.start_addr, v.end_addr, table.concat(access), v.name)) -end diff --git a/scripts/devel/lua-example.lua b/scripts/devel/lua-example.lua deleted file mode 100644 index bbdb04ed6..000000000 --- a/scripts/devel/lua-example.lua +++ /dev/null @@ -1,14 +0,0 @@ --- Example of a lua script. ---[[=begin - -devel/lua-example -================= -An example lua script, which reports the number of times it has -been called. Useful for testing environment persistence. - -=end]] - -run_count = (run_count or 0) + 1 - -print('Arguments: ',...) -print('Command called '..run_count..' times.') diff --git a/scripts/devel/make-dt.pl b/scripts/devel/make-dt.pl deleted file mode 100644 index e3f390802..000000000 --- a/scripts/devel/make-dt.pl +++ /dev/null @@ -1,492 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -my ($version, $timestamp, $hash); - -open FH, 'version.lisp' or die "Cannot open version"; -while () { - if (/df-version-str.*\"(.*)\"/) { - $version = $1; - } elsif (/windows-timestamp.*#x([0-9a-f]+)/) { - $timestamp = $1; - } elsif (/linux-hash.*\"(.*)\"/) { - $hash = $1; - } -} -close FH; - -sub load_csv(\%$) { - my ($rhash, $fname) = @_; - - open FH, $fname or die "Cannot open $fname"; - while () { - next unless /^\"([^\"]*)\",\"(\d+)\",\"(?:0x([0-9a-fA-F]+))?\",\"[^\"]*\",\"([^\"]*)\",\"([^\"]*)\",\"([^\"]*)\"/; - my ($top, $level, $addr, $type, $name, $target) = ($1,$2,$3,$4,$5,$6); - next if defined $rhash->{$top}{$name}; - $rhash->{$top}{$name} = ($type eq 'enum-item' ? $target : hex $addr); - } - close FH; -} - -our $complete; - -sub lookup_addr(\%$$;$) { - my ($rhash, $top, $name, $bias) = @_; - - my $val = $rhash->{$top}{$name}; - unless (defined $val) { - $complete = 0; - return 0; - } - return $val + ($bias||0); -} - -our @lines; - -sub emit_header($) { - my ($name) = @_; - push @lines, '' if @lines; - push @lines, "[$name]"; -} - -sub emit_addr($\%$$;$) { - my ($name, $rhash, $top, $var, $bias) = @_; - - my $val = $rhash->{$top}{$var}; - if (defined $val) { - $val += ($bias||0); - if ($val < 0x10000) { - push @lines, sprintf('%s=0x%04x', $name, $val); - } else { - push @lines, sprintf('%s=0x%08x', $name, $val); - } - } else { - $complete = 0; - push @lines, "$name=0x0"; - } -} - -sub generate_dt_ini($$$$) { - my ($subdir, $version, $checksum, $ssize) = @_; - - my %globals; - load_csv %globals, "$subdir/globals.csv"; - my %all; - load_csv %all, "$subdir/all.csv"; - - local $complete = 1; - local @lines; - - emit_header 'addresses'; - emit_addr 'translation_vector',%globals,'world','world.raws.language.translations'; - emit_addr 'language_vector',%globals,'world','world.raws.language.words'; - emit_addr 'creature_vector',%globals,'world','world.units.all'; - emit_addr 'active_creature_vector',%globals,'world','world.units.active'; - emit_addr 'dwarf_race_index',%globals,'ui','ui.race_id'; - emit_addr 'squad_vector',%globals,'world','world.squads.all'; - emit_addr 'current_year',%globals,'cur_year','cur_year'; - emit_addr 'cur_year_tick',%globals,'cur_year_tick','cur_year_tick'; - emit_addr 'dwarf_civ_index',%globals,'ui','ui.civ_id'; - emit_addr 'races_vector',%globals,'world','world.raws.creatures.all'; - emit_addr 'reactions_vector',%globals,'world','world.raws.reactions'; - emit_addr 'events_vector',%globals,'world','world.history.events'; - emit_addr 'historical_figures_vector',%globals,'world','world.history.figures'; - emit_addr 'fake_identities_vector',%globals,'world','world.identities.all'; - emit_addr 'fortress_entity',%globals,'ui','ui.main.fortress_entity'; - emit_addr 'historical_entities_vector',%globals,'world','world.entities.all'; - emit_addr 'itemdef_weapons_vector',%globals,'world','world.raws.itemdefs.weapons'; - emit_addr 'itemdef_trap_vector',%globals,'world','world.raws.itemdefs.trapcomps'; - emit_addr 'itemdef_toy_vector',%globals,'world','world.raws.itemdefs.toys'; - emit_addr 'itemdef_tool_vector',%globals,'world','world.raws.itemdefs.tools'; - emit_addr 'itemdef_instrument_vector',%globals,'world','world.raws.itemdefs.instruments'; - emit_addr 'itemdef_armor_vector',%globals,'world','world.raws.itemdefs.armor'; - emit_addr 'itemdef_ammo_vector',%globals,'world','world.raws.itemdefs.ammo'; - emit_addr 'itemdef_siegeammo_vector',%globals,'world','world.raws.itemdefs.siege_ammo'; - emit_addr 'itemdef_glove_vector',%globals,'world','world.raws.itemdefs.gloves'; - emit_addr 'itemdef_shoe_vector',%globals,'world','world.raws.itemdefs.shoes'; - emit_addr 'itemdef_shield_vector',%globals,'world','world.raws.itemdefs.shields'; - emit_addr 'itemdef_helm_vector',%globals,'world','world.raws.itemdefs.helms'; - emit_addr 'itemdef_pant_vector',%globals,'world','world.raws.itemdefs.pants'; - emit_addr 'itemdef_food_vector',%globals,'world','world.raws.itemdefs.food'; - emit_addr 'colors_vector',%globals,'world','world.raws.language.colors'; - emit_addr 'shapes_vector',%globals,'world','world.raws.language.shapes'; - emit_addr 'base_materials',%globals,'world','world.raws.mat_table.builtin'; - emit_addr 'inorganics_vector',%globals,'world','world.raws.inorganics'; - emit_addr 'plants_vector',%globals,'world','world.raws.plants.all'; - emit_addr 'material_templates_vector',%globals,'world','world.raws.material_templates'; - emit_addr 'all_syndromes_vector',%globals,'world','world.raws.syndromes.all'; - emit_addr 'world_data',%globals,'world','world.world_data'; - emit_addr 'active_sites_vector',%all,'world_data','active_site'; - emit_addr 'world_site_type',%all,'world_site','type'; - emit_addr 'weapons_vector',%globals,'world','world.items.other[WEAPON]'; - emit_addr 'shields_vector',%globals,'world','world.items.other[SHIELD]'; - emit_addr 'quivers_vector',%globals,'world','world.items.other[QUIVER]'; - emit_addr 'crutches_vector',%globals,'world','world.items.other[CRUTCH]'; - emit_addr 'backpacks_vector',%globals,'world','world.items.other[BACKPACK]'; - emit_addr 'ammo_vector',%globals,'world','world.items.other[AMMO]'; - emit_addr 'flasks_vector',%globals,'world','world.items.other[FLASK]'; - emit_addr 'pants_vector',%globals,'world','world.items.other[PANTS]'; - emit_addr 'armor_vector',%globals,'world','world.items.other[ARMOR]'; - emit_addr 'shoes_vector',%globals,'world','world.items.other[SHOES]'; - emit_addr 'helms_vector',%globals,'world','world.items.other[HELM]'; - emit_addr 'gloves_vector',%globals,'world','world.items.other[GLOVES]'; - emit_addr 'artifacts_vector',%globals,'world','world.artifacts.all'; - - emit_header 'offsets'; - emit_addr 'word_table',%all,'language_translation','words'; - push @lines, 'string_buffer_offset=0x0000'; - - emit_header 'word_offsets'; - emit_addr 'base',%all,'language_word','word'; - emit_addr 'noun_singular',%all,'language_word','forms[Noun]'; - emit_addr 'noun_plural',%all,'language_word','forms[NounPlural]'; - emit_addr 'adjective',%all,'language_word','forms[Adjective]'; - emit_addr 'verb',%all,'language_word','forms[Verb]'; - emit_addr 'present_simple_verb',%all,'language_word','forms[Verb3rdPerson]'; - emit_addr 'past_simple_verb',%all,'language_word','forms[VerbPast]'; - emit_addr 'past_participle_verb',%all,'language_word','forms[VerbPassive]'; - emit_addr 'present_participle_verb',%all,'language_word','forms[VerbGerund]'; - emit_addr 'words',%all,'language_name','words'; - emit_addr 'word_type',%all,'language_name','parts_of_speech'; - emit_addr 'language_id',%all,'language_name','language'; - - emit_header 'general_ref_offsets'; - emit_addr 'ref_type',%all,'general_ref::vtable','getType'; - emit_addr 'artifact_id',%all,'general_ref_artifact','artifact_id'; - emit_addr 'item_id',%all,'general_ref_item','item_id'; - - emit_header 'race_offsets'; - emit_addr 'name_singular',%all,'creature_raw','name'; - emit_addr 'name_plural',%all,'creature_raw','name',$ssize; - emit_addr 'adjective',%all,'creature_raw','name',$ssize*2; - emit_addr 'baby_name_singular',%all,'creature_raw','general_baby_name'; - emit_addr 'baby_name_plural',%all,'creature_raw','general_baby_name',$ssize; - emit_addr 'child_name_singular',%all,'creature_raw','general_child_name'; - emit_addr 'child_name_plural',%all,'creature_raw','general_child_name',$ssize; - emit_addr 'pref_string_vector',%all,'creature_raw','prefstring'; - emit_addr 'castes_vector',%all,'creature_raw','caste'; - emit_addr 'pop_ratio_vector',%all,'creature_raw','pop_ratio'; - emit_addr 'materials_vector',%all,'creature_raw','material'; - emit_addr 'flags',%all,'creature_raw','flags'; - emit_addr 'tissues_vector',%all,'creature_raw','tissue'; - - emit_header 'caste_offsets'; - emit_addr 'caste_name',%all,'caste_raw','caste_name'; - emit_addr 'caste_descr',%all,'caste_raw','description'; - emit_addr 'caste_trait_ranges',%all,'caste_raw','personality.a'; - emit_addr 'caste_phys_att_ranges',%all,'caste_raw','attributes.phys_att_range'; - emit_addr 'baby_age',%all,'caste_raw','misc.baby_age'; - emit_addr 'child_age',%all,'caste_raw','misc.child_age'; - emit_addr 'adult_size',%all,'caste_raw','misc.adult_size'; - emit_addr 'flags',%all,'caste_raw','flags'; - emit_addr 'body_info',%all,'caste_raw','body_info'; - emit_addr 'skill_rates',%all,'caste_raw','skill_rates'; - emit_addr 'caste_att_rates',%all,'caste_raw','attributes.phys_att_rates'; - emit_addr 'caste_att_caps',%all,'caste_raw','attributes.phys_att_cap_perc'; - emit_addr 'shearable_tissues_vector',%all,'caste_raw','shearable_tissue_layer'; - emit_addr 'extracts',%all,'caste_raw','extracts.extract_matidx'; - - emit_header 'hist_entity_offsets'; - emit_addr 'beliefs',%all,'historical_entity','resources.values'; - emit_addr 'squads',%all,'historical_entity','squads'; - emit_addr 'positions',%all,'historical_entity','positions.own'; - emit_addr 'assignments',%all,'historical_entity','positions.assignments'; - emit_addr 'assign_hist_id',%all,'entity_position_assignment','histfig'; - emit_addr 'assign_position_id',%all,'entity_position_assignment','position_id'; - emit_addr 'position_id',%all,'entity_position','id'; - emit_addr 'position_name',%all,'entity_position','name'; - emit_addr 'position_female_name',%all,'entity_position','name_female'; - emit_addr 'position_male_name',%all,'entity_position','name_male'; - - emit_header 'hist_figure_offsets'; - emit_addr 'hist_race',%all,'historical_figure','race'; - emit_addr 'hist_name',%all,'historical_figure','name'; - emit_addr 'id',%all,'historical_figure','id'; - emit_addr 'hist_fig_info',%all,'historical_figure','info'; - emit_addr 'reputation',%all,'historical_figure_info','reputation'; - emit_addr 'current_ident',%all,'historical_figure_info::anon13','cur_identity'; - emit_addr 'fake_name',%all,'identity','name'; - emit_addr 'fake_birth_year',%all,'identity','birth_year'; - emit_addr 'fake_birth_time',%all,'identity','birth_second'; - emit_addr 'kills',%all,'historical_figure_info','kills'; - emit_addr 'killed_race_vector',%all,'historical_kills','killed_race'; - emit_addr 'killed_undead_vector',%all,'historical_kills','killed_undead'; - emit_addr 'killed_counts_vector',%all,'historical_kills','killed_count'; - - emit_header 'hist_event_offsets'; - emit_addr 'event_year',%all,'history_event','year'; - emit_addr 'id',%all,'history_event','id'; - emit_addr 'killed_hist_id',%all,'history_event_hist_figure_diedst','victim_hf'; - - emit_header 'item_offsets'; - if ($subdir eq 'osx') { - push @lines, 'item_type=0x0004'; - } else { - push @lines, 'item_type=0x0001'; - } - emit_addr 'item_def',%all,'item_ammost','subtype'; #currently same for all - emit_addr 'id',%all,'item','id'; - emit_addr 'general_refs',%all,'item','general_refs'; - emit_addr 'stack_size',%all,'item_actual','stack_size'; - emit_addr 'wear',%all,'item_actual','wear'; - emit_addr 'mat_type',%all,'item_crafted','mat_type'; - emit_addr 'mat_index',%all,'item_crafted','mat_index'; - emit_addr 'quality',%all,'item_crafted','quality'; - - emit_header 'item_subtype_offsets'; - emit_addr 'sub_type',%all,'itemdef','subtype'; - emit_addr 'name',%all,'itemdef_armorst','name'; - emit_addr 'name_plural',%all,'itemdef_armorst','name_plural'; - emit_addr 'adjective',%all,'itemdef_armorst','name_preplural'; - - emit_header 'item_filter_offsets'; - emit_addr 'item_subtype',%all,'item_filter_spec','item_subtype'; - emit_addr 'mat_class',%all,'item_filter_spec','material_class'; - emit_addr 'mat_type',%all,'item_filter_spec','mattype'; - emit_addr 'mat_index',%all,'item_filter_spec','matindex'; - - emit_header 'weapon_subtype_offsets'; - emit_addr 'single_size',%all,'itemdef_weaponst','two_handed'; - emit_addr 'multi_size',%all,'itemdef_weaponst','minimum_size'; - emit_addr 'ammo',%all,'itemdef_weaponst','ranged_ammo'; - emit_addr 'melee_skill',%all,'itemdef_weaponst','skill_melee'; - emit_addr 'ranged_skill',%all,'itemdef_weaponst','skill_ranged'; - - emit_header 'armor_subtype_offsets'; - emit_addr 'layer',%all,'armor_properties','layer'; - emit_addr 'mat_name',%all,'itemdef_armorst','material_placeholder'; - emit_addr 'other_armor_level',%all,'itemdef_helmst','armorlevel'; - emit_addr 'armor_adjective',%all,'itemdef_armorst','adjective'; - emit_addr 'armor_level',%all,'itemdef_armorst','armorlevel'; - emit_addr 'chest_armor_properties',%all,'itemdef_armorst','props'; - emit_addr 'pants_armor_properties',%all,'itemdef_pantsst','props'; - emit_addr 'other_armor_properties',%all,'itemdef_helmst','props'; - - emit_header 'material_offsets'; - emit_addr 'solid_name',%all,'material_common','state_name[Solid]'; - emit_addr 'liquid_name',%all,'material_common','state_name[Liquid]'; - emit_addr 'gas_name',%all,'material_common','state_name[Gas]'; - emit_addr 'powder_name',%all,'material_common','state_name[Powder]'; - emit_addr 'paste_name',%all,'material_common','state_name[Paste]'; - emit_addr 'pressed_name',%all,'material_common','state_name[Pressed]'; - emit_addr 'flags',%all,'material_common','flags'; - emit_addr 'inorganic_materials_vector',%all,'inorganic_raw','material'; - emit_addr 'inorganic_flags',%all,'inorganic_raw','flags'; - - emit_header 'plant_offsets'; - emit_addr 'name',%all,'plant_raw','name'; - emit_addr 'name_plural',%all,'plant_raw','name_plural'; - emit_addr 'name_leaf_plural',%all,'plant_raw','leaves_plural'; - emit_addr 'name_seed_plural',%all,'plant_raw','seed_plural'; - emit_addr 'materials_vector',%all,'plant_raw','material'; - emit_addr 'flags',%all,'plant_raw','flags'; - - emit_header 'descriptor_offsets'; - emit_addr 'color_name',%all,'descriptor_color','name'; - emit_addr 'shape_name_plural',%all,'descriptor_shape','name_plural'; - - emit_header 'health_offsets'; - emit_addr 'parent_id',%all,'body_part_raw','con_part_id'; - emit_addr 'layers_vector',%all,'body_part_raw','layers'; - emit_addr 'number',%all,'body_part_raw','number'; - emit_addr 'names_vector',%all,'body_part_raw','name_singular'; - emit_addr 'names_plural_vector',%all,'body_part_raw','name_plural'; - emit_addr 'layer_tissue',%all,'body_part_layer_raw','tissue_id'; - emit_addr 'layer_global_id',%all,'body_part_layer_raw','layer_id'; - emit_addr 'tissue_name',%all,'tissue_template','tissue_name_singular'; - emit_addr 'tissue_flags',%all,'tissue_template','flags'; - - emit_header 'dwarf_offsets'; - emit_addr 'first_name',%all,'unit','name',lookup_addr(%all,'language_name','first_name'); - emit_addr 'nick_name',%all,'unit','name',lookup_addr(%all,'language_name','nickname'); - emit_addr 'last_name',%all,'unit','name',lookup_addr(%all,'language_name','words'); - emit_addr 'custom_profession',%all,'unit','custom_profession'; - emit_addr 'profession',%all,'unit','profession'; - emit_addr 'race',%all,'unit','race'; - emit_addr 'flags1',%all,'unit','flags1'; - emit_addr 'flags2',%all,'unit','flags2'; - emit_addr 'flags3',%all,'unit','flags3'; - emit_addr 'caste',%all,'unit','caste'; - emit_addr 'sex',%all,'unit','sex'; - emit_addr 'id',%all,'unit','id'; - emit_addr 'animal_type',%all,'unit','training_level'; - emit_addr 'civ',%all,'unit','civ_id'; - emit_addr 'specific_refs',%all,'unit','specific_refs'; - emit_addr 'squad_id',%all,'unit','military.squad_id'; - emit_addr 'squad_position',%all,'unit','military.squad_position'; - emit_addr 'recheck_equipment',%all,'unit','military.pickup_flags'; - emit_addr 'mood',%all,'unit','mood'; - emit_addr 'birth_year',%all,'unit','relations.birth_year'; - emit_addr 'birth_time',%all,'unit','relations.birth_time'; - emit_addr 'pet_owner_id',%all,'unit','relations.pet_owner_id'; - emit_addr 'current_job',%all,'unit','job.current_job'; - emit_addr 'physical_attrs',%all,'unit','body.physical_attrs'; - emit_addr 'body_size',%all,'unit','appearance.body_modifiers'; - emit_addr 'size_info',%all,'unit','body.size_info'; - emit_addr 'curse',%all,'unit','curse.name'; - emit_addr 'curse_add_flags1',%all,'unit','curse.add_tags1'; - emit_addr 'turn_count',%all,'unit','curse.time_on_site'; - emit_addr 'souls',%all,'unit','status.souls'; - emit_addr 'states',%all,'unit','status.misc_traits'; - emit_addr 'labors',%all,'unit','status.labors'; - emit_addr 'hist_id',%all,'unit','hist_figure_id'; - emit_addr 'artifact_name',%all,'unit','status.artifact_name'; - emit_addr 'active_syndrome_vector',%all,'unit','syndromes.active'; - emit_addr 'syn_sick_flag',%all,'unit_syndrome','flags.is_sick'; - emit_addr 'unit_health_info',%all,'unit','health'; - emit_addr 'temp_mood',%all,'unit','counters.soldier_mood'; - emit_addr 'counters1',%all,'unit','counters.winded'; - emit_addr 'counters2',%all,'unit','counters.pain'; - emit_addr 'counters3',%all,'unit','counters2.paralysis'; - emit_addr 'limb_counters',%all,'unit','status2.limbs_stand_max'; - emit_addr 'blood',%all,'unit','body.blood_max'; - emit_addr 'body_component_info',%all,'unit','body.components'; - emit_addr 'layer_status_vector',%all,'body_component_info','layer_status'; - emit_addr 'wounds_vector',%all,'unit','body.wounds'; - emit_addr 'mood_skill',%all,'unit','job.mood_skill'; - emit_addr 'used_items_vector',%all,'unit','used_items'; - emit_addr 'affection_level',%all,'unit_item_use','affection_level'; - emit_addr 'inventory',%all,'unit','inventory'; - emit_addr 'inventory_item_mode',%all,'unit_inventory_item','mode'; - emit_addr 'inventory_item_bodypart',%all,'unit_inventory_item','body_part_id'; - - emit_header 'syndrome_offsets'; - emit_addr 'cie_effects',%all,'syndrome','ce'; - emit_addr 'cie_end',%all,'creature_interaction_effect','end'; - emit_addr 'cie_first_perc',%all,'creature_interaction_effect_phys_att_changest','phys_att_perc'; #same for mental - emit_addr 'cie_phys',%all,'creature_interaction_effect_phys_att_changest','phys_att_add'; - emit_addr 'cie_ment',%all,'creature_interaction_effect_ment_att_changest','ment_att_add'; - emit_addr 'syn_classes_vector',%all,'syndrome','syn_class'; - emit_addr 'trans_race_id',%all,'creature_interaction_effect_body_transformationst','race'; - - emit_header 'unit_wound_offsets'; - emit_addr 'parts',%all,'unit_wound','parts'; - emit_addr 'id',%all,'unit_wound::anon2','body_part_id'; - emit_addr 'layer',%all,'unit_wound::anon2','layer_idx'; - emit_addr 'general_flags',%all,'unit_wound','flags'; - emit_addr 'flags1',%all,'unit_wound::anon2','flags1'; - emit_addr 'flags2',%all,'unit_wound::anon2','flags2'; - emit_addr 'effects_vector',%all,'unit_wound::anon2','effect_type'; - emit_addr 'bleeding',%all,'unit_wound::anon2','bleeding'; - emit_addr 'pain',%all,'unit_wound::anon2','pain'; - emit_addr 'cur_pen',%all,'unit_wound::anon2','cur_penetration_perc'; - emit_addr 'max_pen',%all,'unit_wound::anon2','max_penetration_perc'; - - emit_header 'soul_details'; - emit_addr 'name',%all,'unit_soul','name'; - emit_addr 'orientation',%all,'unit_soul','orientation_flags'; - emit_addr 'mental_attrs',%all,'unit_soul','mental_attrs'; - emit_addr 'skills',%all,'unit_soul','skills'; - emit_addr 'preferences',%all,'unit_soul','preferences'; - emit_addr 'personality',%all,'unit_soul','personality'; - emit_addr 'beliefs',%all,'unit_personality','values'; - emit_addr 'emotions',%all,'unit_personality','emotions'; - emit_addr 'goals',%all,'unit_personality','dreams'; - emit_addr 'goal_realized',%all,'unit_personality::anon5','unk8'; - emit_addr 'traits',%all,'unit_personality','traits'; - emit_addr 'stress_level',%all,'unit_personality','stress_level'; - - emit_header 'emotion_offsets'; - emit_addr 'emotion_type',%all,'unit_personality::anon4','type'; - emit_addr 'strength',%all,'unit_personality::anon4','strength'; - emit_addr 'thought_id',%all,'unit_personality::anon4','thought'; - emit_addr 'sub_id',%all,'unit_personality::anon4','subthought'; - emit_addr 'level',%all,'unit_personality::anon4','severity'; - emit_addr 'year',%all,'unit_personality::anon4','year'; - emit_addr 'year_tick',%all,'unit_personality::anon4','year_tick'; - - emit_header 'job_details'; - emit_addr 'id',%all,'job','job_type'; - emit_addr 'mat_type',%all,'job','mat_type'; - emit_addr 'mat_index',%all,'job','mat_index'; - emit_addr 'mat_category',%all,'job','material_category'; - emit_addr 'on_break_flag',%all,'misc_trait_type','OnBreak'; - emit_addr 'sub_job_id',%all,'job','reaction_name'; - emit_addr 'reaction',%all,'reaction','name'; - emit_addr 'reaction_skill',%all,'reaction','skill'; - - emit_header 'squad_offsets'; - emit_addr 'id',%all,'squad','id'; - emit_addr 'name',%all,'squad','name'; - emit_addr 'alias',%all,'squad','alias'; - emit_addr 'members',%all,'squad','positions'; - emit_addr 'carry_food',%all,'squad','carry_food'; - emit_addr 'carry_water',%all,'squad','carry_water'; - emit_addr 'ammunition',%all,'squad','ammunition'; - emit_addr 'quiver',%all,'squad_position','quiver'; - emit_addr 'backpack',%all,'squad_position','backpack'; - emit_addr 'flask',%all,'squad_position','flask'; - emit_addr 'armor_vector',%all,'squad_position','uniform[body]'; - emit_addr 'helm_vector',%all,'squad_position','uniform[head]'; - emit_addr 'pants_vector',%all,'squad_position','uniform[pants]'; - emit_addr 'gloves_vector',%all,'squad_position','uniform[gloves]'; - emit_addr 'shoes_vector',%all,'squad_position','uniform[shoes]'; - emit_addr 'shield_vector',%all,'squad_position','uniform[shield]'; - emit_addr 'weapon_vector',%all,'squad_position','uniform[weapon]'; - emit_addr 'uniform_item_filter',%all,'squad_uniform_spec','item_filter'; - emit_addr 'uniform_indiv_choice',%all,'squad_uniform_spec','indiv_choice'; - - my $body_str = join("\n",@lines); - my $complete_str = ($complete ? 'true' : 'false'); - - open OUT, ">$subdir/therapist.ini" or die "Cannot open output file"; - print OUT <<__END__; -[info] -checksum=0x$checksum -version_name=$version -complete=$complete_str - -$body_str - -[valid_flags_2] -size=0 - -[invalid_flags_1] -size=10 -1\\name=a zombie -1\\value=0x00001000 -2\\name=a skeleton -2\\value=0x00002000 -3\\name=a merchant -3\\value=0x00000040 -4\\name=outpost liason or diplomat -4\\value=0x00000800 -5\\name=an invader or hostile -5\\value=0x00020000 -6\\name=an invader or hostile -6\\value=0x00080000 -7\\name=resident, invader or ambusher -7\\value=0x00600000 -8\\name=part of a merchant caravan -8\\value=0x00000080 -9\\name="Dead, Jim." -9\\value=0x00000002 -10\\name=marauder -10\\value=0x00000010 - -[invalid_flags_2] -size=5 -1\\name="killed, Jim." -1\\value=0x00000080 -2\\name=from the Underworld. SPOOKY! -2\\value=0x00040000 -3\\name=resident -3\\value=0x00080000 -4\\name=uninvited visitor -4\\value=0x00400000 -5\\name=visitor -5\\value=0x00800000 - -[invalid_flags_3] -size=1 -1\\name=a ghost -1\\value=0x00001000 -__END__ - close OUT; -} - -generate_dt_ini 'linux', $version, substr($hash,0,8), 4; -generate_dt_ini 'windows', $version.' (graphics)', $timestamp, 0x1C; -generate_dt_ini 'osx', $version, substr($hash,0,8), 4; \ No newline at end of file diff --git a/scripts/devel/nuke-items.lua b/scripts/devel/nuke-items.lua deleted file mode 100644 index 6b67f81c8..000000000 --- a/scripts/devel/nuke-items.lua +++ /dev/null @@ -1,22 +0,0 @@ --- Delete ALL items not held by units, buildings or jobs ---[[=begin - -devel/nuke-items -================ -Deletes ALL items not held by units, buildings or jobs. -Intended solely for lag investigation. - -=end]] - -local count = 0 - -for _,v in ipairs(df.global.world.items.all) do - if not (v.flags.in_building or v.flags.construction or v.flags.in_job - or dfhack.items.getGeneralRef(v,df.general_ref_type.UNIT_HOLDER)) then - count = count + 1 - v.flags.forbid = true - v.flags.garbage_collect = true - end -end - -print('Deletion requested: '..count) diff --git a/scripts/devel/pop-screen.lua b/scripts/devel/pop-screen.lua deleted file mode 100644 index 8bc5e45c6..000000000 --- a/scripts/devel/pop-screen.lua +++ /dev/null @@ -1,10 +0,0 @@ --- For killing bugged out gui script screens. ---[[=begin - -devel/pop-screen -================ -For killing bugged out gui script screens. - -=end]] - -dfhack.screen.dismiss(dfhack.gui.getCurViewscreen()) diff --git a/scripts/devel/prepare-save.lua b/scripts/devel/prepare-save.lua deleted file mode 100644 index c1e71ef7f..000000000 --- a/scripts/devel/prepare-save.lua +++ /dev/null @@ -1,98 +0,0 @@ --- Prepare the current save for devel/find-offsets ---[[=begin - -devel/prepare-save -================== -WARNING: THIS SCRIPT IS STRICTLY FOR DFHACK DEVELOPERS. - -This script prepares the current savegame to be used -with `devel/find-offsets`. It CHANGES THE GAME STATE -to predefined values, and initiates an immediate -`quicksave`, thus PERMANENTLY MODIFYING the save. - -=end]] - -local utils = require 'utils' - -df.global.pause_state = true - -print[[ -WARNING: THIS SCRIPT IS STRICTLY FOR DFHACK DEVELOPERS. - -This script prepares the current savegame to be used -with devel/find-offsets. It CHANGES THE GAME STATE -to predefined values, and initiates an immediate -quicksave, thus PERMANENTLY MODIFYING the save. -]] - -if not utils.prompt_yes_no('Proceed?') then - return -end - ---[[print('Placing anchor...') - -do - local wp = df.global.ui.waypoints - - for _,pt in ipairs(wp.points) do - if pt.name == 'dfhack_anchor' then - print('Already placed.') - goto found - end - end - - local x,y,z = pos2xyz(df.global.cursor) - - if not x then - error("Place cursor at your preferred anchor point.") - end - - local id = wp.next_point_id - wp.next_point_id = id + 1 - - wp.points:insert('#',{ - new = true, id = id, name = 'dfhack_anchor', - comment=(x..','..y..','..z), - tile = string.byte('!'), fg_color = COLOR_LIGHTRED, bg_color = COLOR_BLUE, - pos = xyz2pos(x,y,z) - }) - -::found:: -end]] - -print('Nicknaming units...') - -for i,unit in ipairs(df.global.world.units.active) do - dfhack.units.setNickname(unit, i..':'..unit.id) -end - -print('Setting weather...') - -local wbytes = { - 2, 1, 0, 2, 0, - 1, 2, 1, 0, 0, - 2, 0, 2, 1, 2, - 1, 2, 0, 1, 1, - 2, 0, 1, 0, 2 -} - -for i=0,4 do - for j = 0,4 do - df.global.current_weather[i][j] = (wbytes[i*5+j+1] or 2) - end -end - -local yearstr = df.global.cur_year..','..df.global.cur_year_tick - -print('Cur year and tick: '..yearstr) - -dfhack.persistent.save{ - key='prepare-save/cur_year', - value=yearstr, - ints={df.global.cur_year, df.global.cur_year_tick} -} - --- Save - -dfhack.run_script('quicksave') - diff --git a/scripts/devel/print-args.lua b/scripts/devel/print-args.lua deleted file mode 100644 index 46024eaa4..000000000 --- a/scripts/devel/print-args.lua +++ /dev/null @@ -1,15 +0,0 @@ ---print-args.lua ---author expwnent ---[[=begin - -devel/print-args -================ -Prints all the arguments you supply to the script on their own line. -Useful for debugging other scripts. - -=end]] - -local args = {...} -for _,arg in ipairs(args) do - print(arg) -end diff --git a/scripts/devel/print-args2.lua b/scripts/devel/print-args2.lua deleted file mode 100644 index ad870623d..000000000 --- a/scripts/devel/print-args2.lua +++ /dev/null @@ -1,17 +0,0 @@ ---print-args2.lua ---author expwnent ---[[=begin - -devel/print-args2 -================= -Prints all the arguments you supply to the script on their own line -with quotes around them. - -=end]] - -local args = {...} -print("print-args") -for _,arg in ipairs(args) do - print("'"..arg.."'") -end - diff --git a/scripts/devel/save-version.lua b/scripts/devel/save-version.lua deleted file mode 100644 index 450aa04cf..000000000 --- a/scripts/devel/save-version.lua +++ /dev/null @@ -1,154 +0,0 @@ --- Display DF version information about the current save ---@module = true ---[[=begin - -devel/save-version -================== -Display DF version information about the current save - -=end]] - -local function dummy() return nil end - -function has_field(tbl, field) - return (pcall(function() assert(tbl[field] ~= nil) end)) -end - -function class_has_field(cls, field) - local obj = cls:new() - local ret = has_field(obj, field) - obj:delete() - return ret -end - -versions = { --- skipped v0.21-v0.28 - [1287] = "0.31.01", - [1288] = "0.31.02", - [1289] = "0.31.03", - [1292] = "0.31.04", - [1295] = "0.31.05", - [1297] = "0.31.06", - [1300] = "0.31.08", - [1304] = "0.31.09", - [1305] = "0.31.10", - [1310] = "0.31.11", - [1311] = "0.31.12", - [1323] = "0.31.13", - [1325] = "0.31.14", - [1326] = "0.31.15", - [1327] = "0.31.16", - [1340] = "0.31.17", - [1341] = "0.31.18", - [1351] = "0.31.19", - [1353] = "0.31.20", - [1354] = "0.31.21", - [1359] = "0.31.22", - [1360] = "0.31.23", - [1361] = "0.31.24", - [1362] = "0.31.25", - - [1372] = "0.34.01", - [1374] = "0.34.02", - [1376] = "0.34.03", - [1377] = "0.34.04", - [1378] = "0.34.05", - [1382] = "0.34.06", - [1383] = "0.34.07", - [1400] = "0.34.08", - [1402] = "0.34.09", - [1403] = "0.34.10", - [1404] = "0.34.11", - - [1441] = "0.40.01", - [1442] = "0.40.02", - [1443] = "0.40.03", - [1444] = "0.40.04", - [1445] = "0.40.05", - [1446] = "0.40.06", - [1448] = "0.40.07", - [1449] = "0.40.08", - [1451] = "0.40.09", - [1452] = "0.40.10", - [1456] = "0.40.11", - [1459] = "0.40.12", - [1462] = "0.40.13", - [1469] = "0.40.14", - [1470] = "0.40.15", - [1471] = "0.40.16", - [1472] = "0.40.17", - [1473] = "0.40.18", - [1474] = "0.40.19", - [1477] = "0.40.20", - [1478] = "0.40.21", - [1479] = "0.40.22", - [1480] = "0.40.23", - [1481] = "0.40.24", - - [1531] = "0.42.01", - [1532] = "0.42.02", - [1533] = "0.42.03", - [1534] = "0.42.04", - [1537] = "0.42.05", - [1542] = "0.42.06", - - [1551] = "0.43.01", - [1552] = "0.43.02", -} - -min_version = math.huge -max_version = -math.huge - -for k in pairs(versions) do - min_version = math.min(min_version, k) - max_version = math.max(max_version, k) -end - -if class_has_field(df.world.T_cur_savegame, 'save_version') then - function get_save_version() - return df.global.world.cur_savegame.save_version - end -elseif class_has_field(df.world.T_pathfinder, 'anon_2') then - function get_save_version() - return df.global.world.pathfinder.anon_2 - end -else - get_save_version = dummy -end - -if class_has_field(df.world, 'original_save_version') then - function get_original_save_version() - return df.global.world.original_save_version - end -else - get_original_save_version = dummy -end - -function describe(version) - if version == 0 then - return 'no world loaded' - elseif versions[version] then - return versions[version] - elseif version < min_version then - return 'unknown old version before ' .. describe(min_version) .. ': ' .. tostring(version) - elseif version > max_version then - return 'unknown new version after ' .. describe(max_version) .. ': ' .. tostring(version) - else - return 'unknown version: ' .. tostring(version) - end -end - -function dump(desc, func) - local ret = tonumber(func()) - if ret then - print(desc .. ': ' .. describe(ret)) - else - dfhack.printerr('could not find ' .. desc .. ' (DFHack version too old)') - end -end - -if not moduleMode then - if not dfhack.isWorldLoaded() then qerror('no world loaded') end - dump('original DF version', get_original_save_version) - dump('most recent DF version', get_save_version) -end diff --git a/scripts/devel/scanitemother.rb b/scripts/devel/scanitemother.rb deleted file mode 100644 index 1bdf58946..000000000 --- a/scripts/devel/scanitemother.rb +++ /dev/null @@ -1,16 +0,0 @@ -# list selected item's indices in world.item.other[] -=begin - -devel/scanitemother -=================== -List indices in ``world.item.other[]`` where current selected item appears. - -=end -tg = df.item_find -raise 'select an item' if not tg - -o = df.world.items.other -# discard ANY/BAD -o._indexenum::ENUM.sort.transpose[1][1..-2].each { |k| - puts k if o[k].find { |i| i == tg } -} diff --git a/scripts/devel/spawn-unit-helper.rb b/scripts/devel/spawn-unit-helper.rb deleted file mode 100644 index 77dadbcdd..000000000 --- a/scripts/devel/spawn-unit-helper.rb +++ /dev/null @@ -1,35 +0,0 @@ -# Allow arena creature spawn after a mode change - -df.world.arena_spawn.race.clear -df.world.arena_spawn.caste.clear - -df.world.raws.creatures.all.length.times { |r_idx| - df.world.raws.creatures.all[r_idx].caste.length.times { |c_idx| - df.world.arena_spawn.race << r_idx - df.world.arena_spawn.caste << c_idx - } -} - -df.world.arena_spawn.creature_cnt[df.world.arena_spawn.race.length-1] = 0 - -puts < [-xy ] [-z ]') -end - -local fname = table.remove(args,1) -local goal = tonumber(table.remove(args,1)) or qerror('Invalid density') -local expr = table.remove(args,1) or qerror('No expression') -local zscale = 2 -local xyscale = 1 - -for i = 1,#args,2 do - if args[i] == '-xy' then - xyscale = tonumber(args[i+1]) or qerror('Invalid xyscale') - end - if args[i] == '-z' then - zscale = tonumber(args[i+1]) or qerror('Invalid zscale') - end -end - -local fn_env = copyall(math) - -fn_env.rng = rng -fn_env.apow = function(x,y) return math.pow(math.abs(x),y) end -fn_env.spow = function(x,y) return x*math.pow(math.abs(x),y-1) end - --- Noise functions are referenced from expressions --- as variables of form like "x3a", where: --- 1) x is one of x/y/z/w independent functions in each octave --- 2) 3 is the octave number; 0 corresponds to the whole range --- 3) a is the subtype - --- Independent noise functions: support 4 -local ids = { 'x', 'y', 'z', 'w' } --- Subtype: provides an offset to the coordinates -local subs = { - [''] = { 0, 0, 0 }, - a = { 0.5, 0, 0 }, - b = { 0, 0.5, 0 }, - c = { 0.5, 0.5, 0 }, - d = { 0, 0, 0.5 }, - e = { 0.5, 0, 0.5 }, - f = { 0, 0.5, 0.5 }, - g = { 0.5, 0.5, 0.5 }, -} - -function mkdelta(v) - if v == 0 then - return '' - else - return '+'..v - end -end - -function mkexpr(expr) - -- Collect referenced variables - local max_octave = -1 - local vars = {} - - for var,id,octave,subtype in string.gmatch(expr,'%f[%w](([xyzw])(%d+)(%a*))%f[^%w]') do - if not vars[var] then - octave = tonumber(octave) - - if octave > max_octave then - max_octave = octave - end - - local sub = subs[subtype] or qerror('Invalid subtype: '..subtype) - - vars[var] = { id = id, octave = octave, subtype = subtype, sub = sub } - end - end - - if max_octave < 0 then - qerror('No noise function references in expression.') - end - - -- Allocate the noise functions - local code = '' - - for i = 0,max_octave do - for j,id in ipairs(ids) do - code = code .. 'local _fn_'..i..'_'..id..' = rng:perlin(3)\n'; - end - end - - -- Evaluate variables - code = code .. 'return function(x,y,z)\n' - - for var,info in pairs(vars) do - local fn = '_fn_'..info.octave..'_'..info.id - local mul = math.pow(2,info.octave) - mul = math.min(48*4, mul) - code = code .. ' local '..var - .. ' = _fn_'..info.octave..'_'..info.id - .. '(x*'..mul..mkdelta(info.sub[1]) - .. ',y*'..mul..mkdelta(info.sub[2]) - .. ',z*'..mul..mkdelta(info.sub[3]) - .. ')\n' - end - - -- Complete and compile the function - code = code .. ' return ('..expr..')\nend\n' - - local f,err = load(code, '=(expr)', 't', fn_env) - if not f then - qerror(err) - end - return f() -end - -local field_fn = mkexpr(expr) - -function render(thresh,file) - local area = 0 - local line, arr = '', {} - - for zy = 0,1 do - for y = 0,48*4-1 do - line = '' - for zx = 0,1 do - for x = 0,48*4-1 do - local tx = (0.5+x)/(48*4/xyscale) - local ty = (0.5+y)/(48*4/xyscale) - local tz = 0.3+(zx+zy*2)/(48*4/zscale) - local v1 = field_fn(tx, ty, tz) - local v = -1 - if v1 > thresh then - v = v1; - area = area + 1 - end - if file then - local c = math.max(0, math.min(255, v * 127 + 128)) - arr[2*x+1] = c - arr[2*x+2] = c - end - end - if file then - line = line..string.char(table.unpack(arr)) - end - end - if file then - file:write(line,line) - end - end - end - - return area/4/(48*4)/(48*4) -end - -function search(fn,min,max,goal,eps) - local center - for i = 1,32 do - center = (max+min)/2 - local cval = fn(center) - print('At '..center..': '..cval) - if math.abs(cval-goal) < eps then - break - end - if cval > goal then - min = center - else - max = center - end - end - return center -end - -local thresh = search(render, -2, 2, goal, math.min(0.001,goal/20)) - -local file,err = io.open(fname, 'wb') -if not file then - print('error: ',err) - return -end -file:write('P5\n') -file:write('# '..goal..' '..expr..' '..xyscale..' '..zscale..'\n') -file:write('768 768\n255\n') -local area = render(thresh, file) -file:close() - -print('Area fraction: '..area) diff --git a/scripts/devel/unforbidall.rb b/scripts/devel/unforbidall.rb deleted file mode 100644 index 176bff9c0..000000000 --- a/scripts/devel/unforbidall.rb +++ /dev/null @@ -1,10 +0,0 @@ -# unforbid all items -=begin - -devel/unforbidall -================= -Unforbid all items. - -=end - -df.world.items.all.each { |i| i.flags.forbid = false } diff --git a/scripts/devel/unit-path.lua b/scripts/devel/unit-path.lua deleted file mode 100644 index 2eff7ed2d..000000000 --- a/scripts/devel/unit-path.lua +++ /dev/null @@ -1,225 +0,0 @@ --- Show the internal path a unit is currently following. ---[[=begin - -devel/unit-path -=============== -Show the internal path a unit is currently following. - -=end]] - -local utils = require 'utils' -local gui = require 'gui' -local guidm = require 'gui.dwarfmode' -local dlg = require 'gui.dialogs' - -local tile_attrs = df.tiletype.attrs - -UnitPathUI = defclass(UnitPathUI, guidm.MenuOverlay) - -UnitPathUI.focus_path = 'unit-path' - -UnitPathUI.ATTRS { - unit = DEFAULT_NIL, - has_path = false, - has_goal = false, -} - -function UnitPathUI:init() - self.saved_mode = df.global.ui.main.mode - if self.unit then - self.has_path = #self.unit.path.path.x > 0 - self.has_goal = self.unit.path.dest.x >= 0 - end -end - -function UnitPathUI:onShow() - -- with cursor, but without those ugly lines from native hauling mode - df.global.ui.main.mode = df.ui_sidebar_mode.Stockpiles -end - -function UnitPathUI:onDestroy() - self:moveCursorTo(copyall(self.unit.pos)) - df.global.ui.main.mode = self.saved_mode -end - -local function getTileType(cursor) - local block = dfhack.maps.getTileBlock(cursor) - if block then - return block.tiletype[cursor.x%16][cursor.y%16] - else - return 0 - end -end - -local function getTileWalkable(cursor) - local block = dfhack.maps.getTileBlock(cursor) - if block then - return block.walkable[cursor.x%16][cursor.y%16] - else - return 0 - end -end - -local function paintMapTile(dc, vp, cursor, pos, ...) - if not same_xyz(cursor, pos) then - local stile = vp:tileToScreen(pos) - if stile.z == 0 then - dc:seek(stile.x,stile.y):char(...) - end - end -end - -local function get_path_point(gpath,i) - return xyz2pos(gpath.x[i], gpath.y[i], gpath.z[i]) -end - -function UnitPathUI:renderPath(dc,vp,cursor) - local gpath = self.unit.path.path - local startp = self.unit.pos - local endp = self.unit.path.dest - local visible = gui.blink_visible(500) - - if visible then - paintMapTile(dc, vp, cursor, endp, '+', COLOR_LIGHTGREEN) - end - - local ok = nil - local pcnt = #gpath.x - if pcnt > 0 then - ok = true - - for i = 0,pcnt-1 do - local pt = get_path_point(gpath, i) - if i == 0 and not same_xyz(startp,pt) then - ok = false - end - if i == pcnt-1 and not same_xyz(endp,pt) then - ok = false - end - --[[local tile = getTileType(pt) - if not isTrackTile(tile) then - ok = false - end]] - if visible then - local char = '+' - if i < pcnt-1 then - local npt = get_path_point(gpath, i+1) - if npt.z == pt.z+1 then - char = 30 - elseif npt.z == pt.z-1 then - char = 31 - elseif npt.x == pt.x+1 then - char = 26 - elseif npt.x == pt.x-1 then - char = 27 - elseif npt.y == pt.y+1 then - char = 25 - elseif npt.y == pt.y-1 then - char = 24 - end - end - local color = COLOR_LIGHTGREEN - if getTileWalkable(pt) == 0 then color = COLOR_LIGHTRED end - paintMapTile(dc, vp, cursor, pt, char, color) - end - end - end - - if gui.blink_visible(120) then - paintMapTile(dc, vp, cursor, startp, 240, COLOR_LIGHTGREEN, COLOR_GREEN) - end - - return ok -end - -function UnitPathUI:onRenderBody(dc) - dc:clear():seek(1,1):pen(COLOR_WHITE):string("Unit Path") - - local prof = dfhack.units.getProfessionName(self.unit) - local name = dfhack.units.getVisibleName(self.unit) - - dc:seek(2,3):pen(COLOR_BLUE):string(prof) - if name and name.has_name then - dc:seek(2,4):pen(COLOR_BLUE):string(dfhack.TranslateName(name)) - end - - local cursor = guidm.getCursorPos() - - local vp = self:getViewport() - local mdc = gui.Painter.new(self.df_layout.map) - - if not self.has_path then - if gui.blink_visible(120) then - paintMapTile(mdc, vp, cursor, self.unit.pos, 15, COLOR_LIGHTRED, COLOR_RED) - end - - dc:seek(1,6):pen(COLOR_RED):string('Not following path') - else - self:renderPath(mdc,vp,cursor) - - dc:seek(1,6):pen(COLOR_GREEN):string(df.unit_path_goal[self.unit.path.goal] or '?') - end - - dc:newline():pen(COLOR_GREY) - dc:newline(2):string('Speed: '..dfhack.units.computeMovementSpeed(self.unit)) - dc:newline(2):string('Slowdown: '..dfhack.units.computeSlowdownFactor(self.unit)) - - dc:newline():newline(1) - - local has_station = self.unit.idle_area_type >= 0 - - if has_station then - if gui.blink_visible(250) then - paintMapTile(mdc, vp, cursor, self.unit.idle_area, 21, COLOR_LIGHTCYAN) - end - - dc:pen(COLOR_GREEN):string(df.unit_station_type[self.unit.idle_area_type]) - dc:newline():newline(2):pen(COLOR_GREY):string('Threshold: '..self.unit.idle_area_threshold) - else - dc:pen(COLOR_RED):string('No station'):newline():newline(2) - end - - dc:newline():newline(1):string('At cursor:') - dc:newline():newline(2) - - local tile = getTileType(cursor) - dc:string(df.tiletype[tile],COLOR_CYAN) - - dc:newline():newline(1):pen(COLOR_WHITE) - dc:key('CUSTOM_Z'):string(": Zoom unit, ") - dc:key('CUSTOM_G'):string(": Zoom goal",COLOR_GREY,nil,self.has_goal) - dc:newline(1) - dc:key('CUSTOM_N'):string(": Zoom station",COLOR_GREY,nil,has_station) - dc:newline():newline(1) - dc:key('LEAVESCREEN'):string(": Back") -end - -function UnitPathUI:onInput(keys) - if keys.CUSTOM_Z then - self:moveCursorTo(copyall(self.unit.pos)) - elseif keys.CUSTOM_G then - if self.has_goal then - self:moveCursorTo(copyall(self.unit.path.dest)) - end - elseif keys.CUSTOM_N then - if self.unit.idle_area_type > 0 then - self:moveCursorTo(copyall(self.unit.idle_area)) - end - elseif keys.LEAVESCREEN then - self:dismiss() - elseif self:propagateMoveKeys(keys) then - return - end -end - -function UnitPathUI:onGetSelectedUnit() - return self.unit -end - -local unit = dfhack.gui.getSelectedUnit(true) - -if not unit or not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/ViewUnits/Some/') then - qerror("This script requires the main dwarfmode view in 'v' mode with a unit selected") -end - -UnitPathUI{ unit = unit }:show() diff --git a/scripts/devel/watch-minecarts.lua b/scripts/devel/watch-minecarts.lua deleted file mode 100644 index f7eba0ca6..000000000 --- a/scripts/devel/watch-minecarts.lua +++ /dev/null @@ -1,83 +0,0 @@ --- Logs minecart coordinates and speeds to console. ---[[=begin - -devel/watch-minecarts -===================== -Logs minecart coordinates and speeds to console. - -Usage: ``devel/watch-minecarts start|stop`` - -=end]] - -last_stats = last_stats or {} - -function compare_one(vehicle) - local last = last_stats[vehicle.id] - local item = df.item.find(vehicle.item_id) - local ipos = item.pos - local new = { - ipos.x*100000 + vehicle.offset_x, vehicle.speed_x, - ipos.y*100000 + vehicle.offset_y, vehicle.speed_y, - ipos.z*100000 + vehicle.offset_z, vehicle.speed_z - } - - if (last == nil) or item.flags.on_ground then - local delta = { vehicle.id } - local show = (last == nil) - - for i=1,6 do - local rv = 0 - if last then - rv = last[i] - end - delta[i*2] = new[i]/100000 - local dv = new[i] - rv - delta[i*2+1] = dv/100000 - if dv ~= 0 then - show = true - end - end - - if show then - print(table.unpack(delta)) - end - end - - last_stats[vehicle.id] = new -end - -function compare_all() - local seen = {} - for _,v in ipairs(df.global.world.vehicles.all) do - seen[v.id] = true - compare_one(v) - end - for k,v in pairs(last_stats) do - if not seen[k] then - print(k,'DEAD') - end - end -end - -function start_timer() - if not dfhack.timeout_active(timeout_id) then - timeout_id = dfhack.timeout(1, 'ticks', function() - compare_all() - start_timer() - end); - if not timeout_id then - dfhack.printerr('Could not start timer in watch-minecarts') - end - end -end - -compare_all() - -local cmd = ... - -if cmd == 'start' then - start_timer() -elseif cmd == 'stop' then - dfhack.timeout_active(timeout_id, nil) -end - diff --git a/scripts/digfort.rb b/scripts/digfort.rb deleted file mode 100644 index d83928172..000000000 --- a/scripts/digfort.rb +++ /dev/null @@ -1,94 +0,0 @@ -# designate an area based on a '.csv' plan -=begin - -digfort -======= -A script to designate an area for digging according to a plan in csv format. - -This script, inspired from quickfort, can designate an area for digging. -Your plan should be stored in a .csv file like this:: - - # this is a comment - d;d;u;d;d;skip this tile;d - d;d;d;i - -Available tile shapes are named after the 'dig' menu shortcuts: -``d`` for dig, ``u`` for upstairs, ``j`` downstairs, ``i`` updown, -``h`` channel, ``r`` upward ramp, ``x`` remove designation. -Unrecognized characters are ignored (eg the 'skip this tile' in the sample). - -Empty lines and data after a ``#`` are ignored as comments. -To skip a row in your design, use a single ``;``. - -One comment in the file may contain the phrase ``start(3,5)``. It is interpreted -as an offset for the pattern: instead of starting at the cursor, it will start -3 tiles left and 5 tiles up from the cursor. - -The script takes the plan filename, starting from the root df folder (where -``Dwarf Fortress.exe`` is found). - -=end - -fname = $script_args[0].to_s - -if not $script_args[0] then - puts " Usage: digfort " - throw :script_finished -end -if not fname[-4..-1] == ".csv" then - puts " The plan file must be in .csv format." - throw :script_finished -end -if not File.file?(fname) then - puts " The specified file does not exist." - throw :script_finished -end - -planfile = File.read(fname) - -if df.cursor.x == -30000 - puts "place the game cursor to the top-left corner of the design and retry" - throw :script_finished -end - -offset = [0, 0] -tiles = [] -planfile.each_line { |l| - if l =~ /#.*start\s*\(\s*(-?\d+)\s*[,;]\s*(-?\d+)/ - raise "Error: multiple start() comments" if offset != [0, 0] - offset = [$1.to_i, $2.to_i] - end - - l = l.chomp.sub(/#.*/, '') - next if l == '' - tiles << l.split(/[;,]/).map { |t| - t = t.strip - (t[0] == ?") ? t[1..-2] : t - } -} - -x = df.cursor.x - offset[0] -y = df.cursor.y - offset[1] -z = df.cursor.z - -tiles.each { |line| - next if line.empty? or line == [''] - line.each { |tile| - t = df.map_tile_at(x, y, z) - s = t.shape_basic - case tile - when 'd'; t.dig(:Default) if s == :Wall - when 'u'; t.dig(:UpStair) if s == :Wall - when 'j'; t.dig(:DownStair) if s == :Wall or s == :Floor - when 'i'; t.dig(:UpDownStair) if s == :Wall - when 'h'; t.dig(:Channel) if s == :Wall or s == :Floor - when 'r'; t.dig(:Ramp) if s == :Wall - when 'x'; t.dig(:No) - end - x += 1 - } - x = df.cursor.x - offset[0] - y += 1 -} - -puts ' done' diff --git a/scripts/drain-aquifer.lua b/scripts/drain-aquifer.lua deleted file mode 100644 index 6a79017bf..000000000 --- a/scripts/drain-aquifer.lua +++ /dev/null @@ -1,40 +0,0 @@ --- Remove all aquifers from the map ---[[=begin - -drain-aquifer -============= -Remove all 'aquifer' tag from the map blocks. Irreversible. - -=end]] - -local function drain() - local layers = {} - local layer_count = 0 - local tile_count = 0 - - for k, block in ipairs(df.global.world.map.map_blocks) do - if block.flags.has_aquifer then - block.flags.has_aquifer = false - block.flags.check_aquifer = false - - for x, row in ipairs(block.designation) do - for y, tile in ipairs(row) do - if tile.water_table then - tile.water_table = false - tile_count = tile_count + 1 - end - end - end - - if not layers[block.map_pos.z] then - layers[block.map_pos.z] = true - layer_count = layer_count + 1 - end - end - end - - print("Cleared "..tile_count.." aquifer tile"..((tile_count ~= 1) and "s" or "").. - " in "..layer_count.." layer"..((layer_count ~= 1) and "s" or "")..".") -end - -drain(...) diff --git a/scripts/elevate-mental.lua b/scripts/elevate-mental.lua deleted file mode 100644 index 9db61ed79..000000000 --- a/scripts/elevate-mental.lua +++ /dev/null @@ -1,52 +0,0 @@ --- Elevate all the mental attributes of a unit --- by vjek ---[[=begin - -elevate-mental -============== -Set all mental attributes of the selected dwarf to 2600, which is very high. -Numbers between 0 and 5000 can be passed as an argument: ``elevate-mental 100`` -for example would make the dwarf very stupid indeed. - -=end]] - -function ElevateMentalAttributes(value) - unit=dfhack.gui.getSelectedUnit() - if unit==nil then - print ("No unit under cursor! Aborting with extreme prejudice.") - return - end - --print name of dwarf - print("Adjusting "..dfhack.TranslateName(dfhack.units.getVisibleName(unit))) - --walk through available attributes, adjust current to max - local ok,f,t,k = pcall(pairs,unit.status.current_soul.mental_attrs) - if ok then - for k,v in f,t,k do - if value ~= nil then - print("Adjusting current value for "..tostring(k).." of "..v.value.." to the value of "..value) - v.value=value - else - print("Adjusting current value for "..tostring(k).." of "..v.value.." to max value of "..v.max_value) - v.value=v.max_value - --below will reset values back to "normal" - --v.value=v.max_value/2 - end - end - end -end ---script execution starts here -local opt = ... -opt = tonumber(opt) - -if opt ~= nil then - if opt >=0 and opt <=5000 then - ElevateMentalAttributes(opt) - end - if opt <0 or opt >5000 then - print("Invalid Range or argument. This script accepts either no argument, in which case it will increase the attribute to the max_value for the unit, or an argument between 0 and 5000, which will set all attributes to that value.") - end -end - -if opt == nil then - ElevateMentalAttributes() -end diff --git a/scripts/elevate-physical.lua b/scripts/elevate-physical.lua deleted file mode 100644 index c210b4ffa..000000000 --- a/scripts/elevate-physical.lua +++ /dev/null @@ -1,51 +0,0 @@ --- Elevate all the physical attributes of a unit --- by vjek ---[[=begin - -elevate-physical -================ -As for elevate-mental, but for physical traits. High is good for soldiers, -while having an ineffective hammerer can be useful too... - -=end]] - -function ElevatePhysicalAttributes(value) - unit=dfhack.gui.getSelectedUnit() - if unit==nil then - print ("No unit under cursor! Aborting with extreme prejudice.") - return - end - --print name of dwarf - print("Adjusting "..dfhack.TranslateName(dfhack.units.getVisibleName(unit))) - --walk through available attributes, adjust current to max - local ok,f,t,k = pcall(pairs,unit.body.physical_attrs) - if ok then - for k,v in f,t,k do - if value ~= nil then - print("Adjusting current value for "..tostring(k).." of "..v.value.." to the value of "..value) - v.value=value - else - print("Adjusting current value for "..tostring(k).." of "..v.value.." to max value of "..v.max_value) - v.value=v.max_value - --below will reset values back to "normal" - --v.value=v.max_value/2 - end - end - end -end ---script execution starts here -local opt = ... -opt = tonumber(opt) - -if opt ~= nil then - if opt >=0 and opt <=5000 then - ElevatePhysicalAttributes(opt) - end - if opt <0 or opt >5000 then - print("Invalid Range or argument. This script accepts either no argument, in which case it will increase the attribute to the max_value for the unit, or an argument between 0 and 5000, which will set all attributes to that value.") - end -end - -if opt == nil then - ElevatePhysicalAttributes() -end diff --git a/scripts/emigration.lua b/scripts/emigration.lua deleted file mode 100644 index aba62fa2b..000000000 --- a/scripts/emigration.lua +++ /dev/null @@ -1,132 +0,0 @@ ---Allow stressed dwarves to emigrate from the fortress --- For 34.11 by IndigoFenix; update and cleanup by PeridexisErrant --- old version: http://dffd.bay12games.com/file.php?id=8404 ---[[=begin - -emigration -========== -Allows dwarves to emigrate from the fortress when stressed, -in proportion to how badly stressed they are and adjusted -for who they would have to leave with - a dwarven merchant -being more attractive than leaving alone (or with an elf). -The check is made monthly. - -A happy dwarf (ie with negative stress) will never emigrate. - -Usage: ``emigration enable|disable`` - -=end]] - -local args = {...} -if args[1] == "enable" then - enabled = true -elseif args[1] == "disable" then - enabled = false -end - -function desireToStay(unit,method,civ_id) - -- on a percentage scale - local value = 100 - unit.status.current_soul.personality.stress_level / 5000 - if method == 'merchant' or method == 'diplomat' then - if civ_id ~= unit.civ_id then value = value*2 end end - if method == 'wild' then - value = value*5 end - return value -end - -function desert(u,method,civ) - u.relations.following = nil - local line = dfhack.TranslateName(dfhack.units.getVisibleName(u)) .. " has " - if method == 'merchant' then - line = line.."joined the merchants" - u.flags1.merchant = true - u.civ_id = civ - elseif method == 'diplomat' then - line = line.."followed the diplomat" - u.flags1.diplomat = true - u.civ_id = civ - else - line = line.."abandoned the settlement in search of a better life." - u.civ_id = -1 - u.flags1.forest = true - u.animal.leave_countdown = 2 - end - print(line) - dfhack.gui.showAnnouncement(line, COLOR_WHITE) -end - -function canLeave(unit) - for _, skill in pairs(unit.status.current_soul.skills) do - if skill.rating > 14 then return false end - end - if unit.flags1.caged - or unit.race ~= df.global.ui.race_id - or unit.civ_id ~= df.global.ui.civ_id - or dfhack.units.isDead(unit) - or dfhack.units.isOpposedToLife(unit) - or unit.flags1.merchant - or unit.flags1.diplomat - or unit.flags1.chained - or dfhack.units.getNoblePositions(unit) ~= nil - or unit.military.squad_id ~= -1 - or dfhack.units.isCitizen(unit) - or dfhack.units.isSane(unit) - or unit.profession ~= 103 - or not dfhack.units.isDead(unit) - then return false end - return true -end - -function checkForDeserters(method,civ_id) - local allUnits = df.global.world.units.active - for i=#allUnits-1,0,-1 do -- search list in reverse - local u = allUnits[i] - if canLeave(u) and math.random(100) < desireToStay(u,method,civ_id) then - desert(u,method,civ_id) - end - end -end - -function checkmigrationnow() - local merchant_civ_ids = {} - local diplomat_civ_ids = {} - local allUnits = df.global.world.units.active - for i=0, #allUnits-1 do - local unit = allUnits[i] - if dfhack.units.isSane(unit) - and not dfhack.units.isDead(unit) - and not dfhack.units.isOpposedToLife(unit) - and not unit.flags1.tame - then - if unit.flags1.merchant then table.insert(merchant_civ_ids, unit.civ_id) end - if unit.flags1.diplomat then table.insert(diplomat_civ_ids, unit.civ_id) end - end - end - - for _, civ_id in pairs(merchant_civ_ids) do checkForDeserters('merchant', civ_id) end - for _, civ_id in pairs(diplomat_civ_ids) do checkForDeserters('diplomat', civ_id) end - checkForDeserters('wild', -1) -end - -local function event_loop() - if enabled then - checkmigrationnow() - dfhack.timeout(1, 'months', event_loop) - end -end - -dfhack.onStateChange.loadEmigration = function(code) - if code==SC_MAP_LOADED then - if enabled then - print("Emigration enabled.") - event_loop() - else - print("Emigration disabled.") - end - end -end - -if dfhack.isMapLoaded() then - dfhack.onStateChange.loadEmigration(SC_MAP_LOADED) -end - diff --git a/scripts/exportlegends.lua b/scripts/exportlegends.lua deleted file mode 100644 index 86542f930..000000000 --- a/scripts/exportlegends.lua +++ /dev/null @@ -1,836 +0,0 @@ --- Export everything from legends mode ---[[=begin - -exportlegends -============= -Controls legends mode to export data - especially useful to set-and-forget large -worlds, or when you want a map of every site when there are several hundred. - -The 'info' option exports more data than is possible in vanilla, to a -:file:`region-date-legends_plus.xml` file developed to extend -:forums:`World Viewer <128932>` and other legends utilities. - -Options: - -:info: Exports the world/gen info, the legends XML, and a custom XML with more information -:custom: Exports a custom XML with more information -:sites: Exports all available site maps -:maps: Exports all seventeen detailed maps -:all: Equivalent to calling all of the above, in that order - -=end]] - -gui = require 'gui' -local args = {...} -local vs = dfhack.gui.getCurViewscreen() -local i = 1 - -local MAPS = { - "Standard biome+site map", - "Elevations including lake and ocean floors", - "Elevations respecting water level", - "Biome", - "Hydrosphere", - "Temperature", - "Rainfall", - "Drainage", - "Savagery", - "Volcanism", - "Current vegetation", - "Evil", - "Salinity", - "Structures/fields/roads/etc.", - "Trade", - "Nobility and Holdings", - "Diplomacy", -} - -function getItemSubTypeName(itemType, subType) - if (dfhack.items.getSubtypeCount(itemType)) <= 0 then - return tostring(-1) - end - local subtypename = dfhack.items.getSubtypeDef(itemType, subType) - if (subtypename == nil) then - return tostring(-1) - else - return tostring(subtypename.name):lower() - end -end - -function findEntity(id) - for k,v in ipairs(df.global.world.entities.all) do - if (v.id == id) then - return v - end - end - return nil -end - -function table.contains(table, element) - for _, value in pairs(table) do - if value == element then - return true - end - end - return false -end - -function table.containskey(table, key) - for value, _ in pairs(table) do - if value == key then - return true - end - end - return false -end - --- wrapper that returns "unknown N" for df.enum_type[BAD_VALUE], --- instead of returning nil or causing an error -df_enums = {} -setmetatable(df_enums, { - __index = function(self, enum) - if not df[enum] or df[enum]._kind ~= 'enum-type' then - error('invalid enum: ' .. enum) - end - local t = {} - setmetatable(t, { - __index = function(self, k) - return df[enum][k] or 'unknown ' .. k - end - }) - return t - end, - __newindex = function() error('read-only') end -}) - ---create an extra legends xml with extra data, by Mason11987 for World Viewer -function export_more_legends_xml() - local month = dfhack.world.ReadCurrentMonth() + 1 --days and months are 1-indexed - local day = dfhack.world.ReadCurrentDay() - local year_str = string.format('%0'..math.max(5, string.len(''..df.global.cur_year))..'d', df.global.cur_year) - local date_str = year_str..string.format('-%02d-%02d', month, day) - - local filename = df.global.world.cur_savegame.save_dir.."-"..date_str.."-legends_plus.xml" - local file = io.open(filename, 'w') - if not file then qerror("could not open file: " .. filename) end - - file:write("\n") - file:write("\n") - file:write(""..dfhack.df2utf(dfhack.TranslateName(df.global.world.world_data.name)).."\n") - file:write(""..dfhack.df2utf(dfhack.TranslateName(df.global.world.world_data.name,1)).."\n") - - file:write("\n") - for landmassK, landmassV in ipairs(df.global.world.world_data.landmasses) do - file:write("\t\n") - file:write("\t\t"..landmassV.index.."\n") - file:write("\t\t"..dfhack.df2utf(dfhack.TranslateName(landmassV.name,1)).."\n") - file:write("\t\t"..landmassV.min_x..","..landmassV.min_y.."\n") - file:write("\t\t"..landmassV.max_x..","..landmassV.max_y.."\n") - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for mountainK, mountainV in ipairs(df.global.world.world_data.mountain_peaks) do - file:write("\t\n") - file:write("\t\t"..mountainK.."\n") - file:write("\t\t"..dfhack.df2utf(dfhack.TranslateName(mountainV.name,1)).."\n") - file:write("\t\t"..mountainV.pos.x..","..mountainV.pos.y.."\n") - file:write("\t\t"..mountainV.height.."\n") - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for regionK, regionV in ipairs(df.global.world.world_data.regions) do - file:write("\t\n") - file:write("\t\t"..regionV.index.."\n") - file:write("\t\t") - for xK, xVal in ipairs(regionV.region_coords.x) do - file:write(xVal..","..regionV.region_coords.y[xK].."|") - end - file:write("\n") - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for regionK, regionV in ipairs(df.global.world.world_data.underground_regions) do - file:write("\t\n") - file:write("\t\t"..regionV.index.."\n") - file:write("\t\t") - for xK, xVal in ipairs(regionV.region_coords.x) do - file:write(xVal..","..regionV.region_coords.y[xK].."|") - end - file:write("\n") - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for siteK, siteV in ipairs(df.global.world.world_data.sites) do - file:write("\t\n") - for k,v in pairs(siteV) do - if (k == "id" or k == "civ_id" or k == "cur_owner_id") then - file:write("\t\t<"..k..">"..tostring(v).."\n") - elseif (k == "buildings") then - if (#siteV.buildings > 0) then - file:write("\t\t\n") - for buildingK, buildingV in ipairs(siteV.buildings) do - file:write("\t\t\t\n") - file:write("\t\t\t\t"..buildingV.id.."\n") - file:write("\t\t\t\t"..df_enums.abstract_building_type[buildingV:getType()]:lower().."\n") - if (df_enums.abstract_building_type[buildingV:getType()]:lower() ~= "underworld_spire" or table.containskey(buildingV,"name")) then - file:write("\t\t\t\t"..dfhack.df2utf(dfhack.TranslateName(buildingV.name, 1)).."\n") - file:write("\t\t\t\t"..dfhack.df2utf(dfhack.TranslateName(buildingV.name)).."\n") - end - if (buildingV:getType() == df.abstract_building_type.TEMPLE) then - file:write("\t\t\t\t"..buildingV.deity.."\n") - file:write("\t\t\t\t"..buildingV.religion.."\n") - end - if (buildingV:getType() == df.abstract_building_type.DUNGEON) then - file:write("\t\t\t\t"..buildingV.dungeon_type.."\n") - end - for inhabitabntK,inhabitabntV in pairs(buildingV.inhabitants) do - file:write("\t\t\t\t"..inhabitabntV.anon_2.."\n") - end - file:write("\t\t\t\n") - end - file:write("\t\t\n") - end - end - end - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for wcK, wcV in ipairs(df.global.world.world_data.constructions.list) do - file:write("\t\n") - file:write("\t\t"..wcV.id.."\n") - file:write("\t\t"..dfhack.df2utf(dfhack.TranslateName(wcV.name,1)).."\n") - file:write("\t\t"..(df_enums.world_construction_type[wcV:getType()]):lower().."\n") - file:write("\t\t") - for xK, xVal in ipairs(wcV.square_pos.x) do - file:write(xVal..","..wcV.square_pos.y[xK].."|") - end - file:write("\n") - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for artifactK, artifactV in ipairs(df.global.world.artifacts.all) do - file:write("\t\n") - file:write("\t\t"..artifactV.id.."\n") - if (artifactV.item:getType() ~= -1) then - file:write("\t\t"..tostring(df_enums.item_type[artifactV.item:getType()]):lower().."\n") - if (artifactV.item:getSubtype() ~= -1) then - file:write("\t\t"..artifactV.item.subtype.name.."\n") - end - for improvementK,impovementV in pairs(artifactV.item.improvements) do - if impovementV:getType() == df.improvement_type.WRITING then - for writingk,writingV in pairs(impovementV["itemimprovement_writingst.anon_1"]) do - file:write("\t\t"..writingV.."\n") - end - elseif impovementV:getType() == df.improvement_type.PAGES then - file:write("\t\t"..impovementV.count.."\n") - for writingk,writingV in pairs(impovementV.contents) do - file:write("\t\t"..writingV.."\n") - end - end - end - end - if (table.containskey(artifactV.item,"description")) then - file:write("\t\t"..dfhack.df2utf(artifactV.item.description:lower()).."\n") - end - if artifactV.item:getMaterial() ~= -1 then - file:write("\t\t"..dfhack.matinfo.toString(dfhack.matinfo.decode(artifactV.item:getMaterial(), artifactV.item:getMaterialIndex())).."\n") - end - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for hfK, hfV in ipairs(df.global.world.history.figures) do - file:write("\t\n") - file:write("\t\t"..hfV.id.."\n") - file:write("\t\t"..hfV.sex.."\n") - if hfV.race >= 0 then file:write("\t\t"..df.global.world.raws.creatures.all[hfV.race].name[0].."\n") end - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for entityPopK, entityPopV in ipairs(df.global.world.entity_populations) do - file:write("\t\n") - file:write("\t\t"..entityPopV.id.."\n") - for raceK, raceV in ipairs(entityPopV.races) do - local raceName = (df.global.world.raws.creatures.all[raceV].creature_id):lower() - file:write("\t\t"..raceName..":"..entityPopV.counts[raceK].."\n") - end - file:write("\t\t"..entityPopV.civ_id.."\n") - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for entityK, entityV in ipairs(df.global.world.entities.all) do - file:write("\t\n") - file:write("\t\t"..entityV.id.."\n") - if entityV.race >= 0 then - file:write("\t\t"..(df.global.world.raws.creatures.all[entityV.race].creature_id):lower().."\n") - end - file:write("\t\t"..(df_enums.historical_entity_type[entityV.type]):lower().."\n") - if entityV.type == df.historical_entity_type.Religion then -- Get worshipped figure - if (entityV.unknown1b ~= nil and entityV.unknown1b.worship ~= nil) then - for k,v in pairs(entityV.unknown1b.worship) do - file:write("\t\t"..v.."\n") - end - end - end - for id, link in pairs(entityV.entity_links) do - file:write("\t\t\n") - for k, v in pairs(link) do - if (k == "type") then - file:write("\t\t\t<"..k..">"..tostring(df_enums.entity_entity_link_type[v]).."\n") - else - file:write("\t\t\t<"..k..">"..v.."\n") - end - end - file:write("\t\t\n") - end - for positionK,positionV in pairs(entityV.positions.own) do - file:write("\t\t\n") - file:write("\t\t\t"..positionV.id.."\n") - if positionV.name[0] ~= "" then file:write("\t\t\t"..positionV.name[0].."\n") end - if positionV.name_male[0] ~= "" then file:write("\t\t\t"..positionV.name_male[0].."\n") end - if positionV.name_female[0] ~= "" then file:write("\t\t\t"..positionV.name_female[0].."\n") end - if positionV.spouse[0] ~= "" then file:write("\t\t\t"..positionV.spouse[0].."\n") end - if positionV.spouse_male[0] ~= "" then file:write("\t\t\t"..positionV.spouse_male[0].."\n") end - if positionV.spouse_female[0] ~= "" then file:write("\t\t\t"..positionV.spouse_female[0].."\n") end - file:write("\t\t\n") - end - for assignmentK,assignmentV in pairs(entityV.positions.assignments) do - file:write("\t\t\n") - for k, v in pairs(assignmentV) do - if (k == "id" or k == "histfig" or k == "position_id" or k == "squad_id") then - file:write("\t\t\t<"..k..">"..v.."\n") - end - end - file:write("\t\t\n") - end - for idx,id in pairs(entityV.histfig_ids) do - file:write("\t\t"..id.."\n") - end - for id, link in ipairs(entityV.children) do - file:write("\t\t"..link.."\n") - end - file:write("\t\t") - for xK, xVal in ipairs(entityV.claims.unk2.x) do - file:write(xVal..","..entityV.claims.unk2.y[xK].."|") - end - file:write("\t\t\n") - if (table.containskey(entityV,"occasion_info") and entityV.occasion_info ~= nil) then - for occasionK, occasionV in pairs(entityV.occasion_info.occasions) do - file:write("\t\t\n") - file:write("\t\t\t"..occasionV.id.."\n") - file:write("\t\t\t"..dfhack.df2utf(dfhack.TranslateName(occasionV.name,1)).."\n") - file:write("\t\t\t"..occasionV.event.."\n") - for scheduleK, scheduleV in pairs(occasionV.schedule) do - file:write("\t\t\t\n") - file:write("\t\t\t\t"..scheduleK.."\n") - file:write("\t\t\t\t"..df_enums.occasion_schedule_type[scheduleV.type]:lower().."\n") - if(scheduleV.type == df.occasion_schedule_type.THROWING_COMPETITION) then - file:write("\t\t\t\t"..df_enums.item_type[scheduleV.reference]:lower().."\n") - file:write("\t\t\t\t"..getItemSubTypeName(scheduleV.reference,scheduleV.reference2).."\n") - else - file:write("\t\t\t\t"..scheduleV.reference.."\n") - file:write("\t\t\t\t"..scheduleV.reference2.."\n") - end - for featureK, featureV in pairs(scheduleV.features) do - file:write("\t\t\t\t\n") - if(df_enums.occasion_schedule_feature[featureV.feature] ~= nil) then - file:write("\t\t\t\t\t"..df_enums.occasion_schedule_feature[featureV.feature]:lower().."\n") - else - file:write("\t\t\t\t\t"..featureV.feature.."\n") - end - file:write("\t\t\t\t\t"..featureV.reference.."\n") - file:write("\t\t\t\t\n") - end - file:write("\t\t\t\n") - end - file:write("\t\t\n") - end - end - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for formK, formV in ipairs(df.global.world.poetic_forms.all) do - file:write("\t\n") - file:write("\t\t"..formV.id.."\n") - file:write("\t\t"..dfhack.df2utf(dfhack.TranslateName(formV.name,1)).."\n") - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for formK, formV in ipairs(df.global.world.musical_forms.all) do - file:write("\t\n") - file:write("\t\t"..formV.id.."\n") - file:write("\t\t"..dfhack.df2utf(dfhack.TranslateName(formV.name,1)).."\n") - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for formK, formV in ipairs(df.global.world.dance_forms.all) do - file:write("\t\n") - file:write("\t\t"..formV.id.."\n") - file:write("\t\t"..dfhack.df2utf(dfhack.TranslateName(formV.name,1)).."\n") - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for wcK, wcV in ipairs(df.global.world.written_contents.all) do - file:write("\t\n") - file:write("\t\t"..wcV.id.."\n") - file:write("\t\t"..wcV.title.."\n") - file:write("\t\t"..wcV.page_start.."\n") - file:write("\t\t"..wcV.page_end.."\n") - for refK, refV in pairs(wcV.refs) do - file:write("\t\t\n") - file:write("\t\t\t"..df_enums.general_ref_type[refV:getType()].."\n") - if refV:getType() == df.general_ref_type.ARTIFACT then file:write("\t\t\t"..refV.artifact_id.."\n") -- artifact - elseif refV:getType() == df.general_ref_type.ENTITY then file:write("\t\t\t"..refV.entity_id.."\n") -- entity - elseif refV:getType() == df.general_ref_type.HISTORICAL_EVENT then file:write("\t\t\t"..refV.event_id.."\n") -- event - elseif refV:getType() == df.general_ref_type.SITE then file:write("\t\t\t"..refV.site_id.."\n") -- site - elseif refV:getType() == df.general_ref_type.SUBREGION then file:write("\t\t\t"..refV.region_id.."\n") -- region - elseif refV:getType() == df.general_ref_type.HISTORICAL_FIGURE then file:write("\t\t\t"..refV.hist_figure_id.."\n") -- hist figure - elseif refV:getType() == df.general_ref_type.WRITTEN_CONTENT then file:write("\t\t\t"..refV.anon_1.."\n") - elseif refV:getType() == df.general_ref_type.POETIC_FORM then file:write("\t\t\t"..refV.poetic_form_id.."\n") -- poetic form - elseif refV:getType() == df.general_ref_type.MUSICAL_FORM then file:write("\t\t\t"..refV.musical_form_id.."\n") -- musical form - elseif refV:getType() == df.general_ref_type.DANCE_FORM then file:write("\t\t\t"..refV.dance_form_id.."\n") -- dance form - elseif refV:getType() == df.general_ref_type.INTERACTION then -- TODO INTERACTION - elseif refV:getType() == df.general_ref_type.KNOWLEDGE_SCHOLAR_FLAG then -- TODO KNOWLEDGE_SCHOLAR_FLAG - elseif refV:getType() == df.general_ref_type.VALUE_LEVEL then -- TODO VALUE_LEVEL - elseif refV:getType() == df.general_ref_type.LANGUAGE then -- TODO LANGUAGE - else - print("unknown reference",refV:getType(),df_enums.general_ref_type[refV:getType()]) - --for k,v in pairs(refV) do print(k,v) end - end - file:write("\t\t\n") - end - file:write("\t\t"..(df_enums.written_content_type[wcV.type] or wcV.type).."\n") - for styleK, styleV in pairs(wcV.styles) do - file:write("\t\t\n") - end - file:write("\t\t"..wcV.author.."\n") - file:write("\t\n") - end - file:write("\n") - - file:write("\n") - for ID, event in ipairs(df.global.world.history.events) do - if event:getType() == df.history_event_type.ADD_HF_ENTITY_LINK - or event:getType() == df.history_event_type.ADD_HF_SITE_LINK - or event:getType() == df.history_event_type.ADD_HF_HF_LINK - or event:getType() == df.history_event_type.ADD_HF_ENTITY_LINK - or event:getType() == df.history_event_type.TOPICAGREEMENT_CONCLUDED - or event:getType() == df.history_event_type.TOPICAGREEMENT_REJECTED - or event:getType() == df.history_event_type.TOPICAGREEMENT_MADE - or event:getType() == df.history_event_type.BODY_ABUSED - or event:getType() == df.history_event_type.CHANGE_CREATURE_TYPE - or event:getType() == df.history_event_type.CHANGE_HF_JOB - or event:getType() == df.history_event_type.CHANGE_HF_STATE - or event:getType() == df.history_event_type.CREATED_BUILDING - or event:getType() == df.history_event_type.CREATURE_DEVOURED - or event:getType() == df.history_event_type.HF_DOES_INTERACTION - or event:getType() == df.history_event_type.HF_LEARNS_SECRET - or event:getType() == df.history_event_type.HIST_FIGURE_NEW_PET - or event:getType() == df.history_event_type.HIST_FIGURE_REACH_SUMMIT - or event:getType() == df.history_event_type.ITEM_STOLEN - or event:getType() == df.history_event_type.REMOVE_HF_ENTITY_LINK - or event:getType() == df.history_event_type.REMOVE_HF_SITE_LINK - or event:getType() == df.history_event_type.REPLACED_BUILDING - or event:getType() == df.history_event_type.MASTERPIECE_CREATED_ARCH_DESIGN - or event:getType() == df.history_event_type.MASTERPIECE_CREATED_DYE_ITEM - or event:getType() == df.history_event_type.MASTERPIECE_CREATED_ARCH_CONSTRUCT - or event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM - or event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM_IMPROVEMENT - or event:getType() == df.history_event_type.MASTERPIECE_CREATED_FOOD - or event:getType() == df.history_event_type.MASTERPIECE_CREATED_ENGRAVING - or event:getType() == df.history_event_type.MASTERPIECE_LOST - or event:getType() == df.history_event_type.ENTITY_ACTION - or event:getType() == df.history_event_type.HF_ACT_ON_BUILDING - or event:getType() == df.history_event_type.ARTIFACT_CREATED - or event:getType() == df.history_event_type.ASSUME_IDENTITY - or event:getType() == df.history_event_type.CREATE_ENTITY_POSITION - or event:getType() == df.history_event_type.DIPLOMAT_LOST - or event:getType() == df.history_event_type.MERCHANT - or event:getType() == df.history_event_type.WAR_PEACE_ACCEPTED - or event:getType() == df.history_event_type.WAR_PEACE_REJECTED - or event:getType() == df.history_event_type.HIST_FIGURE_WOUNDED - or event:getType() == df.history_event_type.HIST_FIGURE_DIED - then - file:write("\t\n") - file:write("\t\t"..event.id.."\n") - file:write("\t\t"..tostring(df_enums.history_event_type[event:getType()]):lower().."\n") - for k,v in pairs(event) do - if k == "year" or k == "seconds" or k == "flags" or k == "id" - or (k == "region" and event:getType() ~= df.history_event_type.HF_DOES_INTERACTION) - or k == "region_pos" or k == "layer" or k == "feature_layer" or k == "subregion" - or k == "anon_1" or k == "anon_2" or k == "flags2" or k == "unk1" then - - elseif event:getType() == df.history_event_type.ADD_HF_ENTITY_LINK and k == "link_type" then - file:write("\t\t<"..k..">"..df_enums.histfig_entity_link_type[v]:lower().."\n") - elseif event:getType() == df.history_event_type.ADD_HF_ENTITY_LINK and k == "position_id" then - local entity = findEntity(event.civ) - if (entity ~= nil and event.civ > -1 and v > -1) then - for entitypositionsK, entityPositionsV in ipairs(entity.positions.own) do - if entityPositionsV.id == v then - file:write("\t\t"..tostring(entityPositionsV.name[0]):lower().."\n") - break - end - end - else - file:write("\t\t-1\n") - end - elseif event:getType() == df.history_event_type.CREATE_ENTITY_POSITION and k == "position" then - local entity = findEntity(event.site_civ) - if (entity ~= nil and v > -1) then - for entitypositionsK, entityPositionsV in ipairs(entity.positions.own) do - if entityPositionsV.id == v then - file:write("\t\t"..tostring(entityPositionsV.name[0]):lower().."\n") - break - end - end - else - file:write("\t\t-1\n") - end - elseif event:getType() == df.history_event_type.REMOVE_HF_ENTITY_LINK and k == "link_type" then - file:write("\t\t<"..k..">"..df_enums.histfig_entity_link_type[v]:lower().."\n") - elseif event:getType() == df.history_event_type.REMOVE_HF_ENTITY_LINK and k == "position_id" then - local entity = findEntity(event.civ) - if (entity ~= nil and event.civ > -1 and v > -1) then - for entitypositionsK, entityPositionsV in ipairs(entity.positions.own) do - if entityPositionsV.id == v then - file:write("\t\t"..tostring(entityPositionsV.name[0]):lower().."\n") - break - end - end - else - file:write("\t\t-1\n") - end - elseif event:getType() == df.history_event_type.ADD_HF_HF_LINK and k == "type" then - file:write("\t\t"..df_enums.histfig_hf_link_type[v]:lower().."\n") - elseif event:getType() == df.history_event_type.ADD_HF_SITE_LINK and k == "type" then - file:write("\t\t"..df_enums.histfig_site_link_type[v]:lower().."\n") - elseif event:getType() == df.history_event_type.REMOVE_HF_SITE_LINK and k == "type" then - file:write("\t\t"..df_enums.histfig_site_link_type[v]:lower().."\n") - elseif (event:getType() == df.history_event_type.ITEM_STOLEN or - event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM or - event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM_IMPROVEMENT or - event:getType() == df.history_event_type.MASTERPIECE_CREATED_DYE_ITEM - ) and k == "item_type" then - file:write("\t\t"..df_enums.item_type[v]:lower().."\n") - elseif (event:getType() == df.history_event_type.ITEM_STOLEN or - event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM or - event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM_IMPROVEMENT or - event:getType() == df.history_event_type.MASTERPIECE_CREATED_DYE_ITEM - ) and k == "item_subtype" then - --if event.item_type > -1 and v > -1 then - file:write("\t\t<"..k..">"..getItemSubTypeName(event.item_type,v).."\n") - --end - elseif event:getType() == df.history_event_type.MASTERPIECE_CREATED_FOOD and k == "item_subtype" then - --if event.item_type > -1 and v > -1 then - file:write("\t\tfood\n") - file:write("\t\t<"..k..">"..getItemSubTypeName(df.item_type.FOOD,v).."\n") - --end - elseif event:getType() == df.history_event_type.ITEM_STOLEN and k == "mattype" then - if (v > -1) then - if (dfhack.matinfo.decode(event.mattype, event.matindex) == nil) then - file:write("\t\t"..event.mattype.."\n") - file:write("\t\t"..event.matindex.."\n") - else - file:write("\t\t"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.mattype, event.matindex)).."\n") - end - end - elseif (event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM or - event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM_IMPROVEMENT or - event:getType() == df.history_event_type.MASTERPIECE_CREATED_FOOD or - event:getType() == df.history_event_type.MASTERPIECE_CREATED_DYE_ITEM - ) and k == "mat_type" then - if (v > -1) then - if (dfhack.matinfo.decode(event.mat_type, event.mat_index) == nil) then - file:write("\t\t"..event.mat_type.."\n") - file:write("\t\t"..event.mat_index.."\n") - else - file:write("\t\t"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.mat_type, event.mat_index)).."\n") - end - end - elseif event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM_IMPROVEMENT and k == "imp_mat_type" then - if (v > -1) then - if (dfhack.matinfo.decode(event.imp_mat_type, event.imp_mat_index) == nil) then - file:write("\t\t"..event.imp_mat_type.."\n") - file:write("\t\t"..event.imp_mat_index.."\n") - else - file:write("\t\t"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.imp_mat_type, event.imp_mat_index)).."\n") - end - end - elseif event:getType() == df.history_event_type.MASTERPIECE_CREATED_DYE_ITEM and k == "dye_mat_type" then - if (v > -1) then - if (dfhack.matinfo.decode(event.dye_mat_type, event.dye_mat_index) == nil) then - file:write("\t\t"..event.dye_mat_type.."\n") - file:write("\t\t"..event.dye_mat_index.."\n") - else - file:write("\t\t"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.dye_mat_type, event.dye_mat_index)).."\n") - end - end - - elseif event:getType() == df.history_event_type.ITEM_STOLEN and k == "matindex" then - --skip - elseif event:getType() == df.history_event_type.ITEM_STOLEN and k == "item" and v == -1 then - --skip - elseif (event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM or - event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM_IMPROVEMENT - ) and k == "mat_index" then - --skip - elseif event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM_IMPROVEMENT and k == "imp_mat_index" then - --skip - elseif (event:getType() == df.history_event_type.WAR_PEACE_ACCEPTED or - event:getType() == df.history_event_type.WAR_PEACE_REJECTED or - event:getType() == df.history_event_type.TOPICAGREEMENT_CONCLUDED or - event:getType() == df.history_event_type.TOPICAGREEMENT_REJECTED or - event:getType() == df.history_event_type.TOPICAGREEMENT_MADE - ) and k == "topic" then - file:write("\t\t"..tostring(df_enums.meeting_topic[v]):lower().."\n") - elseif event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM_IMPROVEMENT and k == "improvement_type" then - file:write("\t\t"..df_enums.improvement_type[v]:lower().."\n") - elseif ((event:getType() == df.history_event_type.HIST_FIGURE_REACH_SUMMIT and k == "group") - or (event:getType() == df.history_event_type.HIST_FIGURE_NEW_PET and k == "group") - or (event:getType() == df.history_event_type.BODY_ABUSED and k == "bodies")) then - for detailK,detailV in pairs(v) do - file:write("\t\t<"..k..">"..detailV.."\n") - end - elseif event:getType() == df.history_event_type.HIST_FIGURE_NEW_PET and k == "pets" then - for detailK,detailV in pairs(v) do - file:write("\t\t<"..k..">"..df.global.world.raws.creatures.all[detailV].name[0].."\n") - end - elseif event:getType() == df.history_event_type.BODY_ABUSED and (k == "props") then - file:write("\t\t"..tostring(df_enums.item_type[event.props.item.item_type]):lower().."\n") - file:write("\t\t"..getItemSubTypeName(event.props.item.item_type,event.props.item.item_subtype).."\n") - if (event.props.item.mat_type > -1) then - if (dfhack.matinfo.decode(event.props.item.mat_type, event.props.item.mat_index) == nil) then - file:write("\t\t"..event.props.item.mat_type.."\n") - file:write("\t\t"..event.props.item.mat_index.."\n") - else - file:write("\t\t"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.props.item.mat_type, event.props.item.mat_index)).."\n") - end - end - --file:write("\t\t<"..k.."_item_mat_type>"..tostring(event.props.item.mat_type).."\n") - --file:write("\t\t<"..k.."_item_mat_index>"..tostring(event.props.item.mat_index).."\n") - file:write("\t\t<"..k.."_pile_type>"..tostring(event.props.pile_type).."\n") - elseif event:getType() == df.history_event_type.ASSUME_IDENTITY and k == "identity" then - if (table.contains(df.global.world.identities.all,v)) then - if (df.global.world.identities.all[v].histfig_id == -1) then - local thisIdentity = df.global.world.identities.all[v] - file:write("\t\t"..thisIdentity.name.first_name.."\n") - file:write("\t\t"..(df.global.world.raws.creatures.all[thisIdentity.race].creature_id):lower().."\n") - file:write("\t\t"..(df.global.world.raws.creatures.all[thisIdentity.race].caste[thisIdentity.caste].caste_id):lower().."\n") - else - file:write("\t\t"..df.global.world.identities.all[v].histfig_id.."\n") - end - end - elseif event:getType() == df.history_event_type.MASTERPIECE_CREATED_ARCH_CONSTRUCT and k == "building_type" then - file:write("\t\t"..df_enums.building_type[v]:lower().."\n") - elseif event:getType() == df.history_event_type.MASTERPIECE_CREATED_ARCH_CONSTRUCT and k == "building_subtype" then - if (df_enums.building_type[event.building_type]:lower() == "furnace") then - file:write("\t\t"..df_enums.furnace_type[v]:lower().."\n") - elseif v > -1 then - file:write("\t\t"..tostring(v).."\n") - end - elseif k == "race" then - if v > -1 then - file:write("\t\t"..df.global.world.raws.creatures.all[v].name[0].."\n") - end - elseif k == "caste" then - if v > -1 then - file:write("\t\t"..(df.global.world.raws.creatures.all[event.race].caste[v].caste_id):lower().."\n") - end - elseif k == "interaction" and event:getType() == df.history_event_type.HF_DOES_INTERACTION then - file:write("\t\t"..df.global.world.raws.interactions[v].str[3].value.."\n") - file:write("\t\t"..df.global.world.raws.interactions[v].str[4].value.."\n") - elseif k == "interaction" and event:getType() == df.history_event_type.HF_LEARNS_SECRET then - file:write("\t\t"..df.global.world.raws.interactions[v].str[2].value.."\n") - elseif event:getType() == df.history_event_type.HIST_FIGURE_DIED and k == "weapon" then - for detailK,detailV in pairs(v) do - if (detailK == "item") then - if detailV > -1 then - file:write("\t\t<"..detailK..">"..detailV.."\n") - local thisItem = df.item.find(detailV) - if (thisItem ~= nil) then - if (thisItem.flags.artifact == true) then - for refk,refv in pairs(thisItem.general_refs) do - if (refv:getType() == df.general_ref_type.IS_ARTIFACT) then - file:write("\t\t"..refv.artifact_id.."\n") - break - end - end - end - end - - end - elseif (detailK == "item_type") then - if event.weapon.item > -1 then - file:write("\t\t<"..detailK..">"..tostring(df_enums.item_type[detailV]):lower().."\n") - end - elseif (detailK == "item_subtype") then - if event.weapon.item > -1 and detailV > -1 then - file:write("\t\t<"..detailK..">"..getItemSubTypeName(event.weapon.item_type,detailV).."\n") - end - elseif (detailK == "mattype") then - if (detailV > -1) then - file:write("\t\t"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.weapon.mattype, event.weapon.matindex)).."\n") - end - elseif (detailK == "matindex") then - - elseif (detailK == "shooter_item") then - if detailV > -1 then - file:write("\t\t<"..detailK..">"..detailV.."\n") - local thisItem = df.item.find(detailV) - if thisItem ~= nil then - if (thisItem.flags.artifact == true) then - for refk,refv in pairs(thisItem.general_refs) do - if (refv:getType() == df.general_ref_type.IS_ARTIFACT) then - file:write("\t\t"..refv.artifact_id.."\n") - break - end - end - end - end - end - elseif (detailK == "shooter_item_type") then - if event.weapon.shooter_item > -1 then - file:write("\t\t<"..detailK..">"..tostring(df_enums.item_type[detailV]):lower().."\n") - end - elseif (detailK == "shooter_item_subtype") then - if event.weapon.shooter_item > -1 and detailV > -1 then - file:write("\t\t<"..detailK..">"..getItemSubTypeName(event.weapon.shooter_item_type,detailV).."\n") - end - elseif (detailK == "shooter_mattype") then - if (detailV > -1) then - file:write("\t\t"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.weapon.shooter_mattype, event.weapon.shooter_matindex)).."\n") - end - elseif (detailK == "shooter_matindex") then - --skip - elseif detailK == "slayer_race" or detailK == "slayer_caste" then - --skip - else - file:write("\t\t<"..detailK..">"..detailV.."\n") - end - end - elseif event:getType() == df.history_event_type.HIST_FIGURE_DIED and k == "death_cause" then - file:write("\t\t<"..k..">"..df_enums.death_type[v]:lower().."\n") - elseif event:getType() == df.history_event_type.CHANGE_HF_JOB and (k == "new_job" or k == "old_job") then - file:write("\t\t<"..k..">"..df_enums.profession[v]:lower().."\n") - elseif event:getType() == df.history_event_type.CHANGE_CREATURE_TYPE and (k == "old_race" or k == "new_race") and v >= 0 then - file:write("\t\t<"..k..">"..df.global.world.raws.creatures.all[v].name[0].."\n") - else - file:write("\t\t<"..k..">"..tostring(v).."\n") - end - end - file:write("\t\n") - end - end - file:write("\n") - file:write("\n") - file:write("\n") - file:write("\n") - file:write("\n") - file:write("\n") - file:close() -end - --- export information and XML ('p, x') -function export_legends_info() - print(' Exporting: World map/gen info') - gui.simulateInput(vs, 'LEGENDS_EXPORT_MAP') - print(' Exporting: Legends xml') - gui.simulateInput(vs, 'LEGENDS_EXPORT_XML') - print(" Exporting: Extra legends_plus xml") - export_more_legends_xml() -end - ---- presses 'd' for detailed maps -function wait_for_legends_vs() - local vs = dfhack.gui.getCurViewscreen() - if i <= #MAPS then - if df.viewscreen_legendsst:is_instance(vs.parent) then - vs = vs.parent - end - if df.viewscreen_legendsst:is_instance(vs) then - gui.simulateInput(vs, 'LEGENDS_EXPORT_DETAILED_MAP') - dfhack.timeout(10,'frames',wait_for_export_maps_vs) - else - dfhack.timeout(10,'frames',wait_for_legends_vs) - end - end -end - --- selects detailed map and export it -function wait_for_export_maps_vs() - local vs = dfhack.gui.getCurViewscreen() - if dfhack.gui.getCurFocus() == "export_graphical_map" then - vs.sel_idx = i-1 - print(' Exporting: '..MAPS[i]..' map') - gui.simulateInput(vs, 'SELECT') - i = i + 1 - dfhack.timeout(10,'frames',wait_for_legends_vs) - else - dfhack.timeout(10,'frames',wait_for_export_maps_vs) - end -end - --- export site maps -function export_site_maps() - local vs = dfhack.gui.getCurViewscreen() - if ((dfhack.gui.getCurFocus() ~= "legends" ) and (not table.contains(vs, "main_cursor"))) then -- Using open-legends - vs = vs.parent - end - print(' Exporting: All possible site maps') - vs.main_cursor = 1 - gui.simulateInput(vs, 'SELECT') - for i=1, #vs.sites do - gui.simulateInput(vs, 'LEGENDS_EXPORT_MAP') - gui.simulateInput(vs, 'STANDARDSCROLL_DOWN') - end - gui.simulateInput(vs, 'LEAVESCREEN') -end - --- main() -if dfhack.gui.getCurFocus() == "legends" or dfhack.gui.getCurFocus() == "dfhack/lua/legends" then - -- either native legends mode, or using the open-legends.lua script - if args[1] == "all" then - export_legends_info() - export_site_maps() - wait_for_legends_vs() - elseif args[1] == "info" then - export_legends_info() - elseif args[1] == "custom" then - export_more_legends_xml() - elseif args[1] == "maps" then - wait_for_legends_vs() - elseif args[1] == "sites" then - export_site_maps() - else dfhack.printerr('Valid arguments are "all", "info", "maps" or "sites"') - end -elseif args[1] == "maps" and - dfhack.gui.getCurFocus() == "export_graphical_map" then - wait_for_export_maps_vs() -else - dfhack.printerr('Exportlegends must be run from the main legends view') -end diff --git a/scripts/exterminate.rb b/scripts/exterminate.rb deleted file mode 100644 index bc25a95b8..000000000 --- a/scripts/exterminate.rb +++ /dev/null @@ -1,192 +0,0 @@ -# exterminate creatures -=begin - -exterminate -=========== -Kills any unit of a given race. - -With no argument, lists the available races and count eligible targets. - -With the special argument ``him``, targets only the selected creature. - -With the special argument ``undead``, targets all undeads on the map, -regardless of their race. - -When specifying a race, a caste can be specified to further restrict the -targeting. To do that, append and colon and the caste name after the race. - -Any non-dead non-caged unit of the specified race gets its ``blood_count`` -set to 0, which means immediate death at the next game tick. For creatures -such as vampires, it also sets animal.vanish_countdown to 2. - -An alternate mode is selected by adding a 2nd argument to the command, -``magma``. In this case, a column of 7/7 magma is generated on top of the -targets until they die (Warning: do not call on magma-safe creatures. Also, -using this mode on birds is not recommended.) The final alternate mode -is ``butcher``, which marks them for butchering but does not kill. - -Will target any unit on a revealed tile of the map, including ambushers, -but ignore caged/chained creatures. - -Ex:: - - exterminate gob - exterminate gob:male - -To kill a single creature, select the unit with the 'v' cursor and:: - - exterminate him - -To purify all elves on the map with fire (may have side-effects):: - - exterminate elve magma - -=end - -race = $script_args[0] - -# if the 2nd parameter is 'magma', magma rain for the targets instead of instant death -# if it is 'butcher' mark all units for butchering (wont work with hostiles) -kill_by = $script_args[1] - -case kill_by -when 'magma' - slain = 'burning' -when 'slaughter', 'butcher' - slain = 'marked for butcher' -when nil - slain = 'slain' -else - race = 'help' -end - -checkunit = lambda { |u| - (u.body.blood_count != 0 or u.body.blood_max == 0) and - not u.flags1.dead and - not u.flags1.caged and not u.flags1.chained and - #not u.flags1.hidden_in_ambush and - not df.map_designation_at(u).hidden -} - -slayit = lambda { |u| - case kill_by - when 'magma' - # it's getting hot around here - # !!WARNING!! do not call on a magma-safe creature - ouh = df.onupdate_register("exterminate ensure #{u.id}", 1) { - if u.flags1.dead - df.onupdate_unregister(ouh) - else - x, y, z = u.pos.x, u.pos.y, u.pos.z - z += 1 while tile = df.map_tile_at(x, y, z+1) and - tile.shape_passableflow and tile.shape_passablelow - df.map_tile_at(x, y, z).spawn_magma(7) - end - } - when 'butcher', 'slaughter' - # mark for slaughter at butcher's shop - u.flags2.slaughter = true - else - # just make them drop dead - u.body.blood_count = 0 - # some races dont mind having no blood, ensure they are still taken care of. - u.animal.vanish_countdown = 2 - end -} - -all_races = Hash.new(0) - -df.world.units.active.map { |u| - if checkunit[u] - if (u.enemy.undead or - (u.curse.add_tags1.OPPOSED_TO_LIFE and not - u.curse.rem_tags1.OPPOSED_TO_LIFE)) - all_races['Undead'] += 1 - else - all_races[u.race_tg.creature_id] += 1 - end - end -} - -case race -when nil - all_races.sort_by { |race, cnt| [cnt, race] }.each{ |race, cnt| puts " #{race} #{cnt}" } - -when 'help', '?' - puts <= #map_features then - qerror('Invalid feature ID') - end - map_features[tonumber(idx)].flags.Discovered = discovered -end - -function list_features() - local name = df.new('string') - for idx, feat in ipairs(map_features) do - local tags = '' - for _, t in pairs({'water', 'magma', 'subterranean', 'chasm', 'layer'}) do - if feat['is' .. t:sub(1, 1):upper() .. t:sub(2)](feat) then - tags = tags .. (' [%s]'):format(t) - end - end - feat:getName(name) - print(('Feature #%i is %s: "%s", type %s%s'):format( - idx, - feat.flags.Discovered and 'shown' or 'hidden', - name.value, - df.feature_type[feat:getType()], - tags - )) - end - df.delete(name) -end - -function enable_magma_funaces() - for idx, feat in ipairs(map_features) do - if tostring(feat):find('magma') ~= nil then - toggle_feature(idx, true) - print('Enabled magma furnaces.') - return - end - end - dfhack.printerr('Could not find a magma-bearing feature.') -end - -local args = {...} -if args[1] == 'list' then - list_features() -elseif args[1] == 'magma' then - enable_magma_funaces() -elseif args[1] == 'show' then - toggle_feature(args[2], true) -elseif args[1] == 'hide' then - toggle_feature(args[2], false) -else - print((help:gsub('=[a-z]+', ''))) -end diff --git a/scripts/fix-ster.lua b/scripts/fix-ster.lua deleted file mode 100644 index 66b4f6765..000000000 --- a/scripts/fix-ster.lua +++ /dev/null @@ -1,122 +0,0 @@ --- makes creatures [in]fertile, by modifying orientation --- usage: fix-ster [fert|ster] [all|animals|only:] --- original author: Tacomagic --- minor fixes by PeridexisErrant, Lethosor ---@ module = true ---[[=begin - -fix-ster -======== -Utilizes the orientation tag to either fix infertile creatures or inflict -infertility on creatures that you do not want to breed. Usage:: - - fix-ster [fert|ster] [all|animals|only:] - -``fert`` or ``ster`` is a required argument; whether to make the target fertile -or sterile. Optional arguments specify the target: no argument for the -selected unit, ``all`` for all units on the map, ``animals`` for all non-dwarf -creatures, or ``only:`` to only process matching creatures. - -=end]] - -function changeorient(unit, ori) - --Sets the fertility flag based on gender. - if not unit.status.current_soul then - return - end - if unit.sex == 0 then - unit.status.current_soul.orientation_flags.marry_male=ori - else - unit.status.current_soul.orientation_flags.marry_female=ori - end -end - -function changelots(creatures, ori, silent) - local v, unit - local c = 0 - --loops through indexes in creatures table and changes orientation flags - for _, v in ipairs(creatures) do - unit = df.global.world.units.active[v] - changeorient(unit,ori) - c = c + 1 - end - if not silent then - print("Changed " .. c .. " creatures.") - end -end - -function process_args(args) - local n, v, ori, crename, crenum - local creatures = {} - --Checks for any arguments at all. - if args == nil or #args == 0 then - print("No arguments. Usage is: fixster [all|animals|only:]") - return - end - for i, a in pairs(args) do - args[i] = tostring(a):lower() - end - if args[1]:sub(1, 1) == "s" then -- sterile - ori = false - elseif args[1]:sub(1, 1) == "f" then -- fertile - ori = true - else - qerror("Unrecognised first argument: " .. args[1] .. ". Aborting.") - end - - --Checks for the existence of the second argument. If it's missing, uses selected unit (if any) - if args[2] == nil then - unit = dfhack.gui.getSelectedUnit() - if not unit then return end - changeorient(unit, ori) - print('Changed selected creature.') - - --ALL arg processing - elseif args[2] == "all" then - --Create table of all current unit indexes - for n,v in ipairs(df.global.world.units.active) do - table.insert(creatures,n) - end - changelots(creatures,ori) - - --ANIMALS arg processing - elseif args[2] == "animals" then - --Create a table of all creature indexes except dwarves on the current map - for n,v in ipairs(df.global.world.units.active) do - if v.race ~= df.global.ui.race_id then - table.insert(creatures,n) - end - end - changelots(creatures,ori) - - -- ONLY: arg processing - elseif args[2]:sub(1,4) == "only" then - crename = args[2]:sub(6):upper() - - --Search raws for creature - for k,v in pairs(df.global.world.raws.creatures.all) do - if v.creature_id == crename then - crenum = k - end - end - --If no match, abort - if crenum == nil then - qerror("Creature not found. Check spelling.") - end - - --create a table of all the matching creature indexes on the map for processing - for n,v in ipairs(df.global.world.units.active) do - if v.race == crenum then - table.insert(creatures,n) - end - end - changelots(creatures,ori) - else - qerror("Unrecognised optional argument. Aborting") - end -end - -if not moduleMode then - local args = table.pack(...) - process_args(args) -end diff --git a/scripts/fix/about.txt b/scripts/fix/about.txt deleted file mode 100644 index 6a2d35c7e..000000000 --- a/scripts/fix/about.txt +++ /dev/null @@ -1 +0,0 @@ -``fix/*`` scripts fix various bugs and issues, some of them obscure. diff --git a/scripts/fix/blood-del.lua b/scripts/fix/blood-del.lua deleted file mode 100644 index ba32d4ab9..000000000 --- a/scripts/fix/blood-del.lua +++ /dev/null @@ -1,62 +0,0 @@ --- Stop traders bringing blood, ichor, or goo ---author Urist Da Vinci; edited by expwnent, scamtank ---[[=begin - -fix/blood-del -============= -Makes it so that future caravans won't bring barrels full of blood, ichor, or goo. - -=end]] -local my_entity=df.historical_entity.find(df.global.ui.civ_id) -local sText=" " -local k=0 -local v=1 - -for x,y in pairs(df.global.world.entities.all) do - my_entity=y - k=0 - while k < #my_entity.resources.misc_mat.extracts.mat_index do - v=my_entity.resources.misc_mat.extracts.mat_type[k] - sText=dfhack.matinfo.decode(v,my_entity.resources.misc_mat.extracts.mat_index[k]) - if (sText==nil) then - --LIQUID barrels - my_entity.resources.misc_mat.extracts.mat_type:erase(k) - my_entity.resources.misc_mat.extracts.mat_index:erase(k) - k=k-1 - else - if(sText.material.id=="BLOOD") then - my_entity.resources.misc_mat.extracts.mat_type:erase(k) - my_entity.resources.misc_mat.extracts.mat_index:erase(k) - k=k-1 - end - if(sText.material.id=="ICHOR") then - my_entity.resources.misc_mat.extracts.mat_type:erase(k) - my_entity.resources.misc_mat.extracts.mat_index:erase(k) - k=k-1 - end - if(sText.material.id=="GOO") then - my_entity.resources.misc_mat.extracts.mat_type:erase(k) - my_entity.resources.misc_mat.extracts.mat_index:erase(k) - k=k-1 - end - if(sText.material.id=="SWEAT") then - my_entity.resources.misc_mat.extracts.mat_type:erase(k) - my_entity.resources.misc_mat.extracts.mat_index:erase(k) - k=k-1 - end - if(sText.material.id=="TEARS") then - my_entity.resources.misc_mat.extracts.mat_type:erase(k) - my_entity.resources.misc_mat.extracts.mat_index:erase(k) - k=k-1 - end - --VENOM - --POISON - --FLUID - --MILK - --EXTRACT - - end - k=k+1 - end -end - diff --git a/scripts/fix/build-location.lua b/scripts/fix/build-location.lua deleted file mode 100644 index 9df518ec3..000000000 --- a/scripts/fix/build-location.lua +++ /dev/null @@ -1,45 +0,0 @@ --- Lets constructions reconsider the build location. - --- Partial work-around for http://www.bay12games.com/dwarves/mantisbt/view.php?id=5991 ---[[=begin - -fix/build-location -================== -Fixes construction jobs that are stuck trying to build a wall while standing -on the same exact tile (:bug:`5991`), designates the tile restricted traffic to -hopefully avoid jamming it again, and unsuspends them. - -=end]] -local utils = require('utils') - -local count = 0 - -for link,job in utils.listpairs(df.global.world.job_list) do - local job = link.item - local place = dfhack.job.getHolder(job) - - if job.job_type == df.job_type.ConstructBuilding - and place and place:isImpassableAtCreation() - and job.item_category[0] - then - local cpos = utils.getBuildingCenter(place) - - if same_xyz(cpos, job.pos) then - -- Reset the flag - job.item_category[0] = false - job.flags.suspend = false - - -- Mark the tile restricted traffic - local dsgn,occ = dfhack.maps.getTileFlags(cpos) - dsgn.traffic = df.tile_traffic.Restricted - - count = count + 1 - end - end -end - -print('Found and unstuck '..count..' construct building jobs.') - -if count > 0 then - df.global.process_jobs = true -end diff --git a/scripts/fix/dead-units.lua b/scripts/fix/dead-units.lua deleted file mode 100644 index 7687a75cc..000000000 --- a/scripts/fix/dead-units.lua +++ /dev/null @@ -1,37 +0,0 @@ --- Remove uninteresting dead units from the unit list. ---[[=begin - -fix/dead-units -============== -Removes uninteresting dead units from the unit list. Doesn't seem to give any -noticeable performance gain, but migrants normally stop if the unit list grows -to around 3000 units, and this script reduces it back. - -=end]] -local units = df.global.world.units.active -local dwarf_race = df.global.ui.race_id -local dwarf_civ = df.global.ui.civ_id -local count = 0 - -for i=#units-1,0,-1 do - local unit = units[i] - local flags1 = unit.flags1 - local flags2 = unit.flags2 - if flags1.dead and unit.race ~= dwarf_race then - local remove = false - if flags2.slaughter then - remove = true - elseif not unit.name.has_name then - remove = true - elseif unit.civ_id ~= dwarf_civ and - not (flags1.merchant or flags1.diplomat) then - remove = true - end - if remove then - count = count + 1 - units:erase(i) - end - end -end - -print('Units removed from active: '..count) diff --git a/scripts/fix/diplomats.lua b/scripts/fix/diplomats.lua deleted file mode 100644 index 4e8c6daf7..000000000 --- a/scripts/fix/diplomats.lua +++ /dev/null @@ -1,106 +0,0 @@ --- Add Elven diplomats to negotiate tree caps ---[[=begin - -fix/diplomats -============= -Adds a Diplomat position to all Elven civilizations, allowing them to negotiate -tree cutting quotas - and you to violate them and start wars. -This was vanilla behaviour until ``0.31.12``, in which the "bug" was "fixed". - -=end]] - - -function update_pos(ent) - local pos = df.entity_position:new() - ent.positions.own:insert('#', pos) - - pos.code = "DIPLOMAT" - pos.id = ent.positions.next_position_id + 1 - ent.positions.next_position_id = ent.positions.next_position_id + 1 - - pos.flags.DO_NOT_CULL = true - pos.flags.MENIAL_WORK_EXEMPTION = true - pos.flags.SLEEP_PRETENSION = true - pos.flags.PUNISHMENT_EXEMPTION = true - pos.flags.ACCOUNT_EXEMPT = true - pos.flags.DUTY_BOUND = true - pos.flags.COLOR = true - pos.flags.HAS_RESPONSIBILITIES = true - pos.flags.IS_DIPLOMAT = true - pos.flags.IS_LEADER = true - -- not sure what these flags do, but the game sets them for a valid diplomat - pos.flags.unk_12 = true - pos.flags.unk_1a = true - pos.flags.unk_1b = true - pos.name[0] = "Diplomat" - pos.name[1] = "Diplomats" - pos.precedence = 70 - pos.color[0] = 7 - pos.color[1] = 0 - pos.color[2] = 1 - - return pos -end - - - -local checked = 0 -local fixed = 0 - -for _,ent in pairs(df.global.world.entities.all) do - if ent.type == df.historical_entity_type.Civilization and ent.entity_raw.flags.TREE_CAP_DIPLOMACY then - checked = checked + 1 - - update = true - local found_position - -- see if we need to add a new position or modify an existing one - for _,pos in pairs(ent.positions.own) do - if pos.responsibilities.MAKE_INTRODUCTIONS and - pos.responsibilities.MAKE_PEACE_AGREEMENTS and - pos.responsibilities.MAKE_TOPIC_AGREEMENTS then - -- a diplomat position exists with the proper responsibilities - skip to the end - update = false - found_position=pos - break - end - -- Diplomat position already exists, but has the wrong options - modify it instead of creating a new one - if pos.code == "DIPLOMAT" then - found_position=pos - break - end - end - if update then - -- either there's no diplomat, or there is one and it's got the wrong responsibilities - if not found_position then - found_position = add_guild_rep( ent ) - end - -- assign responsibilities - found_position.responsibilities.MAKE_INTRODUCTIONS = true - found_position.responsibilities.MAKE_PEACE_AGREEMENTS = true - found_position.responsibilities.MAKE_TOPIC_AGREEMENTS = true - end - -- make sure the diplomat position, whether we created it or not, is set up for proper assignment - local assign = true - for _,p in pairs(ent.positions.assignments) do - if p.position_id == found_position.id then -- it is - nothing more to do here - assign = false - break - end - end - if assign then -- it isn't - set it up - local asn = df.entity_position_assignment:new() - ent.positions.assignments:insert('#', asn); - - asn.id = ent.positions.next_assignment_id - ent.positions.next_assignment_id = asn.id + 1 - asn.position_id = found_position.id - asn.flags:resize(math.max(32, #asn.flags)) -- make room for 32 flags - asn.flags[0] = true -- and set the first one - end - if update or assign then - fixed = fixed + 1 - end - end -end - -print("Enabled tree cap diplomacy for "..fixed.." of "..checked.." civilizations.") diff --git a/scripts/fix/dry-buckets.lua b/scripts/fix/dry-buckets.lua deleted file mode 100644 index 91cff2cd1..000000000 --- a/scripts/fix/dry-buckets.lua +++ /dev/null @@ -1,28 +0,0 @@ --- Removes water from buckets (for lye-making). ---[[=begin - -fix/dry-buckets -=============== -Removes water from all buckets in your fortress, allowing them -to be used for making lye. Skips buckets in buildings (eg a well), -being carried, or currently used by a job. - -=end]] - -local emptied = 0 -local water_type = dfhack.matinfo.find('WATER').type - -for _,item in ipairs(df.global.world.items.all) do - container = dfhack.items.getContainer(item) - if container ~= nil - and container:getType() == df.item_type.BUCKET - and not (container.flags.in_job or container.flags.in_building) - and item:getMaterial() == water_type - and item:getType() == df.item_type.LIQUID_MISC - and not (item.flags.in_job or item.flags.in_building) then - dfhack.items.remove(item) - emptied = emptied + 1 - end -end - -print('Emptied '..emptied..' buckets.') diff --git a/scripts/fix/fat-dwarves.lua b/scripts/fix/fat-dwarves.lua deleted file mode 100644 index 09b5eb28e..000000000 --- a/scripts/fix/fat-dwarves.lua +++ /dev/null @@ -1,33 +0,0 @@ --- Makes fat dwarves non-fat. --- --- See for more info: --- http://www.bay12games.com/dwarves/mantisbt/view.php?id=5971 ---[[=begin - -fix/fat-dwarves -=============== -Avoids 5-10% FPS loss due to constant recalculation of insulation for dwarves at -maximum fatness, by reducing the cap from 1,000,000 to 999,999. -Recalculation is triggered in steps of 250 units, and very fat dwarves -constantly bounce off the maximum value while eating. - -=end]] -local num_fat = 0 -local num_lagging = 0 - -for _,v in ipairs(df.global.world.units.all) do - local fat = v.counters2.stored_fat - if fat > 850000 then - v.counters2.stored_fat = 500000 - if v.race == df.global.ui.race_id then - print(fat,dfhack.TranslateName(dfhack.units.getVisibleName(v))) - num_fat = num_fat + 1 - if fat > 999990 then - num_lagging = num_lagging + 1 - end - end - end -end - -print("Fat dwarves cured: "..num_fat) -print("Lag sources: "..num_lagging) diff --git a/scripts/fix/feeding-timers.lua b/scripts/fix/feeding-timers.lua deleted file mode 100644 index 84fab7086..000000000 --- a/scripts/fix/feeding-timers.lua +++ /dev/null @@ -1,43 +0,0 @@ --- feeding-timers.lua --- original author: tejón --- rewritten by expwnent --- see repeat.lua for how to run this every so often automatically ---[[=begin - -fix/feeding-timers -================== -Reset the GiveWater and GiveFood timers of all units as appropriate. - -=end]] -local args = {...} -if args[1] ~= nil then - print("fix/feeding-timers usage") - print(" fix/feeding-timers") - print(" reset the feeding timers of all units as appropriate") - print(" fix/feeding-timers help") - print(" print this help message") - print(" repeat -time [n] [years/months/ticks/days/etc] -command fix/feeding-timers") - print(" run this script every n time units") - print(" repeat -cancel fix/feeding-timers") - print(" stop automatically running this script") - return -end - -local fixcount = 0 -for _,unit in ipairs(df.global.world.units.all) do - if dfhack.units.isCitizen(unit) and not (unit.flags1.dead) then - for _,v in pairs(unit.status.misc_traits) do - local didfix = 0 - if v.id == 0 then -- I think this should have additional conditions... - v.value = 0 -- GiveWater cooldown set to zero - didfix = 1 - end - if v.id == 1 then -- I think this should have additional conditions... - v.value = 0 -- GiveFood cooldown set to zero - didfix = 1 - end - fixcount = fixcount + didfix - end - end -end -print("Fixed feeding timers for " .. fixcount .. " citizens.") diff --git a/scripts/fix/item-occupancy.lua b/scripts/fix/item-occupancy.lua deleted file mode 100644 index bcf1b8903..000000000 --- a/scripts/fix/item-occupancy.lua +++ /dev/null @@ -1,131 +0,0 @@ --- Verify item occupancy and block item list integrity. - ---[[=begin - -fix/item-occupancy -================== -Diagnoses and fixes issues with nonexistant 'items occupying site', usually -caused by `autodump` bugs or other hacking mishaps. Checks that: - -#. Item has ``flags.on_ground`` <=> it is in the correct block item list -#. A tile has items in block item list <=> it has ``occupancy.item`` -#. The block item lists are sorted - -=end]] -local utils = require 'utils' - -function check_block_items(fix) - local cnt = 0 - local icnt = 0 - local found = {} - local found_somewhere = {} - - local should_fix = false - local can_fix = true - - for _,block in ipairs(df.global.world.map.map_blocks) do - local itable = {} - local bx,by,bz = pos2xyz(block.map_pos) - - -- Scan the block item vector - local last_id = nil - local resort = false - - for _,id in ipairs(block.items) do - local item = df.item.find(id) - local ix,iy,iz = pos2xyz(item.pos) - local dx,dy,dz = ix-bx,iy-by,iz-bz - - -- Check sorted order - if last_id and last_id >= id then - print(bx,by,bz,last_id,id,'block items not sorted') - resort = true - else - last_id = id - end - - -- Check valid coordinates and flags - if not item.flags.on_ground then - print(bx,by,bz,id,dx,dy,'in block & not on ground') - elseif dx < 0 or dx >= 16 or dy < 0 or dy >= 16 or dz ~= 0 then - found_somewhere[id] = true - print(bx,by,bz,id,dx,dy,dz,'invalid pos') - can_fix = false - else - found[id] = true - itable[dx + dy*16] = true; - - -- Check missing occupancy - if not block.occupancy[dx][dy].item then - print(bx,by,bz,dx,dy,'item & not occupied') - if fix then - block.occupancy[dx][dy].item = true - else - should_fix = true - end - end - end - end - - -- Sort the vector if needed - if resort then - if fix then - utils.sort_vector(block.items) - else - should_fix = true - end - end - - icnt = icnt + #block.items - - -- Scan occupancy for spurious marks - for x=0,15 do - local ocx = block.occupancy[x] - for y=0,15 do - if ocx[y].item and not itable[x + y*16] then - print(bx,by,bz,x,y,'occupied & no item') - if fix then - ocx[y].item = false - else - should_fix = true - end - end - end - end - - cnt = cnt + 256 - end - - -- Check if any items are missing from blocks - for _,item in ipairs(df.global.world.items.all) do - if item.flags.on_ground and not found[item.id] then - can_fix = false - if not found_somewhere[item.id] then - print(id,item.pos.x,item.pos.y,item.pos.z,'on ground & not in block') - end - end - end - - -- Report - print(cnt.." tiles and "..icnt.." items checked.") - - if should_fix and can_fix then - print("Use 'fix/item-occupancy --fix' to fix the listed problems.") - elseif should_fix then - print("The problems are too severe to be fixed by this script.") - end -end - -local opt = ... -local fix = false - -if opt then - if opt == '--fix' then - fix = true - else - qerror('Invalid option: '..opt) - end -end - -print("Checking item occupancy - this will take a few seconds.") -check_block_items(fix) diff --git a/scripts/fix/loyaltycascade.rb b/scripts/fix/loyaltycascade.rb deleted file mode 100644 index 561e8b9e4..000000000 --- a/scripts/fix/loyaltycascade.rb +++ /dev/null @@ -1,70 +0,0 @@ -# Cancels a 'loyalty cascade' when citizens are killed -=begin - -fix/loyaltycascade -================== -Aborts loyalty cascades by fixing units whose own civ is the enemy. - -=end -def fixunit(unit) - return if unit.race != df.ui.race_id or unit.civ_id != df.ui.civ_id - links = unit.hist_figure_tg.entity_links - fixed = false - - # check if the unit is a civ renegade - if i1 = links.index { |l| - l.kind_of?(DFHack::HistfigEntityLinkFormerMemberst) and - l.entity_id == df.ui.civ_id - } and i2 = links.index { |l| - l.kind_of?(DFHack::HistfigEntityLinkEnemyst) and - l.entity_id == df.ui.civ_id - } - fixed = true - i1, i2 = i2, i1 if i1 > i2 - links.delete_at i2 - links.delete_at i1 - links << DFHack::HistfigEntityLinkMemberst.cpp_new(:entity_id => df.ui.civ_id, :link_strength => 100) - df.add_announcement "fixloyalty: #{unit.name} is now a member of #{df.ui.civ_tg.name} again" - end - - # check if the unit is a group renegade - if i1 = links.index { |l| - l.kind_of?(DFHack::HistfigEntityLinkFormerMemberst) and - l.entity_id == df.ui.group_id - } and i2 = links.index { |l| - l.kind_of?(DFHack::HistfigEntityLinkEnemyst) and - l.entity_id == df.ui.group_id - } - fixed = true - i1, i2 = i2, i1 if i1 > i2 - links.delete_at i2 - links.delete_at i1 - links << DFHack::HistfigEntityLinkMemberst.cpp_new(:entity_id => df.ui.group_id, :link_strength => 100) - df.add_announcement "fixloyalty: #{unit.name} is now a member of #{df.ui.group_tg.name} again" - end - - # fix the 'is an enemy' cache matrix (mark to be recalculated by the game when needed) - if fixed and unit.enemy.enemy_status_slot != -1 - i = unit.enemy.enemy_status_slot - unit.enemy.enemy_status_slot = -1 - cache = df.world.enemy_status_cache - cache.slot_used[i] = false - cache.rel_map[i].map! { -1 } - cache.rel_map.each { |a| a[i] = -1 } - cache.next_slot = i if cache.next_slot > i - end - - # return true if we actually fixed the unit - fixed -end - -count = 0 -df.unit_citizens.each { |u| - count += 1 if fixunit(u) -} - -if count > 0 - puts "loyalty cascade fixed (#{count} dwarves)" -else - puts "no loyalty cascade found" -end diff --git a/scripts/fix/merchants.lua b/scripts/fix/merchants.lua deleted file mode 100644 index ae3ef4233..000000000 --- a/scripts/fix/merchants.lua +++ /dev/null @@ -1,104 +0,0 @@ --- Allow humans to make trade agreements ---[[=begin - -fix/merchants -============= -Adds the Guild Representative position to all Human civilizations, -allowing them to make trade agreements. This was the default behaviour in -``0.28.181.40d`` and earlier. - -=end]] - - -function add_guild_rep(ent) - -- there was no guild rep - create it - local pos = df.entity_position:new() - ent.positions.own:insert('#', pos) - - pos.code = "GUILD_REPRESENTATIVE" - pos.id = ent.positions.next_position_id + 1 - ent.positions.next_position_id = ent.positions.next_position_id + 1 - - pos.flags.DO_NOT_CULL = true - pos.flags.MENIAL_WORK_EXEMPTION = true - pos.flags.SLEEP_PRETENSION = true - pos.flags.PUNISHMENT_EXEMPTION = true - pos.flags.ACCOUNT_EXEMPT = true - pos.flags.DUTY_BOUND = true - pos.flags.COLOR = true - pos.flags.HAS_RESPONSIBILITIES = true - pos.flags.IS_DIPLOMAT = true - pos.flags.IS_LEADER = true - -- not sure what these flags do, but the game sets them for a valid guild rep - pos.flags.unk_12 = true - pos.flags.unk_1a = true - pos.flags.unk_1b = true - pos.name[0] = "Guild Representative" - pos.name[1] = "Guild Representatives" - pos.precedence = 40 - pos.color[0] = 7 - pos.color[1] = 0 - pos.color[2] = 1 - - return pos -end - -local checked = 0 -local fixed = 0 - -for _,ent in pairs(df.global.world.entities.all) do - if ent.type == df.historical_entity_type.Civilization and ent.entity_raw.flags.MERCHANT_NOBILITY then - checked = checked + 1 - - update = true - -- see if we need to add a new position or modify an existing one - local found_position - for _,pos in pairs(ent.positions.own) do - if pos.responsibilities.TRADE and pos.responsibilities.ESTABLISH_COLONY_TRADE_AGREEMENTS then - -- a guild rep exists with the proper responsibilities - skip to the end - update = false - found_position=pos - break - end - -- Guild Representative position already exists, but has the wrong options - modify it instead of creating a new one - if pos.code == "GUILD_REPRESENTATIVE" then - found_position=pos - break - end - end - if update then - -- either there's no guild rep, or there is one and it's got the wrong responsibilities - if not found_position then - found_position = add_guild_rep(ent) - end - -- assign responsibilities - found_position.responsibilities.ESTABLISH_COLONY_TRADE_AGREEMENTS = true - found_position.responsibilities.TRADE=true - end - - -- make sure the guild rep position, whether we created it or not, is set up for proper assignment - local assign = true - for _,p in pairs(ent.positions.assignments) do - if p.position_id == found_position.id then -- it is - nothing more to do here - assign = false - break - end - end - if assign then - -- it isn't - set it up - local asn = df.entity_position_assignment:new() - ent.positions.assignments:insert('#', asn) - - asn.id = ent.positions.next_assignment_id - ent.positions.next_assignment_id = asn.id + 1 - asn.position_id = found_position.id - asn.flags:resize(math.max(32, #asn.flags)) -- make room for 32 flags - asn.flags[0] = true -- and set the first one - end - if update or assign then - fixed = fixed + 1 - end - end -end - -print("Added merchant nobility for "..fixed.." of "..checked.." civilizations.") diff --git a/scripts/fix/population-cap.lua b/scripts/fix/population-cap.lua deleted file mode 100644 index 1261fb504..000000000 --- a/scripts/fix/population-cap.lua +++ /dev/null @@ -1,47 +0,0 @@ --- Tells mountainhomes your pop. to avoid overshoot - ---[[=begin - -fix/population-cap -================== -Run this after every migrant wave to ensure your population cap is not exceeded. - -The reason for population cap problems is that the population value it -is compared against comes from the last dwarven caravan that successfully -left for mountainhomes. This script instantly updates it. -Note that a migration wave can still overshoot the limit by 1-2 dwarves because -of the last migrant bringing his family. Likewise, king arrival ignores cap. - -=end]] -local args = {...} - -local ui = df.global.ui -local ui_stats = ui.tasks -local civ = df.historical_entity.find(ui.civ_id) - -if not civ then - qerror('No active fortress.') -end - -local civ_stats = civ.activity_stats - -if not civ_stats then - if args[1] ~= 'force' then - qerror('No caravan report object; use "fix/population-cap force" to create one') - end - print('Creating an empty statistics structure...') - civ.activity_stats = { - new = true, - created_weapons = { resize = #ui_stats.created_weapons }, - discovered_creature_foods = { resize = #ui_stats.discovered_creature_foods }, - discovered_creatures = { resize = #ui_stats.discovered_creatures }, - discovered_plant_foods = { resize = #ui_stats.discovered_plant_foods }, - discovered_plants = { resize = #ui_stats.discovered_plants }, - } - civ_stats = civ.activity_stats -end - --- Use max to keep at least some of the original caravan communication idea -civ_stats.population = math.max(civ_stats.population, ui_stats.population) - -print('Home civ notified about current population.') diff --git a/scripts/fix/stable-temp.lua b/scripts/fix/stable-temp.lua deleted file mode 100644 index 7238d88d9..000000000 --- a/scripts/fix/stable-temp.lua +++ /dev/null @@ -1,71 +0,0 @@ --- Reset item temperature to the value of their tile. ---[[=begin - -fix/stable-temp -=============== -Instantly sets the temperature of all free-lying items to be in equilibrium with -the environment, which stops temperature updates until something changes. -To maintain this efficient state, use `tweak fast-heat `. - -=end]] -local args = {...} - -local apply = (args[1] == 'apply') - -local count = 0 -local types = {} - -local function update_temp(item,btemp) - if item.temperature.whole ~= btemp then - count = count + 1 - local tid = item:getType() - types[tid] = (types[tid] or 0) + 1 - end - - if apply then - item.temperature.whole = btemp - item.temperature.fraction = 0 - - if item.contaminants then - for _,c in ipairs(item.contaminants) do - c.temperature.whole = btemp - c.temperature.fraction = 0 - end - end - end - - for _,sub in ipairs(dfhack.items.getContainedItems(item)) do - update_temp(sub,btemp) - end - - if apply then - item:checkTemperatureDamage() - end -end - -local last_frame = df.global.world.frame_counter-1 - -for _,item in ipairs(df.global.world.items.all) do - if item.flags.on_ground and df.item_actual:is_instance(item) and - item.temp_updated_frame == last_frame then - local pos = item.pos - local block = dfhack.maps.getTileBlock(pos) - if block then - update_temp(item, block.temperature_1[pos.x%16][pos.y%16]) - end - end -end - -if apply then - print('Items updated: '..count) -else - print("Use 'fix/stable-temp apply' to force-change temperature.") - print('Items not in equilibrium: '..count) -end - -local tlist = {} -for k,_ in pairs(types) do tlist[#tlist+1] = k end -table.sort(tlist, function(a,b) return types[a] > types[b] end) -for _,k in ipairs(tlist) do - print(' '..df.item_type[k]..':', types[k]) -end diff --git a/scripts/fix/stuckdoors.rb b/scripts/fix/stuckdoors.rb deleted file mode 100644 index f99d0f263..000000000 --- a/scripts/fix/stuckdoors.rb +++ /dev/null @@ -1,32 +0,0 @@ -# fix doors that are frozen in 'open' state - -# this may happen after people mess with the game by (incorrectly) teleporting units or items -# a door may stick open if the map occupancy flags are wrong -=begin - -fix/stuckdoors -============== -Fix doors that are stuck open due to incorrect map occupancy flags, eg due to -incorrect use of `teleport`. - -=end -count = 0 -df.world.buildings.all.each { |bld| - # for all doors - next if bld._rtti_classname != :building_doorst - # check if it is open - next if bld.close_timer == 0 - # check if occupancy is set - occ = df.map_occupancy_at(bld.x1, bld.y1, bld.z) - if (occ.unit or occ.unit_grounded) and not - # check if an unit is present - df.world.units.active.find { |u| u.pos.x == bld.x1 and u.pos.y == bld.y1 and u.pos.z == bld.z } - count += 1 - occ.unit = occ.unit_grounded = false - end - if occ.item and not df.world.items.all.find { |i| i.pos.x == bld.x1 and i.pos.y == bld.y1 and i.pos.z == bld.z } - count += 1 - occ.item = false - end -} -puts "unstuck #{count} doors" diff --git a/scripts/fixnaked.lua b/scripts/fixnaked.lua deleted file mode 100644 index 39cfd2b77..000000000 --- a/scripts/fixnaked.lua +++ /dev/null @@ -1,50 +0,0 @@ ---removes unhappy thoughts due to lack of clothing ---[[=begin - -fixnaked -======== -Removes all unhappy thoughts due to lack of clothing. - -=end]] - -function fixnaked() -local total_fixed = 0 -local total_removed = 0 - -for fnUnitCount,fnUnit in ipairs(df.global.world.units.all) do - if fnUnit.race == df.global.ui.race_id then - local listEvents = fnUnit.status.recent_events - --for lkey,lvalue in pairs(listEvents) do - -- print(df.unit_thought_type[lvalue.type],lvalue.type,lvalue.age,lvalue.subtype,lvalue.severity) - --end - - local found = 1 - local fixed = 0 - while found == 1 do - local events = fnUnit.status.recent_events - found = 0 - for k,v in pairs(events) do - if v.type == df.unit_thought_type.Uncovered - or v.type == df.unit_thought_type.NoShirt - or v.type == df.unit_thought_type.NoShoes - or v.type == df.unit_thought_type.NoCloak - or v.type == df.unit_thought_type.OldClothing - or v.type == df.unit_thought_type.TatteredClothing - or v.type == df.unit_thought_type.RottedClothing then - events:erase(k) - found = 1 - total_removed = total_removed + 1 - fixed = 1 - break - end - end - end - if fixed == 1 then - total_fixed = total_fixed + 1 - print(total_fixed, total_removed, dfhack.TranslateName(dfhack.units.getVisibleName(fnUnit))) - end - end -end -print("Total Fixed: "..total_fixed) -end -fixnaked() diff --git a/scripts/forum-dwarves.lua b/scripts/forum-dwarves.lua deleted file mode 100644 index e6b98395a..000000000 --- a/scripts/forum-dwarves.lua +++ /dev/null @@ -1,133 +0,0 @@ --- Save a copy of a text screen for the DF forums --- original author: Caldfir; edited by expwnent, Mchl ---[[=begin - -forum-dwarves -============= -Saves a copy of a text screen, formatted in bbcode for posting to the Bay12 Forums. -Use ``forum-dwarves help`` for more information. - -=end]] - -local args = {...} - -if args[1] == 'help' then - print([[ -description: - This script will attempt to read the current df-screen, and if it is a - text-viewscreen (such as the dwarf 'thoughts' screen or an item - 'description') then append a marked-up version of this text to the - target file. Previous entries in the file are not overwritten, so you - may use the 'forumdwarves' command multiple times to create a single - document containing the text from multiple screens (eg: text screens - from several dwarves, or text screens from multiple artifacts/items, - or some combination). -known screens: - The screens which have been tested and known to function properly with - this script are: - 1: dwarf/unit 'thoughts' screen - 2: item/art 'description' screen - 3: individual 'historical item/figure' screens - There may be other screens to which the script applies. It should be - safe to attempt running the script with any screen active, with an - error message to inform you when the selected screen is not appropriate - for this script. -target file: - The target file's name is 'forumdwarves.txt'. A remider to this effect - will be displayed if the script is successful. -character encoding: - The text will likely be using system-default encoding, and as such - will likely NOT display special characters (eg:È,ı,Á) correctly. To - fix this, you need to modify the character set that you are reading - the document with. 'Notepad++' is a freely available program which - can do this using the following steps: - 1: open the document in Notepad++ - 2: in the menu-bar, select - Encoding->Character Sets->Western European->OEM-US - 3: copy the text normally to wherever you want to use it -]]) - return -end -local utils = require 'utils' -local gui = require 'gui' -local dialog = require 'gui.dialogs' -local colors_css = { - [0] = 'black', - [1] = 'navy', - [2] = 'green', - [3] = 'teal', - [4] = 'maroon', - [5] = 'purple', - [6] = 'olive', - [7] = 'silver', - [8] = 'gray', - [9] = 'blue', - [10] = 'lime', - [11] = 'cyan', - [12] = 'red', - [13] = 'magenta', - [14] = 'yellow', - [15] = 'white' -} - -local scrn = dfhack.gui.getCurViewscreen() -local flerb = dfhack.gui.getFocusString(scrn) - -local function format_for_forum(strin) - local strout = strin - - local newline_idx = string.find(strout, '[P]', 1, true) - while newline_idx ~= nil do - strout = string.sub(strout,1, newline_idx-1)..'\n'..string.sub(strout,newline_idx+3) - newline_idx = string.find(strout, '[P]', 1, true) - end - - newline_idx = string.find(strout, '[B]', 1, true) - while newline_idx ~= nil do - strout = string.sub(strout,1, newline_idx-1)..'\n'..string.sub(strout,newline_idx+3) - newline_idx = string.find(strout, '[B]', 1, true) - end - - newline_idx = string.find(strout, '[R]', 1, true) - while newline_idx ~= nil do - strout = string.sub(strout,1, newline_idx-1)..'\n'..string.sub(strout,newline_idx+3) - newline_idx = string.find(strout, '[R]', 1, true) - end - - local color_idx = string.find(strout, '[C:', 1, true) - while color_idx ~= nil do - local colormatch = (string.byte(strout, color_idx+3)-48)+((string.byte(strout, color_idx+7)-48)*8) - strout = string.sub(strout,1, color_idx-1)..'[/color][color='..colors_css[colormatch]..']'..string.sub(strout,color_idx+9) - color_idx = string.find(strout, '[C:', 1, true) - end - - return strout -end - -if flerb == 'textviewer' then - print(scrn) - printall(scrn) - local lines = scrn.src_text - local line = "" - - if lines ~= nil then - local log = io.open('forumdwarves.txt', 'a') - log:write("[color=silver]") - log:write(scrn.title) - for n,x in ipairs(lines) do - print(x) - printall(x) - print(x.value) - printall(x.value) - if (x ~= nil) and (x.value ~= nil) then - log:write(format_for_forum(x.value), ' ') - --log:write(x[0],'\n') - end - end - log:write("[/color]\n") - log:close() - end - print 'data prepared for forum in \"forumdwarves.txt\"' -else - print 'this is not a textview screen' -end diff --git a/scripts/full-heal.lua b/scripts/full-heal.lua deleted file mode 100644 index f9388058c..000000000 --- a/scripts/full-heal.lua +++ /dev/null @@ -1,153 +0,0 @@ --- Attempts to fully heal the selected unit ---author Kurik Amudnil, Urist DaVinci ---edited by expwnent - ---[[=begin - -full-heal -========= -Attempts to fully heal the selected unit. ``full-heal -r`` attempts to resurrect the unit. - -=end]] - -local utils=require('utils') - -validArgs = validArgs or utils.invert({ - 'r', - 'help', - 'unit', - 'keep_corpse' -}) - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print('full-heal: heal a unit completely from anything, optionally including death.') - print(' full-heal -unit [unitId]') - print(' heal the unit with the given id') - print(' full-heal -r -unit [unitId]') - print(' heal the unit with the given id and bring them back from death if they are dead') - print(' full-heal -r -keep_corpse -unit [unitId]') - print(' heal the unit with the given id and bring them back from death if they are dead, without removing their corpse') - print(' full-heal') - print(' heal the currently selected unit') - print(' full-heal -r') - print(' heal the currently selected unit and bring them back from death if they are dead') - print(' full-heal -help') - print(' print this help message') - return -end - -if(args.unit) then - unit = df.unit.find(args.unit) -else - unit = dfhack.gui.getSelectedUnit() -end - -if not unit then - qerror('Error: please select a unit or pass its id as an argument.') -end - -if unit then - if args.r then - if unit.flags1.dead then - --print("Resurrecting...") - unit.flags2.slaughter = false - unit.flags3.scuttle = false - end - unit.flags1.dead = false - unit.flags2.killed = false - unit.flags3.ghostly = false - if not args.keep_corpse then - for _,corpse in ipairs(df.global.world.items.other.CORPSE) do - if corpse.unit_id==unit.id then - corpse.flags.garbage_collect=true - corpse.flags.forbid=true - corpse.flags.hidden=true - end - end - end - --unit.unk_100 = 3 - end - - --print("Erasing wounds...") - while #unit.body.wounds > 0 do - unit.body.wounds:erase(#unit.body.wounds-1) - end - unit.body.wound_next_id=1 - - --print("Refilling blood...") - unit.body.blood_count=unit.body.blood_max - - --print("Resetting grasp/stand status...") - unit.status2.limbs_stand_count=unit.status2.limbs_stand_max - unit.status2.limbs_grasp_count=unit.status2.limbs_grasp_max - - --print("Resetting status flags...") - unit.flags2.has_breaks=false - unit.flags2.gutted=false - unit.flags2.circulatory_spray=false - unit.flags2.vision_good=true - unit.flags2.vision_damaged=false - unit.flags2.vision_missing=false - unit.flags2.breathing_good=true - unit.flags2.breathing_problem=false - - unit.flags2.calculated_nerves=false - unit.flags2.calculated_bodyparts=false - unit.flags2.calculated_insulation=false - unit.flags3.compute_health=true - - --print("Resetting counters...") - unit.counters.winded=0 - unit.counters.stunned=0 - unit.counters.unconscious=0 - unit.counters.webbed=0 - unit.counters.pain=0 - unit.counters.nausea=0 - unit.counters.dizziness=0 - - unit.counters2.paralysis=0 - unit.counters2.fever=0 - unit.counters2.exhaustion=0 - unit.counters2.hunger_timer=0 - unit.counters2.thirst_timer=0 - unit.counters2.sleepiness_timer=0 - unit.counters2.vomit_timeout=0 - - --print("Resetting body part status...") - local v=unit.body.components - for i=0,#v.nonsolid_remaining - 1,1 do - v.nonsolid_remaining[i] = 100 -- percent remaining of fluid layers (Urist Da Vinci) - end - - v=unit.body.components - for i=0,#v.layer_wound_area - 1,1 do - v.layer_status[i].whole = 0 -- severed, leaking layers (Urist Da Vinci) - v.layer_wound_area[i] = 0 -- wound contact areas (Urist Da Vinci) - v.layer_cut_fraction[i] = 0 -- 100*surface percentage of cuts/fractures on the body part layer (Urist Da Vinci) - v.layer_dent_fraction[i] = 0 -- 100*surface percentage of dents on the body part layer (Urist Da Vinci) - v.layer_effect_fraction[i] = 0 -- 100*surface percentage of "effects" on the body part layer (Urist Da Vinci) - end - - v=unit.body.components.body_part_status - for i=0,#v-1,1 do - v[i].on_fire = 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 - - if unit.job.current_job and unit.job.current_job.job_type == df.job_type.Rest then - --print("Wake from rest -> clean self...") - unit.job.current_job = df.job_type.CleanSelf - end -end - diff --git a/scripts/gaydar.lua b/scripts/gaydar.lua deleted file mode 100644 index 66aec2c12..000000000 --- a/scripts/gaydar.lua +++ /dev/null @@ -1,208 +0,0 @@ --- Shows the sexual orientation of units ---[[=begin - -gaydar -====== -Shows the sexual orientation of units, useful for social engineering or checking -the viability of livestock breeding programs. Use ``gaydar -help`` for information -on available filters for orientation, citizenship, species, etc. - -=end]] -local utils = require('utils') - -validArgs = utils.invert({ - 'all', - 'citizens', - 'named', - 'notStraight', - 'gayOnly', - 'biOnly', - 'straightOnly', - 'asexualOnly', - 'help' -}) - - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print( -[[gaydar.lua -arguments: - -help - print this help message -unit filters: - -all - shows orientation of every creature - -citizens - shows only orientation of citizens in fort mode - -named - shows orientation of all named units on map -orientation filters: - -notStraight - shows only creatures who are not strictly straight - -gayOnly - shows only creatures who are strictly gay - -biOnly - shows only creatures who can get into romances with - both sexes - -straightOnly - shows only creatures who are strictly straight. - -asexualOnly - shows only creatures who are strictly asexual. - - No argument will show the orientation of the unit - under the cursor. -]]) - return -end - -function dfprint(s) - print(dfhack.df2console(s)) -end - -function getSexString(sex) - local sexStr - if sex==0 then - sexStr=string.char(12) - elseif sex==1 then - sexStr=string.char(11) - else - return "" - end - return string.char(40)..sexStr..string.char(41) -end - -local function determineorientation(unit) - if unit.sex~=-1 and unit.status.current_soul then - local return_string='' - local orientation=unit.status.current_soul.orientation_flags - if orientation.indeterminate then - return ' indeterminate (probably adventurer)' - end - local male_interested,asexual=false,true - if orientation.romance_male then - return_string=return_string..' likes males' - male_interested=true - asexual=false - elseif orientation.marry_male then - return_string=return_string..' will marry males' - male_interested=true - asexual=false - end - if orientation.romance_female then - if male_interested then - return_string=return_string..' and likes females' - else - return_string=return_string..' likes females' - end - asexual=false - elseif orientation.marry_female then - if male_interested then - return_string=return_string..' and will marry females' - else - return_string=return_string..' will marry females' - end - asexual=false - end - if asexual then - return_string=' is asexual' - end - return return_string - else - return " is not biologically capable of sex" - end -end - -local function nameOrSpeciesAndNumber(unit) - if unit.name.has_name then - return dfhack.TranslateName(dfhack.units.getVisibleName(unit))..' '..getSexString(unit.sex),true - else - return 'Unit #'..unit.id..' ('..df.creature_raw.find(unit.race).caste[unit.caste].caste_name[0]..' '..getSexString(unit.sex)..')',false - end -end - -local orientations={} - -if args.citizens then - for k,v in ipairs(df.global.world.units.active) do - if dfhack.units.isCitizen(v) then - table.insert(orientations,nameOrSpeciesAndNumber(v) .. determineorientation(v)) - end - end -elseif args.all then - for k,v in ipairs(df.global.world.units.active) do - table.insert(orientations,nameOrSpeciesAndNumber(v)..determineorientation(v)) - end -elseif args.named then - for k,v in ipairs(df.global.world.units.active) do - local name,ok=nameOrSpeciesAndNumber(v) - if ok then - table.insert(orientations,name..determineorientation(v)) - end - end -else - local unit=dfhack.gui.getSelectedUnit(true) - local name,ok=nameOrSpeciesAndNumber(unit) - dfprint(name..determineorientation(unit)) - return -end - -function isNotStraight(v) - if v:find(string.char(12)) and v:find(' female') then return true end - if v:find(string.char(11)) and v:find(' male') then return true end - if v:find('asexual') then return true end - if v:find('indeterminate') then return true end - return false -end - -function isGay(v) - if v:find('asexual') then return false end - if v:find(string.char(12)) and not v:find(' male') then return true end - if v:find(string.char(11)) and not v:find(' female') then return true end - return false -end - -function isAsexual(v) - if v:find('asexual') or v:find('indeterminate') then return true else return false end -end - -function isBi(v) - if v:find(' female') and v:find(' male') then return true else return false end -end - -if args.notStraight then - local totalNotShown=0 - for k,v in ipairs(orientations) do - if isNotStraight(v) then dfprint(v) else totalNotShown=totalNotShown+1 end - end - print('Total not shown: '..totalNotShown) -elseif args.gayOnly then - local totalNotShown=0 - for k,v in ipairs(orientations) do - if isGay(v) then dfprint(v) else totalNotShown=totalNotShown+1 end - end - print('Total not shown: '..totalNotShown) -elseif args.asexualOnly then - local totalNotShown=0 - for k,v in ipairs(orientations) do - if isAsexual(v) then dfprint(v) else totalNotShown=totalNotShown+1 end - end - print('Total not shown: '..totalNotShown) -elseif args.straightOnly then - local totalNotShown=0 - for k,v in ipairs(orientations) do - if not isNotStraight(v) then dfprint(v) else totalNotShown=totalNotShown+1 end - end - print('Total not shown: '..totalNotShown) -elseif args.biOnly then - local totalNotShown=0 - for k,v in ipairs(orientations) do - if isBi(v) then dfprint(v) else totalNotShown=totalNotShown+1 end - end - print('Total not shown: '..totalNotShown) -else - for k,v in ipairs(orientations) do - dfprint(v) - end -end diff --git a/scripts/growcrops.rb b/scripts/growcrops.rb deleted file mode 100644 index c3c143254..000000000 --- a/scripts/growcrops.rb +++ /dev/null @@ -1,64 +0,0 @@ -# Instantly grow crops in farm plots -=begin - -growcrops -========= -Instantly grow seeds inside farming plots. - -With no argument, this command list the various seed types currently in -use in your farming plots. With a seed type, the script will grow 100 of -these seeds, ready to be harvested. Set the number with a 2nd argument. - -For example, to grow 40 plump helmet spawn:: - - growcrops plump 40 - -=end - -material = $script_args[0] -count_max = $script_args[1].to_i -count_max = 100 if count_max == 0 - -# cache information from the raws -@raws_plant_name ||= {} -@raws_plant_growdur ||= {} -if @raws_plant_name.empty? - df.world.raws.plants.all.each_with_index { |p, idx| - @raws_plant_name[idx] = p.id - @raws_plant_growdur[idx] = p.growdur - } -end - -inventory = Hash.new(0) -df.world.items.other[:SEEDS].each { |seed| - next if not seed.flags.in_building - next if not seed.general_refs.find { |ref| ref._rtti_classname == :general_ref_building_holderst } - next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index] - inventory[seed.mat_index] += 1 -} - -if !material or material == 'help' or material == 'list' - # show a list of available crop types - inventory.sort_by { |mat, c| c }.each { |mat, c| - name = df.world.raws.plants.all[mat].id - puts " #{name} #{c}" - } - -else - - mat = df.match_rawname(material, inventory.keys.map { |k| @raws_plant_name[k] }) - unless wantmat = @raws_plant_name.index(mat) - raise "invalid plant material #{material}" - end - - count = 0 - df.world.items.other[:SEEDS].each { |seed| - next if seed.mat_index != wantmat - next if not seed.flags.in_building - next if not seed.general_refs.find { |ref| ref._rtti_classname == :general_ref_building_holderst } - next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index] - seed.grow_counter = @raws_plant_growdur[seed.mat_index] - count += 1 - } - puts "Grown #{count} #{mat}" -end diff --git a/scripts/gui/about.txt b/scripts/gui/about.txt deleted file mode 100644 index 1c83e50b2..000000000 --- a/scripts/gui/about.txt +++ /dev/null @@ -1,6 +0,0 @@ -``gui/*`` scripts implement dialogs in the main game window. - -In order to avoid user confusion, as a matter of policy all these tools -display the word "DFHack" on the screen somewhere while active. -When that is not appropriate because they merely add keybinding hints to -existing DF screens, they deliberately use red instead of green for the key. diff --git a/scripts/gui/advfort.lua b/scripts/gui/advfort.lua deleted file mode 100644 index 67a0f4bb3..000000000 --- a/scripts/gui/advfort.lua +++ /dev/null @@ -1,1894 +0,0 @@ --- allows to do jobs in adv. mode. - ---[[=begin - -gui/advfort -=========== -This script allows to perform jobs in adventure mode. For more complete help -press :kbd:`?` while script is running. It's most comfortable to use this as a -keybinding. (e.g. ``keybinding set Ctrl-T gui/advfort``). Possible arguments: - -:-a, --nodfassign: uses different method to assign items. -:-i, --inventory: checks inventory for possible items to use in the job. -:-c, --cheat: relaxes item requirements for buildings (e.g. walls from bones). Implies -a -:-u, --unsafe: ignores dangerous conditions. -:-s, --safe: only allow building and etc. only if in site -:-q, --quick: quick item select mode -:job: selects that job (e.g. Dig or FellTree) - -An example of player digging in adventure mode: - -.. image:: /docs/images/advfort.png - -**WARNING:** changes only persist in non procedural sites, namely: player forts, caves, and camps. - -=end]] - ---[==[ - version: 0.05 - changelog: - *0.05 - - fixed some reactions not showing. Now there is a '[fallback]' choice to choose from other way of getting reactions. - - fixed brewing accepting too many items instead of barrel - - fixed tallow making to accept fat - - display filters - *0.044 - - added output to clear_jobs of number of cleared jobs - - another failed attempt at gather plants fix - - added track stop configuration window - *0.043 - - fixed track carving: up/down was reversed and removed (temp) requirements because they were not working correctly - - added checks for unsafe conditions (currently quite stupid). Should save few adventurers that are trying to work in dangerous conditions (e.g. fishing) - - unsafe checks disabled by "-u" ir "--unsafe" - *0.042 - - fixed (probably for sure now) the crash bug. - - added --clear_jobs debug option. Will delete ALL JOBS! - *0.041 - - fixed cooking allowing already cooked meals - *0.04 - - add (-q)uick mode. Autoselects materials. - - fixed few(?) crash bugs - - fixed job errors not being shown in df - *0.031 - - make forbiding optional (-s)afe mode - *0.03 - - forbid doing anything in non-sites unless you are (-c)heating - - a bit more documentation and tidying - - add a deadlock fix - *0.021 - - advfort_items now autofills items - - tried out few things to fix gather plants - *0.02 - - fixed axles not being able to be placed in other direction (thanks SyrusLD) - - added lever linking - - restructured advfort_items, don't forget to update that too! - - Added clutter view if shop is cluttered. - *0.013 - - fixed siege weapons and traps (somewhat). Now you can load them with new menu :) - *0.012 - - fix for some jobs not finding correct building. - *0.011 - - fixed crash with building jobs (other jobs might have been crashing too!) - - fixed bug with building asking twice to input items - *0.01 - - instant job startation - - item selection screen (!) - - BUG:custom jobs need stuff on ground to work - *0.003 - - fixed farms (i think...) - - added faster time pasing (yay for random deaths from local wildlife) - - still hasn't fixed gather plants. but you can visit local market, buy a few local fruits/vegetables eat them and use seeds - - other stuff - *0.002 - - kind-of fixed the item problem... now they get teleported (if teleport_items=true which should be default for adventurer) - - gather plants still not working... Other jobs seem to work. - - added new-and-improved waiting. Interestingly it could be improved to be interuptable. - todo list: - - document everything! Maybe somebody would understand what is happening then and help me :< - - when building trap add to known traps (or known adventurers?) so that it does not trigger on adventurer - bugs list: - - items blocking construction stuck the game - - burning charcoal crashed game - - gem thingies probably broken - - custom reactions semibroken - - gathering plants still broken - ---]==] - ---keybinding, change to your hearts content. Only the key part. -keybinds={ -nextJob={key="CUSTOM_SHIFT_T",desc="Next job in the list"}, -prevJob={key="CUSTOM_SHIFT_R",desc="Previous job in the list"}, -continue={key="A_WAIT",desc="Continue job if available"}, -down_alt1={key="CUSTOM_CTRL_D",desc="Use job down"}, -down_alt2={key="CURSOR_DOWN_Z_AUX",desc="Use job down"}, -up_alt1={key="CUSTOM_CTRL_E",desc="Use job up"}, -up_alt2={key="CURSOR_UP_Z_AUX",desc="Use job up"}, -use_same={key="A_MOVE_SAME_SQUARE",desc="Use job at the tile you are standing"}, -workshop={key="CHANGETAB",desc="Show building menu"}, -quick={key="CUSTOM_Q",desc="Toggle quick item select"}, -} --- building filters -build_filter={ - forbid_all=false, --this forbits all except the "allow" - allow={"MetalSmithsForge"}, --ignored if forbit_all=false - forbid={} --ignored if forbit_all==true -} -build_filter.HUMANish={ - forbid_all=true, - allow={"Masons"}, - forbid={} -} - ---economic stone fix: just disable all of them ---[[ FIXME: maybe let player select which to disable?]] -for k,v in ipairs(df.global.ui.economic_stone) do df.global.ui.economic_stone[k]=0 end - -local gui = require 'gui' -local wid=require 'gui.widgets' -local dialog=require 'gui.dialogs' -local buildings=require 'dfhack.buildings' -local bdialog=require 'gui.buildings' -local workshopJobs=require 'dfhack.workshops' -local utils=require 'utils' -local gscript=require 'gui.script' - -local tile_attrs = df.tiletype.attrs - -settings={build_by_items=false,use_worn=false,check_inv=true,teleport_items=true,df_assign=false,gui_item_select=true,only_in_sites=false} - -function hasValue(tbl,val) - for k,v in pairs(tbl) do - if v==val then - return true - end - end - return false -end -function reverseRaceLookup(id) - return df.global.world.raws.creatures.all[id].creature_id -end -function deon_filter(name,type_id,subtype_id,custom_id, parent) - --print(name) - local adv=df.global.world.units.active[0] - local race_filter=build_filter[reverseRaceLookup(adv.race)] - if race_filter then - if race_filter.forbid_all then - return hasValue(race_filter.allow,name) - else - return not hasValue(race_filter.forbid,name) - end - else - if build_filter.forbid_all then - return hasValue(build_filter.allow,name) - else - return not hasValue(build_filter.forbid,name) - end - end -end -local mode_name -for k,v in ipairs({...}) do --setting parsing - if v=="-c" or v=="--cheat" then - settings.build_by_items=true - settings.df_assign=false - elseif v=="-q" or v=="--quick" then - settings.quick=true - elseif v=="-u" or v=="--unsafe" then --ignore pain and etc - settings.unsafe=true - elseif v=="-s" or v=="--safe" then - settings.safe=true - elseif v=="-i" or v=="--inventory" then - settings.check_inv=true - settings.df_assign=false - elseif v=="-a" or v=="--nodfassign" then - settings.df_assign=false - elseif v=="-h" or v=="--help" then - settings.help=true - elseif v=="--clear_jobs" then - settings.clear_jobs=true - else - mode_name=v - end -end - -mode=mode or 0 -last_building=last_building or {} - -function Disclaimer(tlb) - local dsc={"The Gathering Against ",{text="Goblin ",pen=dfhack.pen.parse{fg=COLOR_GREEN,bg=0}}, "Oppresion ", - "(TGAGO) is not responsible for all ",NEWLINE,"the damage that this tool can (and will) cause to you and your loved worlds",NEWLINE,"and/or sanity.Please use with caution.",NEWLINE,{text="Magma not included.",pen=dfhack.pen.parse{fg=COLOR_LIGHTRED,bg=0}}} - if tlb then - for _,v in ipairs(dsc) do - table.insert(tlb,v) - end - end - return dsc -end -function showHelp() - local helptext={ - "This tool allow you to perform jobs as a dwarf would in dwarf mode. When ",NEWLINE, - "cursor is available you can press ",{key="SELECT", text="select",key_sep="()"}, - " to enqueue a job from",NEWLINE,"pointer location. If job is 'Build' and there is no planed construction",NEWLINE, - "at cursor this tool show possible building choices.",NEWLINE,NEWLINE,{text="Keybindings:",pen=dfhack.pen.parse{fg=COLOR_CYAN,bg=0}},NEWLINE - } - for k,v in pairs(keybinds) do - table.insert(helptext,{key=v.key,text=v.desc,key_sep=":"}) - table.insert(helptext,NEWLINE) - end - table.insert(helptext,{text="CAREFULL MOVE",pen=dfhack.pen.parse{fg=COLOR_LIGHTGREEN,bg=0}}) - table.insert(helptext,": use job in that direction") - table.insert(helptext,NEWLINE) - table.insert(helptext,NEWLINE) - Disclaimer(helptext) - dialog.showMessage("Help!?!",helptext) -end - -if settings.help then - showHelp() - return -end - ---[[ Util functions ]]-- -function advGlobalPos() - local map=df.global.world.map - local wd=df.global.world.world_data - local adv=df.global.world.units.active[0] - --wd.adv_region_x*16+wd.adv_emb_x,wd.adv_region_y*16+wd.adv_emb_y - --return wd.adv_region_x*16+wd.adv_emb_x,wd.adv_region_y*16+wd.adv_emb_y - --return wd.adv_region_x*16+wd.adv_emb_x+adv.pos.x/16,wd.adv_region_y*16+wd.adv_emb_y+adv.pos.y/16 - --print(map.region_x,map.region_y,adv.pos.x,adv.pos.y) - --print(map.region_x+adv.pos.x/48, map.region_y+adv.pos.y/48,wd.adv_region_x*16+wd.adv_emb_x,wd.adv_region_y*16+wd.adv_emb_y) - return math.floor(map.region_x+adv.pos.x/48), math.floor(map.region_y+adv.pos.y/48) -end -function inSite() - - local tx,ty=advGlobalPos() - - for k,v in pairs(df.global.world.world_data.sites) do - local tp={v.pos.x,v.pos.y} - if tx>=tp[1]*16+v.rgn_min_x and tx<=tp[1]*16+v.rgn_max_x and - ty>=tp[2]*16+v.rgn_min_y and ty<=tp[2]*16+v.rgn_max_y then - return v - end - end -end ---[[ low level job management ]]-- -function findAction(unit,ltype) - ltype=ltype or df.unit_action_type.None - for i,v in ipairs(unit.actions) do - if v.type==ltype then - return v - end - end -end -function add_action(unit,action_data) - local action=findAction(unit) --find empty action - if action then - action:assign(action_data) - action.id=unit.next_action_id - unit.next_action_id=unit.next_action_id+1 - else - local tbl=copyall(action_data) - tbl.new=true - tbl.id=unit.next_action_id - unit.actions:insert("#",tbl) - unit.next_action_id=unit.next_action_id+1 - end -end -function addJobAction(job,unit) --what about job2? - if job==nil then - error("invalid job") - end - if findAction(unit,df.unit_action_type.Job) or findAction(unit,df.unit_action_type.Job2) then - print("Already has job action") - return - end - local action=findAction(unit) - local pos=copyall(unit.pos) - --local pos=copyall(job.pos) - unit.path.dest:assign(pos) - --job - local data={type=df.unit_action_type.Job,data={job={x=pos.x,y=pos.y,z=pos.z,timer=10}}} - --job2: - --local data={type=df.unit_action_type.Job2,data={job2={timer=10}}} - add_action(unit,data) - --add_action(unit,{type=df.unit_action_type.Unsteady,data={unsteady={timer=5}}}) -end - -function make_native_job(args) - if args.job == nil then - local newJob=df.job:new() - newJob.id=df.global.job_next_id - df.global.job_next_id=df.global.job_next_id+1 - newJob.flags.special=true - newJob.job_type=args.job_type - newJob.completion_timer=-1 - - newJob.pos:assign(args.pos) - --newJob.pos:assign(args.unit.pos) - args.job=newJob - args.unlinked=true - end -end -function smart_job_delete( job ) - local gref_types=df.general_ref_type - --TODO: unmark items as in job - for i,v in ipairs(job.general_refs) do - if v:getType()==gref_types.BUILDING_HOLDER then - local b=v:getBuilding() - if b then - --remove from building - for i,v in ipairs(b.jobs) do - if v==job then - b.jobs:erase(i) - break - end - end - else - print("Warning: building holder ref was invalid while deleting job") - end - elseif v:getType()==gref_types.UNIT_WORKER then - local u=v:getUnit() - if u then - u.job.current_job =nil - else - print("Warning: unit worker ref was invalid while deleting job") - end - else - print("Warning: failed to remove link from job with type:",gref_types[v:getType()]) - end - end - --unlink job - local link=job.list_link - if link.prev then - link.prev.next=link.next - end - if link.next then - link.next.prev=link.prev - end - link:delete() - --finally delete the job - job:delete() -end ---TODO: this logic might be better with other --starting logic-- -if settings.clear_jobs then - print("Clearing job list!") - local counter=0 - local job_link=df.global.world.job_list.next - while job_link and job_link.item do - local job=job_link.item - job_link=job_link.next - smart_job_delete(job) - counter=counter+1 - end - print("Deleted: "..counter.." jobs") - return -end -function makeJob(args) - gscript.start(function () - make_native_job(args) - local failed - for k,v in ipairs(args.pre_actions or {}) do - local ok,msg=v(args) - if not ok then - failed=msg - break - end - end - if failed==nil then - AssignUnitToJob(args.job,args.unit,args.from_pos) - for k,v in ipairs(args.post_actions or {}) do - local ok,msg=v(args) - if not ok then - failed=msg - break - end - end - if failed then - UnassignJob(args.job,args.unit) - end - end - if failed==nil then - if args.unlinked then - dfhack.job.linkIntoWorld(args.job,true) - args.unlinked=false - end - addJobAction(args.job,args.unit) - args.screen:wait_tick() - else - if not args.no_job_delete then - smart_job_delete(args.job) - end - dfhack.gui.showAnnouncement("Job failed:"..failed,5,1) - end - end) -end - -function UnassignJob(job,unit,unit_pos) - unit.job.current_job=nil -end -function AssignUnitToJob(job,unit,unit_pos) - job.general_refs:insert("#",{new=df.general_ref_unit_workerst,unit_id=unit.id}) - unit.job.current_job=job - unit_pos=unit_pos or {x=job.pos.x,y=job.pos.y,z=job.pos.z} - unit.path.dest:assign(unit_pos) - return true -end -function SetCreatureRef(args) - local job=args.job - local pos=args.pos - for k,v in pairs(df.global.world.units.active) do - if v.pos.x==pos.x and v.pos.y==pos.y and v.pos.z==pos.z then - job.general_refs:insert("#",{new=df.general_ref_unit_cageest,unit_id=v.id}) - return - end - end -end - -function SetWebRef(args) - local pos=args.pos - for k,v in pairs(df.global.world.items.other.ANY_WEBS) do - if v.pos.x==pos.x and v.pos.y==pos.y and v.pos.z==pos.z then - args.job.general_refs:insert("#",{new=df.general_ref_item,item_id=v.id}) - return - end - end -end -function SetPatientRef(args) - local job=args.job - local pos=args.pos - for k,v in pairs(df.global.world.units.active) do - if v.pos.x==pos.x and v.pos.y==pos.y and v.pos.z==pos.z then - job.general_refs:insert("#",{new=df.general_ref_unit_patientst,unit_id=v.id}) - return - end - end -end -function SetCarveDir(args) - local job=args.job - local pos=args.pos - local from_pos=args.from_pos - local dirs={up=18,down=19,right=20,left=21} - if pos.x>from_pos.x then - job.item_category[dirs.right]=true - elseif pos.xfrom_pos.y then - job.item_category[dirs.down]=true - elseif pos.y0 and item_suitable) or settings.build_by_items then - --cur_item.flags.in_job=true - job.items:insert("#",{new=true,item=cur_item,role=df.job_item_ref.T_role.Reagent,job_item_idx=job_id}) - item_counts[job_id]=item_counts[job_id]-cur_item:getTotalDimension() - --print(string.format("item added, job_item_id=%d, item %s, quantity left=%d",job_id,tostring(cur_item),item_counts[job_id])) - used_item_id[cur_item.id]=true - end - end - end - end - end - - return item_suitability,item_counts -end -function AssignJobItems(args) - if settings.df_assign then --use df default logic and hope that it would work - return true - end - -- first find items that you want to use for the job - local job=args.job - local its=EnumItems_with_settings(args) - - local item_suitability,item_counts=find_suitable_items(job,its) - --[[while(#job.items>0) do --clear old job items - job.items[#job.items-1]:delete() - job.items:erase(#job.items-1) - end]] - - if settings.gui_item_select and #job.job_items>0 then - local item_dialog=require('hack.scripts.gui.advfort_items') - - if settings.quick then --TODO not so nice hack. instead of rewriting logic for job item filling i'm using one in gui dialog... - local item_editor=item_dialog.jobitemEditor{ - job = job, - items = item_suitability, - } - if item_editor:jobValid() then - item_editor:commit() - finish_item_assign(args) - return true - else - return false, "Quick select items" - end - else - local ret=item_dialog.showItemEditor(job,item_suitability) - if ret then - finish_item_assign(args) - return true - else - print("Failed job, i'm confused...") - end - --end) - return false,"Selecting items" - end - else - if not settings.build_by_items then - for job_id, trg_job_item in ipairs(job.job_items) do - if item_counts[job_id]>0 then - print("Not enough items for this job") - return false, "Not enough items for this job" - end - end - end - finish_item_assign(args) - return true - end - -end - -CheckAndFinishBuilding=function (args,bld) - args.building=args.building or bld - for idx,job in pairs(bld.jobs) do - if job.job_type==df.job_type.ConstructBuilding then - args.job=job - args.no_job_delete=true - break - end - end - - if args.job~=nil then - args.pre_actions={AssignJobItems} - else - local t={items=buildings.getFiltersByType({},bld:getType(),bld:getSubtype(),bld:getCustomType())} - args.pre_actions={dfhack.curry(setFiltersUp,t),AssignBuildingRef}--,AssignJobItems - end - args.no_job_delete=true - makeJob(args) -end -function AssignJobToBuild(args) - local bld=args.building or dfhack.buildings.findAtTile(args.pos) - args.building=bld - args.job_type=df.job_type.ConstructBuilding - if bld~=nil then - CheckAndFinishBuilding(args,bld) - else - bdialog.BuildingDialog{on_select=dfhack.curry(BuildingChosen,args),hide_none=true,building_filter=deon_filter}:show() - end - return true -end -function BuildLast(args) - local bld=dfhack.buildings.findAtTile(args.pos) - args.job_type=df.job_type.ConstructBuilding - if bld~=nil then - CheckAndFinishBuilding(args,bld) - else - --bdialog.BuildingDialog{on_select=dfhack.curry(BuildingChosen,args),hide_none=true}:show() - if last_building and last_building.type then - BuildingChosen(args,last_building.type,last_building.subtype,last_building.custom) - end - end - return true -end -function CancelJob(unit) - local c_job=unit.job.current_job - if c_job then - unit.job.current_job =nil --todo add real cancelation - for k,v in pairs(c_job.general_refs) do - if df.general_ref_unit_workerst:is_instance(v) then - v:delete() - c_job.general_refs:erase(k) - return - end - end - end -end -function ContinueJob(unit) - local c_job=unit.job.current_job - --no job to continue - if not c_job then return end - --reset suspends... - c_job.flags.suspend=false - for k,v in pairs(c_job.items) do --try fetching missing items - if v.is_fetching==1 then - unit.path.dest:assign(v.item.pos) - return - end - end - - --unit.path.dest:assign(c_job.pos) -- FIXME: job pos is not always the target pos!! - addJobAction(c_job,unit) -end ---TODO: in far far future maybe add real linking? --- function assign_link_refs(args ) --- local job=args.job --- --job.general_refs:insert("#",{new=df.general_ref_building_holderst,building_id=args.building.id}) --- job.general_refs:insert("#",{new=df.general_ref_building_triggertargetst,building_id=args.triggertarget.id}) --- printall(job) --- end --- function assign_link_roles( args ) --- if #args.job.items~=2 then --- print("AAA FAILED!") --- return false --- end --- args.job.items[0].role=df.job_item_ref.T_role.LinkToTarget --- args.job.items[1].role=df.job_item_ref.T_role.LinkToTrigger --- end -function fake_linking(lever,building,slots) - local item1=slots[1].items[1] - local item2=slots[2].items[1] - if not dfhack.items.moveToBuilding(item1,lever,2) then - qerror("failed to move item to building") - end - if not dfhack.items.moveToBuilding(item2,building,2) then - qerror("failed to move item2 to building") - end - item2.general_refs:insert("#",{new=df.general_ref_building_triggerst,building_id=lever.id}) - item1.general_refs:insert("#",{new=df.general_ref_building_triggertargetst,building_id=building.id}) - - lever.linked_mechanisms:insert("#",item2) - --fixes... - if building:getType()==df.building_type.Door then - building.door_flags.operated_by_mechanisms=true - end - - dfhack.gui.showAnnouncement("Linked!",COLOR_YELLOW,true) -end -function LinkBuilding(args) - local bld=args.building or dfhack.buildings.findAtTile(args.pos) - args.building=bld - - local lever_bld - if lever_id then --intentionally global! - lever_bld=df.building.find(lever_id) - if lever_bld==nil then - lever_id=nil - end - end - if lever_bld==nil then - if bld:getType()==df.building_type.Trap and bld:getSubtype()==df.trap_type.Lever then - lever_id=bld.id - dfhack.gui.showAnnouncement("Selected lever for linking",COLOR_YELLOW,true) - return - else - dfhack.gui.showAnnouncement("You first need a lever",COLOR_RED,true) - end - else - if lever_bld==bld then - dfhack.gui.showAnnouncement("Invalid target",COLOR_RED,true) --todo more invalid targets - return - end - -- args.job_type=df.job_type.LinkBuildingToTrigger - -- args.building=lever_bld - -- args.triggertarget=bld - -- args.pre_actions={ - -- dfhack.curry(setFiltersUp,{items={{quantity=1,item_type=df.item_type.TRAPPARTS},{quantity=1,item_type=df.item_type.TRAPPARTS}}}), - -- AssignJobItems, - -- assign_link_refs,} - -- args.post_actions={AssignBuildingRef,assign_link_roles} - -- makeJob(args) - local input_filter_defaults = { --stolen from buildings lua to better customize... - item_type = df.item_type.TRAPPARTS, - item_subtype = -1, - mat_type = -1, - mat_index = -1, - flags1 = {}, - flags2 = { allow_artifact = true }, - flags3 = {}, - flags4 = 0, - flags5 = 0, - reaction_class = '', - has_material_reaction_product = '', - metal_ore = -1, - min_dimension = -1, - has_tool_use = -1, - quantity = 1 - } - local job_items={copyall(input_filter_defaults),copyall(input_filter_defaults)} - local its=EnumItems_with_settings(args) - local suitability=find_suitable_items(nil,its,job_items) - require('hack.scripts.gui.advfort_items').jobitemEditor{items=suitability,job_items=job_items,on_okay=dfhack.curry(fake_linking,lever_bld,bld)}:show() - lever_id=nil - end - --one item as LinkToTrigger role - --one item as LinkToTarget - --genref for holder(lever) - --genref for triggertarget - -end ---[[ Plant gathering attemped fix No. 35]] --[=[ still did not work!]=] -function get_design_block_ev(blk) - for i,v in ipairs(blk.block_events) do - if v:getType()==df.block_square_event_type.designation_priority then - return v - end - end -end -function PlantGatherFix(args) - local pos=args.pos - --[[args.job.flags[17]=false --?? - - - local block=dfhack.maps.getTileBlock(pos) - local ev=get_design_block_ev(block) - if ev==nil then - block.block_events:insert("#",{new=df.block_square_event_designation_priorityst}) - ev=block.block_events[#block.block_events-1] - end - ev.priority[pos.x % 16][pos.y % 16]=bit32.bor(ev.priority[pos.x % 16][pos.y % 16],4000) - - args.job.item_category:assign{furniture=true,corpses=true,ammo=true} --this is actually required in fort mode - ]] - local path=args.unit.path - path.dest=pos - path.goal=df.unit_path_goal.GatherPlant - path.path.x:insert("#",pos.x) - path.path.y:insert("#",pos.y) - path.path.z:insert("#",pos.z) - printall(path) -end -actions={ - {"CarveFortification" ,df.job_type.CarveFortification,{IsWall,IsHardMaterial}}, - {"DetailWall" ,df.job_type.DetailWall,{IsWall,IsHardMaterial}}, - {"DetailFloor" ,df.job_type.DetailFloor,{IsFloor,IsHardMaterial,SameSquare}}, - {"CarveTrack" ,df.job_type.CarveTrack,{} --TODO: check this- carving modifies standing tile but depends on direction! - ,{SetCarveDir}}, - {"Dig" ,df.job_type.Dig,{MakePredicateWieldsItem(df.job_skill.MINING),IsWall}}, - {"CarveUpwardStaircase" ,df.job_type.CarveUpwardStaircase,{MakePredicateWieldsItem(df.job_skill.MINING),IsWall}}, - {"CarveDownwardStaircase",df.job_type.CarveDownwardStaircase,{MakePredicateWieldsItem(df.job_skill.MINING)}}, - {"CarveUpDownStaircase" ,df.job_type.CarveUpDownStaircase,{MakePredicateWieldsItem(df.job_skill.MINING)}}, - {"CarveRamp" ,df.job_type.CarveRamp,{MakePredicateWieldsItem(df.job_skill.MINING),IsWall}}, - {"DigChannel" ,df.job_type.DigChannel,{MakePredicateWieldsItem(df.job_skill.MINING)}}, - {"FellTree" ,df.job_type.FellTree,{MakePredicateWieldsItem(df.job_skill.AXE),IsTree}}, - {"Fish" ,df.job_type.Fish,{IsWater}}, - --{"Diagnose Patient" ,df.job_type.DiagnosePatient,{IsUnit},{SetPatientRef}}, - --{"Surgery" ,df.job_type.Surgery,{IsUnit},{SetPatientRef}}, - {"TameAnimal" ,df.job_type.TameAnimal,{IsUnit},{SetCreatureRef}}, - {"GatherPlants" ,df.job_type.GatherPlants,{IsPlant,SameSquare},{PlantGatherFix}}, - {"RemoveConstruction" ,df.job_type.RemoveConstruction,{IsConstruct}}, - {"RemoveBuilding" ,RemoveBuilding,{IsBuilding}}, - {"RemoveStairs" ,df.job_type.RemoveStairs,{IsStairs,NotConstruct}}, - --{"HandleLargeCreature" ,df.job_type.HandleLargeCreature,{isUnit},{SetCreatureRef}}, - {"Build" ,AssignJobToBuild,{NoConstructedBuilding}}, - {"BuildLast" ,BuildLast,{NoConstructedBuilding}}, - {"Clean" ,df.job_type.Clean,{}}, - {"GatherWebs" ,df.job_type.CollectWebs,{--[[HasWeb]]},{SetWebRef}}, - {"Link Buildings" ,LinkBuilding,{IsBuilding}}, -} - -for id,action in pairs(actions) do - if action[1]==mode_name then - mode=id-1 - break - end -end -usetool=defclass(usetool,gui.Screen) -usetool.focus_path = 'advfort' -function usetool:getModeName() - local adv=df.global.world.units.active[0] - local ret - if adv.job.current_job then - ret= string.format("%s working(%d) ",(actions[(mode or 0)+1][1] or ""),adv.job.current_job.completion_timer) - else - ret= actions[(mode or 0)+1][1] or " " - end - if settings.quick then - ret=ret.."*" - end - return ret -end - -function usetool:update_site() - local site=inSite() - self.current_site=site - local site_label=self.subviews.siteLabel - if site then - - site_label:itemById("site").text=dfhack.TranslateName(site.name) - else - if settings.safe then - site_label:itemById("site").text="" - else - site_label:itemById("site").text="" - end - end -end - -function usetool:init(args) - self:addviews{ - wid.Label{ - view_id="mainLabel", - frame = {xalign=0,yalign=0}, - text={{key=keybinds.prevJob.key},{gap=1,text=self:callback("getModeName")},{gap=1,key=keybinds.nextJob.key}, - } - }, - - wid.Label{ - view_id="shopLabel", - frame = {l=35,xalign=0,yalign=0}, - visible=false, - text={ - {id="text1",gap=1,key=keybinds.workshop.key,key_sep="()", text="Workshop menu",pen=dfhack.pen.parse{fg=COLOR_YELLOW,bg=0}},{id="clutter"}} - }, - - wid.Label{ - view_id="siteLabel", - frame = {t=1,xalign=-1,yalign=0}, - text={ - {id="text1", text="Site:"},{id="site", text="name"} - } - } - } - local labors=df.global.world.units.active[0].status.labors - for i,v in ipairs(labors) do - labors[i]=true - end - self:update_site() -end -MOVEMENT_KEYS = { - A_CARE_MOVE_N = { 0, -1, 0 }, A_CARE_MOVE_S = { 0, 1, 0 }, - A_CARE_MOVE_W = { -1, 0, 0 }, A_CARE_MOVE_E = { 1, 0, 0 }, - A_CARE_MOVE_NW = { -1, -1, 0 }, A_CARE_MOVE_NE = { 1, -1, 0 }, - A_CARE_MOVE_SW = { -1, 1, 0 }, A_CARE_MOVE_SE = { 1, 1, 0 }, - --[[A_MOVE_N = { 0, -1, 0 }, A_MOVE_S = { 0, 1, 0 }, - A_MOVE_W = { -1, 0, 0 }, A_MOVE_E = { 1, 0, 0 }, - A_MOVE_NW = { -1, -1, 0 }, A_MOVE_NE = { 1, -1, 0 }, - A_MOVE_SW = { -1, 1, 0 }, A_MOVE_SE = { 1, 1, 0 },--]] - A_CUSTOM_CTRL_D = { 0, 0, -1 }, - A_CUSTOM_CTRL_E = { 0, 0, 1 }, - CURSOR_UP_Z_AUX = { 0, 0, 1 }, CURSOR_DOWN_Z_AUX = { 0, 0, -1 }, - A_MOVE_SAME_SQUARE={0,0,0}, - SELECT={0,0,0}, -} -ALLOWED_KEYS={ - A_MOVE_N=true,A_MOVE_S=true,A_MOVE_W=true,A_MOVE_E=true,A_MOVE_NW=true, - A_MOVE_NE=true,A_MOVE_SW=true,A_MOVE_SE=true,A_STANCE=true,SELECT=true,A_MOVE_DOWN_AUX=true, - A_MOVE_UP_AUX=true,A_LOOK=true,CURSOR_DOWN=true,CURSOR_UP=true,CURSOR_LEFT=true,CURSOR_RIGHT=true, - CURSOR_UPLEFT=true,CURSOR_UPRIGHT=true,CURSOR_DOWNLEFT=true,CURSOR_DOWNRIGHT=true,A_CLEAR_ANNOUNCEMENTS=true, - CURSOR_UP_Z=true,CURSOR_DOWN_Z=true, -} -function moddedpos(pos,delta) - return {x=pos.x+delta[1],y=pos.y+delta[2],z=pos.z+delta[3]} -end -function usetool:onHelp() - showHelp() -end -function setFiltersUp(specific,args) - local job=args.job - if specific.job_fields~=nil then - job:assign(specific.job_fields) - end - --printall(specific) - for _,v in ipairs(specific.items) do - --printall(v) - local filter=v - filter.new=true - job.job_items:insert("#",filter) - end - return true -end -function onWorkShopJobChosen(args,idx,choice) - args.pos=args.from_pos - args.building=args.building or dfhack.buildings.findAtTile(args.pos) - args.job_type=choice.job_id - args.post_actions={AssignBuildingRef} - args.pre_actions={dfhack.curry(setFiltersUp,choice.filter),AssignJobItems} - makeJob(args) -end -function siegeWeaponActionChosen(args,actionid) - local building=args.building - if actionid==1 then --Turn - building.facing=(args.building.facing+1)%4 - return - elseif actionid==2 then --Load - local action=df.job_type.LoadBallista - if building:getSubtype()==df.siegeengine_type.Catapult then - action=df.job_type.LoadCatapult - args.pre_actions={dfhack.curry(setFiltersUp,{items={{quantity=1}}}),AssignJobItems} --TODO just boulders here - else - args.pre_actions={dfhack.curry(setFiltersUp,{items={{quantity=1,item_type=df.SIEGEAMMO}}}),AssignJobItems} - end - args.job_type=action - args.unit=df.global.world.units.active[0] - local from_pos={x=args.unit.pos.x,y=args.unit.pos.y, z=args.unit.pos.z} - args.from_pos=from_pos - args.pos=from_pos - elseif actionid==3 then --Fire - local action=df.job_type.FireBallista - if building:getSubtype()==df.siegeengine_type.Catapult then - action=df.job_type.FireCatapult - end - args.job_type=action - args.unit=df.global.world.units.active[0] - local from_pos={x=args.unit.pos.x,y=args.unit.pos.y, z=args.unit.pos.z} - args.from_pos=from_pos - args.pos=from_pos - end - args.post_actions={AssignBuildingRef} - makeJob(args) -end -function putItemToBuilding(building,item) - if building:getType()==df.building_type.Table then - dfhack.items.moveToBuilding(item,building,0) - else - local container=building.contained_items[0].item --todo maybe iterate over all, add if usemode==2? - dfhack.items.moveToContainer(item,container) - end -end -function usetool:openPutWindow(building) - local adv=df.global.world.units.active[0] - local items=EnumItems{pos=adv.pos,unit=adv, - inv={[df.unit_inventory_item.T_mode.Hauled]=true,--[df.unit_inventory_item.T_mode.Worn]=true, - [df.unit_inventory_item.T_mode.Weapon]=true,},deep=true} - local choices={} - for k,v in pairs(items) do - table.insert(choices,{text=dfhack.items.getDescription(v,0),item=v}) - end - dialog.showListPrompt("Item choice", "Choose item to put into:", COLOR_WHITE,choices,function (idx,choice) putItemToBuilding(building,choice.item) end) -end -function usetool:openSiegeWindow(building) - local args={building=building,screen=self} - dialog.showListPrompt("Engine job choice", "Choose what to do:",COLOR_WHITE,{"Turn","Load","Fire"}, - dfhack.curry(siegeWeaponActionChosen,args)) -end -function usetool:onWorkShopButtonClicked(building,index,choice) - local adv=df.global.world.units.active[0] - local args={unit=adv,building=building} - if df.interface_button_building_new_jobst:is_instance(choice.button) then - choice.button:click() - if #building.jobs>0 then - local job=building.jobs[#building.jobs-1] - args.job=job - args.pos=adv.pos - args.from_pos=adv.pos - args.pre_actions={AssignJobItems} - args.screen=self - makeJob(args) - end - elseif df.interface_button_building_category_selectorst:is_instance(choice.button) or - df.interface_button_building_material_selectorst:is_instance(choice.button) then - choice.button:click() - self:openShopWindowButtoned(building,true) - end -end -function usetool:openShopWindowFallback( building,list) - local open_window=false - if not list then --if list is not passed we are responsible for showing the menu - list={} - open_window=true - end - - local filter_pile=workshopJobs.getJobs(building:getType(),building:getSubtype(),building:getCustomType()) - local adv=df.global.world.units.active[0] - local state={unit=adv,from_pos={x=adv.pos.x,y=adv.pos.y, z=adv.pos.z},building=building - ,screen=self,bld=building} - if filter_pile then - local count=0 - state.common=filter_pile.common - for i,v in ipairs(filter_pile) do - local label=v.name:lower() - table.insert(list,{job_id=0,text=label,filter=v}) - count=count+1 - end - end - - if open_window then - dialog.showListPrompt("Workshop job choice", "Choose what to make", - COLOR_WHITE,list, - function (index,choice) - onWorkShopJobChosen(state,index,choice) - end - ,nil, nil,true) - end -end ---no reset here means that the button opens submenu -function usetool:openShopWindowButtoned(building,no_reset) - self:setupFields() - local wui=df.global.ui_sidebar_menus.workshop_job - if not no_reset then - -- [[ manual reset incase the df-one does not exist? - wui:assign{category_id=-1,mat_type=-1,mat_index=-1} - for k,v in pairs(wui.material_category) do - wui.material_category[k]=false - end - end - building:fillSidebarMenu() - - local list={} - local names_already_in={} - for id,choice in pairs(wui.choices_visible) do - local label=string.lower(utils.call_with_string(choice,"getLabel")) - table.insert(list,{text=label,button=choice,is_button=true}) - names_already_in[label]=true - end - local adv=df.global.world.units.active[0] - local state={unit=adv,from_pos={x=adv.pos.x,y=adv.pos.y, z=adv.pos.z},building=building - ,screen=self,bld=building} - if #list==0 then - --we couldn't use the df hack so let's fill the list from fallback - self:openShopWindowFallback(building,list) - else - --the hack worked. Though we are not sure how well so let's add a button for fallback - table.insert(list,{text='[fallback]'}) - end - - if #list==0 then - qerror("no jobs for this shop") - end - - dialog.showListPrompt("Workshop job choice", "Choose what to make", - COLOR_WHITE,list, - function (index,choice) - if choice.text=="[fallback]" then - self:openShopWindowFallback(building) - return - end - if choice.is_button then - self:onWorkShopButtonClicked(building,index,choice) - else - onWorkShopJobChosen(state,index,choice) - end - end - ,nil, nil,true) -end - -function track_stop_configure(bld) --TODO: dedicated widget with nice interface and current setting display - local dump_choices={ - {text="no dumping"}, - {text="N",x=0,y=-1},--{t="NE",x=1,y=-1}, - {text="E",x=1,y=0},--{t="SE",x=1,y=1}, - {text="S",x=0,y=1},--{t="SW",x=-1,y=1}, - {text="W",x=-1,y=0},--{t="NW",x=-1,y=-1} - } - local choices={"Friction","Dumping"} - local function chosen(index,choice) - if choice.text=="Friction" then - dialog.showInputPrompt("Choose friction","Friction",nil,tostring(bld.friction),function ( txt ) - local num=tonumber(txt) --TODO allow only vanilla friction settings - if num then - bld.friction=num - end - end) - else - dialog.showListPrompt("Dumping direction", "Choose dumping:",COLOR_WHITE,dump_choices,function ( index,choice) - if choice.x then - bld.use_dump=1 --?? - bld.dump_x_shift=choice.x - bld.dump_y_shift=choice.y - else - bld.use_dump=0 - end - end) - end - end - dialog.showListPrompt("Track stop configure", "Choose what to change:",COLOR_WHITE,choices,chosen) -end -function usetool:armCleanTrap(building) - local adv=df.global.world.units.active[0] - --[[ - Lever, - PressurePlate, - CageTrap, - StoneFallTrap, - WeaponTrap, - TrackStop - --]] - if building.state==0 then - --CleanTrap - --[[ LoadCageTrap, - LoadStoneTrap, - LoadWeaponTrap, - ]] - if building.trap_type==df.trap_type.Lever then - --link - return - end - --building.trap_type==df.trap_type.PressurePlate then - --settings/link - local args={unit=adv,post_actions={AssignBuildingRef},pos=adv.pos,from_pos=adv.pos, - building=building,job_type=df.job_type.CleanTrap} - if building.trap_type==df.trap_type.CageTrap then - args.job_type=df.job_type.LoadCageTrap - local job_filter={items={{quantity=1,item_type=df.item_type.CAGE}} } - args.pre_actions={dfhack.curry(setFiltersUp,job_filter),AssignJobItems} - - elseif building.trap_type==df.trap_type.StoneFallTrap then - args.job_type=df.job_type.LoadStoneTrap - local job_filter={items={{quantity=1,item_type=df.item_type.BOULDER}} } - args.pre_actions={dfhack.curry(setFiltersUp,job_filter),AssignJobItems} - elseif building.trap_type==df.trap_type.TrackStop then - --set dump and friction - track_stop_configure(building) - return - else - print("TODO: trap type:"..df.trap_type[building.trap_type]) - return - end - args.screen=self - makeJob(args) - end -end -function usetool:hiveActions(building) - local adv=df.global.world.units.active[0] - local args={unit=adv,post_actions={AssignBuildingRef},pos=adv.pos, - from_pos=adv.pos,job_type=df.job_type.InstallColonyInHive,building=building,screen=self} - local job_filter={items={{quantity=1,item_type=df.item_type.VERMIN}} } - args.pre_actions={dfhack.curry(setFiltersUp,job_filter),AssignJobItems} - makeJob(args) - --InstallColonyInHive, - --CollectHiveProducts, -end -function usetool:operatePump(building) - - local adv=df.global.world.units.active[0] - makeJob{unit=adv,post_actions={AssignBuildingRef},pos=adv.pos,from_pos=adv.pos,job_type=df.job_type.OperatePump,screen=self} -end -function usetool:farmPlot(building) - local adv=df.global.world.units.active[0] - local do_harvest=false - for id, con_item in pairs(building.contained_items) do - if con_item.use_mode==2 and con_item.item:getType()==df.item_type.PLANT then - if same_xyz(adv.pos,con_item.item.pos) then - do_harvest=true - end - end - end - --check if there tile is without plantseeds,add job - - local args={unit=adv,pos=adv.pos,from_pos=adv.pos,screen=self} - if do_harvest then - args.job_type=df.job_type.HarvestPlants - args.post_actions={AssignBuildingRef} - else - local seedjob={items={{quantity=1,item_type=df.item_type.SEEDS}}} - args.job_type=df.job_type.PlantSeeds - args.pre_actions={dfhack.curry(setFiltersUp,seedjob)} - args.post_actions={AssignBuildingRef,AssignJobItems} - end - - makeJob(args) -end -function usetool:bedActions(building) - local adv=df.global.world.units.active[0] - local args={unit=adv,pos=adv.pos,from_pos=adv.pos,screen=self,building=building, - job_type=df.job_type.Sleep,post_actions={AssignBuildingRef}} - makeJob(args) -end -function usetool:chairActions(building) - local adv=df.global.world.units.active[0] - local eatjob={items={{quantity=1,item_type=df.item_type.FOOD}}} - local args={unit=adv,pos=adv.pos,from_pos=adv.pos,screen=self,job_type=df.job_type.Eat,building=building, - pre_actions={dfhack.curry(setFiltersUp,eatjob),AssignJobItems},post_actions={AssignBuildingRef}} - makeJob(args) -end -MODES={ - [df.building_type.Table]={ --todo filters... - name="Put items", - input=usetool.openPutWindow, - }, - [df.building_type.Coffin]={ - name="Put items", - input=usetool.openPutWindow, - }, - [df.building_type.Box]={ - name="Put items", - input=usetool.openPutWindow, - }, - [df.building_type.Weaponrack]={ - name="Put items", - input=usetool.openPutWindow, - }, - [df.building_type.Armorstand]={ - name="Put items", - input=usetool.openPutWindow, - }, - [df.building_type.Cabinet]={ - name="Put items", - input=usetool.openPutWindow, - }, - [df.building_type.Workshop]={ - name="Workshop menu", - input=usetool.openShopWindowButtoned, - }, - [df.building_type.Furnace]={ - name="Workshop menu", - input=usetool.openShopWindowButtoned, - }, - [df.building_type.SiegeEngine]={ - name="Siege menu", - input=usetool.openSiegeWindow, - }, - [df.building_type.FarmPlot]={ - name="Plant/Harvest", - input=usetool.farmPlot, - }, - [df.building_type.ScrewPump]={ - name="Operate Pump", - input=usetool.operatePump, - }, - [df.building_type.Trap]={ - name="Interact", - input=usetool.armCleanTrap, - }, - [df.building_type.Hive]={ - name="Hive actions", - input=usetool.hiveActions, - }, - [df.building_type.Bed]={ - name="Rest", - input=usetool.bedActions, - }, - [df.building_type.Chair]={ - name="Eat", - input=usetool.chairActions, - }, -} -function usetool:shopMode(enable,mode,building) - self.subviews.shopLabel.visible=enable - if mode then - self.subviews.shopLabel:itemById("text1").text=mode.name - if building:getClutterLevel()<=1 then - self.subviews.shopLabel:itemById("clutter").text="" - else - self.subviews.shopLabel:itemById("clutter").text=" Clutter:"..tostring(building:getClutterLevel()) - end - self.building=building - end - self.mode=mode -end -function usetool:shopInput(keys) - if keys[keybinds.workshop.key] then - self:openShopWindowButtoned(self.in_shop) - end -end -function usetool:wait_tick() - self:sendInputToParent("A_SHORT_WAIT") -end -function usetool:setupFields() - local adv=df.global.world.units.active[0] - local civ_id=df.global.world.units.active[0].civ_id - local ui=df.global.ui - ui.civ_id = civ_id - ui.main.fortress_entity=df.historical_entity.find(civ_id) - ui.race_id=adv.race - local nem=dfhack.units.getNemesis(adv) - if nem then - local links=nem.figure.entity_links - for _,link in ipairs(links) do - local hist_entity=df.historical_entity.find(link.entity_id) - if hist_entity and hist_entity.type==df.historical_entity_type.SiteGovernment then - ui.group_id=link.entity_id - break - end - end - end - local site= inSite() - if site then - ui.site_id=site.id - end -end -function usetool:siteCheck() - if self.site ~= nil or not settings.safe then --TODO: add check if it's correct site (the persistant ones) - return true - end - return false, "You are not on site" -end ---movement and co... Also passes on allowed keys -function usetool:fieldInput(keys) - local adv=df.global.world.units.active[0] - local cur_mode=actions[(mode or 0)+1] - local failed=false - for code,_ in pairs(keys) do - --print(code) - if MOVEMENT_KEYS[code] then - - local state={ - unit=adv, - pos=moddedpos(adv.pos,MOVEMENT_KEYS[code]), - dir=MOVEMENT_KEYS[code], - from_pos={x=adv.pos.x,y=adv.pos.y, z=adv.pos.z}, - post_actions=cur_mode[4], - pre_actions=cur_mode[5], - job_type=cur_mode[2], - screen=self} - - if code=="SELECT" then --do job in the distance, TODO: check if you can still cheat-mine (and co.) remotely - if df.global.cursor.x~=-30000 then - state.pos={x=df.global.cursor.x,y=df.global.cursor.y,z=df.global.cursor.z} - else - break - end - end - - --First check site - local ok,msg=self:siteCheck() --TODO: some jobs might be possible without a site? - if not ok then - dfhack.gui.showAnnouncement(msg,5,1) - failed=true - else - for _,p in pairs(cur_mode[3] or {}) do --then check predicates - local ok,msg=p(state) - if not ok then - dfhack.gui.showAnnouncement(msg,5,1) - failed=true - end - end - end - - if not failed then - local ok,msg - if type(cur_mode[2])=="function" then - ok,msg=cur_mode[2](state) - else - makeJob(state) - --(adv,moddedpos(adv.pos,MOVEMENT_KEYS[code]),cur_mode[2],adv.pos,cur_mode[4]) - - end - - if code=="SELECT" then - self:sendInputToParent("LEAVESCREEN") - end - self.long_wait=true - end - return code - end - if code~="_STRING" and code~="_MOUSE_L" and code~="_MOUSE_R" then - if ALLOWED_KEYS[code] then - self:sendInputToParent(code) - end - end - end - -end - -function usetool:onInput(keys) - - self:update_site() - - local adv=df.global.world.units.active[0] - - if keys.LEAVESCREEN then - if df.global.cursor.x~=-30000 then --if not poiting at anything - self:sendInputToParent("LEAVESCREEN") --leave poiting - else - self:dismiss() --leave the adv-tools all together - CancelJob(adv) - end - elseif keys[keybinds.nextJob.key] then --next job with looping - mode=(mode+1)%#actions - elseif keys[keybinds.prevJob.key] then --prev job with looping - mode=mode-1 - if mode<0 then mode=#actions-1 end - elseif keys["A_SHORT_WAIT"] then - --ContinueJob(adv) - self:sendInputToParent("A_SHORT_WAIT") - elseif keys[keybinds.quick.key] then - settings.quick=not settings.quick - elseif keys[keybinds.continue.key] then - --ContinueJob(adv) - --self:sendInputToParent("A_SHORT_WAIT") - self.long_wait=true - self.long_wait_timer=nil - else - if self.mode~=nil then - if keys[keybinds.workshop.key] then - self.mode.input(self,self.building) - end - self:fieldInput(keys) - else - self:fieldInput(keys) - end - end - -end -function usetool:cancel_wait() - self.long_wait_timer=nil - self.long_wait=false -end -function usetool:onIdle() - local adv=df.global.world.units.active[0] - local job_ptr=adv.job.current_job - local job_action=findAction(adv,df.unit_action_type.Job) - - --some heuristics for unsafe conditions - if self.long_wait and not settings.unsafe then --check if player wants for canceling to happen - local counters=adv.counters - local checked_counters={pain=true,winded=true,stunned=true,unconscious=true,suffocation=true,webbed=true,nausea=true,dizziness=true} - for k,v in pairs(checked_counters) do - if counters[k]>0 then - dfhack.gui.showAnnouncement("Job: canceled waiting because unsafe -"..k,5,1) - self:cancel_wait() - return - end - end - end - - if self.long_wait and self.long_wait_timer==nil then - self.long_wait_timer=1000 --TODO tweak this - end - - if job_ptr and self.long_wait and not job_action then - - if self.long_wait_timer<=0 then --fix deadlocks with force-canceling of waiting - self:cancel_wait() - return - else - self.long_wait_timer=self.long_wait_timer-1 - end - - if adv.job.current_job.completion_timer==-1 then - self.long_wait=false - end - ContinueJob(adv) - self:sendInputToParent("A_SHORT_WAIT") --todo continue till finished - end - self._native.parent:logic() -end -function usetool:isOnBuilding() - local adv=df.global.world.units.active[0] - local bld=dfhack.buildings.findAtTile(adv.pos) - if bld and MODES[bld:getType()]~=nil and bld:getBuildStage()==bld:getMaxBuildStage() then - return true,MODES[bld:getType()],bld - else - return false - end -end -function usetool:onRenderBody(dc) - self:shopMode(self:isOnBuilding()) - self:renderParent() -end -if not (dfhack.gui.getCurFocus()=="dungeonmode/Look" or dfhack.gui.getCurFocus()=="dungeonmode/Default") then - qerror("This script requires an adventurer mode with (l)ook or default mode.") -end -usetool():show() diff --git a/scripts/gui/advfort_items.lua b/scripts/gui/advfort_items.lua deleted file mode 100644 index 4cc05da72..000000000 --- a/scripts/gui/advfort_items.lua +++ /dev/null @@ -1,226 +0,0 @@ ---Does something with items in adventure mode jobs ---[[=begin - -gui/advfort_items -================= -Does something with items in adventure mode jobs. - -=end]] -local _ENV = mkmodule('hack.scripts.gui.advfort_items') - -local gui=require('gui') -local wid=require('gui.widgets') -local gscript=require('gui.script') - -jobitemEditor=defclass(jobitemEditor,gui.FramedScreen) -jobitemEditor.ATTRS{ - frame_style = gui.GREY_LINE_FRAME, - frame_inset = 1, - allow_add=false, - allow_remove=false, - allow_any_item=false, - job=DEFAULT_NIL, - job_items=DEFAULT_NIL, - items=DEFAULT_NIL, - on_okay=DEFAULT_NIL, - autofill=true, -} -function update_slot_text(slot) - local items="" - for i,v in ipairs(slot.items) do - items=items.." "..dfhack.items.getDescription(v,0) - if i~=#slot.items then - items=items.."," - end - end - - slot.text=string.format("%02d. Filled(%d/%d):%s",slot.id+1,slot.filled_amount,slot.job_item.quantity,items) -end ---items-> table => key-> id of job.job_items, value-> table => key (num), value => item(ref) -function jobitemEditor:init(args) - --self.job=args.job - if self.job==nil and self.job_items==nil then qerror("This screen must have job target or job_items list") end - if self.items==nil then qerror("This screen must have item list") end - - self:addviews{ - wid.Label{ - view_id = 'label', - text = args.prompt, - text_pen = args.text_pen, - frame = { l = 0, t = 0 }, - }, - wid.List{ - view_id = 'itemList', - frame = { l = 0, t = 2 ,b=2}, - }, - wid.Label{ - frame = { b=1,l=1}, - text ={{text= ": cancel", - key = "LEAVESCREEN", - on_activate= self:callback("dismiss") - }, - { - gap=3, - text= ": accept", - key = "SEC_SELECT", - on_activate= self:callback("commit"), - enabled=self:callback("jobValid") - }, - { - gap=3, - text= ": add", - key = "CUSTOM_A", - enabled=self:callback("can_add"), - on_activate= self:callback("add_item") - }, - { - gap=3, - text= ": remove", - key = "CUSTOM_R", - enabled=self:callback("can_remove"), - on_activate= self:callback("remove_item") - },} - }, - } - self.assigned={} - self:fill() - if self.autofill then - self:fill_slots() - end -end -function jobitemEditor:get_slot() - local idx,choice=self.subviews.itemList:getSelected() - return choice -end -function jobitemEditor:can_add() - local slot=self:get_slot() - return slot.filled_amount0 -end -function jobitemEditor:get_item_filters( job_item ) - local true_flags={} - for k,v in pairs(job_item.flags1) do - if v then - table.insert(true_flags,k) - end - end - for k,v in pairs(job_item.flags2) do - if v then - table.insert(true_flags,k) - end - end - for k,v in pairs(job_item.flags3) do - if v then - table.insert(true_flags,k) - end - end - return table.concat(true_flags,"\n") -end -function jobitemEditor:add_item() - local cur_slot=self:get_slot() - local choices={} - table.insert(choices,{text=""}) - for k,v in pairs(cur_slot.choices) do - if not self.assigned[v.id] then - table.insert(choices,{text=dfhack.items.getDescription(v,0),item=v}) - end - end - gscript.start(function () - local _,_2,choice=gscript.showListPrompt("which item?", "Select item\nItem filters:\n"..self:get_item_filters(cur_slot.job_item), COLOR_WHITE, choices) - if choice ~= nil and choice.item~=nil then - self:add_item_to_slot(cur_slot,choice.item) - end - end - ) -end -function jobitemEditor:fill_slots() - for i,v in ipairs(self.slots) do - while v.filled_amount`, which has not -been available since DF 0.34.11 - -See :bug:`1445` for more info about the patches. - -=end]] -local utils = require 'utils' -local gui = require 'gui' -local guidm = require 'gui.dwarfmode' -local widgets = require 'gui.widgets' -local dlg = require 'gui.dialogs' -local bp = require 'binpatch' - -AssignRack = defclass(AssignRack, guidm.MenuOverlay) - -AssignRack.focus_path = 'assign-rack' - -AssignRack.ATTRS { - building = DEFAULT_NIL, - frame_inset = 1, - frame_background = COLOR_BLACK, -} - -function list_squads(building,squad_table,squad_list) - local sqlist = building:getSquads() - if not sqlist then - return - end - - for i,v in ipairs(sqlist) do - local obj = df.squad.find(v.squad_id) - if obj then - if not squad_table[v.squad_id] then - squad_table[v.squad_id] = { id = v.squad_id, obj = obj } - table.insert(squad_list, squad_table[v.squad_id]) - end - - -- Set specific use flags - for n,ok in pairs(v.mode) do - if ok then - squad_table[v.squad_id][n] = true - end - end - - -- Check if any use is possible - local btype = building:getType() - if btype == df.building_type.Bed then - if v.mode.sleep then - squad_table[v.squad_id].any = true - end - elseif btype == df.building.Weaponrack then - if v.mode.train or v.mode.indiv_eq then - squad_table[v.squad_id].any = true - end - else - if v.mode.indiv_eq then - squad_table[v.squad_id].any = true - end - end - end - end - - for i,v in ipairs(building.parents) do - list_squads(v, squad_table, squad_list) - end -end - -function filter_invalid(list, id) - for i=#list-1,0,-1 do - local bld = df.building.find(list[i]) - if not bld or bld:getSpecificSquad() ~= id then - list:erase(i) - end - end -end - -function AssignRack:init(args) - self.squad_table = {} - self.squad_list = {} - list_squads(self.building, self.squad_table, self.squad_list) - table.sort(self.squad_list, function(a,b) return a.id < b.id end) - - self.choices = {} - for i,v in ipairs(self.squad_list) do - if v.any and (v.train or v.indiv_eq) then - local name = v.obj.alias - if name == '' then - name = dfhack.TranslateName(v.obj.name, true) - end - - filter_invalid(v.obj.rack_combat, v.id) - filter_invalid(v.obj.rack_training, v.id) - - table.insert(self.choices, { - icon = self:callback('isSelected', v), - icon_pen = COLOR_LIGHTGREEN, - obj = v, - text = { - name, NEWLINE, ' ', - { text = function() - return string.format('%d combat, %d training', #v.obj.rack_combat, #v.obj.rack_training) - end } - } - }) - end - end - - self:addviews{ - widgets.Label{ - frame = { l = 0, t = 0 }, - text = { - 'Assign Weapon Rack' - } - }, - widgets.List{ - view_id = 'list', - frame = { t = 2, b = 2 }, - icon_width = 2, row_height = 2, - scroll_keys = widgets.SECONDSCROLL, - choices = self.choices, - on_submit = self:callback('onSubmit'), - }, - widgets.Label{ - frame = { l = 0, t = 2 }, - text_pen = COLOR_LIGHTRED, - text = 'No appropriate barracks\n\nNote: weapon racks use the\nIndividual equipment flag', - visible = (#self.choices == 0), - }, - widgets.Label{ - frame = { l = 0, b = 0 }, - text = { - { key = 'LEAVESCREEN', text = ': Back', - on_activate = self:callback('dismiss') } - } - }, - } -end - -function AssignRack:isSelected(info) - if self.building.specific_squad == info.id then - return '\xfb' - else - return nil - end -end - -function AssignRack:onSubmit(idx, choice) - local rid = self.building.id - local curid = self.building.specific_squad - - local cur = df.squad.find(curid) - if cur then - utils.erase_sorted(cur.rack_combat, rid) - utils.erase_sorted(cur.rack_training, rid) - end - - self.building.specific_squad = -1 - df.global.ui.equipment.update.buildings = true - - local new = df.squad.find(choice.obj.id) - if new and choice.obj.id ~= curid then - self.building.specific_squad = choice.obj.id - - if choice.obj.indiv_eq then - utils.insert_sorted(new.rack_combat, rid) - end - if choice.obj.train then - utils.insert_sorted(new.rack_training, rid) - end - end -end - -function AssignRack:onInput(keys) - if self:propagateMoveKeys(keys) then - if df.global.world.selected_building ~= self.building then - self:dismiss() - end - else - AssignRack.super.onInput(self, keys) - end -end - -if dfhack.gui.getCurFocus() ~= 'dwarfmode/QueryBuilding/Some/Weaponrack' then - qerror("This script requires a weapon rack selected in the 'q' mode") -end - -AssignRack{ building = dfhack.gui.getSelectedBuilding() }:show() - -if not already_patched then - local patch = bp.load_dif_file('weaponrack-unassign') - if patch and patch:isApplied() then - already_patched = true - end -end - -if not already_patched then - dlg.showMessage( - 'BUG ALERT', - { 'This script requires applying the binary patch', NEWLINE, - 'named weaponrack-unassign. Otherwise the game', NEWLINE, - 'will lose your settings due to a bug.' }, - COLOR_YELLOW - ) -end diff --git a/scripts/gui/autobutcher.lua b/scripts/gui/autobutcher.lua deleted file mode 100644 index 59f3733c1..000000000 --- a/scripts/gui/autobutcher.lua +++ /dev/null @@ -1,662 +0,0 @@ --- A GUI front-end for the autobutcher plugin. ---[[=begin - -gui/autobutcher -=============== -An in-game interface for `autobutcher`. - -=end]] -local gui = require 'gui' -local utils = require 'utils' -local widgets = require 'gui.widgets' -local dlg = require 'gui.dialogs' - -local plugin = require 'plugins.zone' - -WatchList = defclass(WatchList, gui.FramedScreen) - -WatchList.ATTRS { - frame_title = 'Autobutcher Watchlist', - frame_inset = 0, -- cover full DF window - frame_background = COLOR_BLACK, - frame_style = gui.BOUNDARY_FRAME, -} - --- width of the race name column in the UI -local racewidth = 25 - -function nextAutowatchState() - if(plugin.autowatch_isEnabled()) then - return 'Stop ' - end - return 'Start' -end - -function nextAutobutcherState() - if(plugin.autobutcher_isEnabled()) then - return 'Stop ' - end - return 'Start' -end - -function getSleepTimer() - return plugin.autobutcher_getSleep() -end - -function setSleepTimer(ticks) - plugin.autobutcher_setSleep(ticks) -end - -function WatchList:init(args) - local colwidth = 7 - self:addviews{ - widgets.Panel{ - frame = { l = 0, r = 0 }, - frame_inset = 1, - subviews = { - widgets.Label{ - frame = { l = 0, t = 0 }, - text_pen = COLOR_CYAN, - text = { - { text = 'Race', width = racewidth }, ' ', - { text = 'female', width = colwidth }, ' ', - { text = ' male', width = colwidth }, ' ', - { text = 'Female', width = colwidth }, ' ', - { text = ' Male', width = colwidth }, ' ', - { text = 'watch? ' }, - { text = ' butchering' }, - NEWLINE, - { text = '', width = racewidth }, ' ', - { text = ' kids', width = colwidth }, ' ', - { text = ' kids', width = colwidth }, ' ', - { text = 'adults', width = colwidth }, ' ', - { text = 'adults', width = colwidth }, ' ', - { text = ' ' }, - { text = ' ordered' }, - } - }, - widgets.List{ - view_id = 'list', - frame = { t = 3, b = 5 }, - not_found_label = 'Watchlist is empty.', - edit_pen = COLOR_LIGHTCYAN, - text_pen = { fg = COLOR_GREY, bg = COLOR_BLACK }, - cursor_pen = { fg = COLOR_WHITE, bg = COLOR_GREEN }, - --on_select = self:callback('onSelectEntry'), - }, - widgets.Label{ - view_id = 'bottom_ui', - frame = { b = 0, h = 1 }, - text = 'filled by updateBottom()' - } - } - }, - } - - self:initListChoices() - self:updateBottom() -end - --- change the viewmode for stock data displayed in left section of columns -local viewmodes = { 'total stock', 'protected stock', 'butcherable', 'butchering ordered' } -local viewmode = 1 -function WatchList:onToggleView() - if viewmode < #viewmodes then - viewmode = viewmode + 1 - else - viewmode = 1 - end - self:initListChoices() - self:updateBottom() -end - --- update the bottom part of the UI (after sleep timer changed etc) -function WatchList:updateBottom() - self.subviews.bottom_ui:setText( - { - { key = 'CUSTOM_SHIFT_V', text = ': View in colums shows: '..viewmodes[viewmode]..' / target max', - on_activate = self:callback('onToggleView') }, NEWLINE, - { key = 'CUSTOM_F', text = ': f kids', - on_activate = self:callback('onEditFK') }, ', ', - { key = 'CUSTOM_M', text = ': m kids', - on_activate = self:callback('onEditMK') }, ', ', - { key = 'CUSTOM_SHIFT_F', text = ': f adults', - on_activate = self:callback('onEditFA') }, ', ', - { key = 'CUSTOM_SHIFT_M', text = ': m adults', - on_activate = self:callback('onEditMA') }, '. ', - { key = 'CUSTOM_W', text = ': Toggle watch', - on_activate = self:callback('onToggleWatching') }, '. ', - { key = 'CUSTOM_X', text = ': Delete', - on_activate = self:callback('onDeleteEntry') }, '. ', NEWLINE, - --{ key = 'CUSTOM_A', text = ': Add race', - -- on_activate = self:callback('onAddRace') }, ', ', - { key = 'CUSTOM_SHIFT_R', text = ': Set whole row', - on_activate = self:callback('onSetRow') }, '. ', - { key = 'CUSTOM_B', text = ': Remove butcher orders', - on_activate = self:callback('onUnbutcherRace') }, '. ', - { key = 'CUSTOM_SHIFT_B', text = ': Butcher race', - on_activate = self:callback('onButcherRace') }, '. ', NEWLINE, - { key = 'CUSTOM_SHIFT_A', text = ': '..nextAutobutcherState()..' Autobutcher', - on_activate = self:callback('onToggleAutobutcher') }, '. ', - { key = 'CUSTOM_SHIFT_W', text = ': '..nextAutowatchState()..' Autowatch', - on_activate = self:callback('onToggleAutowatch') }, '. ', - { key = 'CUSTOM_SHIFT_S', text = ': Sleep ('..getSleepTimer()..' ticks)', - on_activate = self:callback('onEditSleepTimer') }, '. ', - }) -end - -function stringify(number) - -- cap displayed number to 3 digits - -- after population of 50 per race is reached pets stop breeding anyways - -- so probably this could safely be reduced to 99 - local max = 999 - if number > max then number = max end - return tostring(number) -end - -function WatchList:initListChoices() - - local choices = {} - - -- first two rows are for "edit all races" and "edit new races" - local settings = plugin.autobutcher_getSettings() - local fk = stringify(settings.fk) - local fa = stringify(settings.fa) - local mk = stringify(settings.mk) - local ma = stringify(settings.ma) - - local watched = '' - - local colwidth = 7 - - table.insert (choices, { - text = { - { text = '!! ALL RACES PLUS NEW', width = racewidth, pad_char = ' ' }, --' ', - { text = ' ', width = 3, rjustify = true, pad_char = ' ' }, ' ', - { text = fk, width = 3, rjustify = false, pad_char = ' ' }, ' ', - { text = ' ', width = 3, rjustify = true, pad_char = ' ' }, ' ', - { text = mk, width = 3, rjustify = false, pad_char = ' ' }, ' ', - { text = ' ', width = 3, rjustify = true, pad_char = ' ' }, ' ', - { text = fa, width = 3, rjustify = false, pad_char = ' ' }, ' ', - { text = ' ', width = 3, rjustify = true, pad_char = ' ' }, ' ', - { text = ma, width = 3, rjustify = false, pad_char = ' ' }, ' ', - { text = watched, width = 6, rjustify = true } - } - }) - - table.insert (choices, { - text = { - { text = '!! ONLY NEW RACES', width = racewidth, pad_char = ' ' }, --' ', - { text = ' ', width = 3, rjustify = true, pad_char = ' ' }, ' ', - { text = fk, width = 3, rjustify = false, pad_char = ' ' }, ' ', - { text = ' ', width = 3, rjustify = true, pad_char = ' ' }, ' ', - { text = mk, width = 3, rjustify = false, pad_char = ' ' }, ' ', - { text = ' ', width = 3, rjustify = true, pad_char = ' ' }, ' ', - { text = fa, width = 3, rjustify = false, pad_char = ' ' }, ' ', - { text = ' ', width = 3, rjustify = true, pad_char = ' ' }, ' ', - { text = ma, width = 3, rjustify = false, pad_char = ' ' }, ' ', - { text = watched, width = 6, rjustify = true } - } - }) - - local watchlist = plugin.autobutcher_getWatchList() - - for i,entry in ipairs(watchlist) do - fk = stringify(entry.fk) - fa = stringify(entry.fa) - mk = stringify(entry.mk) - ma = stringify(entry.ma) - if viewmode == 1 then - fkc = stringify(entry.fk_total) - fac = stringify(entry.fa_total) - mkc = stringify(entry.mk_total) - mac = stringify(entry.ma_total) - end - if viewmode == 2 then - fkc = stringify(entry.fk_protected) - fac = stringify(entry.fa_protected) - mkc = stringify(entry.mk_protected) - mac = stringify(entry.ma_protected) - end - if viewmode == 3 then - fkc = stringify(entry.fk_butcherable) - fac = stringify(entry.fa_butcherable) - mkc = stringify(entry.mk_butcherable) - mac = stringify(entry.ma_butcherable) - end - if viewmode == 4 then - fkc = stringify(entry.fk_butcherflag) - fac = stringify(entry.fa_butcherflag) - mkc = stringify(entry.mk_butcherflag) - mac = stringify(entry.ma_butcherflag) - end - local butcher_ordered = entry.fk_butcherflag + entry.fa_butcherflag + entry.mk_butcherflag + entry.ma_butcherflag - local bo = ' ' - if butcher_ordered > 0 then bo = stringify(butcher_ordered) end - - local watched = 'no' - if entry.watched then watched = 'yes' end - - local racestr = entry.name - - -- highlight entries where the target quota can't be met because too many are protected - bad_pen = COLOR_LIGHTRED - good_pen = NONE -- this is stupid, but it works. sue me - fk_pen = good_pen - fa_pen = good_pen - mk_pen = good_pen - ma_pen = good_pen - if entry.fk_protected > entry.fk then fk_pen = bad_pen end - if entry.fa_protected > entry.fa then fa_pen = bad_pen end - if entry.mk_protected > entry.mk then mk_pen = bad_pen end - if entry.ma_protected > entry.ma then ma_pen = bad_pen end - - table.insert (choices, { - text = { - { text = racestr, width = racewidth, pad_char = ' ' }, --' ', - { text = fkc, width = 3, rjustify = true, pad_char = ' ' }, '/', - { text = fk, width = 3, rjustify = false, pad_char = ' ', pen = fk_pen }, ' ', - { text = mkc, width = 3, rjustify = true, pad_char = ' ' }, '/', - { text = mk, width = 3, rjustify = false, pad_char = ' ', pen = mk_pen }, ' ', - { text = fac, width = 3, rjustify = true, pad_char = ' ' }, '/', - { text = fa, width = 3, rjustify = false, pad_char = ' ', pen = fa_pen }, ' ', - { text = mac, width = 3, rjustify = true, pad_char = ' ' }, '/', - { text = ma, width = 3, rjustify = false, pad_char = ' ', pen = ma_pen }, ' ', - { text = watched, width = 6, rjustify = true, pad_char = ' ' }, ' ', - { text = bo, width = 8, rjustify = true, pad_char = ' ' } - }, - obj = entry, - }) - end - - local list = self.subviews.list - list:setChoices(choices) -end - -function WatchList:onInput(keys) - if keys.LEAVESCREEN then - self:dismiss() - else - WatchList.super.onInput(self, keys) - end -end - --- check the user input for target population values -function WatchList:checkUserInput(count, text) - if count == nil then - dlg.showMessage('Invalid Number', 'This is not a number: '..text..NEWLINE..'(for zero enter a 0)', COLOR_LIGHTRED) - return false - end - if count < 0 then - dlg.showMessage('Invalid Number', 'Negative numbers make no sense!', COLOR_LIGHTRED) - return false - end - return true -end - --- check the user input for sleep timer -function WatchList:checkUserInputSleep(count, text) - if count == nil then - dlg.showMessage('Invalid Number', 'This is not a number: '..text..NEWLINE..'(for zero enter a 0)', COLOR_LIGHTRED) - return false - end - if count < 1000 then - dlg.showMessage('Invalid Number', - 'Minimum allowed timer value is 1000!'..NEWLINE..'Too low values could decrease performance'..NEWLINE..'and are not necessary!', - COLOR_LIGHTRED) - return false - end - return true -end - -function WatchList:onEditFK() - local selidx,selobj = self.subviews.list:getSelected() - local settings = plugin.autobutcher_getSettings() - local fk = settings.fk - local mk = settings.mk - local fa = settings.fa - local ma = settings.ma - local race = 'ALL RACES PLUS NEW' - local id = -1 - local watched = false - - if selidx == 2 then - race = 'ONLY NEW RACES' - end - - if selidx > 2 then - local entry = selobj.obj - fk = entry.fk - mk = entry.mk - fa = entry.fa - ma = entry.ma - race = entry.name - id = entry.id - watched = entry.watched - end - - dlg.showInputPrompt( - 'Race: '..race, - 'Enter desired maximum of female kids:', - COLOR_WHITE, - ' '..fk, - function(text) - local count = tonumber(text) - if self:checkUserInput(count, text) then - fk = count - if selidx == 1 then - plugin.autobutcher_setDefaultTargetAll( fk, mk, fa, ma ) - end - if selidx == 2 then - plugin.autobutcher_setDefaultTargetNew( fk, mk, fa, ma ) - end - if selidx > 2 then - plugin.autobutcher_setWatchListRace(id, fk, mk, fa, ma, watched) - end - self:initListChoices() - end - end - ) -end - -function WatchList:onEditMK() - local selidx,selobj = self.subviews.list:getSelected() - local settings = plugin.autobutcher_getSettings() - local fk = settings.fk - local mk = settings.mk - local fa = settings.fa - local ma = settings.ma - local race = 'ALL RACES PLUS NEW' - local id = -1 - local watched = false - - if selidx == 2 then - race = 'ONLY NEW RACES' - end - - if selidx > 2 then - local entry = selobj.obj - fk = entry.fk - mk = entry.mk - fa = entry.fa - ma = entry.ma - race = entry.name - id = entry.id - watched = entry.watched - end - - dlg.showInputPrompt( - 'Race: '..race, - 'Enter desired maximum of male kids:', - COLOR_WHITE, - ' '..mk, - function(text) - local count = tonumber(text) - if self:checkUserInput(count, text) then - mk = count - if selidx == 1 then - plugin.autobutcher_setDefaultTargetAll( fk, mk, fa, ma ) - end - if selidx == 2 then - plugin.autobutcher_setDefaultTargetNew( fk, mk, fa, ma ) - end - if selidx > 2 then - plugin.autobutcher_setWatchListRace(id, fk, mk, fa, ma, watched) - end - self:initListChoices() - end - end - ) -end - -function WatchList:onEditFA() - local selidx,selobj = self.subviews.list:getSelected() - local settings = plugin.autobutcher_getSettings() - local fk = settings.fk - local mk = settings.mk - local fa = settings.fa - local ma = settings.ma - local race = 'ALL RACES PLUS NEW' - local id = -1 - local watched = false - - if selidx == 2 then - race = 'ONLY NEW RACES' - end - - if selidx > 2 then - local entry = selobj.obj - fk = entry.fk - mk = entry.mk - fa = entry.fa - ma = entry.ma - race = entry.name - id = entry.id - watched = entry.watched - end - - dlg.showInputPrompt( - 'Race: '..race, - 'Enter desired maximum of female adults:', - COLOR_WHITE, - ' '..fa, - function(text) - local count = tonumber(text) - if self:checkUserInput(count, text) then - fa = count - if selidx == 1 then - plugin.autobutcher_setDefaultTargetAll( fk, mk, fa, ma ) - end - if selidx == 2 then - plugin.autobutcher_setDefaultTargetNew( fk, mk, fa, ma ) - end - if selidx > 2 then - plugin.autobutcher_setWatchListRace(id, fk, mk, fa, ma, watched) - end - self:initListChoices() - end - end - ) -end - -function WatchList:onEditMA() - local selidx,selobj = self.subviews.list:getSelected() - local settings = plugin.autobutcher_getSettings() - local fk = settings.fk - local mk = settings.mk - local fa = settings.fa - local ma = settings.ma - local race = 'ALL RACES PLUS NEW' - local id = -1 - local watched = false - - if selidx == 2 then - race = 'ONLY NEW RACES' - end - - if selidx > 2 then - local entry = selobj.obj - fk = entry.fk - mk = entry.mk - fa = entry.fa - ma = entry.ma - race = entry.name - id = entry.id - watched = entry.watched - end - - dlg.showInputPrompt( - 'Race: '..race, - 'Enter desired maximum of male adults:', - COLOR_WHITE, - ' '..ma, - function(text) - local count = tonumber(text) - if self:checkUserInput(count, text) then - ma = count - if selidx == 1 then - plugin.autobutcher_setDefaultTargetAll( fk, mk, fa, ma ) - end - if selidx == 2 then - plugin.autobutcher_setDefaultTargetNew( fk, mk, fa, ma ) - end - if selidx > 2 then - plugin.autobutcher_setWatchListRace(id, fk, mk, fa, ma, watched) - end - self:initListChoices() - end - end - ) -end - -function WatchList:onEditSleepTimer() - local sleep = getSleepTimer() - dlg.showInputPrompt( - 'Edit Sleep Timer', - 'Enter new sleep timer in ticks:'..NEWLINE..'(1 ingame day equals 1200 ticks)', - COLOR_WHITE, - ' '..sleep, - function(text) - local count = tonumber(text) - if self:checkUserInputSleep(count, text) then - sleep = count - setSleepTimer(sleep) - self:updateBottom() - end - end - ) -end - -function WatchList:onToggleWatching() - local selidx,selobj = self.subviews.list:getSelected() - if selidx > 2 then - local entry = selobj.obj - plugin.autobutcher_setWatchListRace(entry.id, entry.fk, entry.mk, entry.fa, entry.ma, not entry.watched) - end - self:initListChoices() -end - -function WatchList:onDeleteEntry() - local selidx,selobj = self.subviews.list:getSelected() - if(selidx < 3 or selobj == nil) then - return - end - dlg.showYesNoPrompt( - 'Delete from Watchlist', - 'Really delete the selected entry?'..NEWLINE..'(you could just toggle watch instead)', - COLOR_YELLOW, - function() - plugin.autobutcher_removeFromWatchList(selobj.obj.id) - self:initListChoices() - end - ) -end - -function WatchList:onAddRace() - print('onAddRace - not implemented yet') -end - -function WatchList:onUnbutcherRace() - local selidx,selobj = self.subviews.list:getSelected() - if selidx < 3 then dlg.showMessage('Error', 'Select a specific race.', COLOR_LIGHTRED) end - if selidx > 2 then - local entry = selobj.obj - local race = entry.name - plugin.autobutcher_unbutcherRace(entry.id) - self:initListChoices() - self:updateBottom() - end -end - -function WatchList:onButcherRace() - local selidx,selobj = self.subviews.list:getSelected() - if selidx < 3 then dlg.showMessage('Error', 'Select a specific race.', COLOR_LIGHTRED) end - if selidx > 2 then - local entry = selobj.obj - local race = entry.name - plugin.autobutcher_butcherRace(entry.id) - self:initListChoices() - self:updateBottom() - end -end - --- set whole row (fk, mk, fa, ma) to one value -function WatchList:onSetRow() - local selidx,selobj = self.subviews.list:getSelected() - local race = 'ALL RACES PLUS NEW' - local id = -1 - local watched = false - - if selidx == 2 then - race = 'ONLY NEW RACES' - end - - local watchindex = selidx - 3 - if selidx > 2 then - local entry = selobj.obj - race = entry.name - id = entry.id - watched = entry.watched - end - - dlg.showInputPrompt( - 'Set whole row for '..race, - 'Enter desired maximum for all subtypes:', - COLOR_WHITE, - ' ', - function(text) - local count = tonumber(text) - if self:checkUserInput(count, text) then - if selidx == 1 then - plugin.autobutcher_setDefaultTargetAll( count, count, count, count ) - end - if selidx == 2 then - plugin.autobutcher_setDefaultTargetNew( count, count, count, count ) - end - if selidx > 2 then - plugin.autobutcher_setWatchListRace(id, count, count, count, count, watched) - end - self:initListChoices() - end - end - ) -end - -function WatchList:onToggleAutobutcher() - if(plugin.autobutcher_isEnabled()) then - plugin.autobutcher_setEnabled(false) - plugin.autobutcher_sortWatchList() - else - plugin.autobutcher_setEnabled(true) - end - self:initListChoices() - self:updateBottom() -end - -function WatchList:onToggleAutowatch() - if(plugin.autowatch_isEnabled()) then - plugin.autowatch_setEnabled(false) - else - plugin.autowatch_setEnabled(true) - end - self:initListChoices() - self:updateBottom() -end - -if not dfhack.isMapLoaded() then - qerror('Map is not loaded.') -end - -if string.match(dfhack.gui.getCurFocus(), '^dfhack/lua') then - qerror("This script must not be called while other lua gui stuff is running.") -end - --- maybe this is too strict, there is not really a reason why it can only be called from the status screen --- (other than the hotkey might overlap with other scripts) -if (not string.match(dfhack.gui.getCurFocus(), '^overallstatus') and not string.match(dfhack.gui.getCurFocus(), '^pet/List/Unit')) then - qerror("This script must either be called from the overall status screen or the animal list screen.") -end - - -local screen = WatchList{ } -screen:show() diff --git a/scripts/gui/choose-weapons.lua b/scripts/gui/choose-weapons.lua deleted file mode 100644 index 39e5fbf77..000000000 --- a/scripts/gui/choose-weapons.lua +++ /dev/null @@ -1,169 +0,0 @@ --- Rewrite individual choice weapons to specific types ---[[=begin - -gui/choose-weapons -================== -Bind to a key (the example config uses :kbd:`Ctrl`:kbd:`W`), and activate in the Equip->View/Customize -page of the military screen. - -Depending on the cursor location, it rewrites all 'individual choice weapon' entries -in the selected squad or position to use a specific weapon type matching the assigned -unit's top skill. If the cursor is in the rightmost list over a weapon entry, it rewrites -only that entry, and does it even if it is not 'individual choice'. - -Rationale: individual choice seems to be unreliable when there is a weapon shortage, -and may lead to inappropriate weapons being selected. - -=end]] -local utils = require 'utils' -local dlg = require 'gui.dialogs' - -local defs = df.global.world.raws.itemdefs -local entity = df.global.ui.main.fortress_entity -local tasks = df.global.ui.tasks -local equipment = df.global.ui.equipment - -function find_best_weapon(unit,mode) - local best = nil - local skill = nil - local skill_level = nil - local count = 0 - local function try(id,iskill) - local slevel = dfhack.units.getNominalSkill(unit,iskill) - -- Choose most skill - if (skill ~= nil and slevel > skill_level) - or (skill == nil and slevel > 0) then - best,skill,skill_level,count = id,iskill,slevel,0 - end - -- Then most produced within same skill - if skill == iskill then - local cnt = tasks.created_weapons[id] - if cnt > count then - best,count = id,cnt - end - end - end - for _,id in ipairs(entity.resources.weapon_type) do - local def = defs.weapons[id] - if def.skill_ranged >= 0 then - if mode == nil or mode == 'ranged' then - try(id, def.skill_ranged) - end - else - if mode == nil or mode == 'melee' then - try(id, def.skill_melee) - end - end - end - return best -end - -function unassign_wrong_items(unit,position,spec,subtype) - for i=#spec.assigned-1,0,-1 do - local id = spec.assigned[i] - local item = df.item.find(id) - - if item.subtype.subtype ~= subtype then - spec.assigned:erase(i) - - -- TODO: somewhat unexplored area; maybe missing some steps - utils.erase_sorted(position.assigned_items,id) - if utils.erase_sorted(equipment.items_assigned.WEAPON,item,'id') then - utils.insert_sorted(equipment.items_unassigned.WEAPON,item,'id') - end - equipment.update.weapon = true - unit.military.pickup_flags.update = true - end - end -end - -local count = 0 - -function adjust_uniform_spec(unit,position,spec,force) - if not unit then return end - local best - if spec.indiv_choice.melee then - best = find_best_weapon(unit, 'melee') - elseif spec.indiv_choice.ranged then - best = find_best_weapon(unit, 'ranged') - elseif spec.indiv_choice.any or force then - best = find_best_weapon(unit, nil) - end - if best then - count = count + 1 - spec.item_filter.item_subtype = best - spec.indiv_choice.any = false - spec.indiv_choice.melee = false - spec.indiv_choice.ranged = false - unassign_wrong_items(unit, position, spec, best) - end -end - -function adjust_position(unit,position,force) - if not unit then - local fig = df.historical_figure.find(position.occupant) - if not fig then return end - unit = df.unit.find(fig.unit_id) - end - - for _,v in ipairs(position.uniform.weapon) do - adjust_uniform_spec(unit, position, v, force) - end -end - -function adjust_squad(squad, force) - for _,pos in ipairs(squad.positions) do - adjust_position(nil, pos, force) - end -end - -local args = {...} -local vs = dfhack.gui.getCurViewscreen() -local vstype = df.viewscreen_layer_militaryst -if not vstype:is_instance(vs) then - qerror('Call this from the military screen') -end - -if vs.page == vstype.T_page.Equip -and vs.equip.mode == vstype.T_equip.T_mode.Customize then - local slist = vs.layer_objects[0] - local squad = vs.equip.squads[slist:getListCursor()] - - if slist.active then - print('Adjusting squad.') - adjust_squad(squad) - else - local plist = vs.layer_objects[1] - local pidx = plist:getListCursor() - local pos = squad.positions[pidx] - local unit = vs.equip.units[pidx] - - if plist.active then - print('Adjusting position.') - adjust_position(unit, pos) - elseif unit and vs.equip.edit_mode < 0 then - local wlist = vs.layer_objects[2] - local idx = wlist:getListCursor() - local cat = vs.equip.assigned.category[idx] - - if wlist.active and cat == df.uniform_category.weapon then - print('Adjusting spec.') - adjust_uniform_spec(unit, pos, vs.equip.assigned.spec[idx], true) - end - end - end -else - qerror('Call this from the Equip page of military screen') -end - -if count > 1 then - dlg.showMessage( - 'Choose Weapons', - 'Updated '..count..' uniform entries.', COLOR_GREEN - ) -elseif count == 0 then - dlg.showMessage( - 'Choose Weapons', - 'Did not find any entries to update.', COLOR_YELLOW - ) -end diff --git a/scripts/gui/clone-uniform.lua b/scripts/gui/clone-uniform.lua deleted file mode 100644 index e6ae2fa8a..000000000 --- a/scripts/gui/clone-uniform.lua +++ /dev/null @@ -1,58 +0,0 @@ --- Clone a uniform template in the military screen ---[[=begin - -gui/clone-uniform -================= -Bind to a key (the example config uses :kbd:`Ctrl`:kbd:`C`), and activate in the Uniforms -page of the military screen with the cursor in the leftmost list. - -When invoked, the script duplicates the currently selected uniform template, -and selects the newly created copy. - -=end]] -local utils = require 'utils' -local gui = require 'gui' - -local entity = df.global.ui.main.fortress_entity - -local args = {...} -local vs = dfhack.gui.getCurViewscreen() -local vstype = df.viewscreen_layer_militaryst -if not vstype:is_instance(vs) then - qerror('Call this from the military screen') -end - -local slist = vs.layer_objects[0] - -if vs.page == vstype.T_page.Uniforms -and slist.active and slist.num_entries > 0 -and not vs.equip.in_name_uniform -then - local idx = slist.num_entries - - if #vs.equip.uniforms ~= idx or #entity.uniforms ~= idx then - error('Uniform vector length mismatch') - end - - local uniform = vs.equip.uniforms[slist:getListCursor()] - - local ucopy = uniform:new() - ucopy.id = entity.next_uniform_id - ucopy.name = ucopy.name..'(Copy)' - - for k,v in ipairs(ucopy.uniform_item_info) do - for k2,v2 in ipairs(v) do - v[k2] = v2:new() - end - end - - entity.next_uniform_id = entity.next_uniform_id + 1 - entity.uniforms:insert('#',ucopy) - vs.equip.uniforms:insert('#',ucopy) - - slist.num_entries = idx+1 - slist.cursor = idx-1 - gui.simulateInput(vs, 'STANDARDSCROLL_DOWN') -else - qerror('Call this with a uniform selected on the Uniforms page of military screen') -end diff --git a/scripts/gui/companion-order.lua b/scripts/gui/companion-order.lua deleted file mode 100644 index 60a141d0f..000000000 --- a/scripts/gui/companion-order.lua +++ /dev/null @@ -1,490 +0,0 @@ --- Issue orders to companions in Adventure mode ---[[=begin - -gui/companion-order -=================== -A script to issue orders for companions. Select companions with lower case chars, issue orders with upper -case. Must be in look or talk mode to issue command on tile. - -.. image:: /docs/images/companion-order.png - -* move - orders selected companions to move to location. If companions are following they will move no more than 3 tiles from you. -* equip - try to equip items on the ground. -* pick-up - try to take items into hand (also wield) -* unequip - remove and drop equipment -* unwield - drop held items -* wait - temporarily remove from party -* follow - rejoin the party after "wait" -* leave - remove from party (can be rejoined by talking) - -=end]] - -local gui = require 'gui' -local dlg = require 'gui.dialogs' -local args={...} -local is_cheat=(#args>0 and args[1]=="-c") -local cursor=xyz2pos(df.global.cursor.x,df.global.cursor.y,df.global.cursor.z) -local permited_equips={} - -permited_equips[df.item_backpackst]="UPPERBODY" -permited_equips[df.item_quiverst]="UPPERBODY" -permited_equips[df.item_flaskst]="UPPERBODY" -permited_equips[df.item_armorst]="UPPERBODY" -permited_equips[df.item_shoesst]="STANCE" -permited_equips[df.item_glovesst]="GRASP" -permited_equips[df.item_helmst]="HEAD" -permited_equips[df.item_pantsst]="LOWERBODY" -function DoesHaveSubtype(item) - if df.item_backpackst:is_instance(item) or df.item_flaskst:is_instance(item) or df.item_quiverst:is_instance(item) then - return false - end - return true -end -function CheckCursor(p) - if p.x==-30000 then - dlg.showMessage( - 'Companion orders', - 'You must have a cursor on some tile!', COLOR_LIGHTRED - ) - return false - end - return true -end -function getxyz() -- this will return pointers x,y and z coordinates. - local x=df.global.cursor.x - local y=df.global.cursor.y - local z=df.global.cursor.z - return x,y,z -- return the coords -end - -function GetCaste(race_id,caste_id) - local race=df.creature_raw.find(race_id) - return race.caste[caste_id] -end - -function EnumBodyEquipable(race_id,caste_id) - local caste=GetCaste(race_id,caste_id) - local bps=caste.body_info.body_parts - local ret={} - for k,v in pairs(bps) do - ret[k]={bp=v,layers={[0]={size=0,permit=0},[1]={size=0,permit=0},[2]={size=0,permit=0},[3]={size=0,permit=0} } } - end - return ret -end -function ReadCurrentEquiped(body_equip,unit) - for k,v in pairs(unit.inventory) do - if v.mode==2 then - local bpid=v.body_part_id - if DoesHaveSubtype(v.item) then - local sb=v.item.subtype.props - local trg=body_equip[bpid] - local trg_layer=trg.layers[sb.layer] - - if trg_layer.permit==0 then - trg_layer.permit=sb.layer_permit - else - if trg_layer.permit>sb.layer_permit then - trg_layer.permit=sb.layer_permit - end - end - trg_layer.size=trg_layer.size+sb.layer_size - end - end - end -end -function LayeringPermits(body_part,item) - if not DoesHaveSubtype(item) then - return true - end - local sb=item.subtype.props - local trg_layer=body_part.layers[sb.layer] - if math.min(trg_layer.permit ,sb.layer_permit)0 and #items>0 do - if(dfhack.items.moveToInventory(items[#items],v,1,grasps[#grasps])) then - table.remove(grasps) - end - table.remove(items) - end - local backpack=GetBackpack(v) - if backpack then - while #items>0 do - dfhack.items.moveToContainer(items[#items],backpack) - table.remove(items) - end - end - end - return true -end}, -{name="unequip",f=function (unit_list) - --remove and drop all the stuff (todo maybe a gui too?) - for k,v in pairs(unit_list) do - while #v.inventory ~=0 do - dfhack.items.moveToGround(v.inventory[0].item,v.pos) - end - end - return true -end}, -{name="unwield",f=function (unit_list) - - for k,v in pairs(unit_list) do - local wep_count=0 - for _,it in pairs(v.inventory) do - if it.mode==1 then - wep_count=wep_count+1 - end - end - for i=1,wep_count do - for _,it in pairs(v.inventory) do - if it.mode==1 then - dfhack.items.moveToGround(it.item,v.pos) - break - end - end - end - end - return true -end}, ---[=[ -{name="roam not working :<",f=function (unit_list,pos,dist) --does not work - if not CheckCursor(pos) then - return false - end - dist=dist or 5 - for k,v in pairs(unit_list) do - v.idle_area:assign(pos) - v.idle_area_threshold=dist - end - return true -end}, ---]=] -{name="wait",f=function (unit_list) - for k,v in pairs(unit_list) do - v.relations.group_leader_id=-1 - end - return true -end}, -{name="follow",f=function (unit_list) - local adv=df.global.world.units.active[0] - for k,v in pairs(unit_list) do - v.relations.group_leader_id=adv.id - end - return true -end}, -{name="leave",f=function (unit_list) - local adv=df.global.world.units.active[0] - local t_nem=dfhack.units.getNemesis(adv) - for k,v in pairs(unit_list) do - - v.relations.group_leader_id=-1 - local u_nem=dfhack.units.getNemesis(v) - if u_nem then - u_nem.group_leader_id=-1 - end - if t_nem and u_nem then - for k,v in pairs(t_nem.companions) do - if v==u_nem.id then - t_nem.companions:erase(k) - break - end - end - end - end - return true -end}, - -} -local cheats={ -{name="Patch up",f=function (unit_list) - local dft=require("plugins.dfusion.tools") - for k,v in pairs(unit_list) do - dft.healunit(v) - end - return true -end}, -{name="Power up",f=function (unit_list) - local dft=require("plugins.dfusion.tools") - for k,d in pairs(unit_list) do - dft.powerup(d) - end - return true -end}, -{name="get in",f=function (unit_list,pos) - if not CheckCursor(pos) then - return false - end - adv=df.global.world.units.active[0] - item=getItemsAtPos(getxyz())[1] - print(item.id) - for k,v in pairs(unit_list) do - v.riding_item_id=item.id - local ref=df.general_ref_unit_riderst:new() - ref.unit_id=v.id - item.general_refs:insert("#",ref) - end - return true -end}, -} ---[[ todo: add cheats...]]-- -function getCompanions(unit) - unit=unit or df.global.world.units.active[0] - local t_nem=dfhack.units.getNemesis(unit) - if t_nem==nil then - qerror("Invalid unit! No nemesis record") - end - local ret={} - for k,v in pairs(t_nem.companions) do - local u=df.nemesis_record.find(v) - if u.unit then - table.insert(ret,u.unit) - end - end - return ret -end - - -CompanionUi=defclass(CompanionUi,gui.FramedScreen) -CompanionUi.ATTRS{ - frame_title = "Companions", -} -function CompanionUi:init(args) - self.unit_list=args.unit_list - self.selected={} - for i=0,26 do - self.selected[i]=true - end -end -function CompanionUi:GetSelectedUnits() - local ret={} - for k,v in pairs(self.unit_list) do - if self.selected[k] then - table.insert(ret,v) - end - end - return ret -end -function CompanionUi:onInput(keys) - - - if keys.LEAVESCREEN then - self:dismiss() - elseif keys._STRING then - local s=keys._STRING - if s==string.byte('*') then - local v=self.selected[1] or false - for i=0,26 do - - self.selected[i]=not v - end - end - if s>=string.byte('a') and s<=string.byte('z') then - local idx=s-string.byte('a')+1 - if self.selected[idx] then - self.selected[idx]=false - else - self.selected[idx]=true - end - end - if s>=string.byte('A') and s<=string.byte('Z') then - local idx=s-string.byte('A')+1 - if orders[idx] and orders[idx].f then - if orders[idx].f(self:GetSelectedUnits(),cursor) then - self:dismiss() - end - end - if is_cheat then - idx=idx-#orders - if cheats[idx] and cheats[idx].f then - if cheats[idx].f(self:GetSelectedUnits(),cursor) then - self:dismiss() - end - end - end - end - end -end -function CompanionUi:onRenderBody( dc) - --list widget goes here... - local char_a=string.byte('a')-1 - dc:newline(1):string("*. All") - for k,v in ipairs(self.unit_list) do - if self.selected[k] then - dc:pen(COLOR_GREEN) - else - dc:pen(COLOR_GREY) - end - dc:newline(1):string(string.char(k+char_a)..". "):string(dfhack.TranslateName(v.name)); - end - dc:pen(COLOR_GREY) - local w,h=self:getWindowSize() - local char_A=string.byte('A')-1 - for k,v in ipairs(orders) do - dc:seek(w/2,k):string(string.char(k+char_A)..". "):string(v.name); - end - if is_cheat then - for k,v in ipairs(cheats) do - dc:seek(w/2,k+#orders):string(string.char(k+#orders+char_A)..". "):string(v.name); - end - end -end -local screen=CompanionUi{unit_list=getCompanions()} -screen:show() \ No newline at end of file diff --git a/scripts/gui/confirm-opts.lua b/scripts/gui/confirm-opts.lua deleted file mode 100644 index d2aba2a94..000000000 --- a/scripts/gui/confirm-opts.lua +++ /dev/null @@ -1,74 +0,0 @@ --- confirm plugin options ---[[=begin - -gui/confirm-opts -================ -A basic configuration interface for the `confirm` plugin. - -=end]] - - -confirm = require 'plugins.confirm' -gui = require 'gui' - -Opts = defclass(Opts, gui.FramedScreen) -Opts.ATTRS = { - frame_style = gui.GREY_LINE_FRAME, - frame_title = 'Confirmation dialogs', - frame_width = 32, - frame_height = 20, - frame_inset = 1, - focus_path = 'confirm/opts', -} - -function Opts:init() - self:refresh() - self.cursor = 1 - local active_id = confirm.get_active_id() - for i, c in pairs(self.data) do - if c.id == active_id then - self.cursor = i - break - end - end -end - -function Opts:refresh() - self.data = confirm.get_conf_data() - self.frame_height = #self.data -end - -function Opts:onRenderBody(p) - for i, c in pairs(self.data) do - local highlight = (i == self.cursor and 8 or 0) - p:pen(COLOR_GREY + highlight) - p:string(c.id .. ': ') - p:pen((c.enabled and COLOR_GREEN or COLOR_RED) + highlight) - p:string(c.enabled and 'Enabled' or 'Disabled') - p:newline() - end -end - -function Opts:onInput(keys) - local conf = self.data[self.cursor] - if keys.LEAVESCREEN then - self:dismiss() - elseif keys.SELECT then - confirm.set_conf_state(conf.id, not conf.enabled) - self:refresh() - elseif keys.SEC_SELECT then - for _, c in pairs(self.data) do - confirm.set_conf_state(c.id, not conf.enabled) - end - self:refresh() - elseif keys.STANDARDSCROLL_UP or keys.STANDARDSCROLL_DOWN then - self.cursor = self.cursor + (keys.STANDARDSCROLL_UP and -1 or 1) - if self.cursor < 1 then - self.cursor = #self.data - elseif self.cursor > #self.data then - self.cursor = 1 - end - end -end - -Opts():show() diff --git a/scripts/gui/create-item.lua b/scripts/gui/create-item.lua deleted file mode 100644 index a2c38247a..000000000 --- a/scripts/gui/create-item.lua +++ /dev/null @@ -1,260 +0,0 @@ --- create-item.lua --- A gui-based item creation script. --- author Putnam --- edited by expwnent - ---@module = true ---[[=begin - -gui/create-item -=============== -A graphical interface for creating items. - -=end]] -local function getGenderString(gender) - local genderStr - if gender==0 then - genderStr=string.char(12) - elseif gender==1 then - genderStr=string.char(11) - else - return "" - end - return string.char(40)..genderStr..string.char(41) -end - -local function getCreatureList() - local crList={} - for k,cr in ipairs(df.global.world.raws.creatures.alphabetic) do - for kk,ca in ipairs(cr.caste) do - local str=ca.caste_name[0] - str=str..' '..getGenderString(ca.gender) - table.insert(crList,{str,nil,ca}) - end - end - return crList -end - -local function getRestrictiveMatFilter(itemType) - if not args.restrictive then return nil end - local itemTypes={ - WEAPON=function(mat,parent,typ,idx) - return (mat.flags.ITEMS_WEAPON or mat.flags.ITEMS_WEAPON_RANGED) - end, - AMMO=function(mat,parent,typ,idx) - return (mat.flags.ITEMS_AMMO) - end, - ARMOR=function(mat,parent,typ,idx) - return (mat.flags.ITEMS_ARMOR) - end, - INSTRUMENT=function(mat,parent,typ,idx) - return (mat.flags.ITEMS_HARD) - end, - AMULET=function(mat,parent,typ,idx) - return (mat.flags.ITEMS_SOFT or mat.flags.ITEMS_HARD) - end, - ROCK=function(mat,parent,typ,idx) - return (mat.flags.IS_STONE) - end, - BOULDER=ROCK, - BAR=function(mat,parent,typ,idx) - return (mat.flags.IS_METAL or mat.flags.SOAP or mat.id==COAL) - end - - } - for k,v in ipairs({'GOBLET','FLASK','TOY','RING','CROWN','SCEPTER','FIGURINE','TOOL'}) do - itemTypes[v]=itemTypes.INSTRUMENT - end - for k,v in ipairs({'SHOES','SHIELD','HELM','GLOVES'}) do - itemTypes[v]=itemTypes.ARMOR - end - for k,v in ipairs({'EARRING','BRACELET'}) do - itemTypes[v]=itemTypes.AMULET - end - itemTypes.BOULDER=itemTypes.ROCK - return itemTypes[df.item_type[itemType]] -end - -local function getMatFilter(itemtype) - local itemTypes={ - SEEDS=function(mat,parent,typ,idx) - return mat.flags.SEED_MAT - end, - PLANT=function(mat,parent,typ,idx) - return mat.flags.STRUCTURAL_PLANT_MAT - end, - LEAVES=function(mat,parent,typ,idx) - return mat.flags.LEAF_MAT - end, - MEAT=function(mat,parent,typ,idx) - return mat.flags.MEAT - end, - CHEESE=function(mat,parent,typ,idx) - return (mat.flags.CHEESE_PLANT or mat.flags.CHEESE_CREATURE) - end, - LIQUID_MISC=function(mat,parent,typ,idx) - return (mat.flags.LIQUID_MISC_PLANT or mat.flags.LIQUID_MISC_CREATURE or mat.flags.LIQUID_MISC_OTHER) - end, - POWDER_MISC=function(mat,parent,typ,idx) - return (mat.flags.POWDER_MISC_PLANT or mat.flags.POWDER_MISC_CREATURE) - end, - DRINK=function(mat,parent,typ,idx) - return (mat.flags.ALCOHOL_PLANT or mat.flags.ALCOHOL_CREATURE) - end, - GLOB=function(mat,parent,typ,idx) - return (mat.flags.STOCKPILE_GLOB) - end, - WOOD=function(mat,parent,typ,idx) - return (mat.flags.WOOD) - end, - THREAD=function(mat,parent,typ,idx) - return (mat.flags.THREAD_PLANT) - end, - LEATHER=function(mat,parent,typ,idx) - return (mat.flags.LEATHER) - end - } - return itemTypes[df.item_type[itemtype]] or getRestrictiveMatFilter(itemtype) -end - -local function createItem(mat,itemType,quality,creator,description,amount) - local item=df.item.find(dfhack.items.createItem(itemType[1], itemType[2], mat[1], mat[2], creator)) - assert(item, 'failed to create item') - quality = math.max(0, math.min(5, quality - 1)) - item:setQuality(quality) - if df.item_type[itemType[1]]=='SLAB' then - item.description=description - end - if tonumber(amount) > 1 then - item:setStackSize(amount) - end -end - -local function qualityTable() - return {{'None'}, - {'-Well-crafted-'}, - {'+Finely-crafted+'}, - {'*Superior*'}, - {string.char(240)..'Exceptional'..string.char(240)}, - {string.char(15)..'Masterwork'..string.char(15)} - } -end - -local script=require('gui.script') - -local function showItemPrompt(text,item_filter,hide_none) - require('gui.materials').ItemTypeDialog{ - prompt=text, - item_filter=item_filter, - hide_none=hide_none, - on_select=script.mkresume(true), - on_cancel=script.mkresume(false), - on_close=script.qresume(nil) - }:show() - - return script.wait() -end - -local function showMaterialPrompt(title, prompt, filter, inorganic, creature, plant) --the one included with DFHack doesn't have a filter or the inorganic, creature, plant things available - require('gui.materials').MaterialDialog{ - frame_title = title, - prompt = prompt, - mat_filter = filter, - use_inorganic = inorganic, - use_creature = creature, - use_plant = plant, - on_select = script.mkresume(true), - on_cancel = script.mkresume(false), - on_close = script.qresume(nil) - }:show() - - return script.wait() -end - -local function usesCreature(itemtype) - typesThatUseCreatures={REMAINS=true,FISH=true,FISH_RAW=true,VERMIN=true,PET=true,EGG=true,CORPSE=true,CORPSEPIECE=true} - return typesThatUseCreatures[df.item_type[itemtype]] -end - -local function getCreatureRaceAndCaste(caste) - return df.global.world.raws.creatures.list_creature[caste.index],df.global.world.raws.creatures.list_caste[caste.index] -end - -function hackWish(unit) - script.start(function() - local amountok, amount - local matok,mattype,matindex,matFilter - local itemok,itemtype,itemsubtype=showItemPrompt('What item do you want?',function(itype) return df.item_type[itype]~='CORPSE' and df.item_type[itype]~='FOOD' end ,true) - if not itemok then return end - if not args.notRestrictive then - matFilter=getMatFilter(itemtype) - end - if not usesCreature(itemtype) then - matok,mattype,matindex=showMaterialPrompt('Wish','And what material should it be made of?',matFilter) - if not matok then return end - else - local creatureok,useless,creatureTable=script.showListPrompt('Wish','What creature should it be?',COLOR_LIGHTGREEN,getCreatureList()) - if not creatureok then return end - mattype,matindex=getCreatureRaceAndCaste(creatureTable[3]) - end - local qualityok,quality=script.showListPrompt('Wish','What quality should it be?',COLOR_LIGHTGREEN,qualityTable()) - if not qualityok then return end - local description - if df.item_type[itemtype]=='SLAB' then - local descriptionok - descriptionok,description=script.showInputPrompt('Slab','What should the slab say?',COLOR_WHITE) - if not descriptionok then return end - end - if args.multi then - repeat amountok,amount=script.showInputPrompt('Wish','How many do you want? (numbers only!)',COLOR_LIGHTGREEN) until tonumber(amount) or not amountok - if not amountok then return end - if mattype and itemtype then - if df.item_type.attrs[itemtype].is_stackable then - createItem({mattype,matindex},{itemtype,itemsubtype},quality,unit,description,amount) - else - for i=1,amount do - createItem({mattype,matindex},{itemtype,itemsubtype},quality,unit,description,1) - end - end - return true - end - return false - else - if mattype and itemtype then - createItem({mattype,matindex},{itemtype,itemsubtype},quality,unit,description,1) - return true - end - return false - end - end) -end - -scriptArgs={...} - -utils=require('utils') - -validArgs = validArgs or utils.invert({ - 'startup', - 'all', - 'restrictive', - 'unit', - 'multi' -}) - -args = utils.processArgs({...}, validArgs) - -eventful=require('plugins.eventful') - -if not args.startup then - local unit=args.unit and df.unit.find(args.unit) or dfhack.gui.getSelectedUnit(true) - if unit then - hackWish(unit) - else - qerror('A unit needs to be selected to use gui/create-item.') - end -else - eventful.onReactionComplete.hackWishP=function(reaction,unit,input_items,input_reagents,output_items,call_native) - if not reaction.code:find('DFHACK_WISH') then return nil end - hackWish(unit) - end -end diff --git a/scripts/gui/dfstatus.lua b/scripts/gui/dfstatus.lua deleted file mode 100644 index 9789affbc..000000000 --- a/scripts/gui/dfstatus.lua +++ /dev/null @@ -1,229 +0,0 @@ --- a quick access status screen --- originally written by enjia2000@gmail.com (stolencatkarma) - ---[[=begin - -gui/dfstatus -============ -Show a quick overview of critical stock quantities, including food, drinks, wood, and various bars. -Sections can be enabled/disabled/configured by editing ``dfhack-config/dfstatus.lua``. - -=end]] -local gui = require 'gui' - -function warn(msg) - dfhack.color(COLOR_LIGHTRED) - print(msg) - dfhack.color(nil) -end - -config = { - flags = { - drink = true, - wood = true, - fuel = true, - prepared_meals = true, - tanned_hides = true, - cloth = true, - metals = true, - }, - metal_ids = {}, -} - -function parse_config() - local metal_map = {} - for id, raw in pairs(df.global.world.raws.inorganics) do - if raw.material.flags.IS_METAL then - metal_map[raw.id:upper()] = id - metal_map[id] = raw.id:upper() - end - end - - local function add_metal(...) - for _, m in pairs({...}) do - id = metal_map[tostring(m):upper()] - if id ~= nil then - table.insert(config.metal_ids, id) - elseif m == '-' then - table.insert(config.metal_ids, '-') - else - warn('Invalid metal: ' .. tostring(m)) - end - end - return add_metal - end - - local env = {} - setmetatable(env, { - __index = function(_, k) - if k == 'metal' or k == 'metals' then - return add_metal - elseif k == 'flags' then - return config.flags - else - error('unknown name: ' .. k, 2) - end - end, - __newindex = function(_, k, v) - if config.flags[k] ~= nil then - if v ~= nil then - config.flags[k] = v - else - config.flags[k] = false - end - else - error('unknown flag: ' .. k, 2) - end - end, - }) - local f, err = loadfile('dfhack-config/dfstatus.lua', 't', env) - if not f then - qerror('error loading config: ' .. err) - end - local ok, err = pcall(f) - if not ok then - qerror('error parsing config: ' .. err) - end -end - -function getInorganicName(id) - return (df.inorganic_raw.find(id).material.state_name.Solid:gsub('^[a-z]', string.upper)) -end - -dfstatus = defclass(dfstatus, gui.FramedScreen) -dfstatus.ATTRS = { - frame_style = gui.GREY_LINE_FRAME, - frame_title = 'dfstatus', - frame_width = 16, - frame_height = 17, - frame_inset = 1, - focus_path = 'dfstatus', -} - -function dfstatus:init() - self.text = {} - self.start = 1 - local function write(line) - table.insert(self.text, line) - -- ensure that the window is wide enough for this line plus a scroll arrow - if #line + 1 > self.frame_width then - self.frame_width = #line + 1 - end - end - local function newline() write('') end - local f = config.flags - - local drink = 0 - local wood = 0 - local fuel = 0 - - local prepared_meals = 0 - local tanned_hides = 0 - local cloth = 0 - - local metals = {} - for _, id in pairs(config.metal_ids) do - metals[id] = 0 - end - - for _, item in ipairs(df.global.world.items.all) do - if not item.flags.rotten and not item.flags.dump and not item.flags.forbid then - if item:getType() == df.item_type.WOOD then - wood = wood + item:getStackSize() - elseif item:getType() == df.item_type.DRINK then - drink = drink + item:getStackSize() - elseif item:getType() == df.item_type.SKIN_TANNED then - tanned_hides = tanned_hides + item:getStackSize() - elseif item:getType() == df.item_type.CLOTH then - cloth = cloth + item:getStackSize() - elseif item:getType() == df.item_type.FOOD then - prepared_meals = prepared_meals + item:getStackSize() - elseif item:getType() == df.item_type.BAR then - if item:getMaterial() == df.builtin_mats.COAL then - fuel = fuel + item:getStackSize() - elseif item:getMaterial() == df.builtin_mats.INORGANIC then - local mat_idx = item:getMaterialIndex() - if metals[mat_idx] ~= nil then - metals[mat_idx] = metals[mat_idx] + item:getStackSize() - end - end - end - end - end - if f.drink then - write("Drinks: " .. drink) - end - if f.prepared_meals then - write("Meals: " .. prepared_meals) - end - if f.drink or f.prepared_meals then - newline() - end - if f.wood then - write("Wood: " .. wood) - end - if f.fuel then - write("Fuel: " .. fuel) - end - if f.wood or f.fuel then - newline() - end - if f.tanned_hides then - write("Hides: " .. tanned_hides) - end - if f.cloth then - write("Cloth: " .. cloth) - end - if f.tanned_hides or f.cloth then - newline() - end - if f.metals then - write("Metal bars:") - for _, id in pairs(config.metal_ids) do - if id == '-' then - newline() - else - write(' ' .. ('%-10s'):format(getInorganicName(id) .. ': ') .. metals[id]) - end - end - end - self.start_min = 1 - self.start_max = #self.text - self.frame_height + 1 -end - -function dfstatus:onRenderBody(dc) - dc:pen(COLOR_LIGHTGREEN) - for id, line in pairs(self.text) do - if id >= self.start then - dc:string(line):newline() - end - end - dc:pen(COLOR_LIGHTCYAN) - if self.start > self.start_min then - dc:seek(self.frame_width - 1, 0):char(24) - end - if self.start < self.start_max then - dc:seek(self.frame_width - 1, self.frame_height - 1):char(25) - end -end - -function dfstatus:onInput(keys) - if keys.LEAVESCREEN or keys.SELECT then - self:dismiss() - scr = nil - elseif keys.STANDARDSCROLL_UP then - self.start = math.max(self.start - 1, self.start_min) - elseif keys.STANDARDSCROLL_DOWN then - self.start = math.min(self.start + 1, self.start_max) - end -end - -if not scr then - parse_config() - scr = dfstatus() - scr:show() -else - scr:dismiss() - scr = nil -end - diff --git a/scripts/gui/family-affairs.lua b/scripts/gui/family-affairs.lua deleted file mode 100644 index e3806e9f2..000000000 --- a/scripts/gui/family-affairs.lua +++ /dev/null @@ -1,292 +0,0 @@ --- gui/family-affairs --- derived from v1.2 @ http://www.bay12forums.com/smf/index.php?topic=147779 -local help = [[=begin - -gui/family-affairs -================== -A user-friendly interface to view romantic relationships, -with the ability to add, remove, or otherwise change them at -your whim - fantastic for depressed dwarves with a dead spouse -(or matchmaking players...). - -The target/s must be alive, sane, and in fortress mode. - -.. image:: /docs/images/family-affairs.png - :align: center - -``gui/family-affairs [unitID]`` - shows GUI for the selected unit, or the specified unit ID - -``gui/family-affairs divorce [unitID]`` - removes all spouse and lover information from the unit - and it's partner, bypassing almost all checks. - -``gui/family-affairs [unitID] [unitID]`` - divorces the two specificed units and their partners, - then arranges for the two units to marry, bypassing - almost all checks. Use with caution. - -=end]] - -helpstr = help:gsub('=begin', ''):gsub('=end', '') - -local dlg = require ('gui.dialogs') - -function ErrorPopup (msg,color) - if not tostring(msg) then msg = "Error" end - if not color then color = COLOR_LIGHTRED end - dlg.showMessage("Dwarven Family Affairs", msg, color, nil) -end - -function AnnounceAndGamelog(text) - dfhack.gui.showAnnouncement(text, COLOR_LIGHTMAGENTA) -end - -function ListPrompt (msg, choicelist, bool, yes_func) -dlg.showListPrompt( - "Dwarven Family Affairs", - msg, - COLOR_WHITE, - choicelist, - --called if choice is yes - yes_func, - --called on cancel - function() end, - 15, - bool - ) -end - -function GetMarriageSummary (source) - local familystate = "" - - if source.relations.spouse_id ~= -1 then - if dfhack.units.isSane(df.unit.find(source.relations.spouse_id)) then - familystate = dfhack.TranslateName(source.name).." has a spouse ("..dfhack.TranslateName(df.unit.find(source.relations.spouse_id).name)..")" - end - if dfhack.units.isSane(df.unit.find(source.relations.spouse_id)) == false then - familystate = dfhack.TranslateName(source.name).."'s spouse is dead or not sane, would you like to choose a new one?" - end - end - - if source.relations.spouse_id == -1 and source.relations.lover_id ~= -1 then - if dfhack.units.isSane(df.unit.find(source.relations.lover_id)) then - familystate = dfhack.TranslateName(source.name).." already has a lover ("..dfhack.TranslateName(df.unit.find(source.relations.spouse_id).name)..")" - end - if dfhack.units.isSane(df.unit.find(source.relations.lover_id)) == false then - familystate = dfhack.TranslateName(source.name).."'s lover is dead or not sane, would you like that love forgotten?" - end - end - - if source.relations.spouse_id == -1 and source.relations.lover_id == -1 then - familystate = dfhack.TranslateName(source.name).." is not involved in romantic relationships with anyone" - end - - if source.relations.pregnancy_timer > 0 then - familystate = familystate.."\nShe is pregnant." - local father = df.historical_figure.find(source.relations.pregnancy_spouse) - if father then - familystate = familystate.." The father is "..dfhack.TranslateName(father.name).."." - end - end - - return familystate -end - -function GetSpouseData (source) - local spouse = df.unit.find(source.relations.spouse_id) - local spouse_hf - if spouse then - spouse_hf = df.historical_figure.find (spouse.hist_figure_id) - end - return spouse,spouse_hf -end - -function GetLoverData (source) - local lover = df.unit.find(source.relations.spouse_id) - local lover_hf - if lover then - lover_hf = df.historical_figure.find (lover.hist_figure_id) - end - return lover,lover_hf -end - -function EraseHFLinksLoverSpouse (hf) - for i = #hf.histfig_links-1,0,-1 do - if hf.histfig_links[i]._type == df.histfig_hf_link_spousest or hf.histfig_links[i]._type == df.histfig_hf_link_loverst then - local todelete = hf.histfig_links[i] - hf.histfig_links:erase(i) - todelete:delete() - end - end -end - -function Divorce (source) - local source_hf = df.historical_figure.find(source.hist_figure_id) - local spouse,spouse_hf = GetSpouseData (source) - local lover,lover_hf = GetLoverData (source) - - source.relations.spouse_id = -1 - source.relations.lover_id = -1 - - if source_hf then - EraseHFLinksLoverSpouse (source_hf) - end - if spouse then - spouse.relations.spouse_id = -1 - spouse.relations.lover_id = -1 - end - if lover then - spouse.relations.spouse_id = -1 - spouse.relations.lover_id = -1 - end - if spouse_hf then - EraseHFLinksLoverSpouse (spouse_hf) - end - if lover_hf then - EraseHFLinksLoverSpouse (lover_hf) - end - - local partner = spouse or lover - if not partner then - AnnounceAndGamelog(dfhack.TranslateName(source.name).." is now single") - else - AnnounceAndGamelog(dfhack.TranslateName(source.name).." and "..dfhack.TranslateName(partner.name).." are now single") - end -end - -function Marriage (source,target) - local source_hf = df.historical_figure.find(source.hist_figure_id) - local target_hf = df.historical_figure.find(target.hist_figure_id) - source.relations.spouse_id = target.id - target.relations.spouse_id = source.id - - local new_link = df.histfig_hf_link_spousest:new() -- adding hf link to source - new_link.target_hf = target_hf.id - new_link.link_strength = 100 - source_hf.histfig_links:insert('#',new_link) - - new_link = df.histfig_hf_link_spousest:new() -- adding hf link to target - new_link.target_hf = source_hf.id - new_link.link_strength = 100 - target_hf.histfig_links:insert('#',new_link) -end - -function ChooseNewSpouse (source) - - if not source then - qerror("no unit") return - end - if not dfhack.units.isAdult(source) then - ErrorPopup("target is too young") return - end - if not (source.relations.spouse_id == -1 and source.relations.lover_id == -1) then - ErrorPopup("target already has a spouse or a lover") - qerror("source already has a spouse or a lover") - return - end - - local choicelist = {} - targetlist = {} - - for k,v in pairs (df.global.world.units.active) do - if dfhack.units.isCitizen(v) - and v.race == source.race - and v.sex ~= source.sex - and v.relations.spouse_id == -1 - and v.relations.lover_id == -1 - and dfhack.units.isAdult(v) - then - table.insert(choicelist,dfhack.TranslateName(v.name)..', '..dfhack.units.getProfessionName(v)) - table.insert(targetlist,v) - end - end - - if #choicelist > 0 then - ListPrompt( - "Assign new spouse for "..dfhack.TranslateName(source.name), - choicelist, - true, - function(a,b) - local target = targetlist[a] - Marriage (source,target) - AnnounceAndGamelog(dfhack.TranslateName(source.name).." and "..dfhack.TranslateName(target.name).." have married!") - end) - else - ErrorPopup("No suitable candidates") - end -end - -function MainDialog (source) - - local familystate = GetMarriageSummary(source) - - familystate = familystate.."\nSelect action:" - local choicelist = {} - local on_select = {} - - local adult = dfhack.units.isAdult(source) - local single = source.relations.spouse_id == -1 and source.relations.lover_id == -1 - local ready_for_marriage = single and adult - - if adult then - table.insert(choicelist,"Remove romantic relationships (if any)") - table.insert(on_select, Divorce) - if ready_for_marriage then - table.insert(choicelist,"Assign a new spouse") - table.insert(on_select,ChooseNewSpouse) - end - if not ready_for_marriage then - table.insert(choicelist,"[Assign a new spouse]") - table.insert(on_select,function () ErrorPopup ("Existing relationships must be removed if you wish to assign a new spouse.") end) - end - else - table.insert(choicelist,"Leave this child alone") - table.insert(on_select,nil) - end - - ListPrompt(familystate, choicelist, false, - function(a,b) if on_select[a] then on_select[a](source) end end) -end - - -local args = {...} - -if args[1] == "help" or args[1] == "?" then print(helpstr) return end - -if not dfhack.world.isFortressMode() then - print (helpstr) qerror ("invalid game mode") return -end - -if args[1] == "divorce" and tonumber(args[2]) then - local unit = df.unit.find(args[2]) - if unit then Divorce (unit) return end -end - -if tonumber(args[1]) and tonumber(args[2]) then - local unit1 = df.unit.find(args[1]) - local unit2 = df.unit.find(args[2]) - if unit1 and unit2 then - Divorce (unit1) - Divorce (unit2) - Marriage (unit1,unit2) - return - end -end - -local selected = dfhack.gui.getSelectedUnit(true) -if tonumber(args[1]) then - selected = df.unit.find(tonumber(args[1])) or selected -end - -if selected then - if dfhack.units.isCitizen(selected) and dfhack.units.isSane(selected) then - MainDialog(selected) - else - qerror("You must select a sane fortress citizen.") - return - end -else - print (helpstr) - qerror("Select a sane fortress dwarf") -end diff --git a/scripts/gui/gm-editor.lua b/scripts/gui/gm-editor.lua deleted file mode 100644 index 38394ec36..000000000 --- a/scripts/gui/gm-editor.lua +++ /dev/null @@ -1,536 +0,0 @@ --- Interface powered item editor. - ---[[=begin - -gui/gm-editor -============= -This editor allows to change and modify almost anything in df. Press :kbd:`?` for -in-game help. There are three ways to open this editor: - -* Callling ``gui/gm-editor`` from a command or keybinding opens the editor - on whatever is selected or viewed (e.g. unit/item description screen) - -* using gui/gm-editor - executes lua command and opens editor on - its results (e.g. ``gui/gm-editor "df.global.world.items.all"`` shows all items) - -* using gui/gm-editor dialog - shows an in game dialog to input lua command. Works - the same as version above. - -.. image:: /docs/images/gm-editor.png - -=end]] -local gui = require 'gui' -local dialog = require 'gui.dialogs' -local widgets =require 'gui.widgets' -local guiScript = require 'gui.script' -local args={...} - -find_funcs = find_funcs or (function() - local t = {} - for k in pairs(df) do - pcall(function() - t[k] = df[k].find - end) - end - return t -end)() - -local keybindings={ - offset={key="CUSTOM_ALT_O",desc="Show current items offset"}, - find={key="CUSTOM_F",desc="Find a value by entering a predicate"}, - find_id={key="CUSTOM_I",desc="Find object with this ID"}, - lua_set={key="CUSTOM_ALT_S",desc="Set by using a lua function"}, - insert={key="CUSTOM_ALT_I",desc="Insert a new value to the vector"}, - delete={key="CUSTOM_ALT_D",desc="Delete selected entry"}, - reinterpret={key="CUSTOM_ALT_R",desc="Open selected entry as something else"}, - start_filter={key="CUSTOM_S",desc="Start typing filter, Enter to finish"}, - help={key="HELP",desc="Show this help"}, - displace={key="STRING_A093",desc="Open reference offseted by index"}, - NOT_USED={key="SEC_SELECT",desc="Edit selected entry as a number (for enums)"}, --not a binding... -} -function getTargetFromScreens() - local my_trg - if dfhack.gui.getCurFocus() == 'item' then - my_trg=dfhack.gui.getCurViewscreen().item - elseif dfhack.gui.getCurFocus() == 'joblist' then - local t_screen=dfhack.gui.getCurViewscreen() - my_trg=t_screen.jobs[t_screen.cursor_pos] - elseif dfhack.gui.getCurFocus() == 'createquota' then - local t_screen=dfhack.gui.getCurViewscreen() - my_trg=t_screen.orders[t_screen.sel_idx] - elseif dfhack.gui.getCurFocus() == 'dwarfmode/LookAround/Flow' then - local t_look=df.global.ui_look_list.items[df.global.ui_look_cursor] - my_trg=t_look.flow - - elseif dfhack.gui.getSelectedUnit(true) then - my_trg=dfhack.gui.getSelectedUnit(true) - elseif dfhack.gui.getSelectedItem(true) then - my_trg=dfhack.gui.getSelectedItem(true) - elseif dfhack.gui.getSelectedJob(true) then - my_trg=dfhack.gui.getSelectedJob(true) - else - qerror("No valid target found") - end - return my_trg -end -function search_relevance(search, candidate) - local function clean(str) - return ' ' .. str:lower():gsub('[^a-z0-9]','') .. ' ' - end - search = clean(search) - candidate = clean(candidate) - local ret = 0 - while #search > 0 do - local pos = candidate:find(search:sub(1, 1), 1, true) - if pos then - ret = ret + (#search - pos) - candidate = candidate:sub(pos + 1) - end - search = search:sub(2) - end - return ret -end - - -GmEditorUi = defclass(GmEditorUi, gui.FramedScreen) -GmEditorUi.ATTRS={ - frame_style = gui.GREY_LINE_FRAME, - frame_title = "GameMaster's editor", - } -function GmEditorUi:onHelp() - self.subviews.pages:setSelected(2) -end -function burning_red(input) -- todo does not work! bug angavrilov that so that he would add this, very important!! - local col=COLOR_LIGHTRED - return {text=input,pen=dfhack.pen.parse{fg=COLOR_LIGHTRED,bg=0}} -end -function Disclaimer(tlb) - local dsc={"Association Of ",{text="Psychic ",pen=dfhack.pen.parse{fg=COLOR_YELLOW,bg=0}}, - "Dwarves (AOPD) is not responsible for all the damage",NEWLINE,"that this tool can (and will) cause to you and your loved dwarves",NEWLINE,"and/or saves.Please use with caution.",NEWLINE,{text="Magma not included.",pen=dfhack.pen.parse{fg=COLOR_LIGHTRED,bg=0}}} - if tlb then - for _,v in ipairs(dsc) do - table.insert(tlb,v) - end - end - return dsc -end - -function GmEditorUi:init(args) - self.stack={} - self.item_count=0 - self.keys={} - local helptext={{text="Help"},NEWLINE,NEWLINE} - for k,v in pairs(keybindings) do - table.insert(helptext,{text=v.desc,key=v.key,key_sep=': '}) - table.insert(helptext,NEWLINE) - end - table.insert(helptext,NEWLINE) - Disclaimer(helptext) - - local helpPage=widgets.Panel{ - subviews={widgets.Label{text=helptext,frame = {l=1,t=1,yalign=0}}}} - local mainList=widgets.List{view_id="list_main",choices={},frame = {l=1,t=3,yalign=0},on_submit=self:callback("editSelected"), - on_submit2=self:callback("editSelectedRaw"), - text_pen=dfhack.pen.parse{fg=COLOR_DARKGRAY,bg=0},cursor_pen=dfhack.pen.parse{fg=COLOR_YELLOW,bg=0}} - local mainPage=widgets.Panel{ - subviews={ - mainList, - widgets.Label{text={{text="",id="name"},{gap=1,text="Help",key=keybindings.help.key,key_sep = '()'}}, view_id = 'lbl_current_item',frame = {l=1,t=1,yalign=0}}, - widgets.Label{text={{text="Search",key=keybindings.start_filter.key,key_sep = '()'},{text=": "}},frame={l=1,t=2}, - on_click=function() self:enable_input(true) end}, - widgets.EditField{frame={l=12,t=2},active=false,on_change=self:callback('text_input'),on_submit=self:callback("enable_input",false),view_id="filter_input"}, - --widgets.Label{text="BLAH2"} - } - ,view_id='page_main'} - - local pages=widgets.Pages{subviews={mainPage,helpPage},view_id="pages"} - self:addviews{ - pages - } - self:pushTarget(args.target) -end -function GmEditorUi:text_input(new_text) - self:updateTarget(true,true) -end -function GmEditorUi:enable_input(enable) - self.subviews.filter_input.active=enable -end -function GmEditorUi:find(test) - local trg=self:currentTarget() - - if test== nil then - dialog.showInputPrompt("Test function","Input function that tests(k,v as argument):",COLOR_WHITE,"",dfhack.curry(self.find,self)) - return - end - - local e,what=load("return function(k,v) return "..test.." end") - if e==nil then - dialog.showMessage("Error!","function failed to compile\n"..what,COLOR_LIGHTRED) - end - - if trg.target and trg.target._kind and trg.target._kind=="container" then - - for k,v in pairs(trg.target) do - if e()(k,v)==true then - self:pushTarget(v) - return - end - end - else - local i=1 - for k,v in pairs(trg.target) do - if e()(k,v)==true then - self.subviews.list_main:setSelected(i) - return - end - i=i+1 - end - end -end -function GmEditorUi:find_id() - local key = tostring(self:getSelectedKey()) - local id = tonumber(self:getSelectedValue()) - if not id then return end - local opts = {} - for name, func in pairs(find_funcs) do - table.insert(opts, {text=name, callback=func, weight=search_relevance(key, name)}) - end - table.sort(opts, function(a, b) - return a.weight > b.weight - end) - guiScript.start(function() - local ret,idx,choice=guiScript.showListPrompt("Choose type:",nil,3,opts,nil,true) - if ret then - local obj = choice.callback(id) - if obj then - self:pushTarget(obj) - else - dialog.showMessage("Error!", ('%s with ID %d not found'):format(choice.text, id), COLOR_LIGHTRED) - end - end - end) -end -function GmEditorUi:insertNew(typename) - local tp=typename - if typename == nil then - dialog.showInputPrompt("Class type","You can:\n * Enter type name (without 'df.')\n * Leave empty for default type and 'nil' value\n * Enter '*' for default type and 'new' constructed pointer value",COLOR_WHITE,"",self:callback("insertNew")) - return - end - - local trg=self:currentTarget() - if trg.target and trg.target._kind and trg.target._kind=="container" then - if tp == "" then - trg.target:resize(#trg.target+1) - elseif tp== "*" then - trg.target:insert("#",{new=true}) - else - local ntype=df[tp] - if ntype== nil then - dialog.showMessage("Error!","Type '"..tp.." not found",COLOR_RED) - return - end - trg.target:insert("#",{new=ntype}) - end - self:updateTarget(true,true) - end -end -function GmEditorUi:deleteSelected(key) - local trg=self:currentTarget() - if trg.target and trg.target._kind and trg.target._kind=="container" then - trg.target:erase(key) - self:updateTarget(true,true) - end -end -function GmEditorUi:getSelectedKey() - return self:currentTarget().keys[self.subviews.list_main:getSelected()] -end -function GmEditorUi:getSelectedValue() - return self:currentTarget().target[self:getSelectedKey()] -end -function GmEditorUi:currentTarget() - return self.stack[#self.stack] -end -function GmEditorUi:getSelectedEnumType() - local trg=self:currentTarget() - local trg_key=trg.keys[self.subviews.list_main:getSelected()] - - local ok,ret=pcall(function () --super safe way to check if the field has enum - return trg.target._field==nil or trg.target:_field(trg_key)==nil - end) - if not ok or ret==true then - return nil - end - - local enum=trg.target:_field(trg_key)._type - if enum._kind=="enum-type" then - return enum - else - return nil - end -end -function GmEditorUi:editSelectedEnum(index,choice) - local enum=self:getSelectedEnumType() - if enum then - local trg=self:currentTarget() - local trg_key=self:getSelectedKey() - local list={} - for i=enum._first_item, enum._last_item do - table.insert(list,{text=('%s (%i)'):format(tostring(enum[i]), i),value=i}) - end - guiScript.start(function() - local ret,idx,choice=guiScript.showListPrompt("Choose item:",nil,3,list,nil,true) - if ret then - trg.target[trg_key]=choice.value - self:updateTarget(true) - end - end) - - else - qerror("not an enum") - end -end -function GmEditorUi:openReinterpret(key) - local trg=self:currentTarget() - dialog.showInputPrompt(tostring(trg_key),"Enter new type:",COLOR_WHITE, - "",function(choice) - local ntype=df[tp] - self:pushTarget(df.reinterpret_cast(ntype,trg.target[key])) - end) -end -function GmEditorUi:openOffseted(index,choice) - local trg=self:currentTarget() - local trg_key=trg.keys[index] - - dialog.showInputPrompt(tostring(trg_key),"Enter offset:",COLOR_WHITE,"", - function(choice) - self:pushTarget(trg.target[trg_key]:_displace(tonumber(choice))) - end) -end -function GmEditorUi:editSelectedRaw(index,choice) - self:editSelected(index, choice, {raw=true}) -end -function GmEditorUi:editSelected(index,choice,opts) - opts = opts or {} - local trg=self:currentTarget() - local trg_key=trg.keys[index] - if trg.target and trg.target._kind and trg.target._kind=="bitfield" then - trg.target[trg_key]= not trg.target[trg_key] - self:updateTarget(true) - else - --print(type(trg.target[trg.keys[trg.selected]]),trg.target[trg.keys[trg.selected]]._kind or "") - local trg_type=type(trg.target[trg_key]) - if self:getSelectedEnumType() and not opts.raw then - self:editSelectedEnum() - elseif trg_type=='number' or trg_type=='string' then --ugly TODO: add metatable get selected - dialog.showInputPrompt(tostring(trg_key),"Enter new value:",COLOR_WHITE, - tostring(trg.target[trg_key]),self:callback("commitEdit",trg_key)) - - elseif trg_type == 'boolean' then - trg.target[trg_key] = not trg.target[trg_key] - self:updateTarget(true) - elseif trg_type == 'userdata' or trg_type == 'table' then - self:pushTarget(trg.target[trg_key]) - elseif trg_type == 'nil' or trg_type == 'function' then - -- ignore - else - print("Unknown type:"..trg_type) - pcall(function() print("Subtype:"..tostring(trg.target[trg_key]._kind)) end) - end - end -end - -function GmEditorUi:commitEdit(key,value) - local trg=self:currentTarget() - if type(trg.target[key])=='number' then - trg.target[key]=tonumber(value) - elseif type(trg.target[key])=='string' then - trg.target[key]=value - end - self:updateTarget(true) -end - -function GmEditorUi:set(key,input) - local trg=self:currentTarget() - - if input== nil then - dialog.showInputPrompt("Set to what?","Lua code to set to (v cur target):",COLOR_WHITE,"",self:callback("set",key)) - return - end - local e,what=load("return function(v) return "..input.." end") - if e==nil then - dialog.showMessage("Error!","function failed to compile\n"..what,COLOR_LIGHTRED) - return - end - trg.target[key]=e()(trg) - self:updateTarget(true) -end -function GmEditorUi:onInput(keys) - if keys.LEAVESCREEN then - if self.subviews.filter_input.active then - self:enable_input(false) - return - end - if self.subviews.pages:getSelected()==2 then - self.subviews.pages:setSelected(1) - else - self:popTarget() - end - end - - if self.subviews.filter_input.active then - self.super.onInput(self,keys) - return - end - - if keys[keybindings.offset.key] then - local trg=self:currentTarget() - local _,stoff=df.sizeof(trg.target) - local size,off=df.sizeof(trg.target:_field(self:getSelectedKey())) - dialog.showMessage("Offset",string.format("Size hex=%x,%x dec=%d,%d\nRelative hex=%x dec=%d",size,off,size,off,off-stoff,off-stoff),COLOR_WHITE) - elseif keys[keybindings.displace.key] then - self:openOffseted(self.subviews.list_main:getSelected()) - elseif keys[keybindings.find.key] then - self:find() - elseif keys[keybindings.find_id.key] then - self:find_id() - elseif keys[keybindings.lua_set.key] then - self:set(self:getSelectedKey()) - elseif keys[keybindings.insert.key] then --insert - self:insertNew() - elseif keys[keybindings.delete.key] then --delete - self:deleteSelected(self:getSelectedKey()) - elseif keys[keybindings.reinterpret.key] then - self:openReinterpret(self:getSelectedKey()) - elseif keys[keybindings.start_filter.key] then - self:enable_input(true) - return - end - - self.super.onInput(self,keys) -end -function getStringValue(trg,field) - local obj=trg.target - - local text=tostring(obj[field]) - pcall(function() - if obj._field ~= nil then - local enum=obj:_field(field)._type - if enum._kind=="enum-type" then - text=text.." ("..tostring(enum[obj[field]])..")" - end - end - end) - return text -end -function GmEditorUi:updateTarget(preserve_pos,reindex) - local trg=self:currentTarget() - local filter=self.subviews.filter_input.text - - if reindex then - trg.keys={} - for k,v in pairs(trg.target) do - if filter~= "" then - local ok,ret=dfhack.pcall(string.match,tostring(k),filter) - if not ok then - table.insert(trg.keys,k) - elseif ret then - table.insert(trg.keys,k) - end - else - table.insert(trg.keys,k) - end - end - end - self.subviews.lbl_current_item:itemById('name').text=tostring(trg.target) - local t={} - for k,v in pairs(trg.keys) do - table.insert(t,{text={{text=string.format("%-25s",tostring(v))},{gap=1,text=getStringValue(trg,v)}}}) - end - local last_pos - if preserve_pos then - last_pos=self.subviews.list_main:getSelected() - end - self.subviews.list_main:setChoices(t) - if last_pos then - self.subviews.list_main:setSelected(last_pos) - else - self.subviews.list_main:setSelected(trg.selected) - end -end -function GmEditorUi:pushTarget(target_to_push) - local new_tbl={} - new_tbl.target=target_to_push - new_tbl.keys={} - new_tbl.selected=1 - new_tbl.filter="" - if self:currentTarget()~=nil then - self:currentTarget().selected=self.subviews.list_main:getSelected() - self.stack[#self.stack].filter=self.subviews.filter_input.text - end - for k,v in pairs(target_to_push) do - table.insert(new_tbl.keys,k) - end - new_tbl.item_count=#new_tbl.keys - table.insert(self.stack,new_tbl) - self.subviews.filter_input.text="" - self:updateTarget() -end -function GmEditorUi:popTarget() - table.remove(self.stack) --removes last element - if #self.stack==0 then - self:dismiss() - return - end - self.subviews.filter_input.text=self.stack[#self.stack].filter --restore filter - self:updateTarget() -end -function show_editor(trg) - if not trg then - qerror('Target not found') - end - local screen = GmEditorUi{target=trg} - screen:show() -end -eval_env = {} -setmetatable(eval_env, {__index = function(_, k) - if k == 'scr' or k == 'screen' then - return dfhack.gui.getCurViewscreen() - elseif k == 'bld' or k == 'building' then - return dfhack.gui.getSelectedBuilding() - elseif k == 'item' then - return dfhack.gui.getSelectedItem() - elseif k == 'job' then - return dfhack.gui.getSelectedJob() - elseif k == 'wsjob' or k == 'workshop_job' then - return dfhack.gui.getSelectedWorkshopJob() - elseif k == 'unit' then - return dfhack.gui.getSelectedUnit() - else - for g in pairs(df.global) do - if g == k then - return df.global[k] - end - end - return _G[k] - end -end}) -function eval(s) - local f, err = load("return " .. s, "expression", "t", eval_env) - if err then qerror(err) end - return f() -end -if #args~=0 then - if args[1]=="dialog" then - function thunk(entry) - show_editor(eval(entry)) - end - dialog.showInputPrompt("Gm Editor", "Object to edit:", COLOR_GRAY, "",thunk) - elseif args[1]=="free" then - show_editor(df.reinterpret_cast(df[args[2]],args[3])) - else - show_editor(eval(args[1])) - end -else - show_editor(getTargetFromScreens()) -end - diff --git a/scripts/gui/gm-unit.lua b/scripts/gui/gm-unit.lua deleted file mode 100644 index 3432882cb..000000000 --- a/scripts/gui/gm-unit.lua +++ /dev/null @@ -1,604 +0,0 @@ --- Interface powered, user friendly, unit editor - ---[[=begin - -gui/gm-unit -=========== -An editor for various unit attributes. - -=end]] -local gui = require 'gui' -local dialog = require 'gui.dialogs' -local widgets =require 'gui.widgets' -local guiScript = require 'gui.script' -local utils = require 'utils' -local args={...} - - -local target ---TODO: add more ways to guess what unit you want to edit -if args[1]~= nil then - target=df.units.find(args[1]) -else - target=dfhack.gui.getSelectedUnit(true) -end - -if target==nil then - qerror("No unit to edit") --TODO: better error message -end -local editors={} -function add_editor(editor_class) - table.insert(editors,{text=editor_class.ATTRS.frame_title,on_submit=function ( unit ) - editor_class{target_unit=unit}:show() - end}) -end --------------------------------various subeditors--------- ---TODO set local sould or better yet skills vector to reduce long skill list access typing -editor_skills=defclass(editor_skills,gui.FramedScreen) -editor_skills.ATTRS={ - frame_style = gui.GREY_LINE_FRAME, - frame_title = "Skill editor", - target_unit = DEFAULT_NIL, - learned_only= false, -} -function list_skills(unit,learned_only) - local s_=df.job_skill - local u_skills=unit.status.current_soul.skills - local ret={} - for i,v in ipairs(s_) do - if i>0 then - local u_skill=utils.binsearch(u_skills,i,"id") - if u_skill or not learned_only then - if not u_skill then - u_skill={rating=-1,experience=0} - end - - local rating - if u_skill.rating >=0 then - rating=df.skill_rating.attrs[u_skill.rating] - else - rating={caption="",xp_threshold=0} - end - - local text=string.format("%s: %s %d %d/%d",df.job_skill.attrs[i].caption,rating.caption,u_skill.rating,u_skill.experience,rating.xp_threshold) - table.insert(ret,{text=text,id=i}) - end - end - end - return ret -end -function editor_skills:update_list(no_save_place) - local skill_list=list_skills(self.target_unit,self.learned_only) - if no_save_place then - self.subviews.skills:setChoices(skill_list) - else - self.subviews.skills:setChoices(skill_list,self.subviews.skills:getSelected()) - end -end -function editor_skills:init( args ) - if self.target_unit.status.current_soul==nil then - qerror("Unit does not have soul, can't edit skills") - end - - local skill_list=list_skills(self.target_unit,self.learned_only) - - self:addviews{ - widgets.FilteredList{ - choices=skill_list, - frame = {t=0, b=1,l=1}, - view_id="skills", - }, - widgets.Label{ - frame = { b=0,l=1}, - text ={{text= ": exit editor ", - key = "LEAVESCREEN", - on_activate= self:callback("dismiss") - }, - {text=": remove level ", - key = "SECONDSCROLL_UP", - on_activate=self:callback("level_skill",-1)}, - {text=": add level ", - key = "SECONDSCROLL_DOWN", - on_activate=self:callback("level_skill",1)} - , - {text=": show learned only ", - key = "CHANGETAB", - on_activate=function () - self.learned_only=not self.learned_only - self:update_list(true) - end} - } - }, - } -end -function editor_skills:get_cur_skill() - local list_wid=self.subviews.skills - local _,choice=list_wid:getSelected() - if choice==nil then - qerror("Nothing selected") - end - local u_skill=utils.binsearch(self.target_unit.status.current_soul.skills,choice.id,"id") - return choice,u_skill -end -function editor_skills:level_skill(lvl) - local sk_en,sk=self:get_cur_skill() - if lvl >0 then - local rating - - if sk then - rating=sk.rating+lvl - else - rating=lvl-1 - end - - utils.insert_or_update(self.target_unit.status.current_soul.skills, {new=true, id=sk_en.id, rating=rating}, 'id') --TODO set exp? - elseif sk and sk.rating==0 and lvl<0 then - utils.erase_sorted_key(self.target_unit.status.current_soul.skills,sk_en.id,"id") - elseif sk and lvl<0 then - utils.insert_or_update(self.target_unit.status.current_soul.skills, {new=true, id=sk_en.id, rating=sk.rating+lvl}, 'id') --TODO set exp? - end - self:update_list() -end -function editor_skills:remove_rust(skill) - --TODO -end -add_editor(editor_skills) -------- civ editor -RaceBox = defclass(RaceBox, dialog.ListBox) -RaceBox.focus_path = 'RaceBox' - -RaceBox.ATTRS{ - format_name="$NAME ($TOKEN)", - with_filter=true, - allow_none=false, -} -function RaceBox:format_creature(creature_raw) - local t = {NAME=creature_raw.name[0],TOKEN=creature_raw.creature_id} - return string.gsub(self.format_name, "%$(%w+)", t) -end -function RaceBox:preinit(info) - self.format_name=RaceBox.ATTRS.format_name or info.format_name -- preinit does not have ATTRS set yet - local choices={} - if RaceBox.ATTRS.allow_none or info.allow_none then - table.insert(choices,{text="",num=-1}) - end - for i,v in ipairs(df.global.world.raws.creatures.all) do - local text=self:format_creature(v) - table.insert(choices,{text=text,raw=v,num=i,search_key=text:lower()}) - end - info.choices=choices -end -function showRacePrompt(title, text, tcolor, on_select, on_cancel, min_width,allow_none) - RaceBox{ - frame_title = title, - text = text, - text_pen = tcolor, - on_select = on_select, - on_cancel = on_cancel, - frame_width = min_width, - allow_none = allow_none, - }:show() -end -CivBox = defclass(CivBox,dialog.ListBox) -CivBox.focus_path = "CivBox" - -CivBox.ATTRS={ - format_name="$NAME ($ENGLISH):$ID", - format_no_name=":$ID", - name_other="", - with_filter=true, - allow_other=false, -} - -function civ_name(id,format_name,format_no_name,name_other,name_invalid) - if id==-1 then - return name_other or "" - end - local civ - if type(id)=='userdata' then - civ=id - else - civ=df.historical_entity.find(id) - if civ==nil then - return name_invalid or "" - end - end - local t={NAME=dfhack.TranslateName(civ.name),ENGLISH=dfhack.TranslateName(civ.name,true),ID=civ.id} --TODO race?, maybe something from raws? - if t.NAME=="" then - return string.gsub(format_no_name or ":$ID", "%$(%w+)", t) - end - return string.gsub(format_name or "$NAME ($ENGLISH):$ID", "%$(%w+)", t) -end -function CivBox:update_choices() - local choices={} - if self.allow_other then - table.insert(choices,{text=self.name_other,num=-1}) - end - - for i,v in ipairs(df.global.world.entities.all) do - if not self.race_filter or (v.race==self.race_filter) then --TODO filter type - local text=civ_name(v,self.format_name,self.format_no_name,self.name_other,self.name_invalid) - table.insert(choices,{text=text,raw=v,num=i}) - end - end - self.choices=choices - if self.subviews.list then - self.subviews.list:setChoices(self.choices) - end -end -function CivBox:update_race_filter(id) - local raw=df.creature_raw.find(id) - if raw then - self.subviews.race_label:setText(": "..raw.name[0]) - self.race_filter=id - else - self.subviews.race_label:setText(": ") - self.race_filter=nil - end - - self:update_choices() -end -function CivBox:choose_race() - showRacePrompt("Choose race","Select new race:",nil,function (id,choice) - self:update_race_filter(choice.num) - end,nil,nil,true) -end -function CivBox:init(info) - self.subviews.list.frame={t=3,r=0,l=0} - self:addviews{ - widgets.Label{frame={t=1,l=0},text={ - {text="Filter race ",key="CUSTOM_CTRL_A",key_sep="()",on_activate=self:callback("choose_race")}, - }}, - widgets.Label{frame={t=1,l=21},view_id="race_label", - text=": ", - } - } - self:update_choices() -end -function showCivPrompt(title, text, tcolor, on_select, on_cancel, min_width,allow_other) - CivBox{ - frame_title = title, - text = text, - text_pen = tcolor, - on_select = on_select, - on_cancel = on_cancel, - frame_width = min_width, - allow_other = allow_other, - }:show() -end - -editor_civ=defclass(editor_civ,gui.FramedScreen) -editor_civ.ATTRS={ - frame_style = gui.GREY_LINE_FRAME, - frame_title = "Civilization editor", - target_unit = DEFAULT_NIL, - } - -function editor_civ:update_curren_civ() - self.subviews.civ_name:setText("Currently: "..civ_name(self.target_unit.civ_id)) -end -function editor_civ:init( args ) - if self.target_unit==nil then - qerror("invalid unit") - end - - self:addviews{ - widgets.Label{view_id="civ_name",frame = { t=1,l=1}, text="Currently: "..civ_name(self.target_unit.civ_id)}, - widgets.Label{frame = { t=2,l=1}, text={{text=": set to other (-1, usually enemy)",key="CUSTOM_N", - on_activate= function() self.target_unit.civ_id=-1;self:update_curren_civ() end}}}, - widgets.Label{frame = { t=3,l=1}, text={{text=": set to current civ("..df.global.ui.civ_id..")",key="CUSTOM_C", - on_activate= function() self.target_unit.civ_id=df.global.ui.civ_id;self:update_curren_civ() end}}}, - widgets.Label{frame = { t=4,l=1}, text={{text=": manually enter",key="CUSTOM_E", - on_activate=function () - dialog.showInputPrompt("Civ id","Enter new civ id:",COLOR_WHITE, - tostring(self.target_unit.civ_id),function(new_value) - self.target_unit.civ_id=new_value - self:update_curren_civ() - end) - end}} - }, - widgets.Label{frame= {t=5,l=1}, text={{text=": select from list",key="CUSTOM_L", - on_activate=function ( ) - showCivPrompt("Choose civilization", "Select units civilization",nil,function ( id,choice ) - self.target_unit.civ_id=choice.num - self:update_curren_civ() - end,nil,nil,true) - end - }}}, - widgets.Label{ - frame = { b=0,l=1}, - text ={{text= ": exit editor ", - key = "LEAVESCREEN", - on_activate= self:callback("dismiss") - }, - } - }, - } -end -add_editor(editor_civ) -------- counters editor -editor_counters=defclass(editor_counters,gui.FramedScreen) -editor_counters.ATTRS={ - frame_style = gui.GREY_LINE_FRAME, - frame_title = "Counters editor", - target_unit = DEFAULT_NIL, - counters1={ - "think_counter", - "job_counter", - "swap_counter", - "winded", - "stunned", - "unconscious", - "suffocation", - "webbed", - "soldier_mood_countdown", - "soldier_mood", --todo enum, - "pain", - "nausea", - "dizziness", - }, - counters2={ - "paralysis", - "numbness", - "fever", - "exhaustion", - "hunger_timer", - "thirst_timer", - "sleepiness_timer", - "stomach_content", - "stomach_food", - "vomit_timeout", - "stored_fat" --TODO what to reset to? - } -} -function editor_counters:fill_counters() - local ret={} - local u=self.target_unit - for i,v in ipairs(self.counters1) do - table.insert(ret,{f=u.counters:_field(v),name=v}) - end - for i,v in ipairs(self.counters2) do - table.insert(ret,{f=u.counters2:_field(v),name=v}) - end - return ret -end -function editor_counters:update_counters() - for i,v in ipairs(self.counter_list) do - v.text=string.format("%s:%d",v.name,v.f.value) - end - self.subviews.counters:setChoices(self.counter_list) -end -function editor_counters:set_cur_counter(value,index,choice) - choice.f.value=value - self:update_counters() -end -function editor_counters:choose_cur_counter(index,choice) - dialog.showInputPrompt(choice.name,"Enter new value:",COLOR_WHITE, - tostring(choice.f.value),function(new_value) - self:set_cur_counter(new_value,index,choice) - end) -end -function editor_counters:init( args ) - if self.target_unit==nil then - qerror("invalid unit") - end - - self.counter_list=self:fill_counters() - - - self:addviews{ - widgets.FilteredList{ - choices=self.counter_list, - frame = {t=0, b=1,l=1}, - view_id="counters", - on_submit=self:callback("choose_cur_counter"), - on_submit2=self:callback("set_cur_counter",0),--TODO some things need to be set to different defaults - }, - widgets.Label{ - frame = { b=0,l=1}, - text ={{text= ": exit editor ", - key = "LEAVESCREEN", - on_activate= self:callback("dismiss") - }, - {text=": reset counter ", - key = "SEC_SELECT", - }, - {text=": set counter ", - key = "SELECT", - } - - } - }, - } - self:update_counters() -end -add_editor(editor_counters) - -wound_creator=defclass(wound_creator,gui.FramedScreen) -wound_creator.ATTRS={ - frame_style = gui.GREY_LINE_FRAME, - frame_title = "Wound creator", - target_wound = DEFAULT_NIL, - --filter -} -function wound_creator:init( args ) - if self.target_wound==nil then - qerror("invalid wound") - end - - - self:addviews{ - widgets.List{ - - frame = {t=0, b=1,l=1}, - view_id="fields", - on_submit=self:callback("edit_cur_wound"), - on_submit2=self:callback("delete_current_wound") - }, - widgets.Label{ - frame = { b=0,l=1}, - text ={{text= ": exit editor ", - key = "LEAVESCREEN", - on_activate= self:callback("dismiss")}, - - {text=": edit wound ", - key = "SELECT"}, - - {text=": delete wound ", - key = "SEC_SELECT"}, - {text=": create wound ", - key = "CUSTOM_CTRL_I", - on_activate= self:callback("create_new_wound")}, - - } - }, - } - self:update_wounds() -end -------------------- -editor_wounds=defclass(editor_wounds,gui.FramedScreen) -editor_wounds.ATTRS={ - frame_style = gui.GREY_LINE_FRAME, - frame_title = "Wound editor", - target_unit = DEFAULT_NIL, - --filter -} -function is_scar( wound_part ) - return wound_part.flags1.scar_cut or wound_part.flags1.scar_smashed or - wound_part.flags1.scar_edged_shake1 or wound_part.flags1.scar_blunt_shake1 -end -function format_flag_name( fname ) - return fname:sub(1,1):upper()..fname:sub(2):gsub("_"," ") -end -function name_from_flags( wp ) - for i,v in ipairs(wp.flags1) do - if v then - return format_flag_name(df.wound_damage_flags1[i]) - end - end - for i,v in ipairs(wp.flags2) do - if v then - return format_flag_name(df.wound_damage_flags2[i]) - end - end - return "" -end -function format_wound( list_id,wound, unit) - - local name="" - if #wound.parts>0 and #wound.parts[0].effect_type>0 then --try to make wound name by effect... - name=tostring(df.wound_effect_type[wound.parts[0].effect_type[0]]) - if #wound.parts>1 then --cheap and probably incorrect... - name=name.."s" - end - elseif #wound.parts>0 and is_scar(wound.parts[0]) then - name="Scar" - elseif #wound.parts>0 then - local wp=wound.parts[0] - name=name_from_flags(wp) - end - - return string.format("%d. %s id=%d",list_id,name,wound.id) -end -function editor_wounds:update_wounds() - local ret={} - for i,v in ipairs(self.trg_wounds) do - table.insert(ret,{text=format_wound(i,v,self.target_unit),wound=v}) - end - self.subviews.wounds:setChoices(ret) - self.wound_list=ret -end -function editor_wounds:dirty_unit() - print("todo: implement unit status recalculation") -end -function editor_wounds:get_cur_wound() - local list_wid=self.subviews.wounds - local _,choice=list_wid:getSelected() - if choice==nil then - qerror("Nothing selected") - end - local ret_wound=utils.binsearch(self.trg_wounds,choice.id,"id") - return choice,ret_wound -end -function editor_wounds:delete_current_wound(index,choice) - - utils.erase_sorted(self.trg_wounds,choice.wound,"id") - choice.wound:delete() - self:dirty_unit() - self:update_wounds() -end -function editor_wounds:create_new_wound() - print("Creating") -end -function editor_wounds:edit_cur_wound(index,choice) - -end -function editor_wounds:init( args ) - if self.target_unit==nil then - qerror("invalid unit") - end - self.trg_wounds=self.target_unit.body.wounds - - self:addviews{ - widgets.List{ - - frame = {t=0, b=1,l=1}, - view_id="wounds", - on_submit=self:callback("edit_cur_wound"), - on_submit2=self:callback("delete_current_wound") - }, - widgets.Label{ - frame = { b=0,l=1}, - text ={{text= ": exit editor ", - key = "LEAVESCREEN", - on_activate= self:callback("dismiss")}, - - {text=": edit wound ", - key = "SELECT"}, - - {text=": delete wound ", - key = "SEC_SELECT"}, - {text=": create wound ", - key = "CUSTOM_CTRL_I", - on_activate= self:callback("create_new_wound")}, - - } - }, - } - self:update_wounds() -end -add_editor(editor_wounds) - --------------------------------main window---------------- -unit_editor = defclass(unit_editor, gui.FramedScreen) -unit_editor.ATTRS={ - frame_style = gui.GREY_LINE_FRAME, - frame_title = "GameMaster's unit editor", - target_unit = DEFAULT_NIL, - } - - -function unit_editor:init(args) - - self:addviews{ - widgets.FilteredList{ - choices=editors, - on_submit=function (idx,choice) - if choice.on_submit then - choice.on_submit(self.target_unit) - end - end - }, - widgets.Label{ - frame = { b=0,l=1}, - text ={{text= ": exit editor", - key = "LEAVESCREEN", - on_activate= self:callback("dismiss") - }, - } - }, - } -end - - -unit_editor{target_unit=target}:show() diff --git a/scripts/gui/guide-path.lua b/scripts/gui/guide-path.lua deleted file mode 100644 index be90ffb1c..000000000 --- a/scripts/gui/guide-path.lua +++ /dev/null @@ -1,205 +0,0 @@ --- Show/change the path used by Guide Cart orders ---[[=begin - -gui/guide-path -============== -Bind to a key (the example config uses :kbd:`Alt`:kbd:`P`), and activate in the Hauling menu with -the cursor over a Guide order. - -.. image:: /docs/images/guide-path.png - -The script displays the cached path that will be used by the order; the game -computes it when the order is executed for the first time. - -=end]] -local utils = require 'utils' -local gui = require 'gui' -local guidm = require 'gui.dwarfmode' -local dlg = require 'gui.dialogs' - -local tile_attrs = df.tiletype.attrs - -GuidePathUI = defclass(GuidePathUI, guidm.MenuOverlay) - -GuidePathUI.focus_path = 'guide-path' - -GuidePathUI.ATTRS { - route = DEFAULT_NIL, - stop = DEFAULT_NIL, - order = DEFAULT_NIL, -} - -function GuidePathUI:init() - self.saved_mode = df.global.ui.main.mode - - for i=0,#self.route.stops-1 do - if self.route.stops[i] == self.stop then - self.stop_idx = i - break - end - end - - if not self.stop_idx then - error('Could not find stop index') - end - - self.next_stop = self.route.stops[(self.stop_idx+1)%#self.route.stops] -end - -function GuidePathUI:onShow() - -- with cursor, but without those ugly lines from native hauling mode - df.global.ui.main.mode = df.ui_sidebar_mode.Stockpiles -end - -function GuidePathUI:onDestroy() - df.global.ui.main.mode = self.saved_mode -end - -local function getTileType(cursor) - local block = dfhack.maps.getTileBlock(cursor) - if block then - return block.tiletype[cursor.x%16][cursor.y%16] - else - return 0 - end -end - -local function isTrackTile(tile) - return tile_attrs[tile].special == df.tiletype_special.TRACK -end - -local function paintMapTile(dc, vp, cursor, pos, ...) - if not same_xyz(cursor, pos) then - local stile = vp:tileToScreen(pos) - if stile.z == 0 then - dc:seek(stile.x,stile.y):char(...) - end - end -end - -local function get_path_point(gpath,i) - return xyz2pos(gpath.x[i], gpath.y[i], gpath.z[i]) -end - -function GuidePathUI:renderPath(cursor) - local gpath = self.order.guide_path - local startp = self.stop.pos - local endp = self.next_stop.pos - local vp = self:getViewport() - local dc = gui.Painter.new(self.df_layout.map) - local visible = gui.blink_visible(500) - - if visible then - paintMapTile(dc, vp, cursor, endp, '+', COLOR_LIGHTGREEN) - end - - local ok = nil - local pcnt = #gpath.x - if pcnt > 0 then - ok = true - - for i = 0,pcnt-1 do - local pt = get_path_point(gpath, i) - if i == 0 and not same_xyz(startp,pt) then - ok = false - end - if i == pcnt-1 and not same_xyz(endp,pt) then - ok = false - end - local tile = getTileType(pt) - if not isTrackTile(tile) then - ok = false - end - if visible then - local char = '+' - if i < pcnt-1 then - local npt = get_path_point(gpath, i+1) - if npt.x == pt.x+1 then - char = 26 - elseif npt.x == pt.x-1 then - char = 27 - elseif npt.y == pt.y+1 then - char = 25 - elseif npt.y == pt.y-1 then - char = 24 - end - end - local color = COLOR_LIGHTGREEN - if not ok then color = COLOR_LIGHTRED end - paintMapTile(dc, vp, cursor, pt, char, color) - end - end - end - - if gui.blink_visible(120) then - paintMapTile(dc, vp, cursor, startp, 240, COLOR_LIGHTGREEN, COLOR_GREEN) - end - - return ok -end - -function GuidePathUI:onRenderBody(dc) - dc:clear():seek(1,1):pen(COLOR_WHITE):string("Guide Path") - - dc:seek(2,3) - - local cursor = guidm.getCursorPos() - local path_ok = self:renderPath(cursor) - - if path_ok == nil then - dc:string('No saved path', COLOR_DARKGREY) - elseif path_ok then - dc:string('Valid path', COLOR_GREEN) - else - dc:string('Invalid path', COLOR_RED) - end - - dc:newline():newline(1) - dc:key('CUSTOM_Z'):string(": Reset path",COLOR_GREY,nil,path_ok~=nil) - --dc:key('CUSTOM_P'):string(": Find path",COLOR_GREY,nil,false) - dc:newline(1) - dc:key('CUSTOM_C'):string(": Zoom cur, ") - dc:key('CUSTOM_N'):string(": Zoom next") - - dc:newline():newline(1):string('At cursor:') - dc:newline():newline(2) - - local tile = getTileType(cursor) - if isTrackTile(tile) then - dc:string('Track '..tile_attrs[tile].direction, COLOR_GREEN) - else - dc:string('No track', COLOR_DARKGREY) - end - - dc:newline():newline(1) - dc:key('LEAVESCREEN'):string(": Back") -end - -function GuidePathUI:onInput(keys) - if keys.CUSTOM_C then - self:moveCursorTo(copyall(self.stop.pos)) - elseif keys.CUSTOM_N then - self:moveCursorTo(copyall(self.next_stop.pos)) - elseif keys.CUSTOM_Z then - local gpath = self.order.guide_path - gpath.x:resize(0) - gpath.y:resize(0) - gpath.z:resize(0) - elseif keys.LEAVESCREEN then - self:dismiss() - elseif self:propagateMoveKeys(keys) then - return - end -end - -if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/Hauling/DefineStop/Cond/Guide') then - qerror("This script requires the main dwarfmode view in 'h' mode over a Guide order") -end - -local hauling = df.global.ui.hauling -local route = hauling.view_routes[hauling.cursor_top] -local stop = hauling.view_stops[hauling.cursor_top] -local order = hauling.stop_conditions[hauling.cursor_stop] - -local list = GuidePathUI{ route = route, stop = stop, order = order } -list:show() diff --git a/scripts/gui/hack-wish.lua b/scripts/gui/hack-wish.lua deleted file mode 100644 index de423abc2..000000000 --- a/scripts/gui/hack-wish.lua +++ /dev/null @@ -1,8 +0,0 @@ ---@ alias = 'gui/create-item' ---[[=begin - -gui/hack-wish -============= -An alias for `gui/create-item`. Deprecated. - -=end]] \ No newline at end of file diff --git a/scripts/gui/hello-world.lua b/scripts/gui/hello-world.lua deleted file mode 100644 index 324aa11ad..000000000 --- a/scripts/gui/hello-world.lua +++ /dev/null @@ -1,31 +0,0 @@ --- Test lua viewscreens. ---[[=begin - -gui/hello-world -=============== -A basic example for testing, or to start your own script from. - -=end]] -local gui = require 'gui' - -local text = 'Woohoo, lua viewscreen :)' - -local screen = gui.FramedScreen{ - frame_style = gui.GREY_LINE_FRAME, - frame_title = 'Hello World', - frame_width = #text, - frame_height = 1, - frame_inset = 1, -} - -function screen:onRenderBody(dc) - dc:string(text, COLOR_LIGHTGREEN) -end - -function screen:onInput(keys) - if keys.LEAVESCREEN or keys.SELECT then - self:dismiss() - end -end - -screen:show() diff --git a/scripts/gui/liquids.lua b/scripts/gui/liquids.lua deleted file mode 100644 index 51ebca4bf..000000000 --- a/scripts/gui/liquids.lua +++ /dev/null @@ -1,326 +0,0 @@ --- Interface front-end for liquids plugin. ---[[=begin - -gui/liquids -=========== -To use, bind to a key (the example config uses Alt-L) and activate in the :kbd:`k` mode. - -.. image:: /docs/images/liquids.png - -This script is a gui front-end to `liquids` and works similarly, -allowing you to add or remove water & magma, and create obsidian walls & floors. - -.. warning:: - - There is **no undo support**. Bugs in this plugin have been - known to create pathfinding problems and heat traps. - -The :kbd:`b` key changes how the affected area is selected. The default :guilabel:`Rectangle` -mode works by selecting two corners like any ordinary designation. The :kbd:`p` -key chooses between adding water, magma, obsidian walls & floors, or just -tweaking flags. - -When painting liquids, it is possible to select the desired level with :kbd:`+`:kbd:`-`, -and choose between setting it exactly, only increasing or only decreasing -with :kbd:`s`. - -In addition, :kbd:`f` allows disabling or enabling the flowing water computations -for an area, and :kbd:`r` operates on the "permanent flow" property that makes -rivers power water wheels even when full and technically not flowing. - -After setting up the desired operations using the described keys, use :kbd:`Enter` to apply them. - -=end]] -local utils = require 'utils' -local gui = require 'gui' -local guidm = require 'gui.dwarfmode' -local dlg = require 'gui.dialogs' - -local liquids = require('plugins.liquids') - -local sel_rect = df.global.selection_rect - -local brushes = { - { tag = 'range', caption = 'Rectangle', range = true }, - { tag = 'block', caption = '16x16 block' }, - { tag = 'column', caption = 'Column' }, - { tag = 'flood', caption = 'Flood' }, -} - -local paints = { - { tag = 'water', caption = 'Water', liquid = true, flow = true, key = 'D_LOOK_ARENA_WATER' }, - { tag = 'magma', caption = 'Magma', liquid = true, flow = true, key = 'D_LOOK_ARENA_MAGMA' }, - { tag = 'obsidian', caption = 'Obsidian Wall' }, - { tag = 'obsidian_floor', caption = 'Obsidian Floor' }, - { tag = 'riversource', caption = 'River Source' }, - { tag = 'flowbits', caption = 'Flow Updates', flow = true }, - { tag = 'wclean', caption = 'Clean Salt/Stagnant' }, -} - -local flowbits = { - { tag = '+', caption = 'Enable Updates' }, - { tag = '-', caption = 'Disable Updates' }, - { tag = '.', caption = 'Keep Updates' }, -} - -local setmode = { - { tag = '.', caption = 'Set Exactly' }, - { tag = '+', caption = 'Only Increase' }, - { tag = '-', caption = 'Only Decrease' }, -} - -local permaflows = { - { tag = '.', caption = "Keep Permaflow" }, - { tag = '-', caption = 'Remove Permaflow' }, - { tag = 'N', caption = 'Set Permaflow N' }, - { tag = 'S', caption = 'Set Permaflow S' }, - { tag = 'E', caption = 'Set Permaflow E' }, - { tag = 'W', caption = 'Set Permaflow W' }, - { tag = 'NE', caption = 'Set Permaflow NE' }, - { tag = 'NW', caption = 'Set Permaflow NW' }, - { tag = 'SE', caption = 'Set Permaflow SE' }, - { tag = 'SW', caption = 'Set Permaflow SW' }, -} - -Toggle = defclass(Toggle) - -Toggle.ATTRS{ items = {}, selected = 1 } - -function Toggle:get() - return self.items[self.selected] -end - -function Toggle:render(dc) - local item = self:get() - if item then - dc:string(item.caption) - if item.key then - dc:string(" ("):key(item.key):string(")") - end - else - dc:string('NONE', COLOR_RED) - end -end - -function Toggle:step(delta) - if #self.items > 1 then - delta = delta or 1 - self.selected = 1 + (self.selected + delta - 1) % #self.items - end -end - -LiquidsUI = defclass(LiquidsUI, guidm.MenuOverlay) - -LiquidsUI.focus_path = 'liquids' - -function LiquidsUI:init() - self:assign{ - brush = Toggle{ items = brushes }, - paint = Toggle{ items = paints }, - flow = Toggle{ items = flowbits }, - set = Toggle{ items = setmode }, - permaflow = Toggle{ items = permaflows }, - amount = 7, - } -end - -function LiquidsUI:onDestroy() - guidm.clearSelection() -end - -function render_liquid(dc, block, x, y) - local dsgn = block.designation[x%16][y%16] - - if dsgn.flow_size > 0 then - if dsgn.liquid_type == df.tile_liquid.Magma then - dc:pen(COLOR_RED):string("Magma") - else - dc:pen(COLOR_BLUE) - if dsgn.water_stagnant then dc:string("Stagnant ") end - if dsgn.water_salt then dc:string("Salty ") end - dc:string("Water") - end - dc:string(" ["..dsgn.flow_size.."/7]") - else - dc:string('No Liquid') - end -end - -local permaflow_abbr = { - north = 'N', south = 'S', east = 'E', west = 'W', - northeast = 'NE', northwest = 'NW', southeast = 'SE', southwest = 'SW' -} - -function render_flow_state(dc, block, x, y) - local flow = block.liquid_flow[x%16][y%16] - - if block.flags.update_liquid then - dc:string("Updating", COLOR_GREEN) - else - dc:string("Static") - end - dc:string(", ") - if flow.perm_flow_dir ~= 0 then - local tag = df.tile_liquid_flow_dir[flow.perm_flow_dir] - dc:string("Permaflow "..(permaflow_abbr[tag] or tag), COLOR_CYAN) - elseif flow.temp_flow_timer > 0 then - dc:string("Flowing "..flow.temp_flow_timer, COLOR_GREEN) - else - dc:string("No Flow") - end -end - -function LiquidsUI:onRenderBody(dc) - dc:clear():seek(1,1):string("Paint Liquids Cheat", COLOR_WHITE) - - local cursor = guidm.getCursorPos() - local block = dfhack.maps.getTileBlock(cursor) - - if block then - local x, y = pos2xyz(cursor) - local tile = block.tiletype[x%16][y%16] - - dc:seek(2,3):string(df.tiletype.attrs[tile].caption, COLOR_CYAN) - dc:newline(2):pen(COLOR_DARKGREY) - render_liquid(dc, block, x, y) - dc:newline(2):pen(COLOR_DARKGREY) - render_flow_state(dc, block, x, y) - else - dc:seek(2,3):string("No map data", COLOR_RED):advance(0,2) - end - - dc:newline():pen(COLOR_GREY) - - dc:newline(1):key('CUSTOM_B'):string(": ") - self.brush:render(dc) - dc:newline(1):key('CUSTOM_P'):string(": ") - self.paint:render(dc) - - local paint = self.paint:get() - - dc:newline() - if paint.liquid then - dc:newline(1):string("Amount: "..self.amount) - dc:advance(1):string("("):key('SECONDSCROLL_UP'):key('SECONDSCROLL_DOWN'):string(")") - dc:newline(3):key('CUSTOM_S'):string(": ") - self.set:render(dc) - else - dc:advance(0,2) - end - - dc:newline() - if paint.flow then - dc:newline(1):key('CUSTOM_F'):string(": ") - self.flow:render(dc) - dc:newline(1):key('CUSTOM_R'):string(": ") - self.permaflow:render(dc) - else - dc:advance(0,2) - end - - dc:newline():newline(1):pen(COLOR_WHITE) - dc:key('LEAVESCREEN'):string(": Back, ") - dc:key('SELECT'):string(": Paint") -end - -function ensure_blocks(cursor, size, cb) - size = size or xyz2pos(1,1,1) - local cx,cy,cz = pos2xyz(cursor) - local all = true - for x=1,size.x or 1,16 do - for y=1,size.y or 1,16 do - for z=1,size.z do - if not dfhack.maps.getTileBlock(cx+x-1, cy+y-1, cz+z-1) then - all = false - end - end - end - end - if all then - cb() - return - end - dlg.showYesNoPrompt( - 'Instantiate Blocks', - 'Not all map blocks are allocated - instantiate?\n\nWarning: new untested feature.', - COLOR_YELLOW, - function() - for x=1,size.x or 1,16 do - for y=1,size.y or 1,16 do - for z=1,size.z do - dfhack.maps.ensureTileBlock(cx+x-1, cy+y-1, cz+z-1) - end - end - end - cb() - end, - function() - cb() - end - ) -end - -function LiquidsUI:onInput(keys) - local paint = self.paint:get() - local liquid = paint.liquid - if keys.CUSTOM_B then - self.brush:step() - elseif keys.CUSTOM_P then - self.paint:step() - elseif liquid and keys.SECONDSCROLL_UP then - self.amount = math.max(0, self.amount-1) - elseif liquid and keys.SECONDSCROLL_DOWN then - self.amount = math.min(7, self.amount+1) - elseif liquid and keys.CUSTOM_S then - self.set:step() - elseif paint.flow and keys.CUSTOM_F then - self.flow:step() - elseif paint.flow and keys.CUSTOM_R then - self.permaflow:step() - elseif keys.LEAVESCREEN then - if guidm.getSelection() then - guidm.clearSelection() - return - end - self:dismiss() - self:sendInputToParent('CURSOR_DOWN_Z') - self:sendInputToParent('CURSOR_UP_Z') - elseif keys.SELECT then - local cursor = guidm.getCursorPos() - local sp = guidm.getSelection() - local size = nil - if self.brush:get().range then - if not sp then - guidm.setSelectionStart(cursor) - return - else - guidm.clearSelection() - cursor, size = guidm.getSelectionRange(cursor, sp) - end - else - guidm.clearSelection() - end - local cb = curry( - liquids.paint, - cursor, - self.brush:get().tag, self.paint:get().tag, - self.amount, size, - self.set:get().tag, self.flow:get().tag, - self.permaflow:get().tag - ) - ensure_blocks(cursor, size, cb) - elseif self:propagateMoveKeys(keys) then - return - elseif keys.D_LOOK_ARENA_WATER then - self.paint.selected = 1 - elseif keys.D_LOOK_ARENA_MAGMA then - self.paint.selected = 2 - end -end - -if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/LookAround') then - qerror("This script requires the main dwarfmode view in 'k' mode") -end - -local list = LiquidsUI() -list:show() diff --git a/scripts/gui/mechanisms.lua b/scripts/gui/mechanisms.lua deleted file mode 100644 index d65782469..000000000 --- a/scripts/gui/mechanisms.lua +++ /dev/null @@ -1,146 +0,0 @@ --- Shows mechanisms linked to the current building. ---[[=begin - -gui/mechanisms -============== -To use, bind to a key (the example config uses :kbd:`Ctrl`:kbd:`M`) -and activate in :kbd:`q` mode. - -.. image:: /docs/images/mechanisms.png - -Lists mechanisms connected to the building, and their links. Navigating -the list centers the view on the relevant linked buildings. - -To exit, press :kbd:`Esc` or :kbd:`Enter`; :kbd:`Esc` recenters on -the original building, while :kbd:`Enter` leaves focus on the current -one. :kbd:`Shift`:kbd:`Enter` has an effect equivalent to pressing -:kbd:`Enter`, and then re-entering the mechanisms UI. - -=end]] -local utils = require 'utils' -local gui = require 'gui' -local guidm = require 'gui.dwarfmode' - -function listMechanismLinks(building) - local lst = {} - local function push(item, mode) - if item then - lst[#lst+1] = { - obj = item, mode = mode, - name = utils.getBuildingName(item) - } - end - end - - push(building, 'self') - - if not df.building_actual:is_instance(building) then - return lst - end - - local item, tref, tgt - for _,v in ipairs(building.contained_items) do - item = v.item - if df.item_trappartsst:is_instance(item) then - tref = dfhack.items.getGeneralRef(item, df.general_ref_type.BUILDING_TRIGGER) - if tref then - push(tref:getBuilding(), 'trigger') - end - tref = dfhack.items.getGeneralRef(item, df.general_ref_type.BUILDING_TRIGGERTARGET) - if tref then - push(tref:getBuilding(), 'target') - end - end - end - - return lst -end - -MechanismList = defclass(MechanismList, guidm.MenuOverlay) - -MechanismList.focus_path = 'mechanisms' - -function MechanismList:init(info) - self:assign{ - links = {}, selected = 1 - } - self:fillList(info.building) -end - -function MechanismList:fillList(building) - local links = listMechanismLinks(building) - - self.old_viewport = self:getViewport() - self.old_cursor = guidm.getCursorPos() - - if #links <= 1 then - links[1].mode = 'none' - end - - self.links = links - self.selected = 1 -end - -local colors = { - self = COLOR_CYAN, none = COLOR_CYAN, - trigger = COLOR_GREEN, target = COLOR_GREEN -} -local icons = { - self = 128, none = 63, trigger = 27, target = 26 -} - -function MechanismList:onRenderBody(dc) - dc:clear() - dc:seek(1,1):string("Mechanism Links", COLOR_WHITE):newline() - - for i,v in ipairs(self.links) do - local pen = { fg=colors[v.mode], bold = (i == self.selected) } - dc:newline(1):pen(pen):char(icons[v.mode]) - dc:advance(1):string(v.name) - end - - local nlinks = #self.links - - if nlinks <= 1 then - dc:newline():newline(1):string("This building has no links", COLOR_LIGHTRED) - end - - dc:newline():newline(1):pen(COLOR_WHITE) - dc:key('LEAVESCREEN'):string(": Back, ") - dc:key('SELECT'):string(": Switch") -end - -function MechanismList:changeSelected(delta) - if #self.links <= 1 then return end - self.selected = 1 + (self.selected + delta - 1) % #self.links - self:selectBuilding(self.links[self.selected].obj) -end - -function MechanismList:onInput(keys) - if keys.SECONDSCROLL_UP then - self:changeSelected(-1) - elseif keys.SECONDSCROLL_DOWN then - self:changeSelected(1) - elseif keys.LEAVESCREEN then - self:dismiss() - if self.selected ~= 1 then - self:selectBuilding(self.links[1].obj, self.old_cursor, self.old_view) - end - elseif keys.SELECT_ALL then - if self.selected > 1 then - self:fillList(self.links[self.selected].obj) - end - elseif keys.SELECT then - self:dismiss() - elseif self:simulateViewScroll(keys) then - return - end -end - -if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some') then - qerror("This script requires the main dwarfmode view in 'q' mode") -end - -local list = MechanismList{ building = df.global.world.selected_building } -list:show() -list:changeSelected(1) diff --git a/scripts/gui/mod-manager.lua b/scripts/gui/mod-manager.lua deleted file mode 100644 index 5c3b39375..000000000 --- a/scripts/gui/mod-manager.lua +++ /dev/null @@ -1,361 +0,0 @@ --- a graphical mod manager for df -local gui=require 'gui' -local widgets=require 'gui.widgets' ---[[=begin - -gui/mod-manager -=============== -A simple way to install and remove small mods. - -It looks for specially formatted mods in :file:`{}/mods/`. Mods are not -included, but some examples are `available here`_. - -.. _`available here`: https://github.com/warmist/df-mini-mods - -.. image:: /docs/images/mod-manager.png - -=end]] -local entity_file=dfhack.getDFPath().."/raw/objects/entity_default.txt" -local init_file=dfhack.getDFPath().."/raw/init.lua" -local mod_dir=dfhack.getDFPath().."/hack/mods" ---[[ mod format: lua script that defines: - name - a name that is displayed in list - author - mod author, also displayed - description - mod description - OPTIONAL: - raws_list - a list (table) of file names that need to be copied over to df raws - patch_entity - a chunk of text to patch entity TODO: add settings to which entities to add - patch_init - a chunk of lua to add to lua init - patch_dofile - a list (table) of files to add to lua init as "dofile" - patch_files - a table of files to patch: - filename - a filename (in raws folder) to patch - patch - what to add - after - a string after which to insert - MORE OPTIONAL: - guard - a token that is used in raw files to find editions and remove them on uninstall - guard_init - a token for lua file - [pre|post]_(un)install - callback functions. Can trigger more complicated behavior -]] - -function fileExists(filename) - local file=io.open(filename,"rb") - if file==nil then - return - else - file:close() - return true - end -end -if not fileExists(init_file) then - local initFile=io.open(init_file,"a") - initFile:close() -end -function copyFile(from,to) --oh so primitive - local filefrom=io.open(from,"rb") - local fileto=io.open(to,"w+b") - local buf=filefrom:read("*a") - printall(buf) - fileto:write(buf) - filefrom:close() - fileto:close() -end -function patchInit(initFileName,patch_guard,code) - local initFile=io.open(initFileName,"a") - initFile:write(string.format("\n%s\n%s\n%s",patch_guard[1], - code,patch_guard[2])) - initFile:close() -end -function patchDofile( luaFileName,patch_guard,dofile_list,mod_path ) - local luaFile=io.open(luaFileName,"a") - luaFile:write(patch_guard[1].."\n") - for _,v in ipairs(dofile_list) do - local fixed_path=mod_path:gsub("\\","/") - luaFile:write(string.format("dofile('%s/%s')\n",fixed_path,v)) - end - luaFile:write(patch_guard[2].."\n") - luaFile:close() -end -function patchFile(file_name,patch_guard,after_string,code) - local input_lines=patch_guard[1].."\n"..code.."\n"..patch_guard[2] - - local badchars="[%:%[%]]" - local find_string=after_string:gsub(badchars,"%%%1") --escape some bad chars - - local entityFile=io.open(file_name,"r") - local buf=entityFile:read("*all") - entityFile:close() - local entityFile=io.open(file_name,"w+") - buf=string.gsub(buf,find_string,after_string.."\n"..input_lines) - entityFile:write(buf) - entityFile:close() -end -function findGuards(str,start,patch_guard) - local pStart=string.find(str,patch_guard[1],start) - if pStart==nil then return nil end - local pEnd=string.find(str,patch_guard[2],pStart) - if pEnd==nil then error("Start guard token found, but end was not found") end - return pStart-1,pEnd+#patch_guard[2]+1 -end -function findGuardsFile(filename,patch_guard) - local file=io.open(filename,"r") - local buf=file:read("*all") - return findGuards(buf,1,patch_guard) -end -function unPatchFile(filename,patch_guard) - local file=io.open(filename,"r") - local buf=file:read("*all") - file:close() - - local newBuf="" - local pos=1 - local lastPos=1 - repeat - local endPos - pos,endPos=findGuards(buf,lastPos,patch_guard) - newBuf=newBuf..string.sub(buf,lastPos,pos) - if endPos~=nil then - lastPos=endPos - end - until pos==nil - - local file=io.open(filename,"w+") - file:write(newBuf) - file:close() -end -function checkInstalled(dfMod) --try to figure out if installed - if dfMod.checkInstalled then - return dfMod.checkInstalled() - else - if dfMod.raws_list then - for k,v in pairs(dfMod.raws_list) do - if fileExists(dfhack.getDFPath().."/raw/objects/"..v) then - return true,v - end - end - end - if dfMod.patch_entity then - if findGuardsFile(entity_file,dfMod.guard)~=nil then - return true,"entity_default.txt" - end - end - if dfMod.patch_files then - for k,v in pairs(dfMod.patch_files) do - if findGuardsFile(dfhack.getDFPath().."/raw/objects/"..v.filename,dfMod.guard)~=nil then - return true,"v.filename" - end - end - end - if dfMod.patch_init then - if findGuardsFile(init_file,dfMod.guard_init)~=nil then - return true,"init.lua" - end - end - end -end -manager=defclass(manager,gui.FramedScreen) - -function manager:init(args) - self.mods={} - local mods=self.mods - local mlist=dfhack.internal.getDir(mod_dir) - - if mlist==nil or #mlist==0 then - qerror("Mod directory not found! Are you sure it is in:"..mod_dir) - end - for k,v in ipairs(mlist) do - if v~="." and v~=".." then - local f,modData=pcall(dofile,mod_dir.."/".. v .. "/init.lua") - if f then - mods[modData.name]=modData - modData.guard=modData.guard or {">>"..modData.name.." patch","<" - end -end -function manager:formAuthor() - return self.selected.author or "" -end -function manager:selectMod(idx,choice) - self.selected=choice.data - if self.subviews.info then - self.subviews.info:setText(self:formDescription()) - self:updateLayout() - end -end -function manager:updateState() - for k,v in pairs(self.mods) do - v.installed=checkInstalled(v) - end -end -function manager:installCurrent() - self:install(self.selected) -end -function manager:uninstallCurrent() - self:uninstall(self.selected) -end -function manager:install(trgMod,force) - - if trgMod==nil then - qerror 'Mod does not exist' - end - if not force then - local isInstalled,file=checkInstalled(trgMod) -- maybe load from .installed? - if isInstalled then - qerror("Mod already installed. File:"..file) - end - end - print("installing:"..trgMod.name) - if trgMod.pre_install then - trgMod.pre_install(args) - end - if trgMod.raws_list then - for k,v in pairs(trgMod.raws_list) do - copyFile(trgMod.path..v,dfhack.getDFPath().."/raw/objects/"..v) - end - end - if trgMod.patch_entity then - local entity_target="[ENTITY:MOUNTAIN]" --TODO configure - patchFile(entity_file,trgMod.guard,entity_target,trgMod.patch_entity) - end - if trgMod.patch_files then - for k,v in pairs(trgMod.patch_files) do - patchFile(dfhack.getDFPath().."/raw/objects/"..v.filename,trgMod.guard,v.after,v.patch) - end - end - if trgMod.patch_init then - patchInit(init_file,trgMod.guard_init,trgMod.patch_init) - end - if trgMod.patch_dofile then - patchDofile(init_file,trgMod.guard_init,trgMod.patch_dofile,trgMod.path) - end - trgMod.installed=true - - if trgMod.post_install then - trgMod.post_install(self) - end - print("done") -end -function manager:uninstall(trgMod) - print("Uninstalling:"..trgMod.name) - if trgMod.pre_uninstall then - trgMod.pre_uninstall(args) - end - - if trgMod.raws_list then - for k,v in pairs(trgMod.raws_list) do - os.remove(dfhack.getDFPath().."/raw/objects/"..v) - end - end - if trgMod.patch_entity then - unPatchFile(entity_file,trgMod.guard) - end - if trgMod.patch_files then - for k,v in pairs(trgMod.patch_files) do - unPatchFile(dfhack.getDFPath().."/raw/objects/"..v.filename,trgMod.guard) - end - end - if trgMod.patch_init or trgMod.patch_dofile then - unPatchFile(init_file,trgMod.guard_init) - end - - trgMod.installed=false - if trgMod.post_uninstall then - trgMod.post_uninstall(args) - end - print("done") -end -function manager:onInput(keys) - - if keys.LEAVESCREEN then - self:dismiss() - else - self:inputToSubviews(keys) - end - -end -if dfhack.gui.getCurFocus()~='title' then - qerror("Can only be used in title screen") -end -local m=manager{} -m:show() diff --git a/scripts/gui/no-dfhack-init.lua b/scripts/gui/no-dfhack-init.lua deleted file mode 100644 index c4352211d..000000000 --- a/scripts/gui/no-dfhack-init.lua +++ /dev/null @@ -1,37 +0,0 @@ --- Shows the warning about missing configuration file. ---[[=begin - -gui/no-dfhack-init -================== -Shows a warning at startup if no valid :file:`dfhack.init` file is found. - -=end]] -local gui = require 'gui' -local dlg = require 'gui.dialogs' - -local dfhack_init = { text = 'dfhack.init', pen = COLOR_LIGHTCYAN } -local dfhack_init_example = { text = 'dfhack.init-example', pen = COLOR_LIGHTCYAN } - -local message = { - 'The ', dfhack_init, ' configuration file is missing. To customize', NEWLINE, - 'your DFHack installation, rename the ', dfhack_init_example, ' file', NEWLINE, - 'to ', dfhack_init, ' and edit it to suit your needs.', NEWLINE, NEWLINE, - 'For now, ', dfhack_init_example, ' will be used instead.' -} - -dfhack.print('\n') - -for k,v in ipairs(message) do - if type(v) == 'table' then - dfhack.color(v.pen) - dfhack.print(v.text) - else - dfhack.color(COLOR_YELLOW) - dfhack.print(v) - end -end - -dfhack.color(COLOR_RESET) -dfhack.print('\n\n') - -dlg.showMessage('DFHack is not configured', message, COLOR_YELLOW) diff --git a/scripts/gui/power-meter.lua b/scripts/gui/power-meter.lua deleted file mode 100644 index ac10e5dc7..000000000 --- a/scripts/gui/power-meter.lua +++ /dev/null @@ -1,135 +0,0 @@ --- Interface front-end for power-meter plugin. ---[[=begin - -gui/power-meter -=============== -An in-game interface for `power-meter`. - -Bind it to a key (default :kbd:`Ctrl`:kbd:`Shift`:kbd:`M`) and activate -after selecting Pressure Plate in the build menu. - -.. image:: /docs/images/power-meter.png - -The script follows the general look and feel of the regular pressure -plate build configuration page, but configures parameters relevant to -the modded power meter building. - -=end]] -local utils = require 'utils' -local gui = require 'gui' -local guidm = require 'gui.dwarfmode' -local dlg = require 'gui.dialogs' - -local plugin = require('plugins.power-meter') -local bselector = df.global.ui_build_selector - -PowerMeter = defclass(PowerMeter, guidm.MenuOverlay) - -PowerMeter.focus_path = 'power-meter' - -PowerMeter.ATTRS { - frame_background = false -} - -function PowerMeter:init() - self:assign{ - min_power = 0, max_power = -1, invert = false, - } -end - -function PowerMeter:onShow() - PowerMeter.super.onShow(self) - - -- Send an event to update the errors - bselector.plate_info.flags.whole = 0 - self:sendInputToParent('BUILDING_TRIGGER_ENABLE_WATER') -end - -function PowerMeter:onRenderBody(dc) - dc:fill(0,0,dc.width-1,13,gui.CLEAR_PEN) - dc:seek(1,1):pen(COLOR_WHITE) - dc:string("Power Meter"):newline():newline(1) - dc:string("Placement"):newline():newline(1) - - dc:string("Excess power range:") - - dc:newline(3):key('BUILDING_TRIGGER_MIN_WATER_DOWN') - dc:key('BUILDING_TRIGGER_MIN_WATER_UP') - dc:string(": Min ") - if self.min_power <= 0 then - dc:string("(any)") - else - dc:string(''..self.min_power) - end - - dc:newline(3):key('BUILDING_TRIGGER_MAX_WATER_DOWN') - dc:key('BUILDING_TRIGGER_MAX_WATER_UP') - dc:string(": Max ") - if self.max_power < 0 then - dc:string("(any)") - else - dc:string(''..self.max_power) - end - dc:newline():newline(1) - - dc:key('CUSTOM_I'):string(": ") - if self.invert then - dc:string("Inverted") - else - dc:string("Not inverted") - end -end - -function PowerMeter:onInput(keys) - if keys.CUSTOM_I then - self.invert = not self.invert - elseif keys.BUILDING_TRIGGER_MIN_WATER_UP then - self.min_power = self.min_power + 10 - elseif keys.BUILDING_TRIGGER_MIN_WATER_DOWN then - self.min_power = math.max(0, self.min_power - 10) - elseif keys.BUILDING_TRIGGER_MAX_WATER_UP then - if self.max_power < 0 then - self.max_power = 0 - else - self.max_power = self.max_power + 10 - end - elseif keys.BUILDING_TRIGGER_MAX_WATER_DOWN then - self.max_power = math.max(-1, self.max_power - 10) - elseif keys.LEAVESCREEN then - self:dismiss() - self:sendInputToParent('LEAVESCREEN') - elseif keys.SELECT then - if #bselector.errors == 0 then - if not plugin.makePowerMeter( - bselector.plate_info, - self.min_power, self.max_power, self.invert - ) - then - dlg.showMessage( - 'Power Meter', - 'Could not initialize.', COLOR_LIGHTRED - ) - - self:dismiss() - self:sendInputToParent('LEAVESCREEN') - return - end - - self:sendInputToParent('SELECT') - if bselector.stage ~= 1 then - self:dismiss() - end - end - elseif self:propagateMoveKeys(keys) then - return - end -end - -if dfhack.gui.getCurFocus() ~= 'dwarfmode/Build/Position/Trap' -or bselector.building_subtype ~= df.trap_type.PressurePlate -then - qerror("This script requires the main dwarfmode view in build pressure plate mode") -end - -local list = PowerMeter() -list:show() diff --git a/scripts/gui/prerelease-warning.lua b/scripts/gui/prerelease-warning.lua deleted file mode 100644 index 113375fd1..000000000 --- a/scripts/gui/prerelease-warning.lua +++ /dev/null @@ -1,141 +0,0 @@ --- Shows the warning about missing configuration file. ---[[=begin - -gui/prerelease-warning -====================== -Shows a warning on world load for pre-release builds. - -With no arguments passed, the warning is shown unless the "do not show again" -option has been selected. With the ``force`` argument, the warning is always -shown. - -=end]] - -local gui = require 'gui' -local dlg = require 'gui.dialogs' -local json = require 'json' -local utils = require 'utils' - -local force = ({...})[1] == 'force' -local config = json.open('dfhack-config/prerelease-warning.json') - -if config.data.hide and not force then - return -end -if not dfhack.isPrerelease() and not force then - qerror('not a prerelease build') -end --- Don't fire during worldgen -if dfhack.internal.getAddress('gametype') and df.global.gametype == df.game_type.NONE and not force then - return -end - -local state = dfhack.getDFHackRelease():lower():match('[a-z]+') -if not utils.invert{'alpha', 'beta', 'rc', 'r'}[state] then - dfhack.printerr('warning: unknown release state: ' .. state) - state = 'unknown' -end - - -message = ({ - alpha = { - 'Warning', - COLOR_YELLOW, - 'This is an alpha build of DFHack. Some structures are likely', NEWLINE, - 'to be incorrect, resulting in crashes or save corruption', NEWLINE, - {pen=COLOR_LIGHTRED, text='Make backups of your saves often!'} - }, - beta = { - 'Warning', - COLOR_YELLOW, - 'This is a beta release of DFHack. It is more stable than an', NEWLINE, - 'alpha release, but bugs are still possible, possibly including', NEWLINE, - 'crashes and save corruption.', NEWLINE, - 'Make backups of your saves beforehand to be safe.' - }, - rc = { - 'Notice', - COLOR_YELLOW, - 'This is a DFHack release candidate. It is fairly stable but', NEWLINE, - 'likely contains newer features that are not fully-tested.', NEWLINE, - 'Crashes are unlikely, but always make backups of your saves', NEWLINE, - 'to be safe.' - }, - r = { - 'Error', - COLOR_LIGHTRED, - 'This release is flagged as a prerelease but named as a', NEWLINE, - 'stable release.', NEWLINE, - {pen=COLOR_LIGHTMAGENTA, text='Please report this to the DFHack team or a pack maintainer.'} - }, - unknown = { - 'Error', - COLOR_LIGHTMAGENTA, - 'Unknown prerelease DFHack build. This should never happen!', NEWLINE, - 'Please report this to the DFHack team or a pack maintainer.' - } -})[state] - -title = table.remove(message, 1) -color = table.remove(message, 1) - -pack_message = [[ - -This should not be enabled by default in a pack. -If you are seeing this message and did not enable/install DFHack -yourself, please report this to your pack's maintainer.]] - -path = dfhack.getHackPath():lower() -if #pack_message > 0 and (path:find('lnp') or path:find('starter') or path:find('newb') or path:find('lazy') or path:find('pack')) then - for _, v in pairs(utils.split_string(pack_message, '\n')) do - table.insert(message, NEWLINE) - table.insert(message, {text=v, pen=COLOR_LIGHTMAGENTA}) - end -end - -dfhack.print('\n') - -for k,v in ipairs(message) do - if type(v) == 'table' then - dfhack.color(v.pen) - dfhack.print(v.text) - else - dfhack.color(color) - dfhack.print(v) - end -end - -dfhack.color(COLOR_RESET) -dfhack.print('\n\n') - -WarningBox = defclass(nil, dlg.MessageBox) - -function WarningBox:getWantedFrameSize() - local w, h = WarningBox.super.getWantedFrameSize(self) - return w, h + 2 -end - -function WarningBox:onRenderFrame(dc,rect) - WarningBox.super.onRenderFrame(self,dc,rect) - dc:pen(COLOR_WHITE):key_pen(COLOR_LIGHTRED) - :seek(rect.x1 + 2, rect.y2 - 2) - :key('CUSTOM_D'):string(': Do not show again') - :advance(10) - :key('LEAVESCREEN'):string('/') - :key('SELECT'):string(': Dismiss') -end - -function WarningBox:onInput(keys) - if keys.CUSTOM_D then - config.data.hide = true - config:write() - keys.LEAVESCREEN = true - end - WarningBox.super.onInput(self, keys) -end - -WarningBox{ - frame_title = title, - text = message, - text_pen = color -}:show() diff --git a/scripts/gui/rename.lua b/scripts/gui/rename.lua deleted file mode 100644 index 3433f4881..000000000 --- a/scripts/gui/rename.lua +++ /dev/null @@ -1,93 +0,0 @@ --- Rename various objects via gui. ---[[=begin - -gui/rename -========== -Backed by `rename`, this script allows entering the desired name -via a simple dialog in the game ui. - -* ``gui/rename [building]`` in :kbd:`q` mode changes the name of a building. - - .. image:: /docs/images/rename-bld.png - - The selected building must be one of stockpile, workshop, furnace, trap, or siege engine. - It is also possible to rename zones from the :kbd:`i` menu. - -* ``gui/rename [unit]`` with a unit selected changes the nickname. - - Unlike the built-in interface, this works even on enemies and animals. - -* ``gui/rename unit-profession`` changes the selected unit's custom profession name. - - .. image:: /docs/images/rename-prof.png - - Likewise, this can be applied to any unit, and when used on animals it overrides - their species string. - -The ``building`` or ``unit`` options are automatically assumed when in relevant UI state. - -The example config binds building/unit rename to :kbd:`Ctrl`:kbd:`Shift`:kbd:`N`, and -unit profession change to :kbd:`Ctrl`:kbd:`Shift`:kbd:`T`. - -=end]] -local gui = require 'gui' -local dlg = require 'gui.dialogs' -local plugin = require 'plugins.rename' - -local mode = ... -local focus = dfhack.gui.getCurFocus() - -local function verify_mode(expected) - if mode ~= nil and mode ~= expected then - qerror('Invalid UI state for mode '..mode) - end -end - -local unit = dfhack.gui.getSelectedUnit(true) -local building = dfhack.gui.getSelectedBuilding(true) - -if building and (not unit or mode == 'building') then - verify_mode('building') - - if plugin.canRenameBuilding(building) then - dlg.showInputPrompt( - 'Rename Building', - 'Enter a new name for the building:', COLOR_GREEN, - building.name, - curry(plugin.renameBuilding, building) - ) - else - dlg.showMessage( - 'Rename Building', - 'Cannot rename this type of building.', COLOR_LIGHTRED - ) - end -elseif unit then - if mode == 'unit-profession' then - dlg.showInputPrompt( - 'Rename Unit', - 'Enter a new profession for the unit:', COLOR_GREEN, - unit.custom_profession, - function(newval) - unit.custom_profession = newval - end - ) - else - verify_mode('unit') - - local vname = dfhack.units.getVisibleName(unit) - local vnick = '' - if vname and vname.has_name then - vnick = vname.nickname - end - - dlg.showInputPrompt( - 'Rename Unit', - 'Enter a new nickname for the unit:', COLOR_GREEN, - vnick, - curry(dfhack.units.setNickname, unit) - ) - end -elseif mode then - verify_mode(nil) -end diff --git a/scripts/gui/room-list.lua b/scripts/gui/room-list.lua deleted file mode 100644 index ac6890317..000000000 --- a/scripts/gui/room-list.lua +++ /dev/null @@ -1,259 +0,0 @@ --- Browses rooms owned by a unit. ---[[=begin - -gui/room-list -============= -To use, bind to a key (the example config uses :kbd:`Alt`:kbd:`R`) and activate in :kbd:`q` mode, -either immediately or after opening the assign owner page. - -.. image:: /docs/images/room-list.png - -The script lists other rooms owned by the same owner, or by the unit selected in the assign -list, and allows unassigning them. - -=end]] -local utils = require 'utils' -local gui = require 'gui' -local guidm = require 'gui.dwarfmode' - -local room_type_table = { - [df.building_bedst] = { token = 'bed', qidx = 2, tile = 233 }, - [df.building_tablest] = { token = 'table', qidx = 3, tile = 209 }, - [df.building_chairst] = { token = 'chair', qidx = 4, tile = 210 }, - [df.building_coffinst] = { token = 'coffin', qidx = 5, tile = 48 }, -} - -local room_quality_table = { - { 1, 'Meager Quarters', 'Meager Dining Room', 'Meager Office', 'Grave' }, - { 100, 'Modest Quarters', 'Modest Dining Room', 'Modest Office', "Servant's Burial Chamber" }, - { 250, 'Quarters', 'Dining Room', 'Office', 'Burial Chamber' }, - { 500, 'Decent Quarters', 'Decent Dining Room', 'Decent Office', 'Tomb' }, - { 1000, 'Fine Quarters', 'Fine Dining Room', 'Splendid Office', 'Fine Tomb' }, - { 1500, 'Great Bedroom', 'Great Dining Room', 'Throne Room', 'Mausoleum' }, - { 2500, 'Grand Bedroom', 'Grand Dining Room', 'Opulent Throne Room', 'Grand Mausoleum' }, - { 10000, 'Royal Bedroom', 'Royal Dining Room', 'Royal Throne Room', 'Royal Mausoleum' } -} - -function getRoomName(building, unit) - local info = room_type_table[building._type] - if not info or not building.is_room then - return utils.getBuildingName(building) - end - - local quality = building:getRoomValue(unit) - local row = room_quality_table[1] - for _,v in ipairs(room_quality_table) do - if v[1] <= quality then - row = v - else - break - end - end - return row[info.qidx] -end - -function makeRoomEntry(bld, unit, is_spouse) - local info = room_type_table[bld._type] or {} - - return { - obj = bld, - token = info.token or '?', - tile = info.tile or '?', - caption = getRoomName(bld, unit), - can_use = (not is_spouse or bld:canUseSpouseRoom()), - owner = unit - } -end - -function listRooms(unit, spouse) - local rv = {} - for _,v in pairs(unit.owned_buildings) do - if v.owner == unit then - rv[#rv+1] = makeRoomEntry(v, unit, spouse) - end - end - return rv -end - -function concat_lists(...) - local rv = {} - for i = 1,select('#',...) do - local v = select(i,...) - if v then - for _,x in ipairs(v) do rv[#rv+1] = x end - end - end - return rv -end - -RoomList = defclass(RoomList, guidm.MenuOverlay) - -RoomList.focus_path = 'room-list' - -RoomList.ATTRS{ unit = DEFAULT_NIL } - -function RoomList:init(info) - local unit = info.unit - local base_bld = df.global.world.selected_building - - self:assign{ - base_building = base_bld, - items = {}, selected = 1, - own_rooms = {}, spouse_rooms = {} - } - - self.old_viewport = self:getViewport() - self.old_cursor = guidm.getCursorPos() - - if unit then - self.own_rooms = listRooms(unit) - self.spouse = df.unit.find(unit.relations.spouse_id) - if self.spouse then - self.spouse_rooms = listRooms(self.spouse, unit) - end - self.items = concat_lists(self.own_rooms, self.spouse_rooms) - end - - if base_bld then - for i,v in ipairs(self.items) do - if v.obj == base_bld then - self.selected = i - v.tile = 26 - goto found - end - end - self.base_item = makeRoomEntry(base_bld, unit) - self.base_item.owner = unit - self.base_item.old_owner = base_bld.owner - self.base_item.tile = 26 - self.items = concat_lists({self.base_item}, self.items) - ::found:: - end -end - -local sex_char = { [0] = 12, [1] = 11 } - -function drawUnitName(dc, unit) - dc:pen(COLOR_GREY) - if unit then - local color = dfhack.units.getProfessionColor(unit) - dc:char(sex_char[unit.sex] or '?'):advance(1):pen(color) - - local vname = dfhack.units.getVisibleName(unit) - if vname and vname.has_name then - dc:string(dfhack.TranslateName(vname)..', ') - end - dc:string(dfhack.units.getProfessionName(unit)) - else - dc:string("No Owner Assigned") - end -end - -function drawRoomEntry(dc, entry, selected) - local color = COLOR_GREEN - if not entry.can_use then - color = COLOR_RED - elseif entry.obj.owner ~= entry.owner or not entry.owner then - color = COLOR_CYAN - end - dc:pen{fg = color, bold = (selected == entry)} - dc:char(entry.tile):advance(1):string(entry.caption) -end - -function can_modify(sel_item) - return sel_item and sel_item.owner - and sel_item.can_use and not sel_item.owner.flags1.dead -end - -function RoomList:onRenderBody(dc) - local sel_item = self.items[self.selected] - - dc:clear():seek(1,1) - drawUnitName(dc, self.unit) - - if self.base_item then - dc:newline():newline(2) - drawRoomEntry(dc, self.base_item, sel_item) - end - if #self.own_rooms > 0 then - dc:newline() - for _,v in ipairs(self.own_rooms) do - dc:newline(2) - drawRoomEntry(dc, v, sel_item) - end - end - if #self.spouse_rooms > 0 then - dc:newline():newline(1) - drawUnitName(dc, self.spouse) - - dc:newline() - for _,v in ipairs(self.spouse_rooms) do - dc:newline(2) - drawRoomEntry(dc, v, sel_item) - end - end - if self.unit and #self.own_rooms == 0 and #self.spouse_rooms == 0 then - dc:newline():newline(2):string("No already assigned rooms.", COLOR_LIGHTRED) - end - - dc:newline():newline(1):pen(COLOR_WHITE) - dc:key('LEAVESCREEN'):string(": Back") - - if can_modify(sel_item) then - dc:string(", "):key('SELECT') - if sel_item.obj.owner == sel_item.owner then - dc:string(": Unassign") - else - dc:string(": Assign") - end - end -end - -function RoomList:changeSelected(delta) - if #self.items <= 1 then return end - self.selected = 1 + (self.selected + delta - 1) % #self.items - self:selectBuilding(self.items[self.selected].obj) -end - -function RoomList:onInput(keys) - local sel_item = self.items[self.selected] - - if keys.SECONDSCROLL_UP then - self:changeSelected(-1) - elseif keys.SECONDSCROLL_DOWN then - self:changeSelected(1) - elseif keys.LEAVESCREEN then - self:dismiss() - - if self.base_building then - if not sel_item or self.base_building ~= sel_item.obj then - self:selectBuilding(self.base_building, self.old_cursor, self.old_view) - end - if self.unit and self.base_building.owner == self.unit then - df.global.ui_building_in_assign = false - end - end - elseif keys.SELECT then - if can_modify(sel_item) then - local owner = sel_item.owner - if sel_item.obj.owner == owner then - owner = sel_item.old_owner - end - dfhack.buildings.setOwner(sel_item.obj, owner) - end - elseif self:simulateViewScroll(keys) then - return - end -end - -local focus = dfhack.gui.getCurFocus() - -if focus == 'dwarfmode/QueryBuilding/Some/Assign/Unit' then - local unit = df.global.ui_building_assign_units[df.global.ui_building_item_cursor] - RoomList{ unit = unit }:show() -elseif string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some') then - local base = df.global.world.selected_building - RoomList{ unit = base.owner }:show() -else - qerror("This script requires the main dwarfmode view in 'q' mode") -end diff --git a/scripts/gui/siege-engine.lua b/scripts/gui/siege-engine.lua deleted file mode 100644 index 8ebfe0dc4..000000000 --- a/scripts/gui/siege-engine.lua +++ /dev/null @@ -1,536 +0,0 @@ --- Front-end for the siege engine plugin. ---[[=begin - -gui/siege-engine -================ -An in-game interface for `siege-engine`. - -Bind it to a key (the example config uses :kbd:`Alt`:kbd:`a`) and -activate after selecting a siege engine in :kbd:`q` mode. - -.. image:: /docs/images/siege-engine.png - -The main mode displays the current target, selected ammo item -type, linked stockpiles and the allowed operator skill range. The -map tile color is changed to signify if it can be hit by the -selected engine: green for fully reachable, blue for out of -range, red for blocked, yellow for partially blocked. - -Pressing :kbd:`r` changes into the target selection mode, which -works by highlighting two points with :kbd:`Enter` like all -designations. When a target area is set, the engine projectiles -are aimed at that area, or units within it (this doesn't actually -change the original aiming code, instead the projectile -trajectory parameters are rewritten as soon as it appears). - -After setting the target in this way for one engine, you can -'paste' the same area into others just by pressing :kbd:`p` in -the main page of this script. The area to paste is kept until you -quit DF, or select another area manually. - -Pressing :kbd:`t` switches to a mode for selecting a stockpile to -take ammo from. - -Exiting from the siege engine script via :kbd:`Esc` reverts the -view to the state prior to starting the script. -:kbd:`Shift`:kbd:`Esc` retains the current viewport, and also -exits from the :kbd:`q` mode to main menu. - -=end]] -local utils = require 'utils' -local gui = require 'gui' -local guidm = require 'gui.dwarfmode' -local dlg = require 'gui.dialogs' - -local plugin = require 'plugins.siege-engine' -local wmap = df.global.world.map - -local LEGENDARY = df.skill_rating.Legendary - --- Globals kept between script calls -last_target_min = last_target_min or nil -last_target_max = last_target_max or nil - -local item_choices = { - { caption = 'boulders (default)', item_type = df.item_type.BOULDER }, - { caption = 'blocks', item_type = df.item_type.BLOCKS }, - { caption = 'weapons', item_type = df.item_type.WEAPON }, - { caption = 'trap components', item_type = df.item_type.TRAPCOMP }, - { caption = 'bins', item_type = df.item_type.BIN }, - { caption = 'barrels', item_type = df.item_type.BARREL }, - { caption = 'cages', item_type = df.item_type.CAGE }, - { caption = 'anything', item_type = -1 }, -} - -local item_choice_idx = {} -for i,v in ipairs(item_choices) do - item_choice_idx[v.item_type] = i -end - -SiegeEngine = defclass(SiegeEngine, guidm.MenuOverlay) - -SiegeEngine.focus_path = 'siege-engine' - -SiegeEngine.ATTRS{ building = DEFAULT_NIL } - -function SiegeEngine:init() - self:assign{ - center = utils.getBuildingCenter(self.building), - selected_pile = 1, - mode_main = { - render = self:callback 'onRenderBody_main', - input = self:callback 'onInput_main', - }, - mode_aim = { - render = self:callback 'onRenderBody_aim', - input = self:callback 'onInput_aim', - }, - mode_pile = { - render = self:callback 'onRenderBody_pile', - input = self:callback 'onInput_pile', - } - } -end - -function SiegeEngine:onShow() - SiegeEngine.super.onShow(self) - - self.old_cursor = guidm.getCursorPos() - self.old_viewport = self:getViewport() - - self.mode = self.mode_main - self:showCursor(false) -end - -function SiegeEngine:onDestroy() - if self.save_profile then - plugin.saveWorkshopProfile(self.building) - end - if not self.no_select_building then - self:selectBuilding(self.building, self.old_cursor, self.old_viewport, 10) - end -end - -function SiegeEngine:onGetSelectedBuilding() - return df.global.world.selected_building -end - -function SiegeEngine:showCursor(enable) - local cursor = guidm.getCursorPos() - if cursor and not enable then - self.cursor = cursor - self.target_select_first = nil - guidm.clearCursorPos() - elseif not cursor and enable then - local view = self:getViewport() - cursor = self.cursor - if not cursor or not view:isVisible(cursor) then - cursor = view:getCenter() - end - self.cursor = nil - guidm.setCursorPos(cursor) - end -end - -function SiegeEngine:centerViewOn(pos) - local cursor = guidm.getCursorPos() - if cursor then - guidm.setCursorPos(pos) - else - self.cursor = pos - end - self:getViewport():centerOn(pos):set() -end - -function SiegeEngine:zoomToTarget() - local target_min, target_max = plugin.getTargetArea(self.building) - if target_min then - local cx = math.floor((target_min.x + target_max.x)/2) - local cy = math.floor((target_min.y + target_max.y)/2) - local cz = math.floor((target_min.z + target_max.z)/2) - local pos = plugin.adjustToTarget(self.building, xyz2pos(cx,cy,cz)) - self:centerViewOn(pos) - end -end - -function paint_target_grid(dc, view, origin, p1, p2) - local r1, sz, r2 = guidm.getSelectionRange(p1, p2) - - if view.z < r1.z or view.z > r2.z then - return - end - - local p1 = view:tileToScreen(r1) - local p2 = view:tileToScreen(r2) - local org = view:tileToScreen(origin) - dc:pen{ fg = COLOR_CYAN, bg = COLOR_CYAN, ch = '+', bold = true } - - -- Frame - dc:fill(p1.x,p1.y,p1.x,p2.y) - dc:fill(p1.x,p1.y,p2.x,p1.y) - dc:fill(p2.x,p1.y,p2.x,p2.y) - dc:fill(p1.x,p2.y,p2.x,p2.y) - - -- Grid - local gxmin = org.x+10*math.ceil((p1.x-org.x)/10) - local gxmax = org.x+10*math.floor((p2.x-org.x)/10) - local gymin = org.y+10*math.ceil((p1.y-org.y)/10) - local gymax = org.y+10*math.floor((p2.y-org.y)/10) - for x = gxmin,gxmax,10 do - for y = gymin,gymax,10 do - dc:fill(p1.x,y,p2.x,y) - dc:fill(x,p1.y,x,p2.y) - end - end -end - -function SiegeEngine:renderTargetView(target_min, target_max) - local view = self:getViewport() - local map = self.df_layout.map - local map_dc = gui.Painter.new(map) - - plugin.paintAimScreen( - self.building, view:getPos(), - xy2pos(map.x1, map.y1), view:getSize() - ) - - if target_min and math.floor(dfhack.getTickCount()/500) % 2 == 0 then - paint_target_grid(map_dc, view, self.center, target_min, target_max) - end - - local cursor = guidm.getCursorPos() - if cursor then - local cx, cy, cz = pos2xyz(view:tileToScreen(cursor)) - if cz == 0 then - map_dc:seek(cx,cy):char('X', COLOR_YELLOW) - end - end -end - -function SiegeEngine:scrollPiles(delta) - local links = plugin.getStockpileLinks(self.building) - if links then - self.selected_pile = 1+(self.selected_pile+delta-1) % #links - return links[self.selected_pile] - end -end - -function SiegeEngine:renderStockpiles(dc, links, nlines) - local idx = (self.selected_pile-1) % #links - local page = math.floor(idx/nlines) - for i = page*nlines,math.min(#links,(page+1)*nlines)-1 do - local color = COLOR_BROWN - if i == idx then - color = COLOR_YELLOW - end - dc:newline(2):string(utils.getBuildingName(links[i+1]), color) - end -end - -function SiegeEngine:onRenderBody_main(dc) - dc:newline(1):pen(COLOR_WHITE):string("Target: ") - - local target_min, target_max = plugin.getTargetArea(self.building) - if target_min then - dc:string( - (target_max.x-target_min.x+1).."x".. - (target_max.y-target_min.y+1).."x".. - (target_max.z-target_min.z+1).." Rect" - ) - else - dc:string("None (default)") - end - - dc:newline(3):key('CUSTOM_R'):string(": Rectangle") - if last_target_min then - dc:string(", "):key('CUSTOM_P'):string(": Paste") - end - dc:newline(3) - if target_min then - dc:key('CUSTOM_X'):string(": Clear, ") - dc:key('CUSTOM_Z'):string(": Zoom") - end - - dc:newline():newline(1) - if self.building.type == df.siegeengine_type.Ballista then - dc:string("Uses ballista arrows") - else - local item = plugin.getAmmoItem(self.building) - dc:key('CUSTOM_U'):string(": Use ") - if item_choice_idx[item] then - dc:string(item_choices[item_choice_idx[item]].caption) - else - dc:string(df.item_type[item]) - end - end - - dc:newline():newline(1) - dc:key('CUSTOM_T'):string(": Take from stockpile"):newline(3) - local links = plugin.getStockpileLinks(self.building) - local bottom = dc.height - 5 - if links then - dc:key('CUSTOM_D'):string(": Delete, ") - dc:key('CUSTOM_O'):string(": Zoom"):newline() - self:renderStockpiles(dc, links, bottom-2-dc:cursorY()) - dc:newline():newline() - end - - local prof = self.building:getWorkshopProfile() or {} - dc:seek(1,math.max(dc:cursorY(),19)) - dc:key('CUSTOM_G'):key('CUSTOM_H'):key('CUSTOM_J'):key('CUSTOM_K') - dc:string(': ') - dc:string(df.skill_rating.attrs[prof.min_level or 0].caption):string('-') - dc:string(df.skill_rating.attrs[math.min(LEGENDARY,prof.max_level or 3000)].caption) - dc:newline():newline() - - if self.target_select_first then - self:renderTargetView(self.target_select_first, guidm.getCursorPos()) - else - self:renderTargetView(target_min, target_max) - end -end - -function SiegeEngine:setTargetArea(p1, p2) - self.target_select_first = nil - - if not plugin.setTargetArea(self.building, p1, p2) then - dlg.showMessage( - 'Set Target Area', - 'Could not set the target area', COLOR_LIGHTRED - ) - else - last_target_min = p1 - last_target_max = p2 - end -end - -function SiegeEngine:setAmmoItem(choice) - if self.building.type == df.siegeengine_type.Ballista then - return - end - - if not plugin.setAmmoItem(self.building, choice.item_type) then - dlg.showMessage( - 'Set Ammo Item', - 'Could not set the ammo item', COLOR_LIGHTRED - ) - end -end - -function SiegeEngine:onInput_main(keys) - if keys.CUSTOM_R then - self:showCursor(true) - self.target_select_first = nil - self.mode = self.mode_aim - elseif keys.CUSTOM_P and last_target_min then - self:setTargetArea(last_target_min, last_target_max) - elseif keys.CUSTOM_U then - local item = plugin.getAmmoItem(self.building) - local idx = 1 + (item_choice_idx[item] or 0) % #item_choices - self:setAmmoItem(item_choices[idx]) - elseif keys.CUSTOM_Z then - self:zoomToTarget() - elseif keys.CUSTOM_X then - plugin.clearTargetArea(self.building) - elseif keys.SECONDSCROLL_UP then - self:scrollPiles(-1) - elseif keys.SECONDSCROLL_DOWN then - self:scrollPiles(1) - elseif keys.CUSTOM_D then - local pile = self:scrollPiles(0) - if pile then - plugin.removeStockpileLink(self.building, pile) - end - elseif keys.CUSTOM_O then - local pile = self:scrollPiles(0) - if pile then - self:centerViewOn(utils.getBuildingCenter(pile)) - end - elseif keys.CUSTOM_T then - self:showCursor(true) - self.mode = self.mode_pile - self:sendInputToParent('CURSOR_DOWN_Z') - self:sendInputToParent('CURSOR_UP_Z') - elseif keys.CUSTOM_G then - local prof = plugin.saveWorkshopProfile(self.building) - prof.min_level = math.max(0, prof.min_level-1) - plugin.saveWorkshopProfile(self.building) - elseif keys.CUSTOM_H then - local prof = plugin.saveWorkshopProfile(self.building) - prof.min_level = math.min(LEGENDARY, prof.min_level+1) - plugin.saveWorkshopProfile(self.building) - elseif keys.CUSTOM_J then - local prof = plugin.saveWorkshopProfile(self.building) - prof.max_level = math.max(0, math.min(LEGENDARY,prof.max_level)-1) - plugin.saveWorkshopProfile(self.building) - elseif keys.CUSTOM_K then - local prof = plugin.saveWorkshopProfile(self.building) - prof.max_level = math.min(LEGENDARY, prof.max_level+1) - if prof.max_level >= LEGENDARY then prof.max_level = 3000 end - plugin.saveWorkshopProfile(self.building) - elseif self:simulateViewScroll(keys) then - self.cursor = nil - else - return false - end - return true -end - -local status_table = { - ok = { pen = COLOR_GREEN, msg = "Target accessible" }, - out_of_range = { pen = COLOR_CYAN, msg = "Target out of range" }, - blocked = { pen = COLOR_RED, msg = "Target obstructed" }, - semi_blocked = { pen = COLOR_BROWN, msg = "Partially obstructed" }, -} - -function SiegeEngine:onRenderBody_aim(dc) - local cursor = guidm.getCursorPos() - local first = self.target_select_first - - dc:newline(1):string('Select target rectangle'):newline() - - local info = status_table[plugin.getTileStatus(self.building, cursor)] - if info then - dc:newline(2):string(info.msg, info.pen) - else - dc:newline(2):string('ERROR', COLOR_RED) - end - - dc:newline():newline(1):key('SELECT') - if first then - dc:string(": Finish rectangle") - else - dc:string(": Start rectangle") - end - dc:newline() - - local target_min, target_max = plugin.getTargetArea(self.building) - if target_min then - dc:newline(1):key('CUSTOM_Z'):string(": Zoom to current target") - end - - if first then - self:renderTargetView(first, cursor) - else - local target_min, target_max = plugin.getTargetArea(self.building) - self:renderTargetView(target_min, target_max) - end -end - -function SiegeEngine:onInput_aim(keys) - if keys.SELECT then - local cursor = guidm.getCursorPos() - if self.target_select_first then - self:setTargetArea(self.target_select_first, cursor) - - self.mode = self.mode_main - self:showCursor(false) - else - self.target_select_first = cursor - end - elseif keys.CUSTOM_Z then - self:zoomToTarget() - elseif keys.LEAVESCREEN then - self.mode = self.mode_main - self:showCursor(false) - elseif self:simulateCursorMovement(keys) then - self.cursor = nil - else - return false - end - return true -end - -function SiegeEngine:onRenderBody_pile(dc) - dc:newline(1):string('Select pile to take from'):newline():newline(2) - - local sel = df.global.world.selected_building - - if df.building_stockpilest:is_instance(sel) then - dc:string(utils.getBuildingName(sel), COLOR_GREEN):newline():newline(1) - - if plugin.isLinkedToPile(self.building, sel) then - dc:string("Already taking from here"):newline():newline(2) - dc:key('CUSTOM_D'):string(": Delete link") - else - dc:key('SELECT'):string(": Take from this pile") - end - elseif sel then - dc:string(utils.getBuildingName(sel), COLOR_DARKGREY) - dc:newline():newline(1) - dc:string("Not a stockpile",COLOR_LIGHTRED) - else - dc:string("No building selected", COLOR_DARKGREY) - end -end - -function SiegeEngine:onInput_pile(keys) - if keys.SELECT then - local sel = df.global.world.selected_building - if df.building_stockpilest:is_instance(sel) - and not plugin.isLinkedToPile(self.building, sel) then - plugin.addStockpileLink(self.building, sel) - - df.global.world.selected_building = self.building - self.mode = self.mode_main - self:showCursor(false) - end - elseif keys.CUSTOM_D then - local sel = df.global.world.selected_building - if df.building_stockpilest:is_instance(sel) then - plugin.removeStockpileLink(self.building, sel) - end - elseif keys.LEAVESCREEN then - df.global.world.selected_building = self.building - self.mode = self.mode_main - self:showCursor(false) - elseif self:propagateMoveKeys(keys) then - -- - else - return false - end - return true -end - -function SiegeEngine:onRenderBody(dc) - dc:clear() - dc:seek(1,1):pen(COLOR_WHITE):string(utils.getBuildingName(self.building)):newline() - - self.mode.render(dc) - - dc:seek(1, math.max(dc:cursorY(), 21)):pen(COLOR_WHITE) - dc:key('LEAVESCREEN'):string(": Back, ") - dc:key('CUSTOM_C'):string(": Recenter") -end - -function SiegeEngine:onInput(keys) - if self.mode.input(keys) then - -- - elseif keys.CUSTOM_C then - self:centerViewOn(self.center) - elseif keys.LEAVESCREEN then - self:dismiss() - elseif keys.LEAVESCREEN_ALL then - self:dismiss() - self.no_select_building = true - guidm.clearCursorPos() - df.global.ui.main.mode = df.ui_sidebar_mode.Default - df.global.world.selected_building = nil - end -end - -if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some/SiegeEngine') then - qerror("This script requires a siege engine selected in 'q' mode") -end - -local building = df.global.world.selected_building - -if not df.building_siegeenginest:is_instance(building) then - qerror("A siege engine must be selected") -end -if building:getBuildStage() < building:getMaxBuildStage() then - qerror("This engine is not completely built yet") -end - -local list = SiegeEngine{ building = building } -list:show() diff --git a/scripts/gui/stockpiles.lua b/scripts/gui/stockpiles.lua deleted file mode 100644 index 4e2f28281..000000000 --- a/scripts/gui/stockpiles.lua +++ /dev/null @@ -1,78 +0,0 @@ --- lave/load stockpile settings with a GUI ---[[=begin - -gui/stockpiles -============== -An in-game interface for `stocksettings`, to -load and save stockpile settings from the :kbd:`q` menu. - -Usage: - -:gui/stockpiles -save: to save the current stockpile -:gui/stockpiles -load: to load settings into the current stockpile -:gui/stockpiles -dir : set the default directory to save settings into -:gui/stockpiles -help: to see this message - -Don't forget to ``enable stockpiles`` and create the ``stocksettings`` directory in -the DF folder before trying to use the GUI. - -=end]] -local stock = require 'plugins.stockpiles' - -function check_enabled() - return stock.isEnabled() -end - -function world_guard() - if not dfhack.isMapLoaded() then - qerror("World is not loaded") - return false - end - return true -end - -function guard() - if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some/Stockpile') then - qerror("This script requires a stockpile selected in the 'q' mode") - return false - end - return true -end - -utils = require('utils') -validArgs = validArgs or utils.invert({ - 'help', - 'load', - 'save', - 'dir', -}) - -args = utils.processArgs({...}, validArgs) - -function usage() - print("") - print("Stockpile Settings. Arguments: ") - print("-save to save the current stockpile") - print("-load to load settings into the current stockpile") - print("-dir set the default directory to save settings into") - if dfhack.isMapLoaded() then - print(" Current directory is: " .. stock.get_path()) - end - print("") -end - -if not check_enabled() then - qerror("Stockpiles plugin not enabled. Enable it with: enable stockpiles") -elseif args.load then - if not guard() then return end - stock.load_settings() -elseif args.save then - if not guard() then return end - local sp = dfhack.gui.getSelectedBuilding(true) - stock.save_settings(sp) -elseif args.dir then - if not world_guard() then return end - stock.set_path(args.dir) -else - usage() -end diff --git a/scripts/gui/unit-info-viewer.lua b/scripts/gui/unit-info-viewer.lua deleted file mode 100644 index 1d7cb763d..000000000 --- a/scripts/gui/unit-info-viewer.lua +++ /dev/null @@ -1,800 +0,0 @@ --- unit-info-viewer.lua --- Displays age, birth, maxage, shearing, milking, grazing, egg laying, body size, and death info about a unit. Recommended keybinding Alt-I --- version 1.04 --- original author: Kurik Amudnil --- edited by expwnent ---[[=begin - -gui/unit-info-viewer -==================== -Displays age, birth, maxage, shearing, milking, grazing, egg laying, body size, -and death info about a unit. Recommended keybinding :kbd:`Alt`:kbd:`I`. - -=end]] -local gui = require 'gui' -local widgets =require 'gui.widgets' -local utils = require 'utils' - -local DEBUG = false -if DEBUG then print('-----') end - -local pens = { - BLACK = dfhack.pen.parse{fg=COLOR_BLACK,bg=0}, - BLUE = dfhack.pen.parse{fg=COLOR_BLUE,bg=0}, - GREEN = dfhack.pen.parse{fg=COLOR_GREEN,bg=0}, - CYAN = dfhack.pen.parse{fg=COLOR_CYAN,bg=0}, - RED = dfhack.pen.parse{fg=COLOR_RED,bg=0}, - MAGENTA = dfhack.pen.parse{fg=COLOR_MAGENTA,bg=0}, - BROWN = dfhack.pen.parse{fg=COLOR_BROWN,bg=0}, - GREY = dfhack.pen.parse{fg=COLOR_GREY,bg=0}, - DARKGREY = dfhack.pen.parse{fg=COLOR_DARKGREY,bg=0}, - LIGHTBLUE = dfhack.pen.parse{fg=COLOR_LIGHTBLUE,bg=0}, - LIGHTGREEN = dfhack.pen.parse{fg=COLOR_LIGHTGREEN,bg=0}, - LIGHTCYAN = dfhack.pen.parse{fg=COLOR_LIGHTCYAN,bg=0}, - LIGHTRED = dfhack.pen.parse{fg=COLOR_LIGHTRED,bg=0}, - LIGHTMAGENTA = dfhack.pen.parse{fg=COLOR_LIGHTMAGENTA,bg=0}, - YELLOW = dfhack.pen.parse{fg=COLOR_YELLOW,bg=0}, - WHITE = dfhack.pen.parse{fg=COLOR_WHITE,bg=0}, -} - -function getUnit_byID(id) -- get unit by id from units.all via binsearch - if type(id) == 'number' then - -- (vector,key,field,cmpfun,min,max) { item/nil , found true/false , idx/insert at } - return utils.binsearch(df.global.world.units.all,id,'id') - end -end - -function getUnit_byVS(silent) -- by view screen mode - silent = silent or false - -- if not world loaded, return nil ? - local u,tmp -- u: the unit to return ; tmp: temporary for intermediate tests/return values - local v = dfhack.gui.getCurViewscreen() - u = dfhack.gui.getSelectedUnit(true) -- supports gui scripts/plugin that provide a hook for getSelectedUnit() - if u then - return u - -- else: contexts not currently supported by dfhack.gui.getSelectedUnit() - elseif df.viewscreen_dwarfmodest:is_instance(v) then - tmp = df.global.ui.main.mode - if tmp == 17 or tmp == 42 or tmp == 43 then - -- context: @dwarfmode/QueryBuiding/Some/Cage -- (q)uery cage - -- context: @dwarfmode/ZonesPenInfo/AssignUnit -- i (zone) -> pe(N) - -- context: @dwarfmode/ZonesPitInfo -- i (zone) -> (P)it - u = df.global.ui_building_assign_units[df.global.ui_building_item_cursor] - elseif tmp == 49 and df.global.ui.burrows.in_add_units_mode then - -- @dwarfmode/Burrows/AddUnits - u = df.global.ui.burrows.list_units[ df.global.ui.burrows.unit_cursor_pos ] - - elseif df.global.ui.follow_unit ~= -1 then - -- context: follow unit mode - u = getUnit_byID(df.global.ui.follow_unit) - end -- end viewscreen_dwarfmodest - elseif df.viewscreen_petst:is_instance(v) then - -- context: @pet/List/Unit -- z (status) -> animals - if v.mode == 0 then -- List - if not v.is_vermin[v.cursor] then - u = v.animal[v.cursor].unit - end - --elseif v.mode = 1 then -- training knowledge (no unit reference) - elseif v.mode == 2 then -- select trainer - u = v.trainer_unit[v.trainer_cursor] - end - elseif df.viewscreen_layer_workshop_profilest:is_instance(v) then - -- context: @layer_workshop_profile/Unit -- (q)uery workshop -> (P)rofile -- df.global.ui.main.mode == 17 - u = v.workers[v.layer_objects[0].cursor] - elseif df.viewscreen_layer_overall_healthst:is_instance(v) then - -- context @layer_overall_health/Units -- z -> health - u = v.unit[v.layer_objects[0].cursor] - elseif df.viewscreen_layer_militaryst:is_instance(v) then - local PG_ASSIGNMENTS = 0 - local PG_EQUIPMENT = 2 - local TB_POSITIONS = 1 - local TB_CANDIDATES = 2 - -- layer_objects[0: squads list; 1: positions list; 2: candidates list] - -- page 0:positions/assignments 1:alerts 2:equipment 3:uniforms 4:supplies 5:ammunition - if v.page == PG_ASSIGNMENTS and v.layer_objects[TB_CANDIDATES].enabled and v.layer_objects[TB_CANDIDATES].active then - -- context: @layer_military/Positions/Position/Candidates -- m -> Candidates - u = v.positions.candidates[v.layer_objects[TB_CANDIDATES].cursor] - elseif v.page == PG_ASSIGNMENTS and v.layer_objects[TB_POSITIONS].enabled and v.layer_objects[TB_POSITIONS].active then - -- context: @layer_military/Positions/Position -- m -> Positions - u = v.positions.assigned[v.layer_objects[TB_POSITIONS].cursor] - elseif v.page == PG_EQUIPMENT and v.layer_objects[TB_POSITIONS].enabled and v.layer_objects[TB_POSITIONS].active then - -- context: @layer_military/Equip/Customize/View/Position -- m -> (e)quip -> Positions - -- context: @layer_military/Equip/Uniform/Positions -- m -> (e)quip -> assign (U)niforms -> Positions - u = v.equip.units[v.layer_objects[TB_POSITIONS].cursor] - end - elseif df.viewscreen_layer_noblelistst:is_instance(v) then - if v.mode == 0 then - -- context: @layer_noblelist/List -- (n)obles - u = v.info[v.layer_objects[v.mode].cursor].unit - elseif v.mode == 1 then - -- context: @layer_noblelist/Appoint -- (n)obles -> (r)eplace - u = v.candidates[v.layer_objects[v.mode].cursor].unit - end - elseif df.viewscreen_unitst:is_instance(v) then - -- @unit -- (v)unit -> z ; loo(k) -> enter ; (n)obles -> enter ; others - u = v.unit - elseif df.viewscreen_customize_unitst:is_instance(v) then - -- @customize_unit -- @unit -> y - u = v.unit - elseif df.viewscreen_layer_unit_healthst:is_instance(v) then - -- @layer_unit_health -- @unit -> h ; @layer_overall_health/Units -> enter - if df.viewscreen_layer_overall_healthst:is_instance(v.parent) then - -- context @layer_overall_health/Units -- z (status)-> health - u = v.parent.unit[v.parent.layer_objects[0].cursor] - elseif df.viewscreen_unitst:is_instance(v.parent) then - -- @unit -- (v)unit -> z ; loo(k) -> enter ; (n)obles -> enter ; others - u = v.parent.unit - end - elseif df.viewscreen_textviewerst:is_instance(v) then - -- @textviewer -- @unit -> enter (thoughts and preferences) - if df.viewscreen_unitst:is_instance(v.parent) then - -- @unit -- @unit -> enter (thoughts and preferences) - u = v.parent.unit - elseif df.viewscreen_itemst:is_instance(v.parent) then - tmp = v.parent.entry_ref[v.parent.cursor_pos] - if df.general_ref_unit:is_instance(tmp) then -- general_ref_unit and derived ; general_ref_contains_unitst ; others? - u = getUnit_byID(tmp.unit_id) - end - elseif df.viewscreen_dwarfmodest:is_instance(v.parent) then - tmp = df.global.ui.main.mode - if tmp == 24 then -- (v)iew units {g,i,p,w} -> z (thoughts and preferences) - -- context: @dwarfmode/ViewUnits/... - --if df.global.ui_selected_unit > -1 then -- -1 = 'no units nearby' - u = df.global.world.units.active[df.global.ui_selected_unit] - --end - elseif tmp == 25 then -- loo(k) unit -> enter (thoughs and preferences) - -- context: @dwarfmode/LookAround/Unit - tmp = df.global.ui_look_list.items[df.global.ui_look_cursor] - if tmp.type == 2 then -- 0:item 1:terrain >>2: unit<< 3:building 4:colony/vermin 7:spatter - u = tmp.unit - end - end - elseif df.viewscreen_unitlistst:is_instance(v.parent) then -- (u)nit list -> (v)iew unit (not citizen) - -- context: @unitlist/Citizens ; @unitlist/Livestock ; @unitlist/Others ; @unitlist/Dead - u = v.parent.units[v.parent.page][ v.parent.cursor_pos[v.parent.page] ] - end - end -- switch viewscreen - if not u and not silent then - dfhack.printerr('No unit is selected in the UI or context not supported.') - end - return u -end -- getUnit_byVS() - ---http://lua-users.org/wiki/StringRecipes ---------- -function str2FirstUpper(str) - return str:gsub("^%l", string.upper) -end - --------------------------------------------------- ---http://lua-users.org/wiki/StringRecipes ---------- -local function tchelper(first, rest) - return first:upper()..rest:lower() -end - --- Add extra characters to the pattern if you need to. _ and ' are --- found in the middle of identifiers and English words. --- We must also put %w_' into [%w_'] to make it handle normal stuff --- and extra stuff the same. --- This also turns hex numbers into, eg. 0Xa7d4 -function str2TitleCase(str) - return str:gsub("(%a)([%w_']*)", tchelper) -end - --------------------------------------------------- ---isBlank suggestion by http://stackoverflow.com/a/10330861 -function isBlank(x) - x = tostring(x) or "" - -- returns (not not match_begin), _ = match_end => not not true , _ => true - -- returns not not nil => false (no match) - return not not x:find("^%s*$") -end - ---http://lua-users.org/wiki/StringRecipes (removed indents since I am not using them) -function wrap(str, limit)--, indent, indent1) - --indent = indent or "" - --indent1 = indent1 or indent - local limit = limit or 72 - local here = 1 ---#indent1 - return str:gsub("(%s+)()(%S+)()", --indent1..str:gsub( - function(sp, st, word, fi) - if fi-here > limit then - here = st -- - #indent - return "\n"..word --..indent..word - end - end) -end - - --------------------------------------------------- ----------------------- Time ---------------------- --------------------------------------------------- -local TU_PER_DAY = 1200 ---[[ -if advmode then TU_PER_DAY = 86400 ? or only for cur_year_tick? -advmod_TU / 72 = ticks ---]] -local TU_PER_MONTH = TU_PER_DAY * 28 -local TU_PER_YEAR = TU_PER_MONTH * 12 - -local MONTHS = { - 'Granite', - 'Slate', - 'Felsite', - 'Hematite', - 'Malachite', - 'Galena', - 'Limestone', - 'Sandstone', - 'Timber', - 'Moonstone', - 'Opal', - 'Obsidian', -} -Time = defclass(Time) -function Time:init(args) - self.year = args.year or 0 - self.ticks = args.ticks or 0 -end -function Time:getDays() -- >>float<< Days as age (including years) - return self.year * 336 + (self.ticks / TU_PER_DAY) -end -function Time:getMonths() -- >>int<< Months as age (not including years) - return math.floor (self.ticks / TU_PER_MONTH) -end -function Time:getMonthStr() -- Month as date - return MONTHS[self:getMonths()+1] or 'error' -end -function Time:getDayStr() -- Day as date - local d = math.floor ( (self.ticks % TU_PER_MONTH) / TU_PER_DAY ) + 1 - if d == 11 or d == 12 or d == 13 then - d = tostring(d)..'th' - elseif d % 10 == 1 then - d = tostring(d)..'st' - elseif d % 10 == 2 then - d = tostring(d)..'nd' - elseif d % 10 == 3 then - d = tostring(d)..'rd' - else - d = tostring(d)..'th' - end - return d -end ---function Time:__add() ---end -function Time:__sub(other) - if DEBUG then print(self.year,self.ticks) end - if DEBUG then print(other.year,other.ticks) end - if self.ticks < other.ticks then - return Time{ year = (self.year - other.year - 1) , ticks = (TU_PER_YEAR + self.ticks - other.ticks) } - else - return Time{ year = (self.year - other.year) , ticks = (self.ticks - other.ticks) } - end -end --------------------------------------------------- --------------------------------------------------- - --------------------------------------------------- --------------------- Identity -------------------- --------------------------------------------------- -local SINGULAR = 0 -local PLURAL = 1 ---local POSSESSIVE = 2 - -local PRONOUNS = { - [0]='She', - [1]='He', - [2]='It', -} -local BABY = 0 -local CHILD = 1 -local ADULT = 2 - -local TRAINING_LEVELS = { - [0] = ' (Semi-Wild)', -- Semi-wild - ' (Trained)', -- Trained - ' (-Trained-)', -- Well-trained - ' (+Trained+)', -- Skillfully trained - ' (*Trained*)', -- Expertly trained - ' ('..string.char(240)..'Trained'..string.char(240)..')', -- Exceptionally trained - ' ('..string.char(15)..'Trained'..string.char(15)..')', -- Masterully Trained - ' (Tame)', -- Domesticated - '', -- undefined - '', -- wild/untameable -} - -local DEATH_TYPES = { - [0] = ' died of old age', -- OLD_AGE - ' starved to death', -- HUNGER - ' died of dehydration', -- THIRST - ' was shot and killed', -- SHOT - ' bled to death', -- BLEED - ' drowned', -- DROWN - ' suffocated', -- SUFFOCATE - ' was struck down', -- STRUCK_DOWN - ' was scuttled', -- SCUTTLE - " didn't survive a collision", -- COLLISION - ' took a magma bath', -- MAGMA - ' took a magma shower', -- MAGMA_MIST - ' was incinerated by dragon fire', -- DRAGONFIRE - ' was killed by fire', -- FIRE - ' experienced death by SCALD', -- SCALD - ' was crushed by cavein', -- CAVEIN - ' was smashed by a drawbridge', -- DRAWBRIDGE - ' was killed by falling rocks', -- FALLING_ROCKS - ' experienced death by CHASM', -- CHASM - ' experienced death by CAGE', -- CAGE - ' was murdered', -- MURDER - ' was killed by a trap', -- TRAP - ' vanished', -- VANISH - ' experienced death by QUIT', -- QUIT - ' experienced death by ABANDON', -- ABANDON - ' suffered heat stroke', -- HEAT - ' died of hypothermia', -- COLD - ' experienced death by SPIKE', -- SPIKE - ' experienced death by ENCASE_LAVA', -- ENCASE_LAVA - ' experienced death by ENCASE_MAGMA', -- ENCASE_MAGMA - ' was preserved in ice', -- ENCASE_ICE - ' became headless', -- BEHEAD - ' was crucified', -- CRUCIFY - ' experienced death by BURY_ALIVE', -- BURY_ALIVE - ' experienced death by DROWN_ALT', -- DROWN_ALT - ' experienced death by BURN_ALIVE', -- BURN_ALIVE - ' experienced death by FEED_TO_BEASTS', -- FEED_TO_BEASTS - ' experienced death by HACK_TO_PIECES', -- HACK_TO_PIECES - ' choked on air', -- LEAVE_OUT_IN_AIR - ' experienced death by BOIL', -- BOIL - ' melted', -- MELT - ' experienced death by CONDENSE', -- CONDENSE - ' experienced death by SOLIDIFY', -- SOLIDIFY - ' succumbed to infection', -- INFECTION - "'s ghost was put to rest with a memorial", -- MEMORIALIZE - ' scared to death', -- SCARE - ' experienced death by DARKNESS', -- DARKNESS - ' experienced death by COLLAPSE', -- COLLAPSE - ' was drained of blood', -- DRAIN_BLOOD - ' was slaughtered', -- SLAUGHTER - ' became roadkill', -- VEHICLE - ' killed by a falling object', -- FALLING_OBJECT -} - ---GHOST_TYPES[unit.relations.ghost_info.type].." This spirit has not been properly memorialized or buried." -local GHOST_TYPES = { - [0]="A murderous ghost.", - "A sadistic ghost.", - "A secretive ghost.", - "An energetic poltergeist.", - "An angry ghost.", - "A violent ghost.", - "A moaning spirit returned from the dead. It will generally trouble one unfortunate at a time.", - "A howling spirit. The ceaseless noise is making sleep difficult.", - "A troublesome poltergeist.", - "A restless haunt, generally troubling past acquaintances and relatives.", - "A forlorn haunt, seeking out known locations or drifting around the place of death.", -} - - -Identity = defclass(Identity) -function Identity:init(args) - local u = args.unit - self.ident = dfhack.units.getIdentity(u) - - self.unit = u - self.name = dfhack.TranslateName( dfhack.units.getVisibleName(u) ) - self.name_en = dfhack.TranslateName( dfhack.units.getVisibleName(u) , true) - self.raw_prof = dfhack.units.getProfessionName(u) - self.pronoun = PRONOUNS[u.sex] or 'It' - - if self.ident then - self.birth_date = Time{year = self.ident.birth_year, ticks = self.ident.birth_second} - self.race_id = self.ident.race - self.caste_id = self.ident.caste - if self.ident.histfig_id > -1 then - self.hf_id = self.ident.histfig_id - end - else - self.birth_date = Time{year = self.unit.relations.birth_year, ticks = self.unit.relations.birth_time} - self.race_id = u.race - self.caste_id = u.caste - if u.hist_figure_id > -1 then - self.hf_id = u.hist_figure_id - end - end - self.race = df.global.world.raws.creatures.all[self.race_id] - self.caste = self.race.caste[self.caste_id] - - self.isCivCitizen = (df.global.ui.civ_id == u.civ_id) - self.isStray = u.flags1.tame --self.isCivCitizen and not u.flags1.merchant - self.cur_date = Time{year = df.global.cur_year, ticks = df.global.cur_year_tick} - - - ------------ death ------------ - self.dead = u.flags1.dead - self.ghostly = u.flags3.ghostly - self.undead = u.enemy.undead - - if self.dead and self.hf_id then -- dead-dead not undead-dead - local events = df.global.world.history.events2 - local e - for idx = #events - 1,0,-1 do - e = events[idx] - if df.history_event_hist_figure_diedst:is_instance(e) and e.victim_hf == self.hf_id then - self.death_event = e - break - end - end - end - if u.counters.death_id > -1 then -- if undead/ghostly dead or dead-dead - self.incident = df.global.world.incidents.all[u.counters.death_id] - if not self.incident.flags.discovered then - self.missing = true - end - end - -- slaughtered? - if self.death_event then - self.death_date = Time{year = self.death_event.year, ticks = self.death_event.seconds} - elseif self.incident then - self.death_date = Time{year = self.incident.event_year, ticks = self.incident.event_time} - end - -- age now or age death? - if self.dead and self.death_date then -- if cursed with no age? -- if hacked a ressurection, such that they aren't dead anymore, don't use the death date - self.age_time = self.death_date - self.birth_date - else - self.age_time = self.cur_date - self.birth_date - end - if DEBUG then print( self.age_time.year,self.age_time.ticks,self.age_time:getMonths() ) end - ---------- ---------- ---------- - - - ---------- caste_name ---------- - self.caste_name = {} - if isBlank(self.caste.caste_name[SINGULAR]) then - self.caste_name[SINGULAR] = self.race.name[SINGULAR] - else - self.caste_name[SINGULAR] = self.caste.caste_name[SINGULAR] - end - if isBlank(self.caste.caste_name[PLURAL]) then - self.caste_name[PLURAL] = self.race.name[PLURAL] - else - self.caste_name[PLURAL] = self.caste.caste_name[PLURAL] - end - ---------- ---------- ---------- - - --------- growth_status --------- - -- 'baby_age' is the age the baby becomes a child - -- 'child_age' is the age the child becomes an adult - if self.age_time.year >= self.caste.misc.child_age then -- has child come of age becoming adult? - self.growth_status = ADULT - elseif self.age_time.year >= self.caste.misc.baby_age then -- has baby come of age becoming child? - self.growth_status = CHILD - else - self.growth_status = BABY - end - ---------- ---------- ---------- - - -------- aged_caste_name -------- - local caste_name, race_name - if self.growth_status == ADULT then - caste_name = self.caste.caste_name[SINGULAR] - race_name = self.race.name[SINGULAR] - elseif self.growth_status == CHILD then - caste_name = self.caste.child_name[SINGULAR] - race_name = self.race.general_child_name[SINGULAR] - else --if self.growth_status == BABY then - caste_name = self.caste.baby_name[SINGULAR] - race_name = self.race.general_baby_name[SINGULAR] - end - self.aged_caste_name = {} - if isBlank(caste_name[SINGULAR]) then - self.aged_caste_name[SINGULAR] = race_name[SINGULAR] - else - self.aged_caste_name[SINGULAR] = caste_name[SINGULAR] - end - if isBlank(caste_name[PLURAL]) then - self.aged_caste_name[PLURAL] = race_name[PLURAL] - else - self.aged_caste_name[PLURAL] = caste_name[PLURAL] - end - ---------- ---------- ---------- - - ----- Profession adjustment ----- - local prof = self.raw_prof - if self.undead then - prof = str2TitleCase( self.caste_name[SINGULAR] ) - if isBlank(u.enemy.undead.anon_7) then - prof = prof..' Corpse' - else - prof = u.enemy.undead.anon_7 -- a reanimated body part will use this string instead - end - end - --[[ - if self.ghostly then - prof = 'Ghostly '..prof - end - --]] - if u.curse.name_visible and not isBlank(u.curse.name) then - prof = prof..' '..u.curse.name - end - if isBlank(self.name) then - if self.isStray then - prof = 'Stray '..prof --..TRAINING_LEVELS[u.training_level] - end - end - self.prof = prof - ---------- ---------- ---------- -end --------------------------------------------------- --------------------------------------------------- ---[[ - prof_id ? - group_id ? - fort_race_id - fort_civ_id - --fort_group_id? ---]] - - -UnitInfoViewer = defclass(UnitInfoViewer, gui.FramedScreen) -UnitInfoViewer.focus_path = 'unitinfoviewer' -- -> dfhack/lua/unitinfoviewer -UnitInfoViewer.ATTRS={ - frame_style = gui.GREY_LINE_FRAME, - frame_inset = 2, -- used by init - frame_outset = 1,--3, -- new, used by init; 0 = full screen, suggest 0, 1, or 3 or maybe 5 - --frame_title , -- not used - --frame_width,frame_height calculated by frame inset and outset in init -} -function UnitInfoViewer:init(args) -- requires args.unit - --if DEBUG then print('-----') end - local x,y = dfhack.screen.getWindowSize() - -- what if inset or outset are defined as {l,r,t,b}? - x = x - 2*(self.frame_inset + 1 + self.frame_outset) -- 1=frame border thickness - y = y - 2*(self.frame_inset + 1 + self.frame_outset) -- 1=frame border thickness - self.frame_width = args.frame_width or x - self.frame_height = args.frame_height or y - self.text = {} - if df.unit:is_instance(args.unit) then - self.ident = Identity{ unit = args.unit } - if not isBlank(self.ident.name_en) then - self.frame_title = 'Unit: '..self.ident.name_en - elseif not isBlank(self.ident.prof) then - self.frame_title = 'Unit: '..self.ident.prof - if self.ident.isStray then - self.frame_title = self.frame_title..TRAINING_LEVELS[self.ident.unit.training_level] - end - end - self:chunk_Name() - self:chunk_Description() - if not (self.ident.dead or self.ident.undead or self.ident.ghostly) then --not self.dead - if self.ident.isCivCitizen then - self:chunk_Age() - self:chunk_MaxAge() - end - if self.ident.isStray then - if self.ident.growth_status == ADULT then - self:chunk_Milkable() - end - self:chunk_Grazer() - if self.ident.growth_status == ADULT then - self:chunk_Shearable() - end - if self.ident.growth_status == ADULT then - self:chunk_EggLayer() - end - end - self:chunk_BodySize() - elseif self.ident.ghostly then - self:chunk_Dead() - self:chunk_Ghostly() - elseif self.ident.undead then - self:chunk_BodySize() - self:chunk_Dead() - else - self:chunk_Dead() - end - else - self:insert_chunk("No unit is selected in the UI or context not supported.",pens.LIGHTRED) - end - self:addviews{ widgets.Label{ frame={yalign=0}, text=self.text } } -end -function UnitInfoViewer:onInput(keys) - if keys.LEAVESCREEN or keys.SELECT then - self:dismiss() - end -end -function UnitInfoViewer:onGetSelectedUnit() - return self.ident.unit -end -function UnitInfoViewer:insert_chunk(str,pen) - local lines = utils.split_string( wrap(str,self.frame_width) , NEWLINE ) - for i = 1,#lines do - table.insert(self.text,{text=lines[i],pen=pen}) - table.insert(self.text,NEWLINE) - end - table.insert(self.text,NEWLINE) -end -function UnitInfoViewer:chunk_Name() - local i = self.ident - local u = i.unit - local prof = i.prof - local color = dfhack.units.getProfessionColor(u) - local blurb - if i.ghostly then - prof = 'Ghostly '..prof - end - if i.isStray then - prof = prof..TRAINING_LEVELS[u.training_level] - end - if isBlank(i.name) then - if isBlank(prof) then - blurb = 'I am a mystery' - else - blurb = prof - end - else - if isBlank(prof) then - blurb=i.name - else - blurb=i.name..', '..prof - end - end - self:insert_chunk(blurb,dfhack.pen.parse{fg=color,bg=0}) -end -function UnitInfoViewer:chunk_Description() - local dsc = self.ident.caste.description - if not isBlank(dsc) then - self:insert_chunk(dsc,pens.WHITE) - end -end - -function UnitInfoViewer:chunk_Age() - local i = self.ident - local age_str -- = '' - if i.age_time.year > 1 then - age_str = tostring(i.age_time.year)..' years old' - elseif i.age_time.year > 0 then -- == 1 - age_str = '1 year old' - else --if age_time.year == 0 then - local age_m = i.age_time:getMonths() -- math.floor - if age_m > 1 then - age_str = tostring(age_m)..' months old' - elseif age_m > 0 then -- age_m == 1 - age_str = '1 month old' - else -- if age_m == 0 then -- and age_m < 0 which would be an error - age_str = 'a newborn' - end - end - local blurb = i.pronoun..' is '..age_str - if i.race_id == df.global.ui.race_id then - blurb = blurb..', born on the '..i.birth_date:getDayStr()..' of '..i.birth_date:getMonthStr()..' in the year '..tostring(i.birth_date.year)..PERIOD - else - blurb = blurb..PERIOD - end - self:insert_chunk(blurb,pens.YELLOW) -end - -function UnitInfoViewer:chunk_MaxAge() - local i = self.ident - local maxage = math.floor( (i.caste.misc.maxage_max + i.caste.misc.maxage_min)/2 ) - --or i.unit.curse.add_tags1.NO_AGING hidden ident? - if i.caste.misc.maxage_min == -1 then - maxage = ' die of unnatural causes.' - elseif maxage == 0 then - maxage = ' die at a very young age.' - elseif maxage == 1 then - maxage = ' live about '..tostring(maxage)..' year.' - else - maxage = ' live about '..tostring(maxage)..' years.' - end - --' is expected to '.. - local blurb = str2FirstUpper(i.caste_name[PLURAL])..maxage - self:insert_chunk(blurb,pens.DARKGREY) -end -function UnitInfoViewer:chunk_Grazer() - if self.ident.caste.flags.GRAZER then - local blurb = 'Grazing satisfies '..tostring(self.ident.caste.misc.grazer)..' units of hunger.' - self:insert_chunk(blurb,pens.LIGHTGREEN) - end -end -function UnitInfoViewer:chunk_EggLayer() - local caste = self.ident.caste - if caste.flags.LAYS_EGGS then - local clutch = math.floor( (caste.misc.clutch_size_max + caste.misc.clutch_size_min)/2 ) - local blurb = 'Lays clutches of about '..tostring(clutch) - if clutch > 1 then - blurb = blurb..' eggs.' - else - blurb = blurb..' egg.' - end - self:insert_chunk(blurb,pens.GREEN) - end -end -function UnitInfoViewer:chunk_Milkable() - local i = self.ident - if i.caste.flags.MILKABLE then - local milk = dfhack.matinfo.decode( i.caste.extracts.milkable_mat , i.caste.extracts.milkable_matidx ) - if milk then - local days,seconds = math.modf ( i.caste.misc.milkable / TU_PER_DAY ) - days = (seconds > 0) and (tostring(days)..' to '..tostring(days + 1)) or tostring(days) - --local blurb = pronoun..' produces '..milk:toString()..' every '..days..' days.' - local blurb = (i.growth_status == ADULT) and (i.pronoun..' secretes ') or str2FirstUpper(i.caste_name[PLURAL])..' secrete ' - blurb = blurb..milk:toString()..' every '..days..' days.' - self:insert_chunk(blurb,pens.LIGHTCYAN) - end - end -end -function UnitInfoViewer:chunk_Shearable() - local i = self.ident - local mat_types = i.caste.body_info.materials.mat_type - local mat_idxs = i.caste.body_info.materials.mat_index - local mat_info, blurb - for idx,mat_type in ipairs(mat_types) do - mat_info = dfhack.matinfo.decode(mat_type,mat_idxs[idx]) - if mat_info and mat_info.material.flags.YARN then - blurb = (i.growth_status == ADULT) and (i.pronoun..' produces ') or str2FirstUpper(i.caste_name[PLURAL])..' produce ' - blurb = blurb..mat_info:toString()..PERIOD - self:insert_chunk(blurb,pens.BROWN) - end - end -end -function UnitInfoViewer:chunk_BodySize() - local i = self.ident - local pat = i.unit.body.physical_attrs - local blurb = i.pronoun..' appears to be about '..pat.STRENGTH.value..':'..pat.AGILITY.value..' cubic decimeters in size.' - self:insert_chunk(blurb,pens.LIGHTBLUE) -end -function UnitInfoViewer:chunk_Ghostly() - local blurb = GHOST_TYPES[self.ident.unit.relations.ghost_info.type].." This spirit has not been properly memorialized or buried." - self:insert_chunk(blurb,pens.LIGHTMAGENTA) - -- Arose in relations.curse_year curse_time -end -function UnitInfoViewer:chunk_Dead() - local i = self.ident - local blurb, str, pen - if i.missing then --dfhack.units.isDead(unit) - str = ' is missing.' - pen = pens.WHITE - elseif i.death_event then - --str = "The Caste_name Unit_Name died in year #{e.year}" - --str << " (cause: #{e.death_cause.to_s.downcase})," - --str << " killed by the #{e.slayer_race_tg.name[0]} #{e.slayer_hf_tg.name}" if e.slayer_hf != -1 - --str << " using a #{df.world.raws.itemdefs.weapons[e.weapon.item_subtype].name}" if e.weapon.item_type == :WEAPON - --str << ", shot by a #{df.world.raws.itemdefs.weapons[e.weapon.bow_item_subtype].name}" if e.weapon.bow_item_type == :WEAPON - str = DEATH_TYPES[i.death_event.death_cause]..PERIOD - pen = pens.MAGENTA - elseif i.incident then - --str = "The #{u.race_tg.name[0]}" - --str << " #{u.name}" if u.name.has_name - --str << " died" - --str << " in year #{incident.event_year}" if incident - --str << " (cause: #{u.counters.death_cause.to_s.downcase})," if u.counters.death_cause != -1 - --str << " killed by the #{killer.race_tg.name[0]} #{killer.name}" if killer - str = DEATH_TYPES[i.incident.death_cause]..PERIOD - pen = pens.MAGENTA - elseif i.unit.flags2.slaughter and i.unit.flags2.killed then - str = ' was slaughtered.' - pen = pens.MAGENTA - else - str = ' is dead.' - pen = pens.MAGENTA - end - if i.undead or i.ghostly then - str = ' is undead.' - pen = pens.GREY - end - blurb = 'The '..i.prof -- assume prof is not blank - if not isBlank(i.name) then - blurb = blurb..', '..i.name - end - blurb = blurb..str - self:insert_chunk(blurb,pen) -end - --- only show if UnitInfoViewer isn't the current focus -if dfhack.gui.getCurFocus() ~= 'dfhack/lua/'..UnitInfoViewer.focus_path then - local gui_no_unit = false -- show if not found? - local unit = getUnit_byVS(gui_no_unit) -- silent? or let the gui display - if unit or gui_no_unit then - local kan_viewscreen = UnitInfoViewer{unit = unit} - kan_viewscreen:show() - end -end - diff --git a/scripts/gui/workflow.lua b/scripts/gui/workflow.lua deleted file mode 100644 index 6dda9e764..000000000 --- a/scripts/gui/workflow.lua +++ /dev/null @@ -1,1099 +0,0 @@ --- A GUI front-end for the workflow plugin. ---[[=begin - -gui/workflow -============ -Bind to a key (the example config uses Alt-W), and activate with a job selected -in a workshop in :kbd:`q` mode. - -.. image:: /docs/images/workflow.png - -This script provides a simple interface to constraints managed by `workflow`. -When active, it displays a list of all constraints applicable to the -current job, and their current status. - -A constraint specifies a certain range to be compared against either individual -*item* or whole *stack* count, an item type and optionally a material. When the -current count is below the lower bound of the range, the job is resumed; if it -is above or equal to the top bound, it will be suspended. Within the range, the -specific constraint has no effect on the job; others may still affect it. - -Pressing :kbd:`i` switches the current constraint between counting stacks or items. -Pressing :kbd:`r` lets you input the range directly; -:kbd:`e`, :kbd:`r`, :kbd:`d`, :kbd:`f` adjust the -bounds by 5, 10, or 20 depending on the direction and the :kbd:`i` setting (counting -items and expanding the range each gives a 2x bonus). - -Pressing :kbd:`a` produces a list of possible outputs of this job as guessed by -workflow, and lets you create a new constraint by choosing one as template. If you -don't see the choice you want in the list, it likely means you have to adjust -the job material first using `job` ``item-material`` or `gui/workshop-job`, -as described in the `workflow` documentation. In this manner, this feature -can be used for troubleshooting jobs that don't match the right constraints. - -.. image:: /docs/images/workflow-new1.png - -If you select one of the outputs with :kbd:`Enter`, the matching constraint is simply -added to the list. If you use :kbd:`Shift`:kbd:`Enter`, the interface proceeds to the -next dialog, which allows you to edit the suggested constraint parameters to -suit your need, and set the item count range. - -.. image:: /docs/images/workflow-new2.png - -Pressing :kbd:`s` (or, with the example config, Alt-W in the :kbd:`z` stocks screen) -opens the overall status screen: - -.. image:: /docs/images/workflow-status.png - -This screen shows all currently existing workflow constraints, and allows -monitoring and/or changing them from one screen. The constraint list can -be filtered by typing text in the field below. - -The color of the stock level number indicates how "healthy" the stock level -is, based on current count and trend. Bright green is very good, green is good, -red is bad, bright red is very bad. - -The limit number is also color-coded. Red means that there are currently no -workshops producing that item (i.e. no jobs). If it's yellow, that means the -production has been delayed, possibly due to lack of input materials. - -The chart on the right is a plot of the last 14 days (28 half day plots) worth -of stock history for the selected item, with the rightmost point representing -the current stock value. The bright green dashed line is the target -limit (maximum) and the dark green line is that minus the gap (minimum). - -=end]] -local utils = require 'utils' -local gui = require 'gui' -local guidm = require 'gui.dwarfmode' -local guimat = require 'gui.materials' -local widgets = require 'gui.widgets' -local dlg = require 'gui.dialogs' - -local workflow = require 'plugins.workflow' - -function check_enabled(cb) - if workflow.isEnabled() then - return cb() - else - dlg.showYesNoPrompt( - 'Enable Plugin', - { 'The workflow plugin is not enabled currently.', NEWLINE, NEWLINE, - 'Press ', { key = 'MENU_CONFIRM' }, ' to enable it.' }, - COLOR_YELLOW, - function() - workflow.setEnabled(true) - return cb() - end - ) - end -end - -function check_repeat(job, cb) - if job.flags['repeat'] then - return cb() - else - dlg.showYesNoPrompt( - 'Not Repeat Job', - { 'Workflow only tracks repeating jobs.', NEWLINE, NEWLINE, - 'Press ', { key = 'MENU_CONFIRM' }, ' to make this one repeat.' }, - COLOR_YELLOW, - function() - job.flags['repeat'] = true - return cb() - end - ) - end -end - -function describe_item_type(iobj) - local itemline = 'any item' - if iobj.is_craft then - itemline = 'any craft' - elseif iobj.item_type >= 0 then - itemline = df.item_type.attrs[iobj.item_type].caption or iobj.item_type - local subtype = iobj.item_subtype or -1 - local def = dfhack.items.getSubtypeDef(iobj.item_type, subtype) - local count = dfhack.items.getSubtypeCount(iobj.item_type, subtype) - if def then - itemline = def.name - elseif count >= 0 then - itemline = 'any '..itemline - end - end - return itemline -end - -function is_caste_mat(iobj) - return dfhack.items.isCasteMaterial(iobj.item_type or -1) -end - -function describe_material(iobj) - local matflags = utils.list_bitfield_flags(iobj.mat_mask) - if #matflags > 0 then - matflags = 'any '..table.concat(matflags, '/') - else - matflags = nil - end - - if is_caste_mat(iobj) then - return 'no material' - elseif (iobj.mat_type or -1) >= 0 then - local info = dfhack.matinfo.decode(iobj.mat_type, iobj.mat_index) - local matline - if info then - matline = info:toString() - else - matline = iobj.mat_type..':'..iobj.mat_index - end - return matline, matflags - else - return matflags or 'any material' - end -end - -function current_stock(iobj) - if iobj.goal_by_count then - return iobj.cur_count - else - return iobj.cur_amount - end -end - -function if_by_count(iobj,bc,ba) - if iobj.goal_by_count then - return bc - else - return ba - end -end - -function compute_trend(history,field) - local count = #history - if count == 0 then - return 0 - end - local sumX,sumY,sumXY,sumXX = 0,0,0,0 - for i,v in ipairs(history) do - sumX = sumX + i - sumY = sumY + v[field] - sumXY = sumXY + i*v[field] - sumXX = sumXX + i*i - end - return (count * sumXY - sumX * sumY) / (count * sumXX - sumX * sumX) -end - ------------------------- --- RANGE EDITOR GROUP -- ------------------------- - -local null_cons = { goal_value = 0, goal_gap = 0, goal_by_count = false } - -RangeEditor = defclass(RangeEditor, widgets.Label) - -RangeEditor.ATTRS { - get_cb = DEFAULT_NIL, - save_cb = DEFAULT_NIL, - keys = { - count = 'CUSTOM_SHIFT_I', - modify = 'CUSTOM_SHIFT_R', - min_dec = 'BUILDING_TRIGGER_MIN_SIZE_DOWN', - min_inc = 'BUILDING_TRIGGER_MIN_SIZE_UP', - max_dec = 'BUILDING_TRIGGER_MAX_SIZE_DOWN', - max_inc = 'BUILDING_TRIGGER_MAX_SIZE_UP', - } -} - -function RangeEditor:init(args) - self:setText{ - { key = self.keys.count, - text = function() - local cons = self.get_cb() or null_cons - if cons.goal_by_count then - return ': Count stacks ' - else - return ': Count items ' - end - end, - on_activate = self:callback('onChangeUnit') }, - { key = self.keys.modify, text = ': Range', - on_activate = self:callback('onEditRange') }, - NEWLINE, ' ', - { key = self.keys.min_dec, - on_activate = self:callback('onIncRange', 'goal_gap', 2) }, - { key = self.keys.min_inc, - on_activate = self:callback('onIncRange', 'goal_gap', -1) }, - { text = function() - local cons = self.get_cb() or null_cons - return string.format(': Min %-4d ', cons.goal_value - cons.goal_gap) - end }, - { key = self.keys.max_dec, - on_activate = self:callback('onIncRange', 'goal_value', -1) }, - { key = self.keys.max_inc, - on_activate = self:callback('onIncRange', 'goal_value', 2) }, - { text = function() - local cons = self.get_cb() or null_cons - return string.format(': Max %-4d', cons.goal_value) - end }, - } -end - -function RangeEditor:onChangeUnit() - local cons = self.get_cb() - cons.goal_by_count = not cons.goal_by_count - self.save_cb(cons) -end - -function RangeEditor:onEditRange() - local cons = self.get_cb() - dlg.showInputPrompt( - 'Input Range', - 'Enter the new constraint range:', - COLOR_WHITE, - (cons.goal_value-cons.goal_gap)..'-'..cons.goal_value, - function(text) - local maxv = string.match(text, '^%s*(%d+)%s*$') - if maxv then - cons.goal_value = maxv - return self.save_cb(cons) - end - local minv,maxv = string.match(text, '^%s*(%d+)-(%d+)%s*$') - if minv and maxv and minv ~= maxv then - cons.goal_value = math.max(minv,maxv) - cons.goal_gap = math.abs(maxv-minv) - return self.save_cb(cons) - end - dlg.showMessage('Invalid Range', 'This range is invalid: '..text, COLOR_LIGHTRED) - end - ) -end - -function RangeEditor:onIncRange(field, delta) - local cons = self.get_cb() - if not cons.goal_by_count then - delta = delta * 2 - end - cons[field] = math.max(1, cons[field] + delta*5) - self.save_cb(cons) -end - ---------------------------- --- NEW CONSTRAINT DIALOG -- ---------------------------- - -NewConstraint = defclass(NewConstraint, gui.FramedScreen) - -NewConstraint.focus_path = 'workflow/new' - -NewConstraint.ATTRS { - frame_style = gui.GREY_LINE_FRAME, - frame_title = 'New workflow constraint', - frame_width = 39, - frame_height = 20, - frame_inset = 1, - constraint = DEFAULT_NIL, - on_submit = DEFAULT_NIL, -} - -function NewConstraint:init(args) - self.constraint = args.constraint or { item_type = -1 } - rawset_default(self.constraint, { goal_value = 10, goal_gap = 5, goal_by_count = false }) - - local matlist = {} - local matsel = 1 - local matmask = self.constraint.mat_mask - - for i,v in ipairs(df.dfhack_material_category) do - if v and v ~= 'wood2' then - table.insert(matlist, { icon = self:callback('isMatSelected', v), text = v }) - if matmask and matmask[v] and matsel == 1 then - matsel = #matlist - end - end - end - - self:addviews{ - widgets.Label{ - frame = { l = 0, t = 0 }, - text = 'Items matching:' - }, - widgets.Label{ - frame = { l = 1, t = 2, w = 26 }, - text = { - 'Type: ', - { pen = function() - if self:isValid() then return COLOR_LIGHTCYAN else return COLOR_LIGHTRED end - end, - text = function() - if self:isValid() then - return describe_item_type(self.constraint) - else - return 'item not set' - end - end }, - NEWLINE, ' ', - { key = 'CUSTOM_T', text = ': Select, ', - on_activate = self:callback('chooseType') }, - { key = 'CUSTOM_SHIFT_C', text = ': Crafts', - on_activate = self:callback('chooseCrafts') }, - NEWLINE, NEWLINE, - 'Material: ', - { pen = COLOR_LIGHTCYAN, - text = function() return describe_material(self.constraint) end }, - NEWLINE, ' ', - { key = 'CUSTOM_P', text = ': Specific', - on_activate = self:callback('chooseMaterial') }, - NEWLINE, NEWLINE, - 'Other:', - NEWLINE, ' ', - { key = 'D_MILITARY_SUPPLIES_WATER_DOWN', - on_activate = self:callback('incQuality', -1) }, - { key = 'D_MILITARY_SUPPLIES_WATER_UP', key_sep = ': ', - text = function() - return df.item_quality[self.constraint.min_quality or 0]..' quality' - end, - on_activate = self:callback('incQuality', 1) }, - NEWLINE, ' ', - { key = 'CUSTOM_L', key_sep = ': ', - text = function() - if self.constraint.is_local then - return 'Locally made only' - else - return 'Include foreign' - end - end, - on_activate = self:callback('toggleLocal') }, - } - }, - widgets.Label{ - frame = { l = 0, t = 14 }, - text = { - 'Desired range: ', - { pen = COLOR_LIGHTCYAN, - text = function() - local cons = self.constraint - local goal = (cons.goal_value-cons.goal_gap)..'-'..cons.goal_value - if cons.goal_by_count then - return goal .. ' stacks' - else - return goal .. ' items' - end - end }, - } - }, - RangeEditor{ - frame = { l = 1, t = 16 }, - get_cb = self:cb_getfield('constraint'), - save_cb = self:callback('onRangeChange'), - }, - widgets.Label{ - frame = { l = 30, t = 0 }, - text = 'Mat class' - }, - widgets.List{ - view_id = 'matlist', - frame = { l = 30, t = 2, w = 9, h = 18 }, - scroll_keys = widgets.STANDARDSCROLL, - choices = matlist, - selected = matsel, - on_submit = self:callback('onToggleMatclass') - }, - widgets.Label{ - frame = { l = 0, b = 0, w = 29 }, - text = { - { key = 'LEAVESCREEN', text = ': Cancel, ', - on_activate = self:callback('dismiss') }, - { key = 'MENU_CONFIRM', key_sep = ': ', - enabled = self:callback('isValid'), - text = function() - if self.is_existing then return 'Update' else return 'Create new' end - end, - on_activate = function() - self:dismiss() - if self.on_submit then - self.on_submit(self.constraint) - end - end }, - } - }, - } -end - -function NewConstraint:postinit() - self:onChange() -end - -function NewConstraint:isValid() - return self.constraint.item_type >= 0 or self.constraint.is_craft -end - -function NewConstraint:onChange() - local token = workflow.constraintToToken(self.constraint) - local out - - if self:isValid() then - out = workflow.findConstraint(token) - end - - if out then - self.constraint = out - self.is_existing = true - else - self.constraint.token = token - self.is_existing = false - end -end - -function NewConstraint:chooseType() - guimat.ItemTypeDialog{ - prompt = 'Please select a new item type', - hide_none = true, - on_select = function(itype,isub) - local cons = self.constraint - cons.item_type = itype - cons.item_subtype = isub - cons.is_craft = nil - self:onChange() - end - }:show() -end - -function NewConstraint:chooseCrafts() - local cons = self.constraint - cons.item_type = -1 - cons.item_subtype = -1 - cons.is_craft = true - self:onChange() -end - -function NewConstraint:chooseMaterial() - local cons = self.constraint - guimat.MaterialDialog{ - prompt = 'Please select a new material', - none_caption = 'any material', - frame_width = 37, - on_select = function(mat_type, mat_index) - local cons = self.constraint - cons.mat_type = mat_type - cons.mat_index = mat_index - cons.mat_mask = nil - self:onChange() - end - }:show() -end - -function NewConstraint:incQuality(diff) - local cons = self.constraint - local nq = (cons.min_quality or 0) + diff - if nq < 0 then - nq = df.item_quality.Masterful - elseif nq > df.item_quality.Masterful then - nq = 0 - end - cons.min_quality = nq - self:onChange() -end - -function NewConstraint:toggleLocal() - local cons = self.constraint - cons.is_local = not cons.is_local - self:onChange() -end - -function NewConstraint:isMatSelected(token) - if self.constraint.mat_mask and self.constraint.mat_mask[token] then - return { ch = '\xfb', fg = COLOR_LIGHTGREEN } - else - return nil - end -end - -function NewConstraint:onToggleMatclass(idx,obj) - local cons = self.constraint - if cons.mat_mask and cons.mat_mask[obj.text] then - cons.mat_mask[obj.text] = false - else - cons.mat_mask = cons.mat_mask or {} - cons.mat_mask[obj.text] = true - cons.mat_type = -1 - cons.mat_index = -1 - end - self:onChange() -end - -function NewConstraint:onRangeChange() - local cons = self.constraint - cons.goal_gap = math.max(1, math.min(cons.goal_gap, cons.goal_value-1)) -end - ------------------------------- --- CONSTRAINT HISTORY GRAPH -- ------------------------------- - -HistoryGraph = defclass(HistoryGraph, widgets.Widget) - -HistoryGraph.ATTRS { - frame_inset = 1, - history_pen = COLOR_CYAN, -} - -function HistoryGraph:init(info) -end - -function HistoryGraph:setData(history, bars) - self.history = history or {} - self.bars = bars or {} - - local maxval = 1 - for i,v in ipairs(self.history) do - maxval = math.max(maxval, v) - end - for i,v in ipairs(self.bars) do - maxval = math.max(maxval, v.value) - end - self.max_value = maxval -end - -function HistoryGraph:onRenderFrame(dc,rect) - dc:fill(rect.x1,rect.y1,rect.x1,rect.y2,{ch='\xb3', fg=COLOR_BROWN}) - dc:fill(rect.x1,rect.y2,rect.x2,rect.y2,{ch='\xc4', fg=COLOR_BROWN}) - dc:seek(rect.x1,rect.y1):char('\x1e', COLOR_BROWN) - dc:seek(rect.x1,rect.y2):char('\xc5', COLOR_BROWN) - dc:seek(rect.x2,rect.y2):char('\x10', COLOR_BROWN) - dc:seek(rect.x1,rect.y2-1):char('0', COLOR_BROWN) -end - -function HistoryGraph:onRenderBody(dc) - local coeff = (dc.height-1)/self.max_value - - for i,v in ipairs(self.bars) do - local y = dc.height-1-math.floor(0.5 + coeff*v.value) - dc:fill(0,y,dc.width-1,y,v.pen or {ch='-', fg=COLOR_GREEN}) - end - - local xbase = dc.width-1-#self.history - for i,v in ipairs(self.history) do - local x = xbase + i - local y = dc.height-1-math.floor(0.5 + coeff*v) - dc:seek(x,y):char('*', self.history_pen) - end -end - ------------------------------- --- GLOBAL CONSTRAINT SCREEN -- ------------------------------- - -ConstraintList = defclass(ConstraintList, gui.FramedScreen) - -ConstraintList.focus_path = 'workflow/list' - -ConstraintList.ATTRS { - frame_title = 'Workflow Status', - frame_inset = 0, - frame_background = COLOR_BLACK, - frame_style = gui.BOUNDARY_FRAME, -} - -function ConstraintList:init(args) - local fwidth_cb = self:cb_getfield('fwidth') - - self.fwidth = 20 - self.sort_by_severity = false - - self:addviews{ - widgets.Panel{ - frame = { l = 0, r = 31 }, - frame_inset = 1, - on_layout = function(body) - self.fwidth = body.width - (12+1+1+7+1+1+1+7) - end, - subviews = { - widgets.Label{ - frame = { l = 0, t = 0 }, - text_pen = COLOR_CYAN, - text = { - { text = 'Item', width = 12 }, ' ', - { text = 'Material etc', width = fwidth_cb }, ' ', - { text = 'Stock / Limit' }, - } - }, - widgets.FilteredList{ - view_id = 'list', - frame = { t = 2, b = 2 }, - edit_below = true, - not_found_label = 'No matching constraints', - edit_pen = COLOR_LIGHTCYAN, - text_pen = { fg = COLOR_GREY, bg = COLOR_BLACK }, - cursor_pen = { fg = COLOR_WHITE, bg = COLOR_GREEN }, - on_select = self:callback('onSelectConstraint'), - }, - widgets.Label{ - frame = { b = 0, h = 1 }, - text = { - { key = 'CUSTOM_SHIFT_A', text = ': Add', - on_activate = self:callback('onNewConstraint') }, ', ', - { key = 'CUSTOM_SHIFT_X', text = ': Delete', - on_activate = self:callback('onDeleteConstraint') }, ', ', - { key = 'CUSTOM_SHIFT_O', text = ': Severity Order', - on_activate = self:callback('onSwitchSort'), - pen = function() - if self.sort_by_severity then - return COLOR_LIGHTCYAN - else - return COLOR_WHITE - end - end }, - } - } - } - }, - widgets.Panel{ - frame = { w = 30, r = 0, h = 6, t = 0 }, - frame_inset = 1, - subviews = { - widgets.Label{ - frame = { l = 0, t = 0 }, - enabled = self:callback('isAnySelected'), - text = { - { text = function() - local cur = self:getCurConstraint() - if cur then - return string.format( - 'Currently %d (%d in use)', - current_stock(cur), - if_by_count(cur, cur.cur_in_use_count, cur.cur_in_use_amount) - ) - else - return 'No constraint selected' - end - end } - } - }, - RangeEditor{ - frame = { l = 0, t = 2 }, - enabled = self:callback('isAnySelected'), - get_cb = self:callback('getCurConstraint'), - save_cb = self:callback('saveConstraint'), - keys = { - count = 'CUSTOM_SHIFT_I', - modify = 'CUSTOM_SHIFT_R', - min_dec = 'SECONDSCROLL_PAGEUP', - min_inc = 'SECONDSCROLL_PAGEDOWN', - max_dec = 'SECONDSCROLL_UP', - max_inc = 'SECONDSCROLL_DOWN', - } - }, - } - }, - widgets.Widget{ - active = false, - frame = { w = 1, r = 30 }, - frame_background = gui.BOUNDARY_FRAME.frame_pen, - }, - widgets.Widget{ - active = false, - frame = { w = 30, r = 0, h = 1, t = 6 }, - frame_background = gui.BOUNDARY_FRAME.frame_pen, - }, - HistoryGraph{ - view_id = 'graph', - frame = { w = 30, r = 0, t = 7, b = 0 }, - } - } - - self:initListChoices(nil, args.select_token) -end - -function stock_trend_color(cons) - local stock = current_stock(cons) - if stock >= cons.goal_value - cons.goal_gap then - return COLOR_LIGHTGREEN, 0 - elseif stock <= cons.goal_gap then - return COLOR_LIGHTRED, 4 - elseif stock >= cons.goal_value - 2*cons.goal_gap then - return COLOR_GREEN, 1 - elseif stock <= 2*cons.goal_gap then - return COLOR_RED, 3 - else - local trend = if_by_count(cons, cons.trend_count, cons.trend_amount) - if trend > 0.3 then - return COLOR_GREEN, 1 - elseif trend < -0.3 then - return COLOR_RED, 3 - else - return COLOR_GREY, 2 - end - end -end - -function ConstraintList:initListChoices(clist, sel_token) - clist = clist or workflow.listConstraints(nil, true) - - local fwidth_cb = self:cb_getfield('fwidth') - local choices = {} - - for i,cons in ipairs(clist) do - cons.trend_count = compute_trend(cons.history, 'cur_count') - cons.trend_amount = compute_trend(cons.history, 'cur_amount') - - local itemstr = describe_item_type(cons) - local matstr,matflagstr = describe_material(cons) - if matflagstr then - matstr = matflagstr .. ' ' .. matstr - end - - if cons.min_quality > 0 or cons.is_local then - local lst = {} - if cons.is_local then - table.insert(lst, 'local') - end - if cons.min_quality > 0 then - table.insert(lst, string.lower(df.item_quality[cons.min_quality])) - end - matstr = matstr .. ' ('..table.concat(lst,',')..')' - end - - local goal_color = COLOR_GREY - if #cons.jobs == 0 then - goal_color = COLOR_RED - elseif cons.is_delayed then - goal_color = COLOR_YELLOW - end - - table.insert(choices, { - text = { - { text = itemstr, width = 12, pad_char = ' ' }, ' ', - { text = matstr, width = fwidth_cb, pad_char = ' ' }, ' ', - { text = curry(current_stock,cons), width = 7, rjustify = true, - pen = function() return { fg = stock_trend_color(cons) } end }, - { text = curry(if_by_count,cons,'S','I'), gap = 1, - pen = { fg = COLOR_GREY } }, - { text = function() return cons.goal_value end, gap = 1, - pen = { fg = goal_color } } - }, - severity = select(2, stock_trend_color(cons)), - search_key = itemstr .. ' | ' .. matstr, - token = cons.token, - obj = cons - }) - end - - self:setChoices(choices, sel_token) -end - -function ConstraintList:isAnySelected() - return self.subviews.list:getSelected() ~= nil -end - -function ConstraintList:getCurConstraint() - local selidx,selobj = self.subviews.list:getSelected() - if selobj then return selobj.obj end -end - -function ConstraintList:onSwitchSort() - self.sort_by_severity = not self.sort_by_severity - self:setChoices(self.subviews.list:getChoices()) -end - -function ConstraintList:setChoices(choices, sel_token) - if self.sort_by_severity then - table.sort(choices, function(a,b) - return a.severity > b.severity - or (a.severity == b.severity and - current_stock(a.obj)/a.obj.goal_value < current_stock(b.obj)/b.obj.goal_value) - end) - else - table.sort(choices, function(a,b) return a.search_key < b.search_key end) - end - - local selidx = nil - if sel_token then - selidx = utils.linear_index(choices, sel_token, 'token') - end - - local list = self.subviews.list - local filter = list:getFilter() - - list:setChoices(choices, selidx) - - if filter ~= '' then - list:setFilter(filter, selidx) - - if selidx and list:getSelected() ~= selidx then - list:setFilter('', selidx) - end - end -end - -function ConstraintList:onInput(keys) - if keys.LEAVESCREEN then - self:dismiss() - else - ConstraintList.super.onInput(self, keys) - end -end - -function ConstraintList:onNewConstraint() - NewConstraint{ - on_submit = self:callback('saveConstraint') - }:show() -end - -function ConstraintList:saveConstraint(cons) - local out = workflow.setConstraint(cons.token, cons.goal_by_count, cons.goal_value, cons.goal_gap) - self:initListChoices(nil, out.token) -end - -function ConstraintList:onDeleteConstraint() - local cons = self:getCurConstraint() - dlg.showYesNoPrompt( - 'Delete Constraint', - 'Really delete the current constraint?', - COLOR_YELLOW, - function() - workflow.deleteConstraint(cons.token) - self:initListChoices() - end - ) -end - -function ConstraintList:onSelectConstraint(idx,item) - local history, bars - - if item then - local cons = item.obj - local vfield = if_by_count(cons, 'cur_count', 'cur_amount') - - bars = { - { value = cons.goal_value - cons.goal_gap, pen = {ch='-', fg=COLOR_GREEN} }, - { value = cons.goal_value, pen = {ch='-', fg=COLOR_LIGHTGREEN} }, - } - - history = {} - for i,v in ipairs(cons.history or {}) do - table.insert(history, v[vfield]) - end - - table.insert(history, cons[vfield]) - end - - self.subviews.graph:setData(history, bars) -end - -------------------------------- --- WORKSHOP JOB INFO OVERLAY -- -------------------------------- - -JobConstraints = defclass(JobConstraints, guidm.MenuOverlay) - -JobConstraints.focus_path = 'workflow/job' - -JobConstraints.ATTRS { - job = DEFAULT_NIL, - frame_inset = 1, - frame_background = COLOR_BLACK, -} - -function JobConstraints:init(args) - self.building = dfhack.job.getHolder(self.job) - - self:addviews{ - widgets.Label{ - frame = { l = 0, t = 0 }, - text = { - 'Workflow Constraints' - } - }, - widgets.List{ - view_id = 'list', - frame = { t = 2, b = 6 }, - row_height = 4, - scroll_keys = widgets.SECONDSCROLL, - }, - RangeEditor{ - frame = { l = 0, b = 3 }, - enabled = self:callback('isAnySelected'), - get_cb = self:callback('getCurConstraint'), - save_cb = self:callback('saveConstraint'), - }, - widgets.Label{ - frame = { l = 0, b = 0 }, - text = { - { key = 'CUSTOM_SHIFT_A', text = ': Add limit, ', - on_activate = self:callback('onNewConstraint') }, - { key = 'CUSTOM_SHIFT_X', text = ': Delete', - enabled = self:callback('isAnySelected'), - on_activate = self:callback('onDeleteConstraint') }, - NEWLINE, NEWLINE, - { key = 'LEAVESCREEN', text = ': Back', - on_activate = self:callback('dismiss') }, - ' ', - { key = 'CUSTOM_SHIFT_S', text = ': Status', - on_activate = function() - local sel = self:getCurConstraint() - ConstraintList{ select_token = (sel or {}).token }:show() - end } - } - }, - } - - self:initListChoices(args.clist) -end - -function JobConstraints:onGetSelectedBuilding() - return self.building -end - -function JobConstraints:onGetSelectedJob() - return self.job -end - -function JobConstraints:initListChoices(clist, sel_token) - clist = clist or workflow.listConstraints(self.job) - - local choices = {} - - for i,cons in ipairs(clist) do - local goal = (cons.goal_value-cons.goal_gap)..'-'..cons.goal_value - local curval - if cons.goal_by_count then - goal = goal .. ' stacks' - curval = cons.cur_count - else - goal = goal .. ' items' - curval = cons.cur_amount - end - local order_pen = COLOR_GREY - if cons.request == 'resume' then - order_pen = COLOR_GREEN - elseif cons.request == 'suspend' then - order_pen = COLOR_BLUE - end - local itemstr = describe_item_type(cons) - if cons.min_quality > 0 or cons.is_local then - local lst = {} - if cons.is_local then - table.insert(lst, 'local') - end - if cons.min_quality > 0 then - table.insert(lst, string.lower(df.item_quality[cons.min_quality])) - end - itemstr = itemstr .. ' ('..table.concat(lst,',')..')' - end - local matstr,matflagstr = describe_material(cons) - - table.insert(choices, { - text = { - goal, ' ', { text = '(now '..curval..')', pen = order_pen }, NEWLINE, - ' ', itemstr, NEWLINE, ' ', matstr, NEWLINE, ' ', (matflagstr or '') - }, - token = cons.token, - obj = cons - }) - end - - local selidx = nil - if sel_token then - selidx = utils.linear_index(choices, sel_token, 'token') - end - - self.subviews.list:setChoices(choices, selidx) -end - -function JobConstraints:isAnySelected() - return self.subviews.list:getSelected() ~= nil -end - -function JobConstraints:getCurConstraint() - local i,v = self.subviews.list:getSelected() - if v then return v.obj end -end - -function JobConstraints:saveConstraint(cons) - local out = workflow.setConstraint(cons.token, cons.goal_by_count, cons.goal_value, cons.goal_gap) - self:initListChoices(nil, out.token) -end - -function JobConstraints:onNewConstraint() - local outputs = workflow.listJobOutputs(self.job) - if #outputs == 0 then - dlg.showMessage('Unsupported', 'Workflow cannot guess the outputs of this job.', COLOR_LIGHTRED) - return - end - - local variants = workflow.listWeakenedConstraints(outputs) - - local choices = {} - for i,cons in ipairs(variants) do - local itemstr = describe_item_type(cons) - local matstr,matflags = describe_material(cons) - if matflags then - matstr = matflags..' '..matstr - end - - table.insert(choices, { text = itemstr..' of '..matstr, obj = cons }) - end - - dlg.ListBox{ - frame_title = 'Add limit', - text = 'Select one of the possible outputs:', - text_pen = COLOR_WHITE, - choices = choices, - on_select = function(idx,item) - self:saveConstraint(item.obj) - end, - select2_hint = 'Advanced', - on_select2 = function(idx,item) - NewConstraint{ - constraint = item.obj, - on_submit = self:callback('saveConstraint') - }:show() - end, - }:show() -end - -function JobConstraints:onDeleteConstraint() - local cons = self:getCurConstraint() - dlg.showYesNoPrompt( - 'Delete Constraint', - 'Really delete the current constraint?', - COLOR_YELLOW, - function() - workflow.deleteConstraint(cons.token) - self:initListChoices() - end - ) -end - -function JobConstraints:onInput(keys) - if self:propagateMoveKeys(keys) then - if df.global.world.selected_building ~= self.building then - self:dismiss() - end - else - JobConstraints.super.onInput(self, keys) - end -end - -local args = {...} - -if args[1] == 'status' then - check_enabled(function() ConstraintList{}:show() end) -else - if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some/Workshop/Job') then - qerror("This script requires a workshop job selected in the 'q' mode") - end - - local job = dfhack.gui.getSelectedJob() - - check_enabled(function() - check_repeat(job, function() - local clist = workflow.listConstraints(job) - if not clist then - dlg.showMessage('Not Supported', 'This type of job is not supported by workflow.', COLOR_LIGHTRED) - return - end - JobConstraints{ job = job, clist = clist }:show() - end) - end) -end diff --git a/scripts/gui/workshop-job.lua b/scripts/gui/workshop-job.lua deleted file mode 100644 index f3af15d10..000000000 --- a/scripts/gui/workshop-job.lua +++ /dev/null @@ -1,332 +0,0 @@ --- Show and modify properties of jobs in a workshop. ---[[=begin - -gui/workshop-job -================ -Bind to a key (the example config uses :kbd:`Alt`:kbd:`a`), and activate with a job selected in -a workshop in the :kbd:`q` mode. - -.. image:: /docs/images/workshop-job.png - -The script shows a list of the input reagents of the selected job, and allows changing -them like the `job` ``item-type`` and `job` ``item-material`` commands. - -Specifically, pressing the :kbd:`i` key pops up a dialog that lets you select an item -type from a list. - -.. image:: /docs/images/workshop-job-item.png - -Pressing :kbd:`m`, unless the item type does not allow a material, -lets you choose a material. - -.. image:: /docs/images/workshop-job-material.png - -Since there are a lot more materials than item types, this dialog is more complex -and uses a hierarchy of sub-menus. List choices that open a sub-menu are marked -with an arrow on the left. - -.. warning:: - - Due to the way input reagent matching works in DF, you must select an item type - if you select a material, or the material will be matched incorrectly in some cases. - If you press :kbd:`m` without choosing an item type, the script will auto-choose - if there is only one valid choice, or pop up an error message box instead of the - material selection dialog. - -Note that both materials and item types presented in the dialogs are filtered -by the job input flags, and even the selected item type for material selection, -or material for item type selection. Many jobs would let you select only one -input item type. - -For example, if you choose a *plant* input item type for your prepare meal job, -it will only let you select cookable materials. - -If you choose a *barrel* item instead (meaning things stored in barrels, like -drink or milk), it will let you select any material, since in this case the -material is matched against the barrel itself. Then, if you select, say, iron, -and then try to change the input item type, now it won't let you select *plant*; -you have to unset the material first. - -=end]] -local utils = require 'utils' -local gui = require 'gui' -local guidm = require 'gui.dwarfmode' -local guimat = require 'gui.materials' -local widgets = require 'gui.widgets' -local dlg = require 'gui.dialogs' - -JobDetails = defclass(JobDetails, guidm.MenuOverlay) - -JobDetails.focus_path = 'workshop-job' - -JobDetails.ATTRS { - job = DEFAULT_NIL, - frame_inset = 1, - frame_background = COLOR_BLACK, -} - -function JobDetails:init(args) - self.building = dfhack.job.getHolder(self.job) - - local status = { text = 'No worker', pen = COLOR_DARKGREY } - local worker = dfhack.job.getWorker(self.job) - if self.job.flags.suspend then - status = { text = 'Suspended', pen = COLOR_RED } - elseif worker then - status = { text = dfhack.TranslateName(dfhack.units.getVisibleName(worker)), pen = COLOR_GREEN } - end - - self:addviews{ - widgets.Label{ - frame = { l = 0, t = 0 }, - text = { - { text = df.job_type.attrs[self.job.job_type].caption }, NEWLINE, NEWLINE, - ' ', status - } - }, - widgets.Label{ - frame = { l = 0, t = 4 }, - text = { - { key = 'CUSTOM_I', text = ': Input item, ', - enabled = self:callback('canChangeIType'), - on_activate = self:callback('onChangeIType') }, - { key = 'CUSTOM_M', text = ': Material', - enabled = self:callback('canChangeMat'), - on_activate = self:callback('onChangeMat') } - } - }, - widgets.List{ - view_id = 'list', - frame = { t = 6, b = 2 }, - row_height = 4, - scroll_keys = widgets.SECONDSCROLL, - }, - widgets.Label{ - frame = { l = 0, b = 0 }, - text = { - { key = 'LEAVESCREEN', text = ': Back', - on_activate = self:callback('dismiss') } - } - }, - } - - self:initListChoices() -end - -function JobDetails:onGetSelectedBuilding() - return self.building -end - -function JobDetails:onGetSelectedJob() - return self.job -end - -function describe_item_type(iobj) - local itemline = 'any item' - if iobj.item_type >= 0 then - itemline = df.item_type.attrs[iobj.item_type].caption or iobj.item_type - local def = dfhack.items.getSubtypeDef(iobj.item_type, iobj.item_subtype) - local count = dfhack.items.getSubtypeCount(iobj.item_type, iobj.item_subtype) - if def then - itemline = def.name - elseif count >= 0 then - itemline = 'any '..itemline - end - end - return itemline -end - -function is_caste_mat(iobj) - return dfhack.items.isCasteMaterial(iobj.item_type) -end - -function describe_material(iobj) - local matline = 'any material' - if is_caste_mat(iobj) then - matline = 'material not applicable' - elseif iobj.mat_type >= 0 then - local info = dfhack.matinfo.decode(iobj.mat_type, iobj.mat_index) - if info then - matline = info:toString() - else - matline = iobj.mat_type..':'..iobj.mat_index - end - end - return matline -end - -function list_flags(list, bitfield) - for name,val in pairs(bitfield) do - if val then - table.insert(list, name) - end - end -end - -function JobDetails:initListChoices() - local items = {} - for i,ref in ipairs(self.job.items) do - local idx = ref.job_item_idx - if idx >= 0 then - items[idx] = (items[idx] or 0) + 1 - end - end - - local choices = {} - for i,iobj in ipairs(self.job.job_items) do - local head = 'Item '..(i+1)..': '..(items[i] or 0)..' of '..iobj.quantity - if iobj.min_dimension > 0 then - head = head .. '(size '..iobj.min_dimension..')' - end - - local line1 = {} - local reaction = df.reaction.find(iobj.reaction_id) - if reaction and #iobj.contains > 0 then - for _,ri in ipairs(iobj.contains) do - table.insert(line1, 'has '..utils.call_with_string( - reaction.reagents[ri],'getDescription',iobj.reaction_id - )) - end - end - if iobj.metal_ore >= 0 then - local ore = dfhack.matinfo.decode(0, iobj.metal_ore) - if ore then - table.insert(line1, 'ore of '..ore:toString()) - end - end - if iobj.has_material_reaction_product ~= '' then - table.insert(line1, 'product '..iobj.has_material_reaction_product) - end - if iobj.reaction_class ~= '' then - table.insert(line1, 'class '..iobj.reaction_class) - end - if iobj.has_tool_use >= 0 then - table.insert(line1, 'has use '..df.tool_uses[iobj.has_tool_use]) - end - list_flags(line1, iobj.flags1) - list_flags(line1, iobj.flags2) - list_flags(line1, iobj.flags3) - if #line1 == 0 then - table.insert(line1, 'no flags') - end - - table.insert(choices, { - index = i, - iobj = iobj, - text = { - head, NEWLINE, - ' ', { text = curry(describe_item_type, iobj) }, NEWLINE, - ' ', { text = curry(describe_material, iobj) }, NEWLINE, - ' ', table.concat(line1, ', '), NEWLINE - } - }) - end - - self.subviews.list:setChoices(choices) -end - -function JobDetails:canChangeIType() - local idx, obj = self.subviews.list:getSelected() - return obj ~= nil -end - -function JobDetails:setItemType(obj, item_type, item_subtype) - obj.iobj.item_type = item_type - obj.iobj.item_subtype = item_subtype - - if is_caste_mat(obj.iobj) then - self:setMaterial(obj, -1, -1) - end -end - -function JobDetails:onChangeIType() - local idx, obj = self.subviews.list:getSelected() - guimat.ItemTypeDialog{ - prompt = 'Please select a new item type for input '..idx, - none_caption = 'any item', - item_filter = curry(dfhack.job.isSuitableItem, obj.iobj), - on_select = self:callback('setItemType', obj) - }:show() -end - -function JobDetails:canChangeMat() - local idx, obj = self.subviews.list:getSelected() - return obj ~= nil and not is_caste_mat(obj.iobj) -end - -function JobDetails:setMaterial(obj, mat_type, mat_index) - if obj.index == 0 - and self.job.mat_type == obj.iobj.mat_type - and self.job.mat_index == obj.iobj.mat_index - and self.job.job_type ~= df.job_type.PrepareMeal - then - self.job.mat_type = mat_type - self.job.mat_index = mat_index - end - - obj.iobj.mat_type = mat_type - obj.iobj.mat_index = mat_index -end - -function JobDetails:findUnambiguousItem(iobj) - local count = 0 - local itype - - for i = 0,df.item_type._last_item do - if dfhack.job.isSuitableItem(iobj, i, -1) then - count = count + 1 - if count > 1 then return nil end - itype = i - end - end - - return itype -end - -function JobDetails:onChangeMat() - local idx, obj = self.subviews.list:getSelected() - - if obj.iobj.item_type == -1 and obj.iobj.mat_type == -1 then - -- If the job allows only one specific item type, use it - local vitype = self:findUnambiguousItem(obj.iobj) - - if vitype then - obj.iobj.item_type = vitype - else - dlg.showMessage( - 'Bug Alert', - { 'Please set a specific item type first.\n\n', - 'Otherwise the material will be matched\n', - 'incorrectly due to a limitation in DF code.' }, - COLOR_YELLOW - ) - return - end - end - - guimat.MaterialDialog{ - prompt = 'Please select a new material for input '..idx, - none_caption = 'any material', - mat_filter = function(mat,parent,mat_type,mat_index) - return dfhack.job.isSuitableMaterial(obj.iobj, mat_type, mat_index) - end, - on_select = self:callback('setMaterial', obj) - }:show() -end - -function JobDetails:onInput(keys) - if self:propagateMoveKeys(keys) then - if df.global.world.selected_building ~= self.building then - self:dismiss() - end - else - JobDetails.super.onInput(self, keys) - end -end - -if not string.match(dfhack.gui.getCurFocus(), '^dwarfmode/QueryBuilding/Some/Workshop/Job') then - qerror("This script requires a workshop job selected in the 'q' mode") -end - -local dlg = JobDetails{ job = dfhack.gui.getSelectedJob() } -dlg:show() diff --git a/scripts/hfs-pit.lua b/scripts/hfs-pit.lua deleted file mode 100644 index 7fc571090..000000000 --- a/scripts/hfs-pit.lua +++ /dev/null @@ -1,105 +0,0 @@ --- Creates a pit to the Underworld under the target --- Based on script by IndigoFenix, @ https://gist.github.com/IndigoFenix/8776696 ---[[=begin - -hfs-pit -======= -Creates a pit to the underworld at the cursor. - -Takes three arguments: diameter of the pit in tiles, whether to wall off -the pit, and whether to insert stairs. If no arguments are given, the default -is ``hfs-pit 1 0 0``, ie single-tile wide with no walls or stairs.:: - - hfs-pit 4 0 1 - hfs-pit 2 1 0 - -First example is a four-across pit with stairs but no walls; second is a -two-across pit with stairs but no walls. - -=end]] - -args={...} - -if args[1] == '?' then - print("Example usage: 'hfs-pit 2 1 1'") - print("First parameter is size of the pit in all directions.") - print("Second parameter is 1 to wall off the sides of the pit on all layers except the underworld, or anything else to leave them open.") - print("Third parameter is 1 to add stairs. Stairs are buggy; they will not reveal the bottom until you dig somewhere, but underworld creatures will path in.") - print("If no arguments are given, the default is 'hfs-pit 1 0 0', ie single-tile wide with no walls or stairs.") - return -end - -pos = copyall(df.global.cursor) -size = tonumber(args[1]) -if size == nil or size < 1 then size = 1 end - -wallOff = tonumber(args[2]) -stairs = tonumber(args[3]) - ---Get the layer of the underworld -for index,value in ipairs(df.global.world.features.map_features) do - local featureType=value:getType() - if featureType==9 then --Underworld - underworldLayer = value.layer - end -end - -if pos.x==-30000 then - qerror("Select a location by placing the cursor") -end -local x = 0 -local y = 0 -for x=pos.x-size,pos.x+size,1 do - for y=pos.y-size,pos.y+size,1 do - z=1 - local hitAir = false - local hitCeiling = false - while z <= pos.z do - local block = dfhack.maps.ensureTileBlock(x,y,z) - if block then - if block.tiletype[x%16][y%16] ~= 335 then - hitAir = true - end - if hitAir == true then - if not hitCeiling then - if block.global_feature ~= underworldLayer or z > 10 then hitCeiling = true end - if stairs == 1 and x == pos.x and y == pos.y then - if block.tiletype[x%16][y%16] == 32 then - if z == pos.z then - block.tiletype[x%16][y%16] = 56 - else - block.tiletype[x%16][y%16] = 55 - end - else - block.tiletype[x%16][y%16] = 57 - end - end - end - if hitCeiling == true then - if block.designation[x%16][y%16].flow_size > 0 or wallOff == 1 then needsWall = true else needsWall = false end - if (x == pos.x-size or x == pos.x+size or y == pos.y-size or y == pos.y+size) and z==pos.z then - --Do nothing, this is the lip of the hole - elseif x == pos.x-size and y == pos.y-size then if needsWall == true then block.tiletype[x%16][y%16]=320 end - elseif x == pos.x-size and y == pos.y+size then if needsWall == true then block.tiletype[x%16][y%16]=321 end - elseif x == pos.x+size and y == pos.y+size then if needsWall == true then block.tiletype[x%16][y%16]=322 end - elseif x == pos.x+size and y == pos.y-size then if needsWall == true then block.tiletype[x%16][y%16]=323 end - elseif x == pos.x-size or x == pos.x+size then if needsWall == true then block.tiletype[x%16][y%16]=324 end - elseif y == pos.y-size or y == pos.y+size then if needsWall == true then block.tiletype[x%16][y%16]=325 end - elseif stairs == 1 and x == pos.x and y == pos.y then - if z == pos.z then block.tiletype[x%16][y%16]=56 - else block.tiletype[x%16][y%16]=55 end - else block.tiletype[x%16][y%16]=32 - end - block.designation[x%16][y%16].hidden = false - --block.designation[x%16][y%16].liquid_type = true -- if true, magma. if false, water. - block.designation[x%16][y%16].flow_size = 0 - dfhack.maps.enableBlockUpdates(block) - block.designation[x%16][y%16].flow_forbid = false - end - end - block.designation[x%16][y%16].hidden = false - end - z = z+1 - end - end -end \ No newline at end of file diff --git a/scripts/hotkey-notes.lua b/scripts/hotkey-notes.lua deleted file mode 100644 index 428a02d64..000000000 --- a/scripts/hotkey-notes.lua +++ /dev/null @@ -1,16 +0,0 @@ --- prints info on assigned hotkeys to the console ---[[=begin - -hotkey-notes -============ -Lists the key, name, and jump position of your hotkeys in the DFHack console. - -=end]] - -for i=1, #df.global.ui.main.hotkeys do - local hk = df.global.ui.main.hotkeys[i-1] - local key = dfhack.screen.getKeyDisplay(df.interface_key.D_HOTKEY1 + i - 1) - if hk.cmd ~= -1 then - print(key..': '..hk.name..': x='..hk.x..' y='..hk.y..' z='..hk.z) - end -end diff --git a/scripts/item-descriptions.lua b/scripts/item-descriptions.lua deleted file mode 100644 index b2c57d2e4..000000000 --- a/scripts/item-descriptions.lua +++ /dev/null @@ -1,663 +0,0 @@ --- Holds custom descriptions for view-item-info --- By PeridexisErrant ---[[=begin - -item-descriptions -================= -Exports a table with custom description text for every item in the game. -Used by `view-item-info`; see instructions there for how to override -for mods. - -=end]] - --- Each line near the bottom has 53 characters of room until --- it starts clipping over the UI in an ugly fashion. --- For proper spacing, 50 characters is the maximum. --- Descriptions which aren't pushed down the page by --- barrel contents or such line up with the UI on the --- 11th line down. There is room for a 10th long line --- without clipping, but stopping at 9 leaves enough space --- for ideal legibility. - --- The following people contributed descriptions: --- Raideau, PeridexisErrant, /u/Puffin4Tom, /u/KroyMortlach --- /u/genieus, /u/TeamsOnlyMedic, /u/johny5w, /u/DerTanni --- /u/schmee101, /u/coaldiamond, /u/stolencatkarma, /u/sylth01 --- /u/MperorM, /u/SockHoarder, /u/_enclave_, WesQ3 --- /u/Xen0nex, /u/Jurph - -if not moduleMode then - print("scripts/item-descriptions.lua is a content library; calling it does nothing.") -end - -local help --[[ -This script has a single function: to return a custom description for every -vanilla item in the game. - -If "raw/scripts/item-descriptions.lua" exists, it will entirely replace this one. -Instead, mods should use "raw/scripts/more-item-descriptions.lua" to add content or replace -descriptions on a case-by-case basis. If an item description cannot be found in -the latter script, view-item-info will fall back to the former. -]] - --- see http://dwarffortresswiki.org/index.php/cv:Item_token -descriptions = { - AMULET = { "An item of jewellery worn around the neck for it's aesthetic value.", - "An amulet does not interfere with wearing other equipment."}, - ANIMALTRAP = { - "This tiny trap is used by your trappers to catch vermin. Some dwarves", - "like vermin as pets - if your cats don't get them first. May be built", - "from wood or metal. Catching vermin requires trap to be set with bait."}, - ANVIL = { "An essential component of the forge."}, - ARMORSTAND = { - "A rack for the storage of military equipment, specifically armor.", - "Barracks may be designated and assigned from them. Military squads", - "may use their assigned barracks for training, storage, or sleeping,", - "depending on the settings. Some nobles demand an armor stand of", - "their own."}, - BACKPACK = {"A backpack can be used by militia to carry rations in the field.", - "In Adventure mode, backpacks can be used to carry more equipment."}, - BALLISTAARROWHEAD = { - "The arrowtip used to create metal ballista arrows in a siege workshop."}, - BALLISTAPARTS = { - "Three of these can be used to construct a Ballista."}, - BAR = { "A small ingot made of metal, fuel, ash, or soap, made to facilitate", - "stacking and storage. Aside from the uses unique to each material, bars", - "of any type can also be used as building materials in place of blocks.", - "", - "Metal bars are used with fuel at a Metalsmith's Forge to make metal", - "goods and decorations. Fuel is used at furnaces and workshops requiring", - "intense heat, with the exception of magma furnaces or a Wood Furnace.", - "Soap is used by hospitals to greatly reduce the chance of infected", - "wounds, and rarely used by individual dwarves to clean themselves or", - "surrounding tiles. Ash is processed at an Ashery to make potash or lye,", - "potash is used as farm plot fertilizer or made into pearlash, and", - "pearlash is used to make clear or crystal glass products."}, - BARREL = { "A hollow cylinder with a removable lid. It is used to hold liquids,", - "food, and seeds. It can be made from metal or wood, and is replaceable", - "with a rock pot. A barrel (or rock pot) is needed to brew drinks."}, - BED = { "A pallet for dwarves to sleep on, which must be made from wood.", - "It prevents the stress of sleeping on the ground, and can be used", - "to designate a bedroom (used by one dwarf or couple), a dormitory", - "(used by multiple dwarves), or a barracks (used by a military", - "squad for training or sleep)."}, - BIN = { "A container for the storage of ammunition, armor and weapons, bars,", - "blocks, cloth and leather, coins, finished goods and gems. It can", - "be used to carry multiple items to the Trade Depot at once.", - "A bin can be made from wood or forged from metal."}, - BLOCKS = { "Blocks can be used for constructions in place of raw materials such", - "as logs or bars. Cutting boulders into blocks gives four times as", - "many items, all of which are lighter for faster hauling and yield", - "smooth constructions."}, - BOX = { "A container for storing dwarves' items. They are required by nobles,", - "and will increase the value of rooms they are placed in. Also", - "required to store hospital supplies. They can be made from stone or", - "metal (coffers), wood (chests),or textiles or leather (bags)."}, - BUCKET = { "A small cylindrical or conical container for holding and carrying", - "small amounts of liquid such as water or lye. They are used by", - "dwarves to give water to other dwarves, to store lye, and are", - "required to build wells and certain workshops. They can be made", - "from wood or metal."}, - BOOK = { "It's a book. Some books contain the secrets of life and death."}, - BOULDER = { "Mining may yield loose stones for industry. There are four categories:", - "non-economic stones for building materials, ores for metal industry,", - "gems, and special-purpose economic stones like flux, coal and lignite."}, - BRACELET = {"A bracelet is an item of jewellery worn on the hands."}, - CABINET = { "A place for dwarves to store old clothing, used by any dwarf whose room", - "overlaps the cabinet. May be built from wood, stone, metal, or glass."}, - CAGE = { "A cage can be made of glass, metal or wood. All materials are equally", - "strong as cages can not be broken, however the weight of the material", - "affects the time taken to move cages. Cages can be combined with a", - "mechanism to create a cage trap. Cages can also be built as furniture,", - "after which they can store an infinite number of animals or prisoners."}, - CATAPULTPARTS = { - "Three of these can be used to construct a Catapult."}, - CHAIN = { "A chain made of metal. A chain or rope is required to build a well.", - "Due to the marvels of dwarven engineering, a single chain can be used", - "for a well of any depth. Chains are also used to create restraints", - "for prisoners or animals."}, - CHAIR = { "Furniture used for sitting. Named a chair if made from wood,", - "or a throne if made from stone, glass, or metal. Offices may be", - "designated and assigned from them. Dwarves will complain if there", - "aren't enough chairs in the dining room."}, - CLOTH = { "A piece of fabric made from threads of plant fiber, yarn, silk or", - "adamantine. Cloth may be dyed. It is used at a Clothier's Workshop to", - "make clothing, bags, rope, and decorative sewn images. At a", - "Craftsdwarf's Workshop, it can be made into trade goods. Hospitals", - "use cloth for wound dressing - though expensive cloth confers no", - "benefit. Specific types of cloth can be required by a strange mood."}, - COFFIN = { "A final resting place for dwarves. Must be built and assigned before", - "burial can occur. Named a coffin when made from stone or glass,", - "casket when made from wood, and sarcophagus when made from metal.", - "Tombs may be designated and assigned from them. Corpses contained in", - "coffins may still reanimate."}, - COIN = { "A metal coin, which represents value. Surprisingly useless in trade."}, - CROWN = { "A crown may be worn as headgear, or on top of a helmet. Although", - "usually just decorative or symbolic, crowns sometimes deflect attacks."}, - CRUTCH = { "Item used in health-care. May be made from wood or metal. Given to", - "dwarves who receive injuries that impair or prevent normal movement.", - "Requires one hand."}, - DOOR = { "A barrier that covers a hole in a wall and controls horizontal passage.", - "Intelligent creatures can open and close doors as needed. Doors can", - "also block the flow of liquids as long as they remain closed. May be", - "set as 'locked' to prevent all passage or 'tightly closed' to prevent", - "animal passage. Creatures cannot manually open or close doors that are", - "mechanically controlled. May be linked via mechanisms to devices such", - "as pressure plates and levers. Will become stuck open if an item", - "occupies its tile."}, - EARRING = { "Earrings are decorative jewellery. Eleven can be worn on each ear."}, - FIGURINE = {"A small piece of art carved in the likeness of a creature."}, - FLASK = { "A drink container that is worn on the body, keeping the hands free.", - "Soldiers and adventurers can carry any drink of their choice in this", - "container. Called a flask when made from metal, or a vial when", - "made from glass."}, - FLOODGATE = { - "A mechanical gate used to control the flow of water. Floodgates are", - "initially closed when installed, and must be linked to a lever or", - "pressure plate in order to be either opened or closed. Furniture for", - "blocking horizontal passage. It is built as a solid tile and may be", - "linked via mechanism to devices such as pressure plates and levers.", - "When activated, the floodgate will disappear. Will become stuck open", - "if an item occupies its tile."}, - GOBLET = { "A small drink container that is held in one hand."}, - GRATE = { "A barrier with small openings, grates block solid objects and", - "creatures - but not line of sight, liquids, or projectiles. Grates can", - "be installed vertically on a floor, or horizontally over open space.", - "Grates can be retracted if they are mechanically linked to a lever or", - "pressure plate. Grates are not stable enough to support constructions."}, - HATCH_COVER = { - "A barrier that covers a hole in the floor. A hatch cover acts like a", - "door, but placed over a vertical opening. Hatches can also cover carved", - "stairways and ramps. Hatches can be linked to mechanical controls.", - "Creatures cannot manually open or close hatches that are mechanically", - "controlled. They may also be set as 'locked' to prevent all passage or", - "'tightly closed' to prevent animal passage."}, - ITEM_AMMO_ARROWS = { - "Ammunition for bows, which are primarily used by elves."}, - ITEM_AMMO_BOLTS = { - "Ammunition for crossbows, which are a dwarf's preferred ranged weapon.", - "It is not recommended to store bolts in bins, due to pickup bugs."}, - ITEM_ARMOR_BREASTPLATE = { - "A breastplate is a piece of plate armor that covers the upper body and", - "the lower body. It is usually worn in the armor layer."}, - ITEM_ARMOR_CAPE = { - "A (cool-looking) cape. Protects the chest."}, - ITEM_ARMOR_CLOAK = { - "A cloth cloak. Protects the face, neck, chest, arms and upper legs."}, - ITEM_ARMOR_COAT = { - "A heavy cloth coat. Protects the face, neck, chest, arms and upper legs."}, - ITEM_ARMOR_DRESS = { - "A cloth dress. Protects the face, neck, chest, arms and legs."}, - ITEM_ARMOR_LEATHER = { - "Leather armor is light and covers both arms and legs", - "in addition to body"}, - ITEM_ARMOR_MAIL_SHIRT = { - "A chainmail shirt. Protects the face, neck, chest,", - "upper arms and upper legs."}, - ITEM_ARMOR_ROBE = { - "A cloth robe. Protects the face, neck, chest, arms and legs."}, - ITEM_ARMOR_SHIRT = { - "A cloth shirt. Protects the neck, chest and arms."}, - ITEM_ARMOR_TOGA = { - "A cloth toga. Protects the face, neck, chest, upper arms and upper legs."}, - ITEM_ARMOR_TUNIC = { - "A cloth tunic. Protects the neck, chest and upper arms."}, - ITEM_ARMOR_VEST = { - "A cloth vest. Protects the chest."}, - ITEM_FOOD_BISCUITS = { - "Biscuits are the lowest tier of meals that can be prepared by your", - "dwarves. They are made in a kitchen with the 'Prepare Easy Meal' order", - "and use two ingredients. Preparing easy meals is the easiest way to,", - "get experience for you cooks, but the larger volume produced means more", - "hauling to take them to storage."}, - ITEM_FOOD_ROAST = { - "Roasts are the highest tier of meals that can be prepared by your ", - "dwarves. They are made in a kitchen with the 'Prepare Lavish Meal'", - "order, and use four ingredients. As there are more ingredients, there", - "is a better chance that a dwarf will like at least one ingredient."}, - ITEM_FOOD_STEW = { - "Stews are the middle tier of meals that can be prepared by your ", - "dwarves. They are made in a kitchen with the 'Prepare Fine Meal' order,", - "and use three ingredients. They provide more food than Biscuits,", - "but are less valuable than Roasts."}, - ITEM_GLOVES_GAUNTLETS = { - "Gauntlets are armor worn on any body part that can grasp, which for", - "dwarves are the hands. They are similar to mittens and gloves, but", - "act as an armor layer and provide much more protection. Like other", - "armor, gauntlets can be made of metal, shell, or bone."}, - ITEM_GLOVES_GLOVES = { - "Gloves cover the hands, wrapping each finger and thumb individually", - "to preserve the wearer's dexterity at the cost of some warmth"}, - ITEM_GLOVES_MITTENS = { - "Mittens cover the fingers together and thumb separately, preserving", - "the ability to grasp but keeping fingers together for more warmth in", - "cold climates"}, - ITEM_HELM_CAP = { - "A cap covers only the crown of the head. It prevents heat loss through", - "a bald pate and protects the skull from falling objects and", - "downward strikes."}, - ITEM_HELM_HELM = { - "A helm covers the entire face and head. It protects the wearer from", - "falling objects and a variety of weapon strikes from all directions.", - "Every other type of head covering, save a hood, is worn under a helm", - "for padding."}, - ITEM_HELM_HOOD = { - "A hood is a soft loose covering for the head and sides of the face.", - "It shields the wearer from cold breezes and can cushion blows from", - "any direction. It is pulled over the wearer's other headgear, providing", - "a final outer layer of protection from the elements."}, - ITEM_HELM_MASK = { - "A mask hides the wearer's face from view and protects it from all but", - "the most accurate piercing attacks. Some can be carved to present the", - "enemy with a more fearsome visage, or to show no face at all.", - "Masks are worn underneath other layers of headgear."}, - ITEM_HELM_SCARF_HEAD = { - "A head scarf is a loose wrap of cloth or leather that is typically", - "worn in hot climates to protect the head from the rays of the sun.", - "It provides light cushioning against some blows."}, - ITEM_HELM_TURBAN = { - "A turban is a length of cloth or leather that is wrapped many times", - "around the head to shield the wearer from the sun's rays and provide", - "several layers of insulation. A turban can be pinned or clasped in", - "place, or simply folded and tucked into a stable configuration."}, - ITEM_HELM_VEIL_FACE = { - "A face veil is a soft covering that protects the lower half of the", - "wearer's face, leaving only the eyes to gaze out. It can prevent", - "noxious fluids from splashing into the wearer's mouth.", - "It is worn under every other layer of headgear."}, - ITEM_HELM_VEIL_HEAD = { - "A veil for the whole head is a wall of sheer cloth or finely-punched", - "leather extending from above the brow to below the chin, and often hung", - "from a more solid cloth headpiece that covers the crown and cheeks.", - "It admits some light but almost entirely obscures the wearer's face."}, - ITEM_INSTRUMENT_DRUM = { - "Short, wide, and round, this cylindrical percussion instrument can", - "play music when banged by one's hands. It is only useful to trade."}, - ITEM_INSTRUMENT_FLUTE = { - "This long cylindrical woodwind instrument can make a wide array of", - "tones and music when blown into. It is only useful to trade."}, - ITEM_INSTRUMENT_HARP = { - "Vaguely triangular in shape, this stringed instrument can play a", - "variety of notes by plucking the strings with one's fingers.", - "It is only useful to trade."}, - ITEM_INSTRUMENT_PICCOLO = { - "Similar to a flute, but smaller and with a higher tone, a piccolo is", - "a cylindrical woodwind instrument. It is only useful to trade."}, - ITEM_INSTRUMENT_TRUMPET = { - "A dwarven brass instrument - which need not be made of brass.", - "It is only useful to trade."}, - ITEM_PANTS_BRAIES = { - "Braies are undergarments that cover from the waist to the knees.", - "Dwarves cannot craft braies, so they must be obtained through", - "other means."}, - ITEM_PANTS_GREAVES = { - "Greaves are plated armor meant to protect the lower legs, though", - "they are equipped as pants."}, - ITEM_PANTS_LEGGINGS = { - "Leggings are garments that cover everything from the waist to the", - "ankles, though with a tighter fit than other trousers."}, - ITEM_PANTS_LOINCLOTH = { - "Loincloths are draped undergarments meant to cover little more than", - "the 'geldables'. Dwarves cannot craft loincloths, so they must be", - "obtained through other means."}, - ITEM_PANTS_PANTS = { - "Trousers are a garment that covers everything from the waist to the", - "ankles. They keep the legs and lower body warm."}, - ITEM_PANTS_SKIRT = { - "A skirt is a cone-shaped garment that hangs from the waist, covering", - "part of the legs. Its use is more for modesty than protection.", - "Dwarves cannot craft skirts, so they must be obtained through other means."}, - ITEM_PANTS_SKIRT_LONG = { - "A skirt is a cone-shaped garment that hangs from the waist, covering", - "part of the legs. Its use is more for modesty than protection. Long", - "skirts fulfil this purpose well. Dwarves cannot craft long skirts,", - "so they must be obtained through other means."}, - ITEM_PANTS_SKIRT_SHORT = { - "A skirt is a cone-shaped garment that hangs from the waist, covering", - "part of the legs. Its use is more for modesty than protection, though", - "short skirts offer less in the way of modesty. Dwarves cannot craft", - "short skirts, so they must be obtained through other means."}, - ITEM_PANTS_THONG = { - "Thongs are strapped undergarments meant to cover little more than", - "the 'geldables'. Dwarves cannot craft thongs, so they must be obtained", - "through other means."}, - ITEM_SHIELD_BUCKLER = { - "A smaller and less protective type of shield. A buckler can be used", - "to block attacks, and with skill anything from a goblin axe", - "to Dragonfire can be deflected."}, - ITEM_SHIELD_SHIELD = { - "Larger and more defensive than a buckler, a full-sized shield can be", - "used to block attacks. With skill anything from a goblin axe to", - "Dragonfire can be deflected."}, - ITEM_SHOES_BOOTS = { - "Boots are more protective kind of shoe, covering from the foot up to", - "the knee."}, - ITEM_SHOES_BOOTS_LOW = { - "Low boots are more protective kind of shoe, covering from the foot up", - "to just past the ankle."}, - ITEM_SHOES_CHAUSSE = { - "Chausses are chainmail armor meant to protect the legs, though these", - "are equipped as footwear. Dwarves cannot craft chausses, so they", - "must be obtained through other means."}, - ITEM_SHOES_SANDAL = { - "Sandals are open footwear consisting of soles and some number of", - "straps. Dwarves cannot craft sandals, so they must be obtained", - "through other means."}, - ITEM_SHOES_SHOES = { - "Shoes are closed footwear meant to protect the feet from rough terrain", - "and the elements."}, - ITEM_SHOES_SOCKS = { - "Socks are tubular articles of clothing, worn on each foot along with", - "shoes or other footwear."}, - ITEM_SIEGEAMMO_BALLISTA = { - "Ballista ammunition, for an enormous siege weapon."}, - ITEM_TOOL_BOWL = { - "Bowls are used to contain individual servings of meals.", - "At the moment, dwarves have no use for these."}, - ITEM_TOOL_CAULDRON = { - "Cauldrons are large metal pots used to cook meals like soups or stews", - "over an open fire. At the moment, dwarves have no use for these."}, - ITEM_TOOL_FORK_CARVING = { - "A carving fork typically has only two prongs and is exceptionally long.", - "It is used to hold down a piece of cooked meat while using a knife."}, - ITEM_TOOL_HELVE = { - "A helve is the handle of a tool such as an axe.", - "It is not useful in this state - but adding a rock makes a stone axe,", - "which can be used for woodcutting in Adventure mode."}, - ITEM_TOOL_HIVE = { - "Hives are structures that house colonies of honey bees. To be", - "productive, they need to be constructed on an above-ground tile with", - "an accessible honey bee colony somewhere on the map. Some time after", - "bees are 'installed' by a beekeeper, the hive will be ready to harvest", - "or split into new colonies."}, - ITEM_TOOL_HONEYCOMB = { - "Honeycomb is an intermediate product of beekeeping, produced along", - "with royal jelly when a beekeeper harvests a suitable hive. It must", - "be processed by a Presser at a Screw Press to produce honey, which may", - "be used in cooking or made into mead and a wax cake, which can be used", - "to make low-value crafts."}, - ITEM_TOOL_JUG = { - "Jugs are small food storage containers that hold royal jelly, honey,", - "or oil. They are used by beekeepers when harvesting suitable hives and", - "by pressers when processing honeycomb or seed pastes at a screw press."}, - ITEM_TOOL_KNIFE_BONING = { - "A boning knife has a sharp point and narrow blade. It is an excellent", - "all-around kitchen knife and decent weapon in a pinch."}, - ITEM_TOOL_KNIFE_CARVING = { - "A carving knife is for cutting thin slices of cooked meat for serving.", - "It may be useful as an improvised weapon."}, - ITEM_TOOL_KNIFE_MEAT_CLEAVER = { - "A meat cleaver is a heavy square-bladed knife for cutting bone and", - "meat alike."}, - ITEM_TOOL_KNIFE_SLICING = { - "A slicing knife is for cutting thin slices of cooked meat."}, - ITEM_TOOL_LADLE = { - "A ladle is a large spoon with a long handle and a deep bowl,", - "intended for serving out portions of soups and stews."}, - ITEM_TOOL_LARGE_POT = { - "Large pots are storage containers made of any hard material. They", - "function identically to barrels when brewing or storing food, while", - "being much lighter than barrels made of the same material.", - "Unfortunately, they cannot be used when making dwarven syrup or when", - "building asheries and dyer's workshops."}, - ITEM_TOOL_MINECART = { - "A minecart is a tool for hauling, and can be made from wood or metal.", - "", - "Minecart systems are the most efficient and most complicated way to", - "move items, and can do anything from improving industrial efficiency,", - "to transporting magma or launching hundreds of weapons at enemies.", - "Misuse may result in horrific injury to drivers and pedestrians."}, - ITEM_TOOL_MORTAR = { - "Half of a mortar and pestle, the mortar is a bowl in which to grind", - "up plants or other reagents."}, - ITEM_TOOL_NEST_BOX = { - "A place for birds to lay eggs. Must be built before use.", - "Forbid eggs to hatch into chicks before a dwarf picks them up."}, - ITEM_TOOL_PESTLE = { - "Half of a mortar and pestle, the pestle is a stick used to grind up", - "plants or other reagents."}, - ITEM_TOOL_POUCH = { - "A small bag used to carry a variety of tools."}, - ITEM_TOOL_STEPLADDER = { - "A small stepladder. If you have one of these, you can use zones to", - "assign your dwarves to pick fruit from certain trees."}, - ITEM_TOOL_STONE_AXE = { - "This tool can be made in Adventure mode, and is used to cut", - "trees for building or carpentry."}, - ITEM_TOOL_WHEELBARROW = { - "A small hand-cart with long handles and a single wheel, this", - "wheelbarrow makes heavy hauling jobs much more manageable."}, - ITEM_TOY_AXE = { - "A small toy axe without an edge. Useless except as a trade good."}, - ITEM_TOY_BOAT = { - "A tiny model of a boat. Only good for trade."}, - ITEM_TOY_HAMMER = { - "A toy hammer. Its only use is to sell."}, - ITEM_TOY_MINIFORGE = { - "A model of a blacksmith's forge that dwarf children love.", - "Only useful as a trade good."}, - ITEM_TOY_PUZZLEBOX = { - "A perplexing toy that dwarves of all ages enjoy.", - "Its only use is as a trade good."}, - ITEM_TRAPCOMP_ENORMOUSCORKSCREW = { - "A massive screw-like object. Can be used to make a pump,", - "or as a component in a trap."}, - ITEM_TRAPCOMP_GIANTAXEBLADE = { - "This massive blade is typically made of metal and can be used in weapon", - "traps, swinging once to slice anyone unfortunate enough to activate it."}, - ITEM_TRAPCOMP_LARGESERRATEDDISC = { - "Serrated discs are typically made of metal and can be used in weapon", - "traps, in which they eviscerate its victims with three powerful slicing", - "attacks. Such traps have a tendency to sever multiple body parts and", - "make a gigantic mess."}, - ITEM_TRAPCOMP_MENACINGSPIKE = { - "Menacing spikes are made of wood or metal and can be used in weapon", - "traps or upright spike traps, in which they impale the victim. They", - "are especially effective against unarmored foes or, in an upright", - "spike trap, anyone falling from great heights."}, - ITEM_TRAPCOMP_SPIKEDBALL = { - "This trap component menaces with spikes of wood or metal. It hits three", - "times with its spikes, but does not penetrate as deeply as a menacing", - "spike. Compared to other trap components, spiked balls are slightly", - "more effective against heavily armored foes. They also make for a", - "surprisingly valuable trade good, on par with serrated discs."}, - ITEM_WEAPON_AXE_BATTLE = { - "A battle axe is an edged weapon: essentially a sharp blade", - "mounted along the end of a short and heavy handle.", - "", - "Dwarves can forge battle axes out of any weapon-grade metal,", - "though those with superior edge properties are more effective.", - "", - "A battle axe may also be used as a tool for chopping down trees."}, - ITEM_WEAPON_AXE_GREAT = { - "This is an axe nearly twice as large as a battle axe. Its size", - "makes it unsuitable for a dwarf, but those who can wield it find", - "its increased size and weight contribute to its effectiveness."}, - ITEM_WEAPON_AXE_TRAINING = { - "As a battleaxe made from wood, this practise weapon is useful for", - "training recruits. Thanks to good craftsdwarfship, it can also", - "be used to cut down trees."}, - ITEM_WEAPON_BLOWGUN = { - "A very simple ranged weapon: blow into one end of the long narrow", - "tube, and project a pellet or dart into the body of one's prey.", - "If the prey approaches, this blowgun makes a useless melee weapon."}, - ITEM_WEAPON_BOW = { - "Bows are the preferred ranged weapon for elves and goblins, and", - "shoot arrows as projectiles. As they are a foreign weapon, they", - "cannot be made in your fort. In melee, bowmen will use their bow as", - "a weapon, training the swordsman skill."}, - ITEM_WEAPON_CROSSBOW = { - "The favoured ranged weapon of choice for any dwarf, crossbows can be", - "made of wood, bones or metal, and shoot bolts as projectiles. Hunters", - "or marks-dwarves that run out of ammunition will use their crossbow", - "as a melee weapon, training the hammerdwarf skill."}, - ITEM_WEAPON_DAGGER_LARGE = { - "A large dagger is a edge weapon that is essentially just a bit smaller", - "than a short sword. It's used for stabbing rather than slashing. Large", - "daggers use and train the knife user skill, and are common weapons for", - "kobold and goblin thieves. As foreign weapons dwarves cannot forge", - "large daggers."}, - ITEM_WEAPON_FLAIL = { - "A flail is a blunt weapon that consists of a rounded weight attached to", - "a handle by a length of chain. Flails are the same size as a morningstar,", - "but have a contact area twice as large as a much larger maul. As foreign", - "weapons dwarves cannot forge flails. Flails use and train the", - "macedwarf skill."}, - ITEM_WEAPON_HALBERD = { - "A halberd is a foreign weapon, and cannot be made by your dwarves.", - "Even the largest and strongest dwarves cannot use a halberd, making", - "them useless in military terms. They can however be placed in a weapon", - "trap or melted down to provide metal bars, redeeming them."}, - ITEM_WEAPON_HAMMER_WAR = { - "A war hammer is a blunt weapon that is essentially a hammer with a long", - "handle. War hammers use and train the hammerdwarf skill. Dwarves can", - "forge war hammers out of any weapons-grade metal, though those with", - "higher densities tend to cause more damage."}, - ITEM_WEAPON_MACE = { - "A mace is a blunt weapon that consists of a rounded or flanged weight", - "mounted on the end of a handle. Despite similarities to a morningstar", - "in appearance, a mace is 60% larger and has twice the contact area.", - "Maces use and train the macedwarf skill. Dwarves can forge maces out of", - "any weapons-grade metal, though those with higher densities", - "(like silver) cause more damage."}, - ITEM_WEAPON_MAUL = { - "A maul is a blunt weapon that is essentially a very large war hammer,", - "similar to a sledgehammer. Mauls are more than three times larger than", - "standard war hammers, with a similar 'bash' attack. Mauls also have", - "ten times the contact area and greatly reduced penetration, which", - "reduces their effectiveness. Mauls use and train the hammerdwarf", - "skill. Being foreign weapons, dwarves cannot forge mauls."}, - ITEM_WEAPON_MORNINGSTAR = { - "A morningstar is an edged weapon that consists of a spiked ball mounted", - "on the end of a handle. Despite similarities to a mace in appearance,", - "a morningstar's size and contact area are closer to those of a war", - "hammer. Specifically, a morningstar is 25% larger than a war hammer", - "with the same contact area, and uses piercing damage to inflict internal", - "injuries. Morningstars use and train the macedwarf skill."}, - ITEM_WEAPON_PICK = { - "The most important item for a beginning fortress, a pick can", - "get a party underground. Also crucial mining for stone or", - "metals, expansion of living space, and so on.", - "", - "A pick is also useful as a weapon, though putting miners in the", - "military causes equipment clashes."}, - ITEM_WEAPON_PIKE = { - "A pike is a weapon that is essentially a very long spear.", - "Pikes use and train the Pikedwarf skill. As foreign weapons,", - "dwarves cannot forge pikes."}, - ITEM_WEAPON_SCIMITAR = { - "A scimitar is an edged weapon with a curved blade that is very similar", - "to a short sword. Scimitars use and train the swordsdwarf skill.", - "As foreign weapons dwarves cannot forge scimitars."}, - ITEM_WEAPON_SCOURGE = { - "A scourge is an edge weapon that consists of a spike or bladed weight", - "on the end of a flexible length of material that can be swung at", - "high speed. Scourges are similar to whips, though the whip is a blunt", - "weapon with an even smaller contact area.Scourges use and train the", - "lasher skill. As foreign weapons dwarves cannot forge scourges."}, - ITEM_WEAPON_SPEAR = { - "A pole weapon consisting of a shaft, usually of wood, with a pointed", - "head made of metal or just the sharpened end of the shaft itself.", - "With the ability to pin opponents, spears are most effective with axe", - "or macedwarves for combo attacks. "}, - ITEM_WEAPON_SPEAR_TRAINING = { - "A wooden training spear, this has no sharp edges and thus presents", - "little risk of injury. Military dwarves can become", - "attached to them, and refuse to swap them for weapons that cause", - "actual injury to your enemies."}, - ITEM_WEAPON_SWORD_2H = { - "An enormous sword taller than many humans. Victims may be split in", - "two by a single blow, though no dwarf is large enough to wield a", - "greatsword and do so. As foreign weapons, dwarves cannot forge them."}, - ITEM_WEAPON_SWORD_LONG = { - "A longsword is a classic weapon, consisting of a short handle and a", - "long sharp blade. Most dwarves are large enough to use a longsword,", - "but as foreign weapons cannot forge them."}, - ITEM_WEAPON_SWORD_SHORT = { - "A sword just the right size for dwarves, though small dwarves may", - "need both hands. Shortswords can be made from metal at a forge."}, - ITEM_WEAPON_SWORD_SHORT_TRAINING = { - "A wooden training sword, this has no sharp edges and thus presents", - "little risk of injury. Military dwarves can become", - "attached to them, and refuse to swap them for weapons that cause", - "actual injury to your enemies."}, - ITEM_WEAPON_WHIP = { - "A highly effective weapon known to cause large amounts of pain.", - "It cannot be forged by dwarves."}, - MEAT = { "Butchering an animal gives meat, the amount depending on the size", - "of the butchered animal. Along with plants, meat is the", - "backbone of every food industry."}, - MILLSTONE = { - "A large grinding stone, used in a mill to produce flour, sugar, and", - "dyes much faster than a quern. It is too large to be operated by hand,", - "and must be powered for operation. Millstones are made of stone."}, - ORTHOPEDIC_CAST = { - "Casts are made from plaster, and are used to keep broken bones in", - "place until they are healed. Applying a cast requires a bucket,", - "cloth and a water source."}, - PIPE_SECTION = { - "An enormous piece of pipe, it is a part of a screw pump."}, - QUERN = { "A hand-operated mill for plants, grains, and seeds. It mills plants", - "much slower than a millstone. Must be built from stone."}, - QUIVER = { "Item used to hold ammunition, made out of leather. Hunting dwarves", - "and crossbow dwarves will automatically grab one to store their ammo."}, - RING = { "A ring is an item of jewellery, which does not interfere with", - "wearing other equipment. Eleven rings can be worn on each finger", - "or toe, for a maximum of 220 rings."}, - ROCK = { "A small rock, sharpened as a weapon in Adventure mode."}, - ROUGH = { "Rough gemstones and raw glass are cut by a Gem Cutter at a Jeweler's", - "Workshop into small decorative gems. Sometimes, the gem-cutting job", - "results in a craft or large gem that is useless except as a very", - "valuable trade good."}, - SCEPTER = { "A scepter is a short, ornamental rod or wand typically associated", - "with royalty. It's only use is as a trade good."}, - SKIN_TANNED = { - "The tanned hide of animals is flexible enough to be made into an", - "assortment of goods for military and civilian use. Leather can also", - "be used to decorate items with sewn images at a Leather Works. Armor", - "and shields made from leather are not terribly effective, but are", - "still better than nothing at all."}, - SLAB = { "A memorial stone, used to quiet restless ghost when engraved with", - "the name of the deceased and built."}, - SMALLGEM = {"Cut gemstones and the odd gizzard stone (a product of butchering", - "certain species of animals) are used by a Gem Setter to decorate items", - "at a Jeweler's Workshop."}, - SPLINT = { "Splints are used to immobilise fractured limbs. They are made out of", - "wood or metal, and allow dwarves to leave the hospital and continue", - "their normal jobs. Splints are applied with the bonedoctor skill."}, - STATUE = { "A large piece of art carved in the likeness of a creature. Statues", - "can be installed on any open floor space, but cannot share the space", - "with creatures. Statues can be used as the focal point of a", - "recreational statue garden. Dwarves will admire or revile as they", - "pass, depending on the statue and the individual's preferences."}, - TABLE = { "A flat-topped piece of furniture useful as a work-surface for a", - "scribe or a dining-surface for a hungry dwarf. Typically found in", - "shops, dinning rooms, and offices. Dining rooms may be designated and", - "assigned from them, though dwarves will complain if there are too few."}, - THREAD = { "A small bundle of processed material, ready to be woven into cloth.", - "Thread made from animal hair will not be used to make cloth. Thread", - "can also be used by doctors to sew wounds shut. It is sourced from", - "shearing, plant processing, trade, or web gathering. It can be dyed", - "for additional value before being woven."}, - TOTEM = { "A carved and polished skull."}, - TRACTION_BENCH = { - "A special hospital bed made to secure dwarves with complex or", - "overlapping fractures until healed. Patients may need several months", - "or more in a traction bench to heal. Constructed from a table,", - "a mechanism, and a rope or chain."}, - TRAPPARTS = { - "Used to build traps, levers and other machines."}, - WEAPONRACK = { - "Furniture used for training. Barracks may be designated and assigned", - "from them. Military squads may use their assigned barracks for", - "training, storage, or sleeping, depending on the settings."}, - WINDOW = { "Furniture used for ambiance. Either made in a glass furnace from glass", - "or built on site using three cut gems. While it is treated as a wall,", - "it does not support constructions. Passing dwarves will admire them."}, - WOOD = { "A porous and fibrous structural tissue found in the stems and roots", - "of trees and underground fungus. Wood is renewable and essential for", - "numerous industries. It can be made into charcoal, ash for further", - "processing, furniture, crafts, tools, some trap components, training", - "gear, and (ineffective) weapons and armor. Elves take serious offence", - "when wood or wooden items are offered in trade."} -} diff --git a/scripts/lever.rb b/scripts/lever.rb deleted file mode 100644 index 05a159861..000000000 --- a/scripts/lever.rb +++ /dev/null @@ -1,152 +0,0 @@ -# control your levers from the dfhack console -=begin - -lever -===== -Allow manipulation of in-game levers from the dfhack console. - -Can list levers, including state and links, with:: - - lever list - -To queue a job so that a dwarf will pull the lever 42, use ``lever pull 42``. -This is the same as :kbd:`q` querying the building and queue a :kbd:`P` pull request. - -To magically toggle the lever immediately, use:: - - lever pull 42 --now - -=end - -def lever_pull_job(bld) - ref = DFHack::GeneralRefBuildingHolderst.cpp_new - ref.building_id = bld.id - - job = DFHack::Job.cpp_new - job.job_type = :PullLever - job.pos = [bld.centerx, bld.centery, bld.z] - job.general_refs << ref - bld.jobs << job - df.job_link job - - puts lever_descr(bld) -end - -def lever_pull_cheat(bld) - bld.linked_mechanisms.each { |i| - i.general_refs.grep(DFHack::GeneralRefBuildingHolderst).each { |r| - r.building_tg.setTriggerState(bld.state) - } - } - - bld.state = (bld.state == 0 ? 1 : 0) - - puts lever_descr(bld) -end - -def lever_descr(bld, idx=nil) - ret = [] - - # lever description - descr = '' - descr << "#{idx}: " if idx - descr << "lever ##{bld.id} " - descr << "(#{bld.name}) " if bld.name.length != 0 - descr << "@[#{bld.centerx}, #{bld.centery}, #{bld.z}] #{bld.state == 0 ? '\\' : '/'}" - bld.jobs.each { |j| - if j.job_type == :PullLever - flags = '' - flags << ', repeat' if j.flags.repeat - flags << ', suspended' if j.flags.suspend - descr << " (pull order#{flags})" - end - } - - bld.linked_mechanisms.map { |i| - i.general_refs.grep(DFHack::GeneralRefBuildingHolderst) - }.flatten.each { |r| - # linked building description - tg = r.building_tg - state = '' - if tg.respond_to?(:gate_flags) - state << (tg.gate_flags.closed ? 'closed' : 'opened') - state << ", closing (#{tg.timer})" if tg.gate_flags.closing - state << ", opening (#{tg.timer})" if tg.gate_flags.opening - end - - ret << (descr + " linked to #{tg._rtti_classname} ##{tg.id} @[#{tg.centerx}, #{tg.centery}, #{tg.z}] #{state}") - - # indent other links - descr = descr.gsub(/./, ' ') - } - - ret << descr if ret.empty? - - ret -end - -def lever_list - @lever_list = [] - df.world.buildings.other[:TRAP].find_all { |bld| - bld.trap_type == :Lever - }.sort_by { |bld| bld.id }.each { |bld| - puts lever_descr(bld, @lever_list.length) - @lever_list << bld.id - } -end - - -@lever_list ||= [] - -case $script_args[0] -when 'pull' - cheat = $script_args.delete('--cheat') || $script_args.delete('--now') - - if $script_args[1].nil? - bld = df.building_find(:selected) if not bld - raise 'no lever under cursor and no lever id given' if not bld - else - id = $script_args[1].to_i - id = @lever_list[id] || id - bld = df.building_find(id) - raise 'invalid lever id' if not bld - end - - if cheat - lever_pull_cheat(bld) - else - lever_pull_job(bld) - end - -when 'list' - lever_list - -when /^\d+$/ - id = $script_args[0].to_i - id = @lever_list[id] || id - bld = df.building_find(id) - raise 'invalid lever id' if not bld - - puts lever_descr(bld) - -else - - puts < 0 and bx+dx < df.world.map.x_count-1 and by+dy > 0 and by+dy < df.world.map.y_count-1 - pos = [bx+dx, by+dy, bz] - end - } - } - } - } - df.center_viewscreen(*pos) - df.map_tile_at(*pos).dig - puts "Here is some #{df.world.raws.inorganics[found_mat].id}" - else - puts "Cannot find unmined #{mats.map { |mat| df.world.raws.inorganics[mat].id }.join(', ')}" - end - -else - puts "Available ores:", $ore_veins.sort_by { |mat, pos| pos.length }.map { |mat, pos| - ore = df.world.raws.inorganics[mat] - metals = ore.metal_ore.mat_index.map { |m| df.world.raws.inorganics[m] } - ' ' + ore.id.downcase + ' (' + metals.map { |m| m.id.downcase }.join(', ') + ')' - } - -end diff --git a/scripts/lua.lua b/scripts/lua.lua deleted file mode 100644 index 17c47d41a..000000000 --- a/scripts/lua.lua +++ /dev/null @@ -1,91 +0,0 @@ --- Execute lua commands interactively or from files. ---[[=begin - -lua -=== -There are the following ways to invoke this command: - -1. ``lua`` (without any parameters) - - This starts an interactive lua interpreter. - -2. ``lua -f "filename"`` or ``lua --file "filename"`` - - This loads and runs the file indicated by filename. - -3. ``lua -s ["filename"]`` or ``lua --save ["filename"]`` - - This loads and runs the file indicated by filename from the save - directory. If the filename is not supplied, it loads "dfhack.lua". - -4. ``:lua`` *lua statement...* - - Parses and executes the lua statement like the interactive interpreter would. - -=end]] - -local args={...} -local cmd = args[1] - -env = env or {} -setmetatable(env, {__index = function(self, k) - if k == 'scr' or k == 'screen' then - return dfhack.gui.getCurViewscreen() - elseif k == 'bld' or k == 'building' then - return dfhack.gui.getSelectedBuilding() - elseif k == 'item' then - return dfhack.gui.getSelectedItem() - elseif k == 'job' then - return dfhack.gui.getSelectedJob() - elseif k == 'wsjob' or k == 'workshop_job' then - return dfhack.gui.getSelectedWorkshopJob() - elseif k == 'unit' then - return dfhack.gui.getSelectedUnit() - else - return _G[k] - end -end}) - -if cmd=="--file" or cmd=="-f" then - local f,err=loadfile (args[2]) - if f==nil then - qerror(err) - end - dfhack.safecall(f,table.unpack(args,3)) -elseif cmd=="--save" or cmd=="-s" then - if df.global.world.cur_savegame.save_dir=="" then - qerror("Savefile not loaded") - end - local fname=args[2] or "dfhack.lua" - fname=string.format("data/save/%s/%s",df.global.world.cur_savegame.save_dir,fname) - local f,err=loadfile (fname) - if f==nil then - qerror(err) - end - dfhack.safecall(f,table.unpack(args,3)) -elseif cmd~=nil then - -- Support some of the prefixes allowed by dfhack.interpreter - local prefix - if string.match(cmd, "^[~@!]") then - prefix = string.sub(cmd, 1, 1) - cmd = 'return '..string.sub(cmd, 2) - end - - local f,err=load(cmd,'=(lua command)', 't') - if f==nil then - qerror(err) - end - - local rv = table.pack(dfhack.safecall(f,table.unpack(args,2))) - - if rv[1] and prefix then - print(table.unpack(rv,2,rv.n)) - if prefix == '~' then - printall(rv[2]) - elseif prefix == '@' then - printall_ipairs(rv[2]) - end - end -else - dfhack.interpreter("lua","lua.history",env) -end diff --git a/scripts/make-legendary.lua b/scripts/make-legendary.lua deleted file mode 100644 index da29cd4c2..000000000 --- a/scripts/make-legendary.lua +++ /dev/null @@ -1,141 +0,0 @@ --- Make a skill or skills of a unit Legendary +5 --- by vjek ---[[=begin - -make-legendary -============== -Makes the selected dwarf legendary in one skill, a group of skills, or all -skills. View groups with ``make-legendary classes``, or all skills with -``make-legendary list``. Use ``make-legendary MINING`` when you need something -dug up, or ``make-legendary all`` when only perfection will do. - -=end]] - --- this function will return the number of elements, starting at zero. --- useful for counting things where #foo doesn't work -function count_this(to_be_counted) - local count = -1 - local var1 = "" - while var1 ~= nil do - count = count + 1 - var1 = (to_be_counted[count]) - end - count=count-1 - return count -end - -function getName(unit) - return dfhack.df2console(dfhack.TranslateName(dfhack.units.getVisibleName(unit))) -end - -function make_legendary(skillname) - local skillnamenoun,skillnum - unit=dfhack.gui.getSelectedUnit() - - if unit==nil then - print ("No unit under cursor! Aborting with extreme prejudice.") - return - end - - if (df.job_skill[skillname]) then - skillnamenoun = df.job_skill.attrs[df.job_skill[skillname]].caption_noun - else - print ("The skill name provided is not in the list.") - return - end - - if skillnamenoun ~= nil then - utils = require 'utils' - skillnum = df.job_skill[skillname] - utils.insert_or_update(unit.status.current_soul.skills, { new = true, id = skillnum, rating = 20 }, 'id') - print (getName(unit) .. " is now a Legendary "..skillnamenoun) - else - print ("Empty skill name noun, bailing out!") - return - end -end - -function PrintSkillList() - local count_max = count_this(df.job_skill) - local i - for i=0, count_max do - print("'"..df.job_skill.attrs[i].caption.."' "..df.job_skill[i].." Type: "..df.job_skill_class[df.job_skill.attrs[i].type]) - end - print ("Provide the UPPER CASE argument, for example: PROCESSPLANTS rather than Threshing") -end - -function BreathOfArmok() - unit=dfhack.gui.getSelectedUnit() - if unit==nil then - print ("No unit under cursor! Aborting with extreme prejudice.") - return - end - local i - local count_max = count_this(df.job_skill) - utils = require 'utils' - for i=0, count_max do - utils.insert_or_update(unit.status.current_soul.skills, { new = true, id = i, rating = 20 }, 'id') - end - print ("The breath of Armok has engulfed "..getName(unit)) -end - -function LegendaryByClass(skilltype) - unit=dfhack.gui.getSelectedUnit() - if unit==nil then - print ("No unit under cursor! Aborting with extreme prejudice.") - return - end - - utils = require 'utils' - local i - local skillclass - local count_max = count_this(df.job_skill) - for i=0, count_max do - skillclass = df.job_skill_class[df.job_skill.attrs[i].type] - if skilltype == skillclass then - print ("Skill "..df.job_skill.attrs[i].caption.." is type: "..skillclass.." and is now Legendary for "..getName(unit)) - utils.insert_or_update(unit.status.current_soul.skills, { new = true, id = i, rating = 20 }, 'id') - end - end -end - -function PrintSkillClassList() - local i - local count_max = count_this(df.job_skill_class) - for i=0, count_max do - print(df.job_skill_class[i]) - end - print ("Provide one of these arguments, and all skills of that type will be made Legendary") - print ("For example: Medical will make all medical skills legendary") -end - ---main script operation starts here ----- -local opt = ... -local skillname - -if opt then - if opt=="list" then - PrintSkillList() - return - end - if opt=="classes" then - PrintSkillClassList() - return - end - if opt=="all" then - BreathOfArmok() - return - end - if opt=="Normal" or opt=="Medical" or opt=="Personal" or opt=="Social" or opt=="Cultural" or opt=="MilitaryWeapon" or opt=="MilitaryAttack" or opt=="MilitaryDefense" or opt=="MilitaryMisc" then - LegendaryByClass(opt) - return - end - skillname = opt -else - print ("No skillname supplied.\nUse argument 'list' to see a list, 'classes' to show skill classes, or use 'all' if you want it all!") - print ("Example: To make a legendary miner, use make_legendary MINING") - return -end - -make_legendary(skillname) diff --git a/scripts/make-monarch.lua b/scripts/make-monarch.lua deleted file mode 100644 index 1c8764367..000000000 --- a/scripts/make-monarch.lua +++ /dev/null @@ -1,39 +0,0 @@ ---set target unit as king/queen ---[[=begin - -make-monarch -============ -Make the selected unit King or Queen of your civilisation. - -=end]] - -local unit=dfhack.gui.getSelectedUnit() -if not unit then qerror("No unit selected") end -local newfig=dfhack.units.getNemesis(unit).figure -local my_entity=df.historical_entity.find(df.global.ui.civ_id) -local monarch_id -for k,v in pairs(my_entity.positions.own) do - if v.code=="MONARCH" then - monarch_id=v.id - break - end -end -if not monarch_id then qerror("No monarch found!") end -local old_id -for pos_id,v in pairs(my_entity.positions.assignments) do - if v.position_id==monarch_id then - old_id=v.histfig - v.histfig=newfig.id - local oldfig=df.historical_figure.find(old_id) - - for k,v in pairs(oldfig.entity_links) do - if df.histfig_entity_link_positionst:is_instance(v) and v.assignment_id==pos_id and v.entity_id==df.global.ui.civ_id then - oldfig.entity_links:erase(k) - break - end - end - newfig.entity_links:insert("#",{new=df.histfig_entity_link_positionst,entity_id=df.global.ui.civ_id, - link_strength=100,assignment_id=pos_id,start_year=df.global.cur_year}) - break - end -end diff --git a/scripts/markdown.lua b/scripts/markdown.lua deleted file mode 100644 index 14ecdaa2c..000000000 --- a/scripts/markdown.lua +++ /dev/null @@ -1,239 +0,0 @@ --- Save a text screen in markdown (eg for reddit) --- This is a derivatiwe work based upon scripts/forum-dwarves.lua by Caldfir and expwnent --- Adapted for markdown by Mchl https://github.com/Mchl ---[[=begin - -markdown -======== -Save a copy of a text screen in markdown (for reddit among others). -Use ``markdown help`` for more details. - -=end]] - -local args = {...} - -if args[1] == 'help' then - print([[ -description: - This script will attempt to read the current df-screen, and if it is a - text-viewscreen (such as the dwarf 'thoughts' screen or an item / creature - 'description') or an announcement list screen (such as announcements and - combat reports) then append a marked-down version of this text to the - target file (for easy pasting on reddit for example). - Previous entries in the file are not overwritten, so you - may use the 'markdown' command multiple times to create a single - document containing the text from multiple screens (eg: text screens - from several dwarves, or text screens from multiple artifacts/items, - or some combination). - -usage: - markdown [/n] [filename] - - /n - overwrites contents of output file - filename - if provided, the data will be saved in md_filename.md instead - of default md_export.md - -known screens: - The screens which have been tested and known to function properly with - this script are: - 1: dwarf/unit 'thoughts' screen - 2: item/art 'description' screen - 3: individual 'historical item/figure' screens - 4: manual - 4: announements screen - 5: combat reports screen - 6: latest news (when meeting with liaison) - There may be other screens to which the script applies. It should be - safe to attempt running the script with any screen active, with an - error message to inform you when the selected screen is not appropriate - for this script. - -target file: - The default target file's name is 'md_export.md'. A remider to this effect - will be displayed if the script is successful. - -character encoding: - The text will likely be using system-default encoding, and as such - will likely NOT display special characters (eg:é,õ,ç) correctly. To - fix this, you need to modify the character set that you are reading - the document with. 'Notepad++' is a freely available program which - can do this using the following steps: - 1: open the document in Notepad++ - 2: in the menu-bar, select - Encoding->Character Sets->Western European->OEM-US - 3: copy the text normally to wherever you want to use it -]]) - return -end - -local writemode = 'a' - --- check if we want to append to an existing file (default) or overwrite previous contents -if args[1] == '/n' then - writemode = 'w' - table.remove(args, 1) -end - -local filename - -if args[1] ~= nil then - filename = 'md_' .. table.remove(args, 1) .. '.md' -else - filename = 'md_export.md' -end - -local utils = require 'utils' -local gui = require 'gui' -local dialog = require 'gui.dialogs' - - - - -local scrn = dfhack.gui.getCurViewscreen() -local flerb = dfhack.gui.getFocusString(scrn) - -local months = { - [1] = 'Granite', - [2] = 'Slate', - [3] = 'Felsite', - [4] = 'Hematite', - [5] = 'Malachite', - [6] = 'Galena', - [7] = 'Limestone', - [8] = 'Sandstone', - [9] = 'Timber', - [10] = 'Moonstone', - [11] = 'Opal', - [12] = 'Obsidian', -} - -local function getFileHandle() - return io.open(filename, writemode) -end - -local function closeFileHandle(handle) - handle:write('\n***\n\n') - handle:close() - print ('Data exported to "' .. filename .. '"') -end - -local function reformat(strin) - local strout = strin - - -- [P] tags seem to indicate a new paragraph - local newline_idx = string.find(strout, '[P]', 1, true) - while newline_idx ~= nil do - strout = string.sub(strout, 1, newline_idx - 1) .. '\n***\n\n' .. string.sub(strout, newline_idx + 3) - newline_idx = string.find(strout, '[P]', 1, true) - end - - -- [R] tags seem to indicate a new 'section'. Let's mark it with a horizontal line. - newline_idx = string.find(strout, '[R]', 1, true) - while newline_idx ~= nil do - strout = string.sub(strout, 1, newline_idx - 1) .. '\n***\n\n' .. string.sub(strout,newline_idx + 3) - newline_idx = string.find(strout, '[R]', 1, true) - end - - -- No idea what [B] tags might indicate. Just removing them seems to work fine - newline_idx = string.find(strout, '[B]', 1, true) - while newline_idx ~= nil do - strout = string.sub(strout, 1, newline_idx - 1) .. string.sub(strout,newline_idx + 3) - newline_idx = string.find(strout, '[B]', 1, true) - end - - -- Reddit doesn't support custom colors in markdown. We need to remove all color information :( - local color_idx = string.find(strout, '[C:', 1, true) - while color_idx ~= nil do - strout = string.sub(strout, 1, color_idx - 1) .. string.sub(strout, color_idx + 9) - color_idx = string.find(strout, '[C:', 1, true) - end - - return strout -end - -local function formattime(year, ticks) - -- Dwarf Mode month is 33600 ticks long - local month = math.floor(ticks / 33600) - local dayRemainder = ticks - month * 33600 - - -- Dwarf Mode day is 1200 ticks long - local day = math.floor(dayRemainder / 1200) - local timeRemainder = dayRemainder - day * 1200 - - -- Assuming a 24h day each Dwarf Mode tick corresponds to 72 seconds - local seconds = timeRemainder * 72 - - local H = string.format("%02.f", math.floor(seconds / 3600)); - local m = string.format("%02.f", math.floor(seconds / 60 - (H * 60))); - local i = string.format("%02.f", math.floor(seconds - H * 3600 - m * 60)); - - day = day + 1 - if (day == 1 or day == 21) then - day = day .. 'st' - elseif (day == 2 or day == 22) then - day = day .. 'nd' - else - day = day .. 'th' - end - - return (day .. " " .. months[month + 1] .. " " .. year .. " " .. H .. ":" .. m..":" .. i) -end - -if flerb == 'textviewer' then - - local lines = scrn.src_text - - if lines ~= nil then - - local log = getFileHandle() - log:write('### ' .. scrn.title .. '\n') - - print('Exporting ' .. scrn.title .. '\n') - - for n,x in ipairs(lines) do - log:write(reformat(x.value).." ") --- debug output --- print(x.value) - end - closeFileHandle(log) - end - -elseif flerb == 'announcelist' then - - local lines = scrn.reports - - if lines ~= nil then - local log = getFileHandle() - local lastTime = "" - - for n,x in ipairs(lines) do - local currentTime = formattime(x.year, x.time) - if (currentTime ~= lastTime) then - lastTime = currentTime - log:write('\n***\n\n') - log:write('## ' .. currentTime .. '\n') - end --- debug output --- print(x.text) - log:write(x.text .. '\n') - end - closeFileHandle(log) - end - - -elseif flerb == 'topicmeeting' then - local lines = scrn.text - - if lines ~= nil then - local log = getFileHandle() - - for n,x in ipairs(lines) do --- debug output --- print(x.value) - log:write(x.value .. '\n') - end - closeFileHandle(log) - end -else - print 'This is not a textview, announcelist or topicmeeting screen. Can\'t export data, sorry.' -end diff --git a/scripts/masspit.rb b/scripts/masspit.rb deleted file mode 100644 index 9c276faf5..000000000 --- a/scripts/masspit.rb +++ /dev/null @@ -1,51 +0,0 @@ -# pit all caged creatures in a zone -=begin - -masspit -======= -Designate all creatures in cages on top of a pit/pond activity zone for pitting. -Works best with an animal stockpile on top of the zone. - -Works with a zone number as argument (eg ``Activity Zone #6`` -> ``masspit 6``) -or with the game cursor on top of the area. - -=end - -case $script_args[0] -when '?', 'help' - puts <= df.cursor.x and zone.y1 <= df.cursor.y and zone.y2 >= df.cursor.y - } - -end - -if not bld - puts "Please select a pit/pond zone" - throw :script_finished -end - -found = 0 -df.world.items.other[:CAGE].each { |cg| - next if not cg.flags.on_ground - next if cg.pos.z != bld.z or cg.pos.x < bld.x1 or cg.pos.x > bld.x2 or cg.pos.y < bld.y1 or cg.pos.y > bld.y2 - next if not uref = cg.general_refs.grep(DFHack::GeneralRefContainsUnitst).first - found += 1 - u = uref.unit_tg - puts "Pitting #{u.race_tg.name[0]} #{u.id} #{u.name}" - u.general_refs << DFHack::GeneralRefBuildingCivzoneAssignedst.cpp_new(:building_id => bld.id) - bld.assigned_units << u.id -} -puts "No creature available for pitting" if found == 0 diff --git a/scripts/migrants-now.lua b/scripts/migrants-now.lua deleted file mode 100644 index aa9ad7b3f..000000000 --- a/scripts/migrants-now.lua +++ /dev/null @@ -1,16 +0,0 @@ --- Force a migrant wave (only after hardcoded waves) ---[[=begin - -migrants-now -============ -Forces an immediate migrant wave. Only works after migrants have -arrived naturally. Equivalent to `modtools/force` ``-eventType migrants`` - -=end]] -df.global.timed_events:insert('#',{ - new = true, - type = df.timed_event_type.Migrants, - season = df.global.cur_season, - season_ticks = df.global.cur_season_tick+1, - entity = df.historical_entity.find(df.global.ui.civ_id) -}) diff --git a/scripts/modtools/about.txt b/scripts/modtools/about.txt deleted file mode 100644 index d2924b301..000000000 --- a/scripts/modtools/about.txt +++ /dev/null @@ -1,17 +0,0 @@ -``modtools/*`` scripts provide tools for modders, often with changes -to the raw files, and are not intended to be called manually by end-users. - -These scripts are mostly useful for raw modders and scripters. They all have -standard arguments: arguments are of the form ``tool -argName1 argVal1 --argName2 argVal2``. This is equivalent to ``tool -argName2 argVal2 -argName1 -argVal1``. It is not necessary to provide a value to an argument name: ``tool --argName3`` is fine. Supplying the same argument name multiple times will -result in an error. Argument names are preceded with a dash. The ``-help`` -argument will print a descriptive usage string describing the nature of the -arguments. For multiple word argument values, brackets must be used: ``tool --argName4 [ sadf1 sadf2 sadf3 ]``. In order to allow passing literal braces as -part of the argument, backslashes are used: ``tool -argName4 [ \] asdf \foo ]`` -sets ``argName4`` to ``\] asdf foo``. The ``*-trigger`` scripts have a similar -policy with backslashes. - -Any of these scripts can be called with ``-help`` for more information. diff --git a/scripts/modtools/add-syndrome.lua b/scripts/modtools/add-syndrome.lua deleted file mode 100644 index 2de2462b1..000000000 --- a/scripts/modtools/add-syndrome.lua +++ /dev/null @@ -1,114 +0,0 @@ --- Add or remove syndromes from units ---author expwnent ---[[=begin - -modtools/add-syndrome -===================== -This allows adding and removing syndromes from units. - -=end]] -local syndromeUtil = require 'syndrome-util' -local utils = require 'utils' - -validArgs = validArgs or utils.invert({ - 'help', - 'syndrome', - 'resetPolicy', - 'erase', - 'eraseOldest', - 'eraseAll', - 'target', - 'skipImmunities', - 'eraseClass' -}) - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print([[scripts/modtools/add-syndrome usage: -arguments: - -help - print this help message - -syndrome name - the name of the syndrome to operate on - examples: - "gila monster bite" - -resetPolicy policy - specify a policy of what to do if the unit already has an instance of the syndrome - examples: - NewInstance - default behavior: create a new instance of the syndrome - DoNothing - ResetDuration - AddDuration - -erase - instead of adding an instance of the syndrome, erase one - -eraseAll - erase every instance of the syndrome - -eraseClass SYN_CLASS - erase every instance of every syndrome with the given SYN_CLASS - -target id - the unit id of the target unit - examples: - 0 - 28 - -skipImmunities - add the syndrome to the target regardless of whether it is immune to the syndrome -]]) - return -end - -if not args.target then - error 'Specify a target.' -end -local targ = df.unit.find(tonumber(args.target)) -if not targ then - error ('Could not find target: ' .. args.target) -end -args.target = targ - -if args.eraseClass then - local count = syndromeUtil.eraseSyndromeClass(args.target, args.eraseClass) - --print('deleted ' .. tostring(count) .. ' syndromes') - return -end - -if args.resetPolicy then - args.resetPolicy = syndromeUtil.ResetPolicy[args.resetPolicy] - if not args.resetPolicy then - error ('Invalid reset policy.') - end -end - -if not args.syndrome then - error 'Specify a syndrome name.' -end - -local syndrome -for _,syn in ipairs(df.global.world.raws.syndromes.all) do - if syn.syn_name == args.syndrome then - syndrome = syn - break - end -end -if not syndrome then - error ('Invalid syndrome: ' .. args.syndrome) -end -args.syndrome = syndrome - -if args.erase then - syndromeUtil.eraseSyndrome(args.target,args.syndrome.id,args.eraseOldest) - return -end - -if args.eraseAll then - syndromeUtil.eraseSyndromes(args.target,args.syndrome.id) - return -end - -if skipImmunities then - syndromeUtil.infectWithSyndrome(args.target,args.syndrome,args.resetPolicy) -else - syndromeUtil.infectWithSyndromeIfValidTarget(args.target,args.syndrome,args.resetPolicy) -end - diff --git a/scripts/modtools/anonymous-script.lua b/scripts/modtools/anonymous-script.lua deleted file mode 100644 index 73eaefb92..000000000 --- a/scripts/modtools/anonymous-script.lua +++ /dev/null @@ -1,27 +0,0 @@ --- invoke simple lua scripts from strings ---author expwnent ---[[=begin - -modtools/anonymous-script -========================= -This allows running a short simple Lua script passed as an argument instead of -running a script from a file. This is useful when you want to do something too -complicated to make with the existing modtools, but too simple to be worth its -own script file. Example:: - - anonymous-script "print(args[1])" arg1 arg2 - # prints "arg1" - -=end]] -local args = {...} - ---automatically collect arguments to make the anonymous script more succinct ---load(ld,source,mode,env) -local f,err = load('local args = {...}; ' .. args[1], '=(anonymous lua script)') --,'=(lua command)', 't') -if err then - error(err) -end - ---we don't care about the result even if they return something for some reason: we just want to ensure its side-effects happen and print appropriate error messages -dfhack.safecall(f,table.unpack(args,2)) - diff --git a/scripts/modtools/create-item.lua b/scripts/modtools/create-item.lua deleted file mode 100644 index 0e4149222..000000000 --- a/scripts/modtools/create-item.lua +++ /dev/null @@ -1,140 +0,0 @@ --- creates an item of a given type and material ---author expwnent ---[[=begin - -modtools/create-item -==================== -Replaces the `createitem` plugin, with standard -arguments. The other versions will be phased out in a later version. - -=end]] -local utils = require 'utils' - -validArgs = --[[validArgs or--]] utils.invert({ - 'help', - 'creator', - 'material', - 'item', --- 'creature', --- 'caste', - 'leftHand', - 'rightHand', - 'quality' -}) - -organicTypes = organicTypes or utils.invert({ - df.item_type.REMAINS, - df.item_type.FISH, - df.item_type.FISH_RAW, - df.item_type.VERMIN, - df.item_type.PET, - df.item_type.EGG, -}) - -badTypes = badTypes or utils.invert({ - df.item_type.CORPSE, - df.item_type.CORPSEPIECE, - df.item_type.FOOD, -}) - -function createItem(creatorID, item, material, leftHand, rightHand, quality) - local itemQuality = quality and df.item_quality[quality] - print(itemQuality) - - local creator = df.unit.find(creatorID) - if not creator then - error 'Invalid creator.' - end - - if not item then - error 'Invalid item.' - end - local itemType = dfhack.items.findType(item) - if itemType == -1 then - error 'Invalid item.' - end - local itemSubtype = dfhack.items.findSubtype(item) - - if organicTypes[itemType] then - --TODO: look up creature and caste - error 'Not yet supported.' - end - - if badTypes[itemType] then - error 'Not supported.' - end - - if not material then - error 'Invalid material.' - end - local materialInfo = dfhack.matinfo.find(material) - if not materialInfo then - error 'Invalid material.' - end - - local item1 = dfhack.items.createItem(itemType, itemSubtype, materialInfo['type'], materialInfo.index, creator) - local item = df.item.find(item1) - if leftHand then - item:setGloveHandedness(2) - elseif rightHand then - item:setGloveHandedness(1) - end - - if itemQuality then - item:setQuality(itemQuality) - end - --[[if matchingGloves or matchingShoes then - if matchingGloves then - item1 = df.item.find(item1) - item1:setGloveHandedness(1); - end - local item2 = dfhack.items.createItem(itemType, itemSubtype, materialInfo['type'], materialInfo.index, creator) - if matchingGloves then - item2 = df.item.find(item2) - item2:setGloveHandedness(2); - end - end --]] - return item1 -end - -if moduleMode then - return -end - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print( -[[scripts/modtools/create-item.lua -arguments: - -help - print this help message - -creator id - specify the id of the unit who will create the item, or \\LAST to indicate the unit with id df.global.unit_next_id-1 - examples: - 0 - 2 - \\LAST - -material matstring - specify the material of the item to be created - examples: - INORGANIC:IRON - CREATURE_MAT:DWARF:BRAIN - PLANT_MAT:MUSHROOM_HELMET_PLUMP:DRINK - -item itemstr - specify the itemdef of the item to be created - examples: - WEAPON:ITEM_WEAPON_PICK - -matchingShoes - create two of this item - -matchingGloves - create two of this item, and set handedness appropriately - ]]) - return -end - -if args.creator == '\\LAST' then - args.creator = tostring(df.global.unit_next_id-1) -end - -createItem(tonumber(args.creator), args.item, args.material, args.leftHand, args.rightHand, args.quality) diff --git a/scripts/modtools/create-unit.lua b/scripts/modtools/create-unit.lua deleted file mode 100644 index 9505ec739..000000000 --- a/scripts/modtools/create-unit.lua +++ /dev/null @@ -1,575 +0,0 @@ --- Creates a unit. Beta; use at own risk. --- Originally created by warmist, edited by Putnam for the dragon ball mod to be used in reactions, modified by Dirst for use in The Earth Strikes Back mod, incorporating fixes discovered by Boltgun then Mifiki wrote the bit where it switches to arena mode briefly to do some of the messy work, then Expwnent combined that with the old script to make it function for histfigs --- version 0.51 --- This is a beta version. Use at your own risk. - --- Modifications from 0.5: civ -1 creates are NOT historical figures, mitigated screen-movement bug in createUnit() - ---[[ - TODO - children and babies: set child/baby job - confirm body size is computed appropriately for different ages / life stages - incarnate pre-existing historical figures - some sort of invasion helper script - set invasion_id, etc - announcement for fake natural birth if appropriate -]] ---[[=begin - -modtools/create-unit -==================== -Creates a unit. Use ``modtools/create-unit -help`` for more info. - -=end]] - ---[[ -if dfhack.gui.getCurViewscreen()._type ~= df.viewscreen_dwarfmodest or df.global.ui.main.mode ~= df.ui_sidebar_mode.LookAround then - print 'activate loo[k] mode' - return -end ---]] - -local utils=require 'utils' - -function createUnit(race_id, caste_id) - local view_x = df.global.window_x - local view_y = df.global.window_y - local view_z = df.global.window_z - - local curViewscreen = dfhack.gui.getCurViewscreen() - local dwarfmodeScreen = df.viewscreen_dwarfmodest:new() - curViewscreen.child = dwarfmodeScreen - dwarfmodeScreen.parent = curViewscreen - local oldMode = df.global.ui.main.mode - df.global.ui.main.mode = df.ui_sidebar_mode.LookAround - - local gui = require 'gui' - - df.global.world.arena_spawn.race:resize(0) - df.global.world.arena_spawn.race:insert(0,race_id) --df.global.ui.race_id) - - df.global.world.arena_spawn.caste:resize(0) - df.global.world.arena_spawn.caste:insert(0,caste_id) - - df.global.world.arena_spawn.creature_cnt:resize(0) - df.global.world.arena_spawn.creature_cnt:insert(0,0) - - --df.global.world.arena_spawn.equipment.skills:insert(0,99) - --df.global.world.arena_spawn.equipment.skill_levels:insert(0,0) - - local old_gametype = df.global.gametype - df.global.gametype = df.game_type.DWARF_ARENA - - gui.simulateInput(dfhack.gui.getCurViewscreen(), 'D_LOOK_ARENA_CREATURE') - gui.simulateInput(dfhack.gui.getCurViewscreen(), 'SELECT') - - df.global.gametype = old_gametype - - curViewscreen.child = nil - dwarfmodeScreen:delete() - df.global.ui.main.mode = oldMode - - local id = df.global.unit_next_id-1 - - df.global.window_x = view_x - df.global.window_y = view_y - df.global.window_z = view_z - - return id -end - ---local u = df.unit.find(df.global.unit_next_id-1) ---u.civ_id = df.global.ui.civ_id ---u.population_id = df.historical_entity.find(df.global.ui.civ_id).populations[0] ---local group = df.global.ui.group_id - --- Picking a caste or gender at random -function getRandomCasteId(race_id) - local cr = df.creature_raw.find(race_id) - local caste_id, casteMax - - casteMax = #cr.caste - 1 - - if casteMax > 0 then - return math.random(0, casteMax) - end - - return 0 -end - -local function allocateNewChunk(hist_entity) - hist_entity.save_file_id=df.global.unit_chunk_next_id - df.global.unit_chunk_next_id=df.global.unit_chunk_next_id+1 - hist_entity.next_member_idx=0 - print("allocating chunk:",hist_entity.save_file_id) -end - -local function allocateIds(nemesis_record,hist_entity) - if hist_entity.next_member_idx==100 then - allocateNewChunk(hist_entity) - end - nemesis_record.save_file_id=hist_entity.save_file_id - nemesis_record.member_idx=hist_entity.next_member_idx - hist_entity.next_member_idx=hist_entity.next_member_idx+1 -end - -function createFigure(trgunit,he,he_group) - local hf=df.historical_figure:new() - hf.id=df.global.hist_figure_next_id - hf.race=trgunit.race - hf.caste=trgunit.caste - hf.profession = trgunit.profession - hf.sex = trgunit.sex - df.global.hist_figure_next_id=df.global.hist_figure_next_id+1 - hf.appeared_year = df.global.cur_year - - hf.born_year = trgunit.relations.birth_year - hf.born_seconds = trgunit.relations.birth_time - hf.curse_year = trgunit.relations.curse_year - hf.curse_seconds = trgunit.relations.curse_time - hf.birth_year_bias = trgunit.relations.birth_year_bias - hf.birth_time_bias = trgunit.relations.birth_time_bias - hf.old_year = trgunit.relations.old_year - hf.old_seconds = trgunit.relations.old_time - hf.died_year = -1 - hf.died_seconds = -1 - hf.name:assign(trgunit.name) - hf.civ_id = trgunit.civ_id - hf.population_id = trgunit.population_id - hf.breed_id = -1 - hf.unit_id = trgunit.id - - df.global.world.history.figures:insert("#",hf) - - hf.info = df.historical_figure_info:new() - hf.info.unk_14 = df.historical_figure_info.T_unk_14:new() -- hf state? - --unk_14.region_id = -1; unk_14.beast_id = -1; unk_14.unk_14 = 0 - hf.info.unk_14.unk_18 = -1; hf.info.unk_14.unk_1c = -1 - -- set values that seem related to state and do event - --change_state(hf, dfg.ui.site_id, region_pos) - - - --lets skip skills for now - --local skills = df.historical_figure_info.T_skills:new() -- skills snap shot - -- ... - -- note that innate skills are automaticaly set by DF - hf.info.skills = {new=true} - - - he.histfig_ids:insert('#', hf.id) - he.hist_figures:insert('#', hf) - if he_group then - he_group.histfig_ids:insert('#', hf.id) - he_group.hist_figures:insert('#', hf) - hf.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=he_group.id,link_strength=100}) - end - trgunit.flags1.important_historical_figure = true - trgunit.flags2.important_historical_figure = true - trgunit.hist_figure_id = hf.id - trgunit.hist_figure_id2 = hf.id - - hf.entity_links:insert("#",{new=df.histfig_entity_link_memberst,entity_id=trgunit.civ_id,link_strength=100}) - - --add entity event - local hf_event_id=df.global.hist_event_next_id - df.global.hist_event_next_id=df.global.hist_event_next_id+1 - df.global.world.history.events:insert("#",{new=df.history_event_add_hf_entity_linkst,year=trgunit.relations.birth_year, - seconds=trgunit.relations.birth_time,id=hf_event_id,civ=hf.civ_id,histfig=hf.id,link_type=0}) - return hf -end - -function createNemesis(trgunit,civ_id,group_id) - local id=df.global.nemesis_next_id - local nem=df.nemesis_record:new() - - nem.id=id - nem.unit_id=trgunit.id - nem.unit=trgunit - nem.flags:resize(4) - --not sure about these flags... - -- [[ - nem.flags[4]=true - nem.flags[5]=true - nem.flags[6]=true - nem.flags[7]=true - nem.flags[8]=true - nem.flags[9]=true - --]] - --[[for k=4,8 do - nem.flags[k]=true - end]] - nem.unk10=-1 - nem.unk11=-1 - nem.unk12=-1 - df.global.world.nemesis.all:insert("#",nem) - df.global.nemesis_next_id=id+1 - trgunit.general_refs:insert("#",{new=df.general_ref_is_nemesisst,nemesis_id=id}) - trgunit.flags1.important_historical_figure=true - - nem.save_file_id=-1 - - local he=df.historical_entity.find(civ_id) - he.nemesis_ids:insert("#",id) - he.nemesis:insert("#",nem) - local he_group - if group_id and group_id~=-1 then - he_group=df.historical_entity.find(group_id) - end - if he_group then - he_group.nemesis_ids:insert("#",id) - he_group.nemesis:insert("#",nem) - end - allocateIds(nem,he) - nem.figure=createFigure(trgunit,he,he_group) -end - ---createNemesis(u, u.civ_id,group) -function createUnitInCiv(race_id, caste_id, civ_id, group_id) - local uid = createUnit(race_id, caste_id) - local unit = df.unit.find(uid) - if ( civ_id ) then - createNemesis(unit, civ_id, group_id) - end - return uid -end - -function createUnitInFortCiv(race_id, caste_id) - return createUnitInCiv(race_id, caste_id, df.global.ui.civ_id) -end - -function createUnitInFortCivAndGroup(race_id, caste_id) - return createUnitInCiv(race_id, caste_id, df.global.ui.civ_id, df.global.ui.group_id) -end - -function domesticate(uid, group_id) - local u = df.unit.find(uid) - group_id = group_id or df.global.ui.group_id - -- If a friendly animal, make it domesticated. From Boltgun & Dirst - local caste=df.creature_raw.find(u.race).caste[u.caste] - if not(caste.flags.CAN_SPEAK and caste.flags.CAN_LEARN) then - -- Fix friendly animals (from Boltgun) - u.flags2.resident = false; - u.flags3.body_temp_in_range = true; - u.population_id = -1 - u.status.current_soul.unit_id = u.id - - u.animal.population.region_x = -1 - u.animal.population.region_y = -1 - u.animal.population.unk_28 = -1 - u.animal.population.population_idx = -1 - u.animal.population.depth = -1 - - u.counters.soldier_mood_countdown = -1 - u.counters.death_cause = -1 - - u.enemy.anon_4 = -1 - u.enemy.anon_5 = -1 - u.enemy.anon_6 = -1 - - -- And make them tame (from Dirst) - u.flags1.tame = true - u.training_level = 7 - end -end - -function wild(uid) - local u = df.unit.find(uid) - local caste=df.creature_raw.find(u.race).caste[u.caste] - -- x = df.global.world.world_data.active_site[0].pos.x - -- y = df.global.world.world_data.active_site[0].pos.y - -- region = df.global.map.map_blocks[df.global.map.x_count_block*x+y] - if not(caste.flags.CAN_SPEAK and caste.flags.CAN_LEARN) then - u.animal.population.region_x = 1 - u.animal.population.region_y = 1 - u.animal.population.unk_28 = -1 - u.animal.population.population_idx = 1 - u.animal.population.depth = 0 - end -end - - -function nameUnit(id, entityRawName, civ_id) - --pick a random appropriate name - --choose three random words in the appropriate things - local unit = df.unit.find(id) - local entity_raw - if entityRawName then - for k,v in ipairs(df.global.world.raws.entities) do - if v.code == entityRawName then - entity_raw = v - break - end - end - else - local entity = df.historical_entity.find(civ_id) - entity_raw = entity.entity_raw - end - - if not entity_raw then - error('entity raw = nil: ', id, entityRawName, civ_id) - end - - local translation = entity_raw.translation - local translationIndex - for k,v in ipairs(df.global.world.raws.language.translations) do - if v.name == translation then - translationIndex = k - break - end - end - --translation = df.language_translation.find(translation) - local language_word_table = entity_raw.symbols.symbols1[0] --educated guess - function randomWord() - local index = math.random(0, #language_word_table.words[0] - 1) - return index - end - local firstName = randomWord() - local lastName1 = randomWord() - local lastName2 = randomWord() - local name = unit.status.current_soul.name - name.words[0] = language_word_table.words[0][lastName1] - name.parts_of_speech[0] = language_word_table.parts[0][lastName1] - name.words[1] = language_word_table.words[0][lastName2] - name.parts_of_speech[1] = language_word_table.parts[0][lastName2] - name.first_name = df.language_word.find(language_word_table.words[0][firstName]).forms[language_word_table.parts[0][firstName]] - name.has_name = true - name.language = translationIndex - name = unit.name - name.words[0] = language_word_table.words[0][lastName1] - name.parts_of_speech[0] = language_word_table.parts[0][lastName1] - name.words[1] = language_word_table.words[0][lastName2] - name.parts_of_speech[1] = language_word_table.parts[0][lastName2] - name.first_name = df.language_word.find(language_word_table.words[0][firstName]).forms[language_word_table.parts[0][firstName]] - name.has_name = true - name.language = translationIndex - if unit.hist_figure_id ~= -1 then - local histfig = df.historical_figure.find(unit.hist_figure_id) - name = histfig.name - name.words[0] = language_word_table.words[0][lastName1] - name.parts_of_speech[0] = language_word_table.parts[0][lastName1] - name.words[1] = language_word_table.words[0][lastName2] - name.parts_of_speech[1] = language_word_table.parts[0][lastName2] - name.first_name = df.language_word.find(language_word_table.words[0][firstName]).forms[language_word_table.parts[0][firstName]] - name.has_name = true - name.language = translationIndex - end -end - -validArgs = --[[validArgs or]]utils.invert({ - 'help', - 'race', - 'caste', - 'domesticate', - 'civId', - 'groupId', - 'flagSet', - 'flagClear', - 'name', - 'nick', - 'location', - 'age' -}) - -if moduleMode then - return -end - -local args = utils.processArgs({...}, validArgs) -if args.help then - print( -[[scripts/modtools/create-unit.lua -arguments: - -help - print this help message - -race raceName - specify the race of the unit to be created - examples: - DWARF - HUMAN - -caste casteName - specify the caste of the unit to be created - examples: - MALE - FEMALE - -domesticate - if the unit can't learn or can't speak, then make it a friendly animal - -civId id - make the created unit a member of the specified civ (or none if id = -1) - if id is \\LOCAL, then make it a member of the civ associated with the current fort - otherwise id must be an integer - -groupId id - make the created unit a member of the specified group (or none if id = -1) - if id is \\LOCAL, then make it a member of the group associated with the current fort - otherwise id must be an integer - -name entityRawName - set the unit's name to be a random name appropriate for the given entity - examples: - MOUNTAIN - -nick nickname - set the unit's nickname directly - -location [ x y z ] - create the unit at the specified coordinates - -age howOld - set the birth date of the unit to the specified number of years ago - -flagSet [ flag1 flag2 ... ] - set the specified unit flags in the new unit to true - flags may be selected from df.unit_flags1, df.unit_flags2, or df.unit_flags3 - -flagClear [ flag1 flag2 ... ] - set the specified unit flags in the new unit to false - flags may be selected from df.unit_flags1, df.unit_flags2, or df.unit_flags3 -]]) - return -end - -local race -local raceIndex -local casteIndex - -if not args.race or not args.caste then - error 'Specfiy a race and caste for the new unit.' -end - ---find race -for i,v in ipairs(df.global.world.raws.creatures.all) do - if v.creature_id == args.race then - raceIndex = i - race = v - break - end -end - -if not race then - error 'Invalid race.' -end - -for i,v in ipairs(race.caste) do - if v.caste_id == args.caste then - casteIndex = i - break - end -end - -if not casteIndex then - error 'Invalid caste.' -end - -local age -if args.age then - age = tonumber(args.age) - if not age and not age == 0 then - error('Invalid age: ' .. args.age) - end -end - -local civ_id -if args.civId == '\\LOCAL' then - civ_id = df.global.ui.civ_id -elseif args.civId and tonumber(args.civId) then - civ_id = tonumber(args.civId) -end - -local group_id -if args.groupId == '\\LOCAL' then - group_id = df.global.ui.group_id -elseif args.groupId and tonumber(args.groupId) then - group_id = tonumber(args.groupId) -end - -local unitId -if civ_id == -1 then - unitId = createUnit(raceIndex, casteIndex) - else - unitId = createUnitInCiv(raceIndex, casteIndex, civ_id, group_id) -end - -if args.domesticate then - domesticate(unitId, group_id) - else - wild(unitId) -end - -if age or age == 0 then - local u = df.unit.find(unitId) - local oldYearDelta = u.relations.old_year - u.relations.birth_year - u.relations.birth_year = df.global.cur_year - age - u.relations.old_year = u.relations.birth_year + oldYearDelta - --these flags are an educated guess of how to get the game to compute sizes correctly: use -flagSet and -flagClear arguments to override or supplement - u.flags2.calculated_nerves = false - u.flags2.calculated_bodyparts = false - u.flags3.body_part_relsize_computed = false - u.flags3.size_modifier_computed = false - u.flags3.compute_health = true - u.flags3.weight_computed = false - --TODO: if the unit is a child or baby it will still behave like an adult -end - -if args.flagSet or args.flagClear then - local u = df.unit.find(unitId) - local flagsToSet = {} - local flagsToClear = {} - for _,v in ipairs(args.flagSet or {}) do - flagsToSet[v] = true - end - for _,v in ipairs(args.flagClear or {}) do - flagsToClear[v] = true - end - for _,k in ipairs(df.unit_flags1) do - if flagsToSet[k] then - u.flags1[k] = true; - elseif flagsToClear[k] then - u.flags1[k] = false; - end - end - for _,k in ipairs(df.unit_flags2) do - if flagsToSet[k] then - u.flags2[k] = true; - elseif flagsToClear[k] then - u.flags2[k] = false; - end - end - for _,k in ipairs(df.unit_flags3) do - if flagsToSet[k] then - u.flags3[k] = true; - elseif flagsToClear[k] then - u.flags3[k] = false; - end - end -end - -if args.name then - nameUnit(unitId, args.name, civ_id) -else - local unit = df.unit.find(unitId) - unit.name.has_name = false - if unit.status.current_soul then - unit.status.current_soul.name.has_name = false - end - --[[if unit.hist_figure_id ~= -1 then - local histfig = df.historical_figure.find(unit.hist_figure_id) - histfig.name.has_name = false - end--]] -end - -if args.nick and type(args.nick) == 'string' then - dfhack.units.setNickname(df.unit.find(unitId), args.nick) -end - -if civ_id then - local u = df.unit.find(unitId) - u.civ_id = civ_id -end - -if args.location then - local u = df.unit.find(unitId) - local pos = df.coord:new() - pos.x = tonumber(args.location[1]) - pos.y = tonumber(args.location[2]) - pos.z = tonumber(args.location[3]) - local teleport = dfhack.script_environment('teleport') - teleport.teleport(u, pos) -end - ---[[if group_id then - local u = df.unit.find(unitId) - u.group_id = group_id -end--]] diff --git a/scripts/modtools/equip-item.lua b/scripts/modtools/equip-item.lua deleted file mode 100644 index bf17fc127..000000000 --- a/scripts/modtools/equip-item.lua +++ /dev/null @@ -1,105 +0,0 @@ --- equip an item on a unit with a particular body part ---[[=begin - -modtools/equip-item -=================== -Force a unit to equip an item with a particular body part; useful in -conjunction with the ``create`` scripts above. See also `forceequip`. - -=end]] -local utils = require 'utils' - -function equipItem(unit, item, bodyPart, mode) - --it is assumed that the item is on the ground - item.flags.on_ground = false - item.flags.in_inventory = true - local block = dfhack.maps.getTileBlock(item.pos) - local occupancy = block.occupancy[item.pos.x%16][item.pos.y%16] - for k,v in ipairs(block.items) do - --local blockItem = df.item.find(v) - if v == item.id then - block.items:erase(k) - break - end - end - local foundItem = false - for k,v in ipairs(block.items) do - local blockItem = df.item.find(v) - if blockItem.pos.x == item.pos.x and blockItem.pos.y == item.pos.y then - foundItem = true - break - end - end - if not foundItem then - occupancy.item = false - end - - local inventoryItem = df.unit_inventory_item:new() - inventoryItem.item = item - inventoryItem.mode = mode - inventoryItem.body_part_id = bodyPart - unit.inventory:insert(#unit.inventory,inventoryItem) - -end - -validArgs = --[[validArgs or--]] utils.invert({ - 'help', - 'unit', - 'item', - 'bodyPart', - 'mode' -}) - -if moduleMode then - return -end - - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print( -[[scripts/modtools/equip-item.lua -arguments: - -help - print this help message - ]]) - return -end - -local unitId = tonumber(args.unit) or ((args.unit == '\\LAST') and (df.global.unit_next_id-1)) -local unit = df.unit.find(unitId) -if not unit then - error('invalid unit!', args.unit) -end - -local itemId = tonumber(args.item) or ((args.item == '\\LAST') and (df.global.item_next_id-1)) -local item = df.item.find(itemId) -if not item then - error('invalid item!', args.item) -end - -local bodyPartName = args.bodyPart -local creature_raw = df.global.world.raws.creatures.all[unit.race] -local caste_raw = creature_raw.caste[unit.caste] -local body_info = caste_raw.body_info - -local partId -local part -for k,v in ipairs(body_info.body_parts) do - if v.token == bodyPartName then - partId = k - part = v - break - end -end - -if not part then - error('invalid body part name: ', bodyPartName) -end - -local mode = args.mode -mode = df.unit_inventory_item.T_mode[mode] - -equipItem(unit, item, partId, mode) - diff --git a/scripts/modtools/extra-gamelog.lua b/scripts/modtools/extra-gamelog.lua deleted file mode 100644 index 0046ceb26..000000000 --- a/scripts/modtools/extra-gamelog.lua +++ /dev/null @@ -1,192 +0,0 @@ --- Regularly writes extra info to gamelog.txt -local help = [[=begin - -modtools/extra-gamelog -====================== -This script writes extra information to the gamelog. -This is useful for tools like :forums:`Soundsense <106497>`. - -=end]] - -msg = dfhack.gui.writeToGamelog - -function log_on_load(op) - if op ~= SC_WORLD_LOADED then return end - - -- Seasons fix for Soundsense - local seasons = { - [-1] = 'Nothing', -- worldgen - [0] = 'Spring', - [1] = 'Summer', - [2] = 'Autumn', - [3] = 'Winter'} - msg(seasons[df.global.cur_season]..' has arrived on the calendar.') - - -- Weather fix for Soundsense - local raining = false - local snowing = false - for _, row in ipairs(df.global.current_weather) do - for _, weather in ipairs(row) do - raining = raining or weather == 1 - snowing = snowing or weather == 2 - end - end - if (not snowing and not raining) then msg("The weather has cleared.") - elseif raining then msg("It has started raining.") - elseif snowing then msg("A snow storm has come.") - end - - -- Log site information for forts - if df.world_site.find(df.global.ui.site_id) == nil then return end - local site = df.world_site.find(df.global.ui.site_id) - local fort_ent = df.global.ui.main.fortress_entity - local civ_ent = df.historical_entity.find(df.global.ui.civ_id) - local function fullname(item) - return dfhack.TranslateName(item.name)..' ('..dfhack.TranslateName(item.name ,true)..')' - end - msg('Loaded '..df.global.world.cur_savegame.save_dir..', '..fullname(df.global.world.world_data).. - ' at coordinates ('..site.pos.x..','..site.pos.y..')') - msg('Loaded the fortress '..fullname(site).. - (fort_ent and ', colonized by the group '..fullname(fort_ent) or '').. - (civ_ent and ' of the civilization '..fullname(civ_ent)..'.' or '.')) -end - - -old_expedition_leader = nil -old_mayor = nil -function log_nobles() - local expedition_leader = nil - local mayor = nil - local function check(unit) - if not dfhack.units.isCitizen(unit) then return end - for _, pos in ipairs(dfhack.units.getNoblePositions(unit) or {}) do - if pos.position.name[0] == "expedition leader" then - expedition_leader = unit - elseif pos.position.name[0] == "mayor" then - mayor = unit - end - end - end - for _, unit in ipairs(df.global.world.units.active) do - check(unit) - end - - if old_mayor == nil and expedition_leader == nil and mayor ~= nil and old_expedition_leader ~= nil then - msg("Expedition leader was replaced by mayor.") - end - - if expedition_leader ~= old_expedition_leader then - if expedition_leader == nil then - msg("Expedition leader position is now vacant.") - else - msg(dfhack.TranslateName(dfhack.units.getVisibleName(expedition_leader)).." became expedition leader.") - end - end - - if mayor ~= old_mayor then - if mayor == nil then - msg("Mayor position is now vacant.") - else - msg(dfhack.TranslateName(dfhack.units.getVisibleName(mayor)).." became mayor.") - end - end - old_mayor = mayor - old_expedition_leader = expedition_leader -end - -siege = false -function log_siege() - local function cur_siege() - for _, unit in ipairs(df.global.world.units.active) do - if unit.flags1.active_invader then return true end - end - return false - end - local old_siege = siege - siege = cur_siege() - if siege ~= old_siege and siege then - msg("A vile force of darkness has arrived!") - elseif siege ~= old_siege and not siege then - msg("Siege was broken.") - end -end - - -local workshopTypes = { - [0]="Carpenters Workshop", - "Farmers Workshop", - "Masons Workshop", - "Craftsdwarfs Workshop", - "Jewelers Workshop", - "Metalsmiths Forge", - "Magma Forge", - "Bowyers Workshop", - "Mechanics Workshop", - "Siege Workshop", - "Butchers Workshop", - "Leatherworks Workshop", - "Tanners Workshop", - "Clothiers Workshop", - "Fishery", - "Still", - "Loom", - "Quern", - "Kennels", - "Kitchen", - "Ashery", - "Dyers Workshop", - "Millstone", - "Custom", - "Tool", - } - -local furnaceTypes = { - [0]="Wood Furnace", - "Smelter", - "Glass Furnace", - "Kiln", - "Magma Smelter", - "Magma Glass Furnace", - "Magma Kiln", - "Custom Furnace", - } - -buildStates = {} - -function log_buildings() - for _, building in ipairs(df.global.world.buildings.all) do - if getmetatable(building) == "building_workshopst" or getmetatable(building) == "building_furnacest" then - buildStates[building.id] = buildStates[building.id] or building.flags.exists - if buildStates[building.id] ~= building.flags.exists then - buildStates[building.id] = building.flags.exists - if building.flags.exists then - if getmetatable(building) == "building_workshopst" then - msg(workshopTypes[building.type].." was built.") - elseif getmetatable(building) == "building_furnacest" then - msg(furnaceTypes[building.type].." was built.") - end - end - end - end - end -end - -local function event_loop() - log_nobles() - log_siege() - log_buildings() - if extra_gamelog_enabled then dfhack.timeout(50, 'ticks', event_loop) end -end - -extra_gamelog_enabled = false -local args = {...} -if args[1] == 'disable' then - dfhack.onStateChange[_ENV] = nil - extra_gamelog_enabled = false -elseif args[1] == 'enable' then - dfhack.onStateChange[_ENV] = log_on_load - extra_gamelog_enabled = true - event_loop() -else - print(help) -end diff --git a/scripts/modtools/force.lua b/scripts/modtools/force.lua deleted file mode 100644 index 3b25b0895..000000000 --- a/scripts/modtools/force.lua +++ /dev/null @@ -1,90 +0,0 @@ --- Forces an event (caravan, migrants, etc) --- author Putnam --- edited by expwnent ---[[=begin - -modtools/force -============== -This tool triggers events like megabeasts, caravans, and migrants. - -=end]] -local utils = require 'utils' - -local function findCiv(arg) - local entities = df.global.world.entities.all - if tonumber(arg) then return arg end - if arg then - for eid,entity in ipairs(entities) do - if entity.entity_raw.code == arg then return entity end - end - end - return nil -end - -validArgs = validArgs or utils.invert({ - 'eventType', - 'help', - 'civ' -}) - -local args = utils.processArgs({...}, validArgs) -if next(args) == nil or args.help then - print([[force usage -arguments: - -help - print this help message - -eventType event - specify the type of the event to trigger - examples: - MegaBeast - Migrants - Caravan - Diplomat - WildlifeCurious - WildlifeMischievous - WildlifeFlier - NightCreature - -civ entity - specify the civ of the event, if applicable - examples: - player - MOUNTAIN - EVIL - 28 -]]) - print('force: -eventType [Megabeast, Migrants, Caravan, Diplomat, WildlifeCurious, WildlifeMischievous, WildlifeFlier, NightCreature] -civ [player,ENTITY_ID]') - return -end - -if not args.eventType then - error 'Specify an eventType.' -elseif not df.timed_event_type[args.eventType] then - error('Invalid eventType: ' .. args.eventType) -end - -if args.civ == 'player' then - args.civ = df.historical_entity.find(df.global.ui.civ_id) -elseif args.civ then - local civ = args.civ - args.civ = findCiv(args.civ) - if not args.civ then - error('Invalid civ: ' .. civ) - end -elseif args.eventType == 'Caravan' or args.eventType == 'Diplomat' then - error('Specify civ for this eventType') -end - -if args.eventType == 'Migrants' then - args.civ = df.historical_entity.find(df.global.ui.civ_id) -end - -local timedEvent = df.timed_event:new() -timedEvent['type'] = df.timed_event_type[args.eventType] -timedEvent.season = df.global.cur_season -timedEvent.season_ticks = df.global.cur_season_tick -if args.civ then - timedEvent.entity = args.civ -end - -df.global.timed_events:insert('#', timedEvent) - diff --git a/scripts/modtools/interaction-trigger.lua b/scripts/modtools/interaction-trigger.lua deleted file mode 100644 index e611a27fe..000000000 --- a/scripts/modtools/interaction-trigger.lua +++ /dev/null @@ -1,176 +0,0 @@ --- triggers scripts based on unit interactions ---author expwnent ---[[=begin - -modtools/interaction-trigger -============================ -This triggers events when a unit uses an interaction on another. It works by -scanning the announcements for the correct attack verb, so the attack verb -must be specified in the interaction. It includes an option to suppress this -announcement after it finds it. - -=end]] -local eventful = require 'plugins.eventful' -local utils = require 'utils' - -attackTriggers = attackTriggers or {} -defendTriggers = defendTriggers or {} -commands = commands or {} -commandCount = commandCount or 0 - -eventful.enableEvent(eventful.eventType.INTERACTION,1) --cheap, so every tick is fine -eventful.enableEvent(eventful.eventType.UNLOAD,1) - -eventful.onUnload.interactionTrigger = function() - attackTriggers = {} - defendTriggers = {} - commands = {} - commandCount = 0 -end - -local function processTrigger(args) - local command = {} - for _,arg in ipairs(args.command) do - if arg == '\\ATTACK_VERB' then - table.insert(command,args.attackVerb) - elseif arg == '\\DEFEND_VERB' then - table.insert(command,args.defendVerb) - elseif arg == '\\ATTACKER_ID' then - table.insert(command,args.attackerId) - elseif arg == '\\DEFENDER_ID' then - table.insert(command,args.defenderId) - elseif arg == '\\ATTACK_REPORT' then - table.insert(command,args.attackReport) - elseif arg == '\\DEFEND_REPORT' then - table.insert(command,args.defendReport) - elseif string.sub(arg,1,1) == '\\' then - table.insert(command,string.sub(arg,2)) - else - table.insert(command,arg) - end - end - dfhack.run_command(table.unpack(command)) -end - -eventful.onInteraction.interactionTrigger = function(attackVerb, defendVerb, attacker, defender, attackReport, defendReport) - local extras = {} - extras.attackVerb = attackVerb - extras.defendVerb = defendVerb - extras.attackReport = attackReport - extras.defendReport = defendReport - extras.attackerId = attacker - extras.defenderId = defender - local suppressAttack = false - local suppressDefend = false - local todo = {} - for _,trigger in ipairs(attackTriggers[attackVerb] or {}) do - todo[trigger] = true - end - for _,trigger in ipairs(defendTriggers[defendVerb] or {}) do - todo[trigger] = true - end - for k,v in pairs(todo) do - command = commands[k] - suppressAttack = suppressAttack or command.suppressAttack - suppressDefend = suppressDefend or command.suppressDefend - utils.fillTable(command,extras) - processTrigger(command) - utils.unfillTable(command,extras) - end - - local eraseReport = function(unit,report) - for i,v in ipairs(unit.reports.log.Combat) do - if v == report then - unit.reports.log.Combat:erase(i) - break - end - end - end - if suppressAttack or suppressDefend then - attacker = df.unit.find(tonumber(attacker)) - defender = df.unit.find(tonumber(defender)) - end - if suppressAttack then - eraseReport(attacker,attackReport) - eraseReport(defender,attackReport) - end - if suppressDefend then - eraseReport(attacker,defendReport) - eraseReport(defender,defendReport) - end - --TODO: get rid of combat report on LHS of screen -end - ----------------------------------------------------- ---argument processing - -validArgs = validArgs or utils.invert({ - 'clear', - 'help', - 'onAttackStr', - 'onDefendStr', - 'command', - 'suppressAttack', - 'suppressDefend', -}) - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print([[scripts/modtools/interaction-trigger.lua -arguments: - -help - print this help message - -clear - unregisters all triggers - -onAttackStr str - trigger the command when the attack verb is "str". both onAttackStr and onDefendStr MUST be specified - -onDefendStr str - trigger the command when the defend verb is "str". both onAttackStr and onDefendStr MUST be specified - -suppressAttack - delete the attack announcement from the combat logs - -suppressDefend - delete the defend announcement from the combat logs - -command [ commandStrs ] - specify the command to be executed - commandStrs - \\ATTACK_VERB - \\DEFEND_VERB - \\ATTACKER_ID - \\DEFENDER_ID - \\ATTACK_REPORT - \\DEFEND_REPORT - \\anything -> \anything - anything -> anything -You must specify both an attack string and a defend string to guarantee correct performance. Either will trigger the script when it happens, but it will not be triggered twice in a row if both happen. -]]) - return -end - -if args.clear then - triggers = {} - commands = {} - commandCount = 0 -end - -if not args.command then - return -end - -commands[commandCount] = args - -if args.onAttackStr then - if not attackTriggers[args.onAttackStr] then - attackTriggers[args.onAttackStr] = {} - end - table.insert(attackTriggers[args.onAttackStr],commandCount) -end - -if args.onDefendStr then - if not defendTriggers[args.onDefendStr] then - defendTriggers[args.onDefendStr] = {} - end - table.insert(defendTriggers[args.onDefendStr],commandCount) -end - -commandCount = commandCount+1 diff --git a/scripts/modtools/invader-item-destroyer.lua b/scripts/modtools/invader-item-destroyer.lua deleted file mode 100644 index 9b06c944e..000000000 --- a/scripts/modtools/invader-item-destroyer.lua +++ /dev/null @@ -1,184 +0,0 @@ --- delete invader items when they die ---author expwnent ---[[=begin - -modtools/invader-item-destroyer -=============================== -This tool configurably destroys invader items to prevent clutter or to prevent -the player from getting tools exclusive to certain races. - -=end]] -local eventful = require 'plugins.eventful' -local utils = require 'utils' - ---invaders = invaders or {} -entities = entities or {} -items = items or {} - -allEntities = allEntities or false -allItems = allitems or true - -eventful.enableEvent(eventful.eventType.UNLOAD,1) -eventful.onUnload.invaderItemDestroyer = function() - entities = {} - items = {} - allEntities = false - allItems = true -end - -eventful.enableEvent(eventful.eventType.UNIT_DEATH, 1) --requires iterating through all units -eventful.onUnitDeath.invaderItemDestroyer = function(unitId) - local unit = df.unit.find(unitId) - if not unit then - return - end - - local entity = df.historical_entity.find(unit.civ_id) - if not allEntities and not entity then - return - end - - if not allEntities and not entities[entity.entity_raw.code] then - return - end - - if dfhack.units.isCitizen(unit) then - return - end - - local function forEach(item) - if not allItems and not items[dfhack.items.getSubtypeDef(item:getType(),item:getSubtype()).id] then - return - end - if not (item.flags.foreign and item.flags.forbid) then - return - end - if item.pos.x ~= unit.pos.x then - return - end - if item.pos.y ~= unit.pos.y then - return - end - if item.pos.z ~= unit.pos.z then - return - end - item.flags.garbage_collect = true - item.flags.forbid = true - item.flags.hidden = true - end - - for _,item in ipairs(unit.inventory) do - local item2 = df.item.find(item.item) - forEach(item2) - end - --for each item on the ground - local block = dfhack.maps.getTileBlock(unit.pos.x, unit.pos.y, unit.pos.z) - for _,item in ipairs(block.items) do - local item2 = df.item.find(item) - forEach(item2) - end -end ---[[eventful.onUnitDeath.invaderItemDestroyer = function(unit) - if invaders[unit] then - print ('Invader ' .. unit .. ' dies.') - end - for _,item in ipairs(invaders[unit] or {}) do - local item2 = df.item.find(item) - if item2 then - print ('deleting item ' .. item) - item2.flags.garbage_collect = true - item2.flags.forbid = true - item2.flags.hidden = true - item2.flags.encased = true - end - end - invaders[unit] = nil - --TODO: delete corpses? -end]] - -validArgs = validArgs or utils.invert({ - 'clear', - 'help', - 'allRaces', - 'allEntities', - 'allItems', - 'item', - 'entity', - 'race', -}) - -local args = utils.processArgs({...}, validArgs) - -if args.clear then - entities = {} - items = {} - allEntities = false - allItems = true -end - -if args.help then - print([[scripts/modtools/invader-item-destroyer.lua usage -arguments: - -help - print this help message - -clear - reset all registered data - -allEntities [true/false] - set whether it should delete items from invaders from any civ - -allItems [true/false] - set whether it should delete all invader items regardless of type when an appropriate invader dies - -item itemdef - set a particular itemdef to be destroyed when an invader from an appropriate civ dies - examples: - ITEM_WEAPON_PICK - -entity entityName - set a particular entity up so that its invaders destroy their items shortly after death - examples: - MOUNTAIN - EVIL -]]) - return -end - -if args.allEntities then - if args.allEntities == 'true' then - allEntities = true - else - allEntities = false - end -end -if args.allItems then - if args.allItems == 'true' then - allItems = true - else - allItems = false - end -end - -if args.item then - local itemType - for _,itemdef in ipairs(df.global.world.raws.itemdefs.all) do - if itemdef.id == args.item then - itemType = itemdef.id - break - end - end - if not itemType then - error ('Invalid item type: ' .. args.item) - end - items[itemType] = true -end -if args.entity then - local success - for _,entity in ipairs(df.global.world.entities.all) do - if entity.entity_raw.code == args.entity then - success = true - break - end - end - if not success then - error 'Invalid entity' - end - entities[args.entity] = true -end - diff --git a/scripts/modtools/item-trigger.lua b/scripts/modtools/item-trigger.lua deleted file mode 100644 index ee44fd447..000000000 --- a/scripts/modtools/item-trigger.lua +++ /dev/null @@ -1,294 +0,0 @@ --- trigger commands based on attacks with certain items ---author expwnent ---based on itemsyndrome by Putnam ---triggers scripts when a unit attacks another with a weapon type, a weapon of a particular material, or a weapon contaminated with a particular material, or when a unit equips/unequips a particular item type, an item of a particular material, or an item contaminated with a particular material ---[[=begin - -modtools/item-trigger -===================== -This powerful tool triggers DFHack commands when a unit equips, unequips, or -attacks another unit with specified item types, specified item materials, or -specified item contaminants. - -=end]] -local eventful = require 'plugins.eventful' -local utils = require 'utils' - -itemTriggers = itemTriggers or {} -materialTriggers = materialTriggers or {} -contaminantTriggers = contaminantTriggers or {} - -eventful.enableEvent(eventful.eventType.UNIT_ATTACK,1) -- this event type is cheap, so checking every tick is fine -eventful.enableEvent(eventful.eventType.INVENTORY_CHANGE,5) --this is expensive, but you might still want to set it lower -eventful.enableEvent(eventful.eventType.UNLOAD,1) - -eventful.onUnload.itemTrigger = function() - itemTriggers = {} - materialTriggers = {} - contaminantTriggers = {} -end - -function processTrigger(command) - local command2 = {} - for i,arg in ipairs(command.command) do - if arg == '\\ATTACKER_ID' then - command2[i] = '' .. command.attacker.id - elseif arg == '\\DEFENDER_ID' then - command2[i] = '' .. command.defender.id - elseif arg == '\\ITEM_MATERIAL' then - command2[i] = command.itemMat:getToken() - elseif arg == '\\ITEM_MATERIAL_TYPE' then - command2[i] = command.itemMat['type'] - elseif arg == '\\ITEM_MATERIAL_INDEX' then - command2[i] = command.itemMat.index - elseif arg == '\\ITEM_ID' then - command2[i] = '' .. command.item.id - elseif arg == '\\ITEM_TYPE' then - command2[i] = command.itemType - elseif arg == '\\CONTAMINANT_MATERIAL' then - command2[i] = command.contaminantMat:getToken() - elseif arg == '\\CONTAMINANT_MATERIAL_TYPE' then - command2[i] = command.contaminantMat['type'] - elseif arg == '\\CONTAMINANT_MATERIAL_INDEX' then - command2[i] = command.contaminantMat.index - elseif arg == '\\MODE' then - command2[i] = command.mode - elseif arg == '\\UNIT_ID' then - command2[i] = command.unit.id - elseif string.sub(arg,1,1) == '\\' then - command2[i] = string.sub(arg,2) - else - command2[i] = arg - end - end - dfhack.run_command(table.unpack(command2)) -end - -function getitemType(item) - if item:getSubtype() ~= -1 then - itemType = dfhack.items.getSubtypeDef(item:getType(),item:getSubtype()).id - else - itemType = df.item_type[item:getType()] - end - return itemType -end - -function handler(table) - local itemMat = dfhack.matinfo.decode(table.item) - local itemMatStr = itemMat:getToken() - local itemType = getitemType(table.item) - table.itemMat = itemMat - table.itemType = itemType - - for _,command in ipairs(itemTriggers[itemType] or {}) do - if command[table.mode] then - utils.fillTable(command,table) - processTrigger(command) - utils.unfillTable(command,table) - end - end - - for _,command in ipairs(materialTriggers[itemMatStr] or {}) do - if command[table.mode] then - utils.fillTable(command,table) - processTrigger(command) - utils.unfillTable(command,table) - end - end - - for _,contaminant in ipairs(table.item.contaminants or {}) do - local contaminantMat = dfhack.matinfo.decode(contaminant.mat_type, contaminant.mat_index) - local contaminantStr = contaminantMat:getToken() - table.contaminantMat = contaminantMat - for _,command in ipairs(contaminantTriggers[contaminantStr] or {}) do - utils.fillTable(command,table) - processTrigger(command) - utils.unfillTable(command,table) - end - table.contaminantMat = nil - end -end - -function equipHandler(unit, item, isEquip) - local mode = (isEquip and 'onEquip') or (not isEquip and 'onUnequip') - - local table = {} - table.mode = mode - table.item = df.item.find(item) - table.unit = df.unit.find(unit) - handler(table) -end - -eventful.onInventoryChange.equipmentTrigger = function(unit, item, item_old, item_new) - if item_old and item_new then - return - end - - local isEquip = item_new and not item_old - equipHandler(unit,item,isEquip) -end - -eventful.onUnitAttack.attackTrigger = function(attacker,defender,wound) - attacker = df.unit.find(attacker) - defender = df.unit.find(defender) - - if not attacker then - return - end - - local attackerWeapon - for _,item in ipairs(attacker.inventory) do - if item.mode == df.unit_inventory_item.T_mode.Weapon then - attackerWeapon = item.item - break - end - end - - if not attackerWeapon then - return - end - - local table = {} - table.attacker = attacker - table.defender = defender - table.item = attackerWeapon - table.mode = 'onStrike' - handler(table) -end - -validArgs = validArgs or utils.invert({ - 'clear', - 'help', - 'checkAttackEvery', - 'checkInventoryEvery', - 'command', - 'itemType', - 'onStrike', - 'onEquip', - 'onUnequip', - 'material', - 'contaminant', -}) -local args = utils.processArgs({...}, validArgs) - -if args.help then - print([[scripts/modtools/item-trigger.lua usage -arguments: - -help - print this help message - -clear - clear all registered triggers - -checkAttackEvery n - check the attack event at least every n ticks - -checkInventoryEvery n - check inventory event at least every n ticks - -itemType type - trigger the command for items of this type - examples: - ITEM_WEAPON_PICK - RING - -onStrike - trigger the command when someone strikes someone with an appropriate weapon - -onEquip - trigger the command when someone equips an appropriate item - -onUnequip - trigger the command when someone unequips an appropriate item - -material mat - trigger the commmand on items with the given material - examples - INORGANIC:IRON - CREATURE_MAT:DWARF:BRAIN - PLANT_MAT:MUSHROOM_HELMET_PLUMP:DRINK - -contaminant mat - trigger the command on items with a given material contaminant - examples - INORGANIC:IRON - CREATURE_MAT:DWARF:BRAIN - PLANT_MAT:MUSHROOM_HELMET_PLUMP:DRINK - -command [ commandStrs ] - specify the command to be executed - commandStrs - \\ATTACKER_ID - \\DEFENDER_ID - \\ITEM_MATERIAL - \\ITEM_MATERIAL_TYPE - \\ITEM_ID - \\ITEM_TYPE - \\CONTAMINANT_MATERIAL - \\CONTAMINANT_MATERIAL_TYPE - \\CONTAMINANT_MATERIAL_INDEX - \\MODE - \\UNIT_ID - \\anything -> \anything - anything -> anything -]]) - return -end - -if args.clear then - itemTriggers = {} - materialTriggers = {} - contaminantTriggers = {} -end - -if args.checkAttackEvery then - if not tonumber(args.checkAttackEvery) then - error('checkAttackEvery must be a number') - end - eventful.enableEvent(eventful.eventType.UNIT_ATTACK,tonumber(args.checkAttackEvery)) -end - -if args.checkInventoryEvery then - if not tonumber(args.checkInventoryEvery) then - error('checkInventoryEvery must be a number') - end - eventful.enableEvent(eventful.eventType.INVENTORY_CHANGE,tonumber(args.checkInventoryEvery)) -end - -if not args.command then - if not args.clear then - error 'specify a command' - end - return -end - -if args.itemType then - if dfhack.items.findType(args.itemType) == -1 then - local temp - for _,itemdef in ipairs(df.global.world.raws.itemdefs.all) do - if itemdef.id == args.itemType then - temp = args.itemType --itemdef.subtype - break - end - end - if not temp then - error 'Could not find item type.' - end - args.itemType = temp - end -end - -local numConditions = (args.material and 1 or 0) + (args.itemType and 1 or 0) + (args.contaminant and 1 or 0) -if numConditions > 1 then - error 'too many conditions defined: not (yet) supported (pester expwnent if you want it)' -elseif numConditions == 0 then - error 'specify a material, weaponType, or contaminant' -end - -if args.material then - if not materialTriggers[args.material] then - materialTriggers[args.material] = {} - end - table.insert(materialTriggers[args.material],args) -elseif args.itemType then - if not itemTriggers[args.itemType] then - itemTriggers[args.itemType] = {} - end - table.insert(itemTriggers[args.itemType],args) -elseif args.contaminant then - if not contaminantTriggers[args.contaminant] then - contaminantTriggers[args.contaminant] = {} - end - table.insert(contaminantTriggers[args.contaminant],args) -end - diff --git a/scripts/modtools/moddable-gods.lua b/scripts/modtools/moddable-gods.lua deleted file mode 100644 index 9445af7a9..000000000 --- a/scripts/modtools/moddable-gods.lua +++ /dev/null @@ -1,102 +0,0 @@ --- Create gods from the command-line ---based on moddableGods by Putnam ---edited by expwnent ---[[=begin - -modtools/moddable-gods -====================== -This is a standardized version of Putnam's moddableGods script. It allows you -to create gods on the command-line. - -=end]] -local utils = require 'utils' - -validArgs = validArgs or utils.invert({ - 'help', - 'name', - 'spheres', - 'gender', - 'depictedAs', - 'domain', - 'description', --- 'entities', -}) -local args = utils.processArgs({...}) - -if args.help then - print([[scripts/modtools/moddable-gods.lua -arguments: - -help - print this help message - -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 - -depictedAs str - often depicted as a str - -domain str - set the domain of the god - -description str - set the description of the god -]]) - return -end - -if not args.name or not args.depictedAs or not args.domain or not args.description or not args.spheres or not args.gender then - error('All arguments must be specified.') -end - -local templateGod -for _,fig in ipairs(df.global.world.history.figures) do - if fig.flags.deity then - templateGod = fig - break - end -end -if not templateGod then - error 'Could not find template god.' -end - -if args.gender == 'male' then - args.gender = 1 -elseif args.gender == 'female' then - args.gender = 0 -else - error 'invalid gender' -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 -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.secret = df.historical_figure_info.T_secret:new() - -godFig.sex = args.gender -godFig.name.first_name = args.name -for _,sphere in ipairs(args.spheres) do - godFig.info.spheres:insert('#',df.sphere_type[sphere]) -end -df.global.world.history.figures:insert('#',godFig) - - diff --git a/scripts/modtools/outside-only.lua b/scripts/modtools/outside-only.lua deleted file mode 100644 index f4ed1414f..000000000 --- a/scripts/modtools/outside-only.lua +++ /dev/null @@ -1,141 +0,0 @@ --- enables outside only and inside only buildings ---author expwnent ---[[=begin - -modtools/outside-only -===================== -This allows you to specify certain custom buildings as outside only, or inside -only. If the player attempts to build a building in an inappropriate location, -the building will be destroyed. - -=end]] -local eventful = require 'plugins.eventful' -local utils = require 'utils' - -buildingType = buildingType or utils.invert({'EITHER','OUTSIDE_ONLY','INSIDE_ONLY'}) -registeredBuildings = registeredBuildings or {} -checkEvery = checkEvery or 100 -timeoutId = timeoutId or nil - -eventful.enableEvent(eventful.eventType.UNLOAD,1) -eventful.onUnload.outsideOnly = function() - registeredBuildings = {} - checkEvery = 100 - timeoutId = nil -end - -local function destroy(building) - if #building.jobs > 0 and building.jobs[0] and building.jobs[0].job_type == df.job_type.DestroyBuilding then - return - end - local b = dfhack.buildings.deconstruct(building) - if b then - --TODO: print an error message to the user so they know - return - end --- building.flags.almost_deleted = 1 -end - -local function checkBuildings() - local toDestroy = {} - local function forEach(building) - if building:getCustomType() < 0 then - --TODO: support builtin building types if someone wants - return - end - local pos = df.coord:new() - pos.x = building.centerx - pos.y = building.centery - pos.z = building.z - local outside = dfhack.maps.getTileBlock(pos).designation[pos.x%16][pos.y%16].outside - local def = df.global.world.raws.buildings.all[building:getCustomType()] - local btype = registeredBuildings[def.code] - if btype then --- print('outside: ' .. outside==true .. ', type: ' .. btype) - end - - if not btype or btype == buildingType.EITHER then - registeredBuildings[def.code] = nil - return - elseif btype == buildingType.OUTSIDE_ONLY then - if outside then - return - end - else - if not outside then - return - end - end - table.insert(toDestroy,building) - end - for _,building in ipairs(df.global.world.buildings.all) do - forEach(building) - end - for _,building in ipairs(toDestroy) do - destroy(building) - end - if timeoutId then - dfhack.timeout_active(timeoutId,nil) - timeoutId = nil - end - timeoutId = dfhack.timeout(checkEvery, 'ticks', checkBuildings) -end - -eventful.enableEvent(eventful.eventType.BUILDING, 100) -eventful.onBuildingCreatedDestroyed.outsideOnly = function(buildingId) - checkBuildings() -end - -validArgs = validArgs or utils.invert({ - 'help', - 'clear', - 'checkEvery', - 'building', - 'type' -}) -local args = utils.processArgs({...}, validArgs) -if args.help then - print([[scripts/modtools/outside-only -arguments - -help - print this help message - -clear - clears the list of registered buildings - -checkEvery n - set how often existing buildings are checked for whether they are in the appropriate location to n ticks - -type [EITHER, OUTSIDE_ONLY, INSIDE_ONLY] - specify what sort of restriction to put on the building - -building name - specify the id of the building -]]) - return -end - -if args.clear then - registeredBuildings = {} -end - -if args.checkEvery then - if not tonumber(args.checkEvery) then - error('Invalid checkEvery.') - end - checkEvery = tonumber(args.checkEvery) -end - -if not args.building then - return -end - -if not args['type'] then - print 'outside-only: please specify type' - return -end - -if not buildingType[args['type']] then - error('Invalid building type: ' .. args['type']) -end - -registeredBuildings[args.building] = buildingType[args['type']] - -checkBuildings() - diff --git a/scripts/modtools/projectile-trigger.lua b/scripts/modtools/projectile-trigger.lua deleted file mode 100644 index 8e6227e0a..000000000 --- a/scripts/modtools/projectile-trigger.lua +++ /dev/null @@ -1,108 +0,0 @@ --- trigger commands when projectiles hit targets ---author expwnent ---based on Putnam's projectileExpansion ---TODO: trigger based on contaminants ---[[=begin - -modtools/projectile-trigger -=========================== -This triggers dfhack commands when projectiles hit their targets. - -=end]] -local eventful = require 'plugins.eventful' -local utils = require 'utils' - -materialTriggers = materialTriggers or {} - -eventful.enableEvent(eventful.eventType.UNLOAD,1) -eventful.onUnload.projectileTrigger = function() - materialTriggers = {} -end - -function processTrigger(args) - local command2 = {} - for _,arg in ipairs(args.command) do - if arg == '\\LOCATION' then - table.insert(command2,args.pos.x) - table.insert(command2,args.pos.y) - table.insert(command2,args.pos.z) - elseif arg == '\\PROJECTILE_ID' then - table.insert(command2,args.projectile.id) - elseif arg == '\\FIRER_ID' then - table.insert(command2,args.projectile.firer.id) - elseif string.sub(arg,1,1) == '\\' then - table.insert(command2,string.sub(arg,2)) - else - table.insert(command2,arg) - end - end - dfhack.run_command(table.unpack(command2)) -end - -eventful.onProjItemCheckImpact.expansion = function(projectile) - local matStr = dfhack.matinfo.decode(projectile.item):getToken() - local table = {} - table.pos = projectile.cur_pos - table.projectile = projectile - table.item = projectile.item - for _,args in ipairs(materialTriggers[matStr] or {}) do - utils.fillTable(args,table) - processTrigger(args) - utils.unfillTable(args,table) - end -end - -validArgs = validArgs or utils.invert({ - 'help', - 'clear', - 'command', - 'material', -}) - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print([[scripts/modtools/projectile-trigger.lua -arguments - -help - print this help message - -clear - unregister all triggers - -material - specify a material for projectiles that will trigger the command - examples: - INORGANIC:IRON - CREATURE_MAT:DWARF:BRAIN - PLANT_MAT:MUSHROOM_HELMET_PLUMP:DRINK - -command [ commandList ] - \\LOCATION - \\PROJECTILE_ID - \\FIRER_ID - \\anything -> \anything - anything -> anything -]]) - return -end - -if args.clear then - materialTriggers = {} -end - -if not args.command then - return -end - -if not args.material then - error 'specify a material' -end - -if not dfhack.matinfo.find(args.material) then - error ('invalid material: ' .. args.material) -end - -if not materialTriggers[args.material] then - materialTriggers[args.material] = {} -end -table.insert(materialTriggers[args.material], args) - - diff --git a/scripts/modtools/random-trigger.lua b/scripts/modtools/random-trigger.lua deleted file mode 100644 index b234b85ef..000000000 --- a/scripts/modtools/random-trigger.lua +++ /dev/null @@ -1,179 +0,0 @@ --- triggers random scripts ---[[=begin - -modtools/random-trigger -======================= -This triggers random dfhack commands with specified probabilities. -Register a few scripts, then tell it to "go" and it will pick one -based on the probability weights you specified. Outcomes are mutually -exclusive. To make independent random events, call the script multiple -times. - -=end]] -local utils = require 'utils' -local eventful = require 'plugins.eventful' - -outcomeLists = outcomeLists or {} -randomGen = randomGen or dfhack.random.new() - -eventful.enableEvent(eventful.eventType.UNLOAD, 1) -eventful.onUnload.randomTrigger = function() - outcomeLists = {} -end - -validArgs = validArgs or utils.invert({ - 'help', - 'command', - 'outcomeListName', - 'weight', - 'seed', - 'trigger', - 'preserveList', - 'withProbability', - 'listOutcomes', - 'clear', -}) - -local function triggerEvent(outcomeListName) - local outcomeList = outcomeLists[outcomeListName] - local r = randomGen:random(outcomeList.total) - local sum = 0 - --print ('r = ' .. r) - for i,outcome in ipairs(outcomeList.outcomes) do - sum = sum + outcome.weight - if sum > r then - local temp = outcome.command - --print('triggering outcome ' .. i .. ': "' .. table.concat(temp, ' ') .. '"') - --dfhack.run_command(table.unpack(temp)) - dfhack.run_script(table.unpack(temp)) - break - else - --print ('sum = ' .. sum .. ' <= r = ' .. r) - end - end - --print('Done.') - --dfhack.print('\n') -end - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print([[scripts/modtools/random-trigger.lua -Allows mutually-exclusive random events. Register a list of scripts along with positive integer relative weights, then tell the script to select one of them with the specified probabilities and run it. -The weights must be positive integers, but they do NOT have to sum to 100 or any other particular number. -The outcomes are mutually exclusive: only one will be triggered. -If you want multiple independent random events, call the script multiple times. -99% of the time, you won't need to worry about this, but just in case, you can specify a name of a list of outcomes to prevent interference from other scripts that call this one. -That also permits situations where you don't know until runtime what outcomes you want. -For example, you could make a reaction-trigger that registers the worker as a mayor candidate, then run this script to choose a random mayor from the list of units that did the mayor reaction. - -arguments: - -help - print this help message - -outcomeListName name - specify the name of this list of outcomes to prevent interference if two scripts are registering outcomes at the same time - if none is specified, the default outcome list is selected automatically - -command [ commandStrs ] - specify the command to be run if this outcome is selected - must be specified unless the -trigger argument is given - -weight n - the relative probability weight of this outcome - n must be a non-negative integer - if not specified, n=1 is used by default - -trigger - selects a random script based on the specified outcomeList (or the default one if none is specified) - -preserveList - when combined with trigger, preserves the list of outcomes so you don't have to register them again - it is extremely highly recommended that you always specify the outcome list name when you give this command to prevent almost certain interference - if you want to trigger one of 5 outcomes three times, you might want this option even without -outcomeListName - most of the time, you won't want this - will NOT be preserved after the user saves/loads (ask expwnent if you want this: it's not that hard but if nobody wants it I won't bother) - performance will be slightly faster if you preserve the outcome lists when possible and trigger them multiple times instead of reregistering each time, but the effect should be small - -withProbability p - p is a real number between 0 and 1 inclusive - triggers the command immediately with this probability - -seed s - sets the random seed (guarantees the same sequence of random numbers will be produced internally) - use for debugging purposes - -listOutcomes - lists the currently registered list of outcomes of the outcomeList along with their probability weights - use for debugging purposes - -clear - unregister everything -]]) - return -end - -if args.clear then - outcomeLists = {} -end - -if args.weight and not tonumber(args.weight) then - error ('Invalid weight: ' .. args.weight) -end -args.weight = (args.weight and tonumber(args.weight)) or 1 -if args.weight ~= math.floor(args.weight) then - error 'Noninteger weight.' -end -if args.weight < 0 then - error 'invalid weight: must be non-negative' -end - -if args.seed then - randomGen:init(tonumber(args.seed), 37) --37 is probably excessive and definitely arbitrary -end - -args.outcomeListName = args.outcomeListName or '' -args.outcomeListName = 'outcomeList ' .. args.outcomeListName - -if args.withProbability then - args.withProbability = tonumber(args.withProbability) - if not args.withProbability or args.withProbability < 0 or args.withProbability > 1 then - error('Invalid withProbability: ' .. (args.withProbability or 'nil')) - end - if randomGen:drandom() < args.withProbability then - dfhack.run_command(table.unpack(args.command)) - end -end - -if args.trigger then - triggerEvent(args.outcomeListName) - if not args.preserveList then - outcomeLists[args.outcomeListName] = nil - end - return -end - -if args.listOutcomes then - local outcomeList = outcomeLists[args.outcomeListName] - if not outcomeList then - print ('No outcomes registered.') - return - end - print ('Total weight: ' .. outcomeList.total) - for _,outcome in ipairs(outcomeList.outcomes) do - print(' outcome weight ' .. outcome.weight .. ': ' .. table.concat(outcome.command, ' ')) - end - print('\n') - return -end - -if not args.command then - return -end - ---actually register -local outcomeList = outcomeLists[args.outcomeListName] -if not outcomeList then - outcomeLists[args.outcomeListName] = {} - outcomeList = outcomeLists[args.outcomeListName] -end - -outcomeList.total = args.weight + (outcomeList.total or 0) -local outcome = {} -outcome.weight = args.weight -outcome.command = args.command -outcomeList.outcomes = outcomeList.outcomes or {} -table.insert(outcomeList.outcomes, outcome) - - diff --git a/scripts/modtools/reaction-product-trigger.lua b/scripts/modtools/reaction-product-trigger.lua deleted file mode 100644 index 69688ce2c..000000000 --- a/scripts/modtools/reaction-product-trigger.lua +++ /dev/null @@ -1,132 +0,0 @@ --- trigger commands before/after reactions produce items --- author expwnent ---@ module = true ---[[=begin - -modtools/reaction-product-trigger -================================= -This triggers dfhack commands when reaction products are produced, once per -product. - -=end]] -local eventful = require 'plugins.eventful' -local utils = require 'utils' - ---TODO: onUnload -productHooks = productHooks or {} - -reactionInputItems = reactionInputItems - -function preserveReagents() - reactionInputItems:resize(0) -end - -eventful.enableEvent(eventful.eventType.UNLOAD,1) -eventful.onUnload.reactionProductTrigger = function() - productHooks = {} -end - ---productHooks.before = productHooks.before or {} ---productHooks.after = productHooks.after or {} - -local function processArgs(args, reaction, reaction_product, unit, input_items, input_reagents, output_items, buildingId) - local result = {} - for _,arg in ipairs(args) do - if arg == '\\WORKER_ID' then - table.insert(result,tostring(unit.id)) - elseif arg == '\\REACTION' then - table.insert(result,reaction.code) --- elseif arg == '\\REACTION_PRODUCT' then --- table.insert(result,reaction_product) - elseif arg == '\\INPUT_ITEMS' then - --table.insert(result,'[') - for _,item in ipairs(input_items) do - table.insert(result,tostring(item.id)) - end - --table.insert(result,']') - elseif arg == '\\OUTPUT_ITEMS' then - --table.insert(result,'[') - for _,item in ipairs(output_items) do - table.insert(result,tostring(item.id)) - end - --table.insert(result,']') - elseif arg == '\\BUILDING_ID' then - table.insert(result,tostring(buildingId)) - elseif string.sub(arg,1,1) == '\\' then - table.insert(result,string.sub(arg,2)) - else - table.insert(result,arg) - end - end - return result -end - -local function afterProduce(reaction,reaction_product,unit,input_items,input_reagents,output_items) - --printall(unit.job.current_job) - local _,buildingId = dfhack.script_environment('modtools/reaction-trigger').getWorkerAndBuilding(unit.job.current_job) - for _,hook in ipairs(productHooks[reaction.code] or {}) do - local command = hook.command - local processed = processArgs(command, reaction, reaction_product, unit, input_items, input_reagents, output_items, buildingId) - dfhack.run_command(table.unpack(processed)) - end -end - -eventful.onReactionComplete.reactionProductTrigger = function(reaction,reaction_product,unit,input_items,input_reagents,output_items) - reactionInputItems = input_items - afterProduce(reaction,reaction_product,unit,input_items,input_reagents,output_items) - reactionInputItems = nil -end - -validArgs = validArgs or utils.invert({ - 'help', - 'clear', - 'reactionName', - 'command', -}) - -if moduleMode then - return -end - -local args = {...} or {} -args = utils.processArgs(args, validArgs) - -if args.help then - print([[scripts/modtools/reaction-product-trigger.lua -arguments: - -help - print this help message - -clear - unregister all reaction hooks - -reactionName name - specify the name of the reaction - -command [ commandStrs ] - specify the command to be run on the target(s) - special args - \\WORKER_ID - \\REACTION - \\BUILDING_ID - \\LOCATION - \\INPUT_ITEMS - \\OUTPUT_ITEMS - \\anything -> \anything - anything -> anything -]]) - return -end - -if args.clear then - productHooks = {} -end - -if not args.reactionName then - error('No reactionName.') -end - -if not args.command then - error('No command.') -end - -productHooks[args.reactionName] = productHooks[args.reactionName] or {} -table.insert(productHooks[args.reactionName], args) - diff --git a/scripts/modtools/reaction-trigger-transition.lua b/scripts/modtools/reaction-trigger-transition.lua deleted file mode 100644 index 3d0b7ee65..000000000 --- a/scripts/modtools/reaction-trigger-transition.lua +++ /dev/null @@ -1,147 +0,0 @@ --- help transition from autoSyndrome --- author expwnent ---[[=begin - -modtools/reaction-trigger-transition -==================================== -Scans raw files and creates a file to help modders transition from -autoSyndrome to reaction-trigger. - -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, -and will be removed eventually. - -=end]] -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 deleted file mode 100644 index 9589a9c4c..000000000 --- a/scripts/modtools/reaction-trigger.lua +++ /dev/null @@ -1,241 +0,0 @@ --- trigger commands when custom reactions complete --- author expwnent --- replaces autoSyndrome ---@ module = true ---[[=begin - -modtools/reaction-trigger -========================= -Triggers dfhack commands when custom reactions complete, regardless of whether -it produced anything, once per completion. Use the ``-help`` command -for more information. - -=end]] -local eventful = require 'plugins.eventful' -local syndromeUtil = require 'syndrome-util' -local utils = require 'utils' - -reactionHooks = reactionHooks or {} - -eventful.enableEvent(eventful.eventType.UNLOAD,1) -eventful.onUnload.reactionTrigger = function() - reactionHooks = {} -end - -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(result,''..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) - elseif string.sub(arg,1,1) == '\\' then - table.insert(result,string.sub(arg,2)) - 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.command then - local processed = processCommand(job, worker, worker, building, action.command) - 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.command then - processCommand(job, worker, unit, building, action.command) - 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) --0 is necessary to catch cancelled jobs and not trigger them - -validArgs = validArgs or utils.invert({ - 'help', - 'clear', - 'reactionName', - 'syndrome', - 'command', - 'allowNonworkerTargets', - 'allowMultipleTargets' -}) - -if moduleMode then - return -end -local args = utils.processArgs({...}, validArgs) - -if args.help then - print([[scripts/modtools/reaction-trigger.lua -arguments: - -help - print this help message - -clear - unregister all reaction hooks - -reactionName name - specify the name of the reaction - -syndrome name - specify the name of the syndrome to be applied to the targets - -allowNonworkerTargets - allow other units in the same building to be targetted by either the script or the syndrome - -allowMultipleTargets - allow multiple targets to the script or syndrome - if absent: - if running a script, only one target will be used - if applying a syndrome, then only one target will be infected - -resetPolicy policy - set the reset policy in the case that the syndrome is already present - policy - NewInstance (default) - DoNothing - ResetDuration - AddDuration - -command [ commandStrs ] - specify the command to be run on the target(s) - special args - \\WORKER_ID - \\TARGET_ID - \\BUILDING_ID - \\LOCATION - \\REACTION_NAME - \\anything -> \anything - anything -> anything -]]) - return -end - -if args.clear then - reactionHooks = {} -end - -if not args.reactionName then - return -end - -if not reactionHooks[args.reactionName] then - reactionHooks[args.reactionName] = {} -end - -if args.syndrome then - local foundIt - for _,syndrome in ipairs(df.global.world.raws.syndromes.all) do - if syndrome.syn_name == args.syndrome then - args.syndrome = syndrome - foundIt = true - break - end - end - if not foundIt then - error('Could not find syndrome ' .. args.syndrome) - end -end - -table.insert(reactionHooks[args.reactionName], args) - diff --git a/scripts/modtools/skill-change.lua b/scripts/modtools/skill-change.lua deleted file mode 100644 index c5cd52889..000000000 --- a/scripts/modtools/skill-change.lua +++ /dev/null @@ -1,115 +0,0 @@ --- Sets or modifies a skill of a unit ---author expwnent ---based on skillChange.lua by Putnam ---TODO: update skill level once experience increases/decreases ---TODO: skill rust? ---[[=begin - -modtools/skill-change -===================== -Sets or modifies a skill of a unit. Args: - -:-help: print the help message -:-skill skillName: set the skill that we're talking about -:-mode (add/set): are we adding experience/levels or setting them? -:-granularity (experience/level): - direct experience, or experience levels? -:-unit id: id of the target unit -:-value amount: how much to set/add - -=end]] -local utils = require 'utils' - -validArgs = validArgs or utils.invert({ - 'help', - 'skill', - 'mode', - 'value', - 'granularity', - 'unit' -}) - -mode = mode or utils.invert({ - 'add', - 'set', -}) - -granularity = granularity or utils.invert({ - 'experience', - 'level', -}) - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print([[scripts/modtools/skill-change.lua -arguments - -help - print this help message - -skill skillName - set the skill that we're talking about - -mode (add/set) - are we adding experience/levels or setting them? - -granularity (experience/level) - direct experience, or experience levels? - -unit id - id of the target unit - -value amount - how much to set/add -]]) - return -end - -if not args.unit or not tonumber(args.unit) or not df.unit.find(tonumber(args.unit)) then - error 'Invalid unit.' -end -args.unit = df.unit.find(tonumber(args.unit)) - -args.skill = df.job_skill[args.skill] -args.mode = mode[args.mode or 'set'] -args.granularity = granularity[args.granularity or 'level'] -args.value = tonumber(args.value) - -if not args.skill then - error('invalid skill') -end -if not args.value then - error('invalid value') -end - -local skill -for _,skill_c in ipairs(args.unit.status.current_soul.skills) do - if skill_c.id == args.skill then - skill = skill_c - end -end - -if not skill then - skill = df.unit_skill:new() - skill.id = args.skill - utils.insert_sorted(args.unit.status.current_soul.skills,skill,'id') -end - -print('old: ' .. skill.rating .. ': ' .. skill.experience) -if args.granularity == granularity.experience then - if args.mode == mode.set then - skill.experience = args.value - elseif args.mode == mode.add then - skill.experience = skill.experience + args.value - else - error 'bad mode' - end -elseif args.granularity == granularity.level then - if args.mode == mode.set then - skill.rating = args.value - elseif args.mode == mode.add then - skill.rating = args.value + skill.rating - else - error 'bad mode' - end -else - error 'bad granularity' -end - -print('new: ' .. skill.rating .. ': ' .. skill.experience) - diff --git a/scripts/modtools/spawn-flow.lua b/scripts/modtools/spawn-flow.lua deleted file mode 100644 index 6bca9ace4..000000000 --- a/scripts/modtools/spawn-flow.lua +++ /dev/null @@ -1,91 +0,0 @@ --- spawns flows at locations ---author expwnent ---[[=begin - -modtools/spawn-flow -=================== -Creates flows at the specified location. - -=end]] -local utils = require 'utils' - -validArgs = validArgs or utils.invert({ - 'help', - 'material', - 'flowType', - 'location', - 'flowSize', -}) -local args = utils.processArgs({...}, validArgs) - -if args.help then - print([[scripts/modtools/spawn-flow.lua -arguments: - -help - print this help message - -material mat - specify the material of the flow, if applicable - examples: - INORGANIC:IRON - CREATURE_MAT:DWARF:BRAIN - PLANT_MAT:MUSHROOM_HELMET_PLUMP:DRINK - -location [ x y z] - the location to spawn the flow - -flowType type - specify the flow type - examples: - Miasma - Steam - Mist - MaterialDust - MagmaMist - Smoke - Dragonfire - Fire - Web - MaterialGas - MaterialVapor - OceanWave - SeaFoam - -flowSize size - specify how big the flow is -]]) - return -end - -local mat_index = -1; -local mat_type = -1; -if args.material then - local mat = dfhack.matinfo.find(args.material) - if not mat then - error ('Invalid material: ' .. mat) - end - mat_index = mat.index - mat_type = mat['type'] -end - -if args.flowSize and not tonumber(args.flowSize) then - error ('Invalid flow size: ' .. args.flowSize) -end - -args.flowSize = tonumber(args.flowSize or 'z') or 100 - -if not args.flowType or not df.flow_type[args.flowType] then - error ('Invalid flow type: ' .. (args.flowType or 'none specified')) -end -args.flowType = df.flow_type[args.flowType] - -if not args.location then - error 'Specify a location.' -end - -local pos = df.coord:new(); -pos.x = tonumber(args.location[1] or 'a') -pos.y = tonumber(args.location[2] or 'a') -pos.z = tonumber(args.location[3] or 'a') -if not pos.x or not pos.y or not pos.z then - error ('Invalid pos.') -end - -dfhack.maps.spawnFlow(pos, args.flowType, mat_type, mat_index, args.flowSize) - diff --git a/scripts/modtools/syndrome-trigger.lua b/scripts/modtools/syndrome-trigger.lua deleted file mode 100644 index f46bbc5ad..000000000 --- a/scripts/modtools/syndrome-trigger.lua +++ /dev/null @@ -1,122 +0,0 @@ --- triggers scripts when a syndrome is applied ---author expwnent ---[[=begin - -modtools/syndrome-trigger -========================= -Triggers dfhack commands when syndromes are applied to units. - -=end]] -local eventful = require 'plugins.eventful' -local utils = require 'utils' - -onInfection = onInfection or {} - -eventful.enableEvent(eventful.eventType.UNLOAD,1) -eventful.onUnload.syndromeTrigger = function() - onInfection = {} -end - -eventful.enableEvent(eventful.eventType.SYNDROME,5) --requires iterating through every unit, so not cheap, but not slow either - -local function processTrigger(args) - local command = {} - for i,arg in ipairs(args.command) do - if arg == '\\SYNDROME_ID' then - table.insert(command, '' .. args.syndrome.id) - elseif arg == '\\UNIT_ID' then - table.insert(command, '' .. args.unit.id) - elseif arg == '\\LOCATION' then - table.insert(command, '' .. args.unit.pos.x) - table.insert(command, '' .. args.unit.pos.y) - table.insert(command, '' .. args.unit.pos.z) - elseif string.sub(arg,1,1) == '\\' then - table.insert(command, string.sub(arg,2)) - else - table.insert(command, arg) - end - end - dfhack.run_command(table.unpack(command)) -end - -eventful.onSyndrome.syndromeTrigger = function(unitId, syndromeIndex) - local unit = df.unit.find(unitId) - local unit_syndrome = unit.syndromes.active[syndromeIndex] - local syn_id = unit_syndrome['type'] - if not onInfection[syn_id] then - return - end - local syndrome = df.syndrome.find(syn_id) - local table = {} - table.unit = unit - table.unit_syndrome = unit_syndrome - table.syndrome = syndrome - for _,args in ipairs(onInfection[syn_id] or {}) do - utils.fillTable(args,table) - processTrigger(args) - utils.unfillTable(args,table) - end -end - ------------------------------- ---argument processing - -validArgs = validArgs or utils.invert({ - 'clear', - 'help', - 'command', - 'syndrome' -}) - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print([[scripts/modtools/syndrome-trigger.lua -arguments - -help - print this help message - -clear - clear all triggers - -syndrome name - specify the name of a syndrome - -command [ commandStrs ] - specify the command to be executed after infection - args - \\SYNDROME_ID - \\UNIT_ID - \\LOCATION - \\anything -> \anything - anything -> anything -]]) - return -end - -if args.clear then - onInfection = {} -end - -if not args.command then - return -end - -if not args.syndrome then - error 'Select a syndrome.' -end - -local syndrome -for _,syn in ipairs(df.global.world.raws.syndromes.all) do - if syn.syn_name == args.syndrome then - if syndrome then - error ('Multiple syndromes with same name: ' .. syn.syn_name) - end - syndrome = syn.id - end -end - -if not syndrome then - error ('Could not find syndrome named ' .. args.syndrome) -end - -onInfection[syndrome] = onInfection[syndrome] or {} -table.insert(onInfection[syndrome], args) - diff --git a/scripts/modtools/transform-unit.lua b/scripts/modtools/transform-unit.lua deleted file mode 100644 index 2ebdc3c8a..000000000 --- a/scripts/modtools/transform-unit.lua +++ /dev/null @@ -1,164 +0,0 @@ --- Transforms a unit into another unit type ---author expwnent ---based on shapechange by Putnam ---[[=begin - -modtools/transform-unit -======================= -Transforms a unit into another unit type, possibly permanently. -Warning: this will crash arena mode if you view the unit on the -same tick that it transforms. If you wait until later, it will be fine. - -=end]] -local utils = require 'utils' - -normalRace = normalRace or {} - -local function transform(unit,race,caste) - unit.enemy.normal_race = race - unit.enemy.normal_caste = caste - unit.enemy.were_race = race - unit.enemy.were_caste = caste -end - -validArgs = validArgs or utils.invert({ - 'clear', - 'help', - 'unit', - 'duration', - 'setPrevRace', - 'keepInventory', - 'race', - 'caste', - 'suppressAnnouncement', - 'untransform', -}) - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print([[scripts/modtools/transform-unit.lua -arguments - -help - print this help message - -clear - clear records of normal races - -unit id - set the target unit - -duration ticks - how long it should last, or "forever" - -setPrevRace - make a record of the previous race so that you can change it back with -untransform - -keepInventory - move items back into inventory after transformation - -race raceName - -caste casteName - -suppressAnnouncement - don't show the Unit has transformed into a Blah! event - -untransform - turn the unit back into what it was before -]]) - return -end - -if args.clear then - normalRace = {} -end - -if not args.unit then - error 'Specify a unit.' -end - -if not args.duration then - args.duration = 'forever' -end - -local raceIndex -local race -local caste -if args.untransform then - local unit = df.unit.find(tonumber(args.unit)) - raceIndex = normalRace[args.unit].race - race = df.creature_raw.find(raceIndex) - caste = normalRace[args.unit].caste - normalRace[args.unit] = nil -else - if not args.race or not args.caste then - error 'Specficy a target form.' - end - - --find race - for i,v in ipairs(df.global.world.raws.creatures.all) do - if v.creature_id == args.race then - raceIndex = i - race = v - break - end - end - - if not race then - error 'Invalid race.' - end - - for i,v in ipairs(race.caste) do - if v.caste_id == args.caste then - caste = i - break - end - end - - if not caste then - error 'Invalid caste.' - end -end - -local unit = df.unit.find(tonumber(args.unit)) -local oldRace = unit.enemy.normal_race -local oldCaste = unit.enemy.normal_caste -if args.setPrevRace then - normalRace[args.unit] = {} - normalRace[args.unit].race = oldRace - normalRace[args.unit].caste = oldCaste -end -transform(unit,raceIndex,caste,args.setPrevRace) - -local inventoryItems = {} - -local function getInventory() - local result = {} - for _,item in ipairs(unit.inventory) do - table.insert(result, item:new()); - end - return result -end - -local function restoreInventory() - dfhack.timeout(1, 'ticks', function() - for _,item in ipairs(inventoryItems) do - dfhack.items.moveToInventory(item.item, unit, item.mode, item.body_part_id) - item:delete() - end - inventoryItems = {} - end) -end - -if args.keepInventory then - inventoryItems = getInventory() -end - -if args.keepInventory then - restoreInventory() -end -if args.duration and args.duration ~= 'forever' then - --when the timeout ticks down, transform them back - dfhack.timeout(tonumber(args.duration), 'ticks', function() - if args.keepInventory then - inventoryItems = getInventory() - end - transform(unit,oldRace,oldCaste) - if args.keepInventory then - restoreInventory() - end - end) -end - diff --git a/scripts/multicmd.rb b/scripts/multicmd.rb deleted file mode 100644 index 1481cc52c..000000000 --- a/scripts/multicmd.rb +++ /dev/null @@ -1,16 +0,0 @@ -# run many dfhack commands separated by ; -=begin - -multicmd -======== -Run multiple dfhack commands. The argument is split around the -character ; and all parts are run sequentially as independent -dfhack commands. Useful for hotkeys. - -Example:: - - multicmd locate-ore IRON ; digv ; digcircle 16 - -=end - -$script_args.join(' ').split(/\s*;\s*/).each { |cmd| df.dfhack_run cmd } diff --git a/scripts/points.lua b/scripts/points.lua deleted file mode 100644 index b81d4637b..000000000 --- a/scripts/points.lua +++ /dev/null @@ -1,13 +0,0 @@ --- Set available points at the embark screen --- http://www.bay12forums.com/smf/index.php?topic=135506.msg4925005#msg4925005 ---[[=begin - -points -====== -Sets available points at the embark screen to the specified number. Eg. -``points 1000000`` would allow you to buy everything, or ``points 0`` would -make life quite difficult. - -=end]] - -df.global.world.worldgen.worldgen_parms.embark_points=tonumber(...) diff --git a/scripts/position.lua b/scripts/position.lua deleted file mode 100644 index 2ff44c14a..000000000 --- a/scripts/position.lua +++ /dev/null @@ -1,51 +0,0 @@ ---prints current time and position ---[[=begin - -position -======== -Reports the current time: date, clock time, month, and season. Also reports -location: z-level, cursor position, window size, and mouse location. - -=end]] - -local months = { - 'Granite, in early Spring.', - 'Slate, in mid Spring.', - 'Felsite, in late Spring.', - 'Hematite, in early Summer.', - 'Malachite, in mid Summer.', - 'Galena, in late Summer.', - 'Limestone, in early Autumn.', - 'Sandstone, in mid Autumn.', - 'Timber, in late Autumn.', - 'Moonstone, in early Winter.', - 'Opal, in mid Winter.', - 'Obsidian, in late Winter.', -} - ---Fortress mode counts 1200 ticks per day and 403200 per year ---Adventurer mode counts 86400 ticks to a day and 29030400 ticks per year ---Twelve months per year, 28 days to every month, 336 days per year - -local julian_day = math.floor(df.global.cur_year_tick / 1200) + 1 -local month = math.floor(julian_day / 28) + 1 --days and months are 1-indexed -local day = julian_day % 28 - -local time_of_day = math.floor(df.global.cur_year_tick_advmode / 336) -local second = time_of_day % 60 -local minute = math.floor(time_of_day / 60) % 60 -local hour = math.floor(time_of_day / 3600) % 24 - -print('Time:') -print(' The time is '..string.format('%02d:%02d:%02d', hour, minute, second)) -print(' The date is '..string.format('%05d-%02d-%02d', df.global.cur_year, month, day)) -print(' It is the month of '..months[month]) ---TODO: print(' It is the Age of '..age_name) - -print('Place:') -print(' The z-level is z='..df.global.window_z) -print(' The cursor is at x='..df.global.cursor.x..', y='..df.global.cursor.y) -print(' The window is '..df.global.gps.dimx..' tiles wide and '..df.global.gps.dimy..' tiles high') -if df.global.gps.mouse_x == -1 then print(' The mouse is not in the DF window') else -print(' The mouse is at x='..df.global.gps.mouse_x..', y='..df.global.gps.mouse_y..' within the window') end ---TODO: print(' The fortress is at '..x, y..' on the world map ('..worldsize..' square)') diff --git a/scripts/pref-adjust.lua b/scripts/pref-adjust.lua deleted file mode 100644 index a43ee8712..000000000 --- a/scripts/pref-adjust.lua +++ /dev/null @@ -1,106 +0,0 @@ --- Adjust all preferences of all dwarves in play --- by vjek ---[[=begin - -pref-adjust -=========== -A two-stage script: ``pref-adjust clear`` removes preferences from all dwarves, -and ``pref-adjust`` inserts an 'ideal' set which is easy to satisfy:: - - Feb Idashzefon likes wild strawberries for their vivid red color, - fisher berries for their round shape, prickle berries for their - precise thorns, plump helmets for their rounded tops, prepared meals, - plants, drinks, doors, thrones, tables and beds. When possible, she - prefers to consume wild strawberries, fisher berries, prickle - berries, plump helmets, strawberry wine, fisher berry wine, prickle - berry wine, and dwarven wine. - -=end]] - --- --------------------------------------------------------------------------- -function brainwash_unit(unit) - - if unit==nil then - print ("No unit available! Aborting with extreme prejudice.") - return - end - - local pss_counter=31415926 - - local prefcount = #(unit.status.current_soul.preferences) - print ("Before, unit "..dfhack.TranslateName(dfhack.units.getVisibleName(unit)).." has "..prefcount.." preferences") - - utils = require 'utils' - -- below populates an array with all creature names and id's, used for 'detests...' - rtbl={} - vec=df.global.world.raws.creatures.all - for k=0,#vec-1 do - local name=vec[k].creature_id - rtbl[name]=k - end - - -- Now iterate through for the type 3 detests... - utils.insert_or_update(unit.status.current_soul.preferences, { new = true, type = 3 , item_type = rtbl.TROLL , creature_id = rtbl.TROLL , color_id = rtbl.TROLL , shape_id = rtbl.TROLL , plant_id = rtbl.TROLL , item_subtype = -1 , mattype = -1 , matindex = -1 , active = true, prefstring_seed = pss_counter }, 'prefstring_seed') - pss_counter = pss_counter + 1 - utils.insert_or_update(unit.status.current_soul.preferences, { new = true, type = 3 , item_type = rtbl.BIRD_BUZZARD , creature_id = rtbl.BIRD_BUZZARD , color_id = rtbl.BIRD_BUZZARD , shape_id = rtbl.BIRD_BUZZARD , plant_id = rtbl.BIRD_BUZZARD , item_subtype = -1 , mattype = -1 , matindex = -1 , active = true, prefstring_seed = pss_counter }, 'prefstring_seed') - pss_counter = pss_counter + 1 - utils.insert_or_update(unit.status.current_soul.preferences, { new = true, type = 3 , item_type = rtbl.BIRD_VULTURE , creature_id = rtbl.BIRD_VULTURE , color_id = rtbl.BIRD_VULTURE , shape_id = rtbl.BIRD_VULTURE , plant_id = rtbl.BIRD_VULTURE , item_subtype = -1 , mattype = -1 , matindex = -1 , active = true, prefstring_seed = pss_counter }, 'prefstring_seed') - pss_counter = pss_counter + 1 - utils.insert_or_update(unit.status.current_soul.preferences, { new = true, type = 3 , item_type = rtbl.CRUNDLE , creature_id = rtbl.CRUNDLE , color_id = rtbl.CRUNDLE , shape_id = rtbl.CRUNDLE , plant_id = rtbl.CRUNDLE , item_subtype = -1 , mattype = -1 , matindex = -1 , active = true, prefstring_seed = pss_counter }, 'prefstring_seed') - pss_counter = pss_counter + 1 - -- and the type 4 likes - utils.insert_or_update(unit.status.current_soul.preferences, { new = true, type = 4 , item_type = df.item_type.WEAPON , creature_id = df.item_type.WEAPON , color_id = df.item_type.WEAPON , shape_id = df.item_type.WEAPON , plant_id = df.item_type.WEAPON , item_subtype = -1 , mattype = -1 , matindex = -1 , active = true, prefstring_seed = pss_counter }, 'prefstring_seed') - pss_counter = pss_counter + 1 - utils.insert_or_update(unit.status.current_soul.preferences, { new = true, type = 4 , item_type = df.item_type.ARMOR , creature_id = df.item_type.ARMOR , color_id = df.item_type.ARMOR , shape_id = df.item_type.ARMOR , plant_id = df.item_type.ARMOR , item_subtype = -1 , mattype = -1 , matindex = -1 , active = true, prefstring_seed = pss_counter }, 'prefstring_seed') - pss_counter = pss_counter + 1 - utils.insert_or_update(unit.status.current_soul.preferences, { new = true, type = 4 , item_type = df.item_type.SHIELD , creature_id = df.item_type.SHIELD , color_id = df.item_type.SHIELD , shape_id = df.item_type.SHIELD , plant_id = df.item_type.SHIELD , item_subtype = -1 , mattype = -1 , matindex = -1 , active = true, prefstring_seed = pss_counter }, 'prefstring_seed') - pss_counter = pss_counter + 1 - -- prefers plump helmets for their ... - local ph_mat_type=dfhack.matinfo.find("MUSHROOM_HELMET_PLUMP:STRUCTURAL").index - utils.insert_or_update(unit.status.current_soul.preferences, { new = true, type = 5 , item_type = ph_mat_type , creature_id = ph_mat_type , color_id = ph_mat_type , shape_id = ph_mat_type , plant_id = ph_mat_type , item_subtype = -1 , mattype = -1 , matindex = -1 , active = true, prefstring_seed = pss_counter }, 'prefstring_seed') - pss_counter = pss_counter + 1 - -- prefers to consume dwarven wine: - utils.insert_or_update(unit.status.current_soul.preferences, { new = true, type = 2 , item_type = 68 , creature_id = 68 , color_id = 68 , shape_id = 68 , plant_id = 68 , item_subtype = -1 , mattype = dfhack.matinfo.find("MUSHROOM_HELMET_PLUMP:DRINK").type , matindex = dfhack.matinfo.find("MUSHROOM_HELMET_PLUMP:DRINK").index , active = true, prefstring_seed = pss_counter }, 'prefstring_seed') - pss_counter = pss_counter + 1 - -- likes iron, steel (0,8) adam is 25 - utils.insert_or_update(unit.status.current_soul.preferences, { new = true, type = 0 , item_type = -1 , creature_id = -1 , color_id = -1 , shape_id = -1 , plant_id = -1 , item_subtype = -1 , mattype = 0 , matindex = dfhack.matinfo.find("IRON").index , active = true, prefstring_seed = pss_counter }, 'prefstring_seed') - pss_counter = pss_counter + 1 - utils.insert_or_update(unit.status.current_soul.preferences, { new = true, type = 0 , item_type = -1 , creature_id = -1 , color_id = -1 , shape_id = -1 , plant_id = -1 , item_subtype = -1 , mattype = 0 , matindex = dfhack.matinfo.find("STEEL").index , active = true, prefstring_seed = pss_counter }, 'prefstring_seed') - - prefcount = #(unit.status.current_soul.preferences) - print ("After, unit "..dfhack.TranslateName(dfhack.units.getVisibleName(unit)).." has "..prefcount.." preferences") - -end --- --------------------------------------------------------------------------- -function clear_preferences(v) - unit=v - - local prefs=unit.status.current_soul.preferences - for index,pref in ipairs(prefs) do - pref:delete() - end - prefs:resize(0) -end --- --------------------------------------------------------------------------- -function clearpref_all_dwarves() - for _,v in ipairs(df.global.world.units.active) do - if v.race == df.global.ui.race_id then - print("Clearing Preferences for "..dfhack.TranslateName(dfhack.units.getVisibleName(v))) - clear_preferences(v) - end - end -end --- --------------------------------------------------------------------------- -function adjust_all_dwarves() - for _,v in ipairs(df.global.world.units.active) do - if v.race == df.global.ui.race_id then - print("Adjusting "..dfhack.TranslateName(dfhack.units.getVisibleName(v))) - brainwash_unit(v) - end - end -end --- --------------------------------------------------------------------------- --- main script operation starts here --- --------------------------------------------------------------------------- -clearpref_all_dwarves() -adjust_all_dwarves() diff --git a/scripts/putontable.lua b/scripts/putontable.lua deleted file mode 100644 index daba8a46f..000000000 --- a/scripts/putontable.lua +++ /dev/null @@ -1,36 +0,0 @@ --- Makes item appear on the table (just like in shops) ---[[=begin - -putontable -========== -Makes item appear on the table, like in adventure mode shops. -Arguments: ``-a`` or ``--all`` for all items. - -=end]] - -local pos=df.global.cursor -local args={...} -local doall -if args[1]=="-a" or args[1]=="--all" then - doall=true -end -local build,items -items={} -build=dfhack.buildings.findAtTile(pos.x,pos.y,pos.z) -if not df.building_tablest:is_instance(build) then - error("No table found at cursor") -end -for k,v in pairs(df.global.world.items.all) do - if pos.x==v.pos.x and pos.y==v.pos.y and pos.z==v.pos.z and v.flags.on_ground then - table.insert(items,v) - if not doall then - break - end - end -end -if #items==0 then - error("No items found!") -end -for k,v in pairs(items) do - dfhack.items.moveToBuilding(v,build,0) -end diff --git a/scripts/quicksave.lua b/scripts/quicksave.lua deleted file mode 100644 index 3f4cc3954..000000000 --- a/scripts/quicksave.lua +++ /dev/null @@ -1,39 +0,0 @@ --- Makes the game immediately save the state. ---[[=begin - -quicksave -========= -If called in dwarf mode, makes DF immediately saves the game by setting a flag -normally used in seasonal auto-save. - -=end]] - -if not dfhack.isMapLoaded() then - qerror("World and map aren't loaded.") -end - -if not dfhack.world.isFortressMode() then - qerror('This script can only be used in fortress mode') -end - -local ui_main = df.global.ui.main -local flags4 = df.global.d_init.flags4 - -local function restore_autobackup() - if ui_main.autosave_request and dfhack.isMapLoaded() then - dfhack.timeout(10, 'frames', restore_autobackup) - else - flags4.AUTOBACKUP = true - end -end - --- Request auto-save -ui_main.autosave_request = true - --- And since it will overwrite the backup, disable it temporarily -if flags4.AUTOBACKUP then - flags4.AUTOBACKUP = false - restore_autobackup() -end - -print 'The game should save the state now.' diff --git a/scripts/region-pops.lua b/scripts/region-pops.lua deleted file mode 100644 index 935cd0f02..000000000 --- a/scripts/region-pops.lua +++ /dev/null @@ -1,199 +0,0 @@ --- Show or edit regional plant and animal populations ---[[=begin - -region-pops -=========== -Show or modify the populations of animals in the region. - -Usage: - -:region-pops list [pattern]: - Lists encountered populations of the region, possibly restricted by pattern. -:region-pops list-all [pattern]: - Lists all populations of the region. -:region-pops boost : - Multiply all populations of TOKEN by factor. - If the factor is greater than one, increases the - population, otherwise decreases it. -:region-pops boost-all : - Same as above, but match using a pattern acceptable to list. -:region-pops incr : - Augment (or diminish) all populations of TOKEN by factor (additive). -:region-pops incr-all : - Same as above, but match using a pattern acceptable to list. - -=end]] - -local utils = require 'utils' - -local function sort_keys(tab) - local kt = {} - for k,v in pairs(tab) do table.insert(kt,k) end - table.sort(kt) - return ipairs(kt) -end - -local is_plant_map = { - Animal = false, Vermin = false, VerminInnumerable = false, - ColonyInsect = false, Tree = true, Grass = true, Bush = true -} - -function enum_populations() - local stat_table = { - plants = {}, - creatures = {}, - any = {} - } - - for i,v in ipairs(df.global.world.populations) do - local typeid = df.world_population_type[v.type] - local is_plant = is_plant_map[typeid] - local id, obj, otable, idtoken - - if is_plant then - id = v.plant - obj = df.plant_raw.find(id) - otable = stat_table.plants - idtoken = obj.id - else - id = v.race - obj = df.creature_raw.find(id) - otable = stat_table.creatures - idtoken = obj.creature_id - end - - local entry = otable[idtoken] - if not entry then - entry = { - obj = obj, token = idtoken, id = id, records = {}, - count = 0, known_count = 0, - known = false, infinite = false - } - otable[idtoken] = entry - stat_table.any[idtoken] = entry - end - - table.insert(entry.records, v) - entry.known = entry.known or v.flags.discovered - - if v.quantity < 10000001 then - entry.count = entry.count + v.quantity - if v.flags.discovered then - entry.known_count = entry.known_count + v.quantity - end - else - entry.infinite = true - end - end - - return stat_table -end - -function list_poptable(entries, all, pattern) - for _,k in sort_keys(entries) do - local entry = entries[k] - if (all or entry.known) and (not pattern or string.match(k,pattern)) then - local count = entry.known_count - if all then - count = entry.count - end - if entry.infinite then - count = 'innumerable' - end - print(string.format('%-40s %s', entry.token, count)) - end - end -end - -function list_populations(stat_table, all, pattern) - print('Plants:') - list_poptable(stat_table.plants, true, pattern) - print('\nCreatures and vermin:') - list_poptable(stat_table.creatures, all, pattern) -end - - -function boost_population(entry, factor, boost_count) - for _,v in ipairs(entry.records) do - if v.quantity < 10000001 then - boost_count = boost_count + 1 - v.quantity = math.floor(v.quantity * factor) - end - end - return boost_count -end - -function incr_population(entry, factor, boost_count) - for _,v in ipairs(entry.records) do - if v.quantity < 10000001 then - boost_count = boost_count + 1 - v.quantity = math.max(0, v.quantity + factor) - end - end - return boost_count -end - -local args = {...} -local pops = enum_populations() - -if args[1] == 'list' or args[1] == 'list-all' then - list_populations(pops, args[1] == 'list-all', args[2]) -elseif args[1] == 'boost' or args[1] == 'boost-all' then - local factor = tonumber(args[3]) - if not factor or factor < 0 then - qerror('Invalid boost factor.') - end - - local count = 0 - - if args[1] == 'boost' then - local entry = pops.any[args[2]] or qerror('Unknown population token.') - count = boost_population(entry, factor, count) - else - for k,entry in pairs(pops.any) do - if string.match(k, args[2]) then - count = boost_population(entry, factor, count) - end - end - end - - print('Updated '..count..' populations.') -elseif args[1] == 'incr' or args[1] == 'incr-all' then - local factor = tonumber(args[3]) - if not factor then - qerror('Invalid increment factor.') - end - - local count = 0 - - if args[1] == 'incr' then - local entry = pops.any[args[2]] or qerror('Unknown population token.') - count = incr_population(entry, factor, count) - else - for k,entry in pairs(pops.any) do - if string.match(k, args[2]) then - count = incr_population(entry, factor, count) - end - end - end - - print('Updated '..count..' populations.') -else - print([[ -Usage: - region-pops list [pattern] - Lists encountered populations of the region, possibly restricted by pattern. - region-pops list-all [pattern] - Lists all populations of the region. - region-pops boost - Multiply all populations of TOKEN by factor. - If the factor is greater than one, increases the - population, otherwise decreases it. - region-pops boost-all - Same as above, but match using a pattern acceptable to list. - region-pops incr - Augment (or diminish) all populations of TOKEN by factor (additive). - region-pops incr-all - Same as above, but match using a pattern acceptable to list. -]]) -end diff --git a/scripts/rejuvenate.lua b/scripts/rejuvenate.lua deleted file mode 100644 index fe19ad545..000000000 --- a/scripts/rejuvenate.lua +++ /dev/null @@ -1,30 +0,0 @@ --- make the selected dwarf 20 years old --- by vjek ---[[=begin - -rejuvenate -========== -Set the age of the selected dwarf to 20 years. Useful if valuable citizens are -getting old, or there are too many babies around... - -=end]] - -function rejuvenate() - local current_year,newbirthyear - unit=dfhack.gui.getSelectedUnit() - - if unit==nil then print ("No unit under cursor! Aborting.") return end - - current_year=df.global.cur_year - newbirthyear=current_year - 20 - if unit.relations.birth_year < newbirthyear then - unit.relations.birth_year=newbirthyear - end - if unit.relations.old_year < current_year+100 then - unit.relations.old_year=current_year+100 - end - print (dfhack.TranslateName(dfhack.units.getVisibleName(unit)).." is now 20 years old and will live at least 100 years") - -end - -rejuvenate() diff --git a/scripts/remove-stress.lua b/scripts/remove-stress.lua deleted file mode 100644 index d384129fd..000000000 --- a/scripts/remove-stress.lua +++ /dev/null @@ -1,41 +0,0 @@ --- Sets stress to negative one million ---By Putnam; http://www.bay12forums.com/smf/index.php?topic=139553.msg5820486#msg5820486 ---[[=begin - -remove-stress -============= -Sets stress to -1,000,000; the normal range is 0 to 500,000 with very stable or -very stressed dwarves taking on negative or greater values respectively. -Applies to the selected unit, or use ``remove-stress -all`` to apply to all units. - -=end]] - -local utils = require 'utils' - -validArgs = validArgs or utils.invert({ - 'help', - 'all' -}) - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print([[ -remove-stress [-all] - sets the stress level of every unit to -1000000, or just the selected unit if the '-all' argument is not given -]]) - return -end - -if args.all then - for k,v in ipairs(df.global.world.units.active) do - v.status.current_soul.personality.stress_level=-1000000 - end -else - local unit = dfhack.gui.getSelectedUnit() - if unit then - unit.status.current_soul.personality.stress_level=-1000000 - else - error 'Invalid usage: No unit selected and -all argument not given.' - end -end diff --git a/scripts/remove-wear.lua b/scripts/remove-wear.lua deleted file mode 100644 index e1ed3989a..000000000 --- a/scripts/remove-wear.lua +++ /dev/null @@ -1,62 +0,0 @@ --- Resets all items in your fort to 0 wear --- original author: Laggy, edited by expwnent ---[[=begin - -remove-wear -=========== -Sets the wear on all items in your fort to zero. - -=end]] - -local args = {...} - -if args[1] == 'help' then - print([[remove-wear - this script removes wear from all items, or from individual ones - -remove-wear all - remove wear from all items -remove-wear n1 n2 n3 ... - remove wear from items with the given ids. order does not matter -repeat -time 2 months -command remove-wear all - remove wear from all items every 2 months. see repeat.lua for details -]]) - do return end -elseif args[1] == 'all' then - local count = 0; - for _,item in ipairs(df.global.world.items.all) do - if (item.wear > 0) then - item:setWear(0) - count = count+1 - end - end - print('remove-wear removed wear from '..count..' objects') -else - local argIndex = 1 - local isCompleted = {} - for i,x in ipairs(args) do - args[i] = tonumber(x) - end - table.sort(args) - for _,item in ipairs(df.global.world.items.all) do - local function loop() - if argIndex > #args then - return - elseif item.id > args[argIndex] then - argIndex = argIndex+1 - loop() - return - elseif item.id == args[argIndex] then - --print('removing wear from item with id ' .. args[argIndex]) - item:setWear(0) - isCompleted[args[argIndex]] = true - argIndex = argIndex+1 - end - end - loop() - end - for _,arg in ipairs(args) do - if isCompleted[arg] ~= true then - print('failed to remove wear from item ' .. arg .. ': could not find item with that id') - end - end -end diff --git a/scripts/repeat.lua b/scripts/repeat.lua deleted file mode 100644 index b048584d8..000000000 --- a/scripts/repeat.lua +++ /dev/null @@ -1,85 +0,0 @@ --- repeatedly call a lua script --- eg "repeat -time 1 months -command cleanowned"; to disable "repeat -cancel cleanowned" --- repeat -help for details --- author expwnent --- vaguely based on a script by Putnam ---[[=begin - -repeat -====== -Repeatedly calls a lua script at the specified interval. - -This allows neat background changes to the function of the game, especially when -invoked from an init file. For detailed usage instructions, use ``repeat -help``. - -Usage examples:: - - repeat -name jim -time delay -timeUnits units -printResult true -command [ printArgs 3 1 2 ] - repeat -time 1 -timeUnits months -command [ multicmd cleanowned scattered x; clean all ] -name clean - -The first example is abstract; the second will regularly remove all contaminants -and worn items from the game. - -``-name`` sets the name for the purposes of cancelling and making sure you don't schedule the -same repeating event twice. If not specified, it's set to the first argument after ``-command``. -``-time delay -timeUnits units``; delay is some positive integer, and units is some valid time -unit for ``dfhack.timeout(delay,timeUnits,function)``. ``-command [ ... ]`` specifies the -command to be run. - -=end]] - -local repeatUtil = require 'repeat-util' -local utils = require 'utils' - -validArgs = validArgs or utils.invert({ - 'help', - 'cancel', - 'name', - 'time', - 'timeUnits', - 'command' -}) - -local args = utils.processArgs({...}, validArgs) - -if args.help then - print([[repeat.lua - repeat -help - print this help message - repeat -cancel bob - cancels the repetition with the name bob - repeat -name jim -time delay -timeUnits units -printResult true -command [ printArgs 3 1 2 ] - -name sets the name for the purposes of cancelling and making sure you don't schedule the same repeating event twice - if not specified, it's set to the first argument after -command - -time delay -timeUnits units - delay is some positive integer - units is some valid time unit for dfhack.timeout(delay,timeUnits,function) - -command [ ... ] - specify the command to be run - ]]) - return -end - -if args.cancel then - repeatUtil.cancel(args.cancel) - if args.name then - repeatUtil.cancel(args.name) - end - return -end - -args.time = tonumber(args.time) -if not args.name then - args.name = args.command[1] -end - -if not args.timeUnits then - args.timeUnits = 'ticks' -end - -local callCommand = function() - dfhack.run_command(table.unpack(args.command)) -end - -repeatUtil.scheduleEvery(args.name,args.time,args.timeUnits,callCommand) - diff --git a/scripts/setfps.lua b/scripts/setfps.lua deleted file mode 100644 index 6deb68bb2..000000000 --- a/scripts/setfps.lua +++ /dev/null @@ -1,18 +0,0 @@ --- Set the FPS cap at runtime. ---[[=begin - -setfps -====== -Run ``setfps `` to set the FPS cap at runtime, in case you want to watch -combat in slow motion or something. - -=end]] - -local cap = ... -local capnum = tonumber(cap) - -if not capnum or capnum < 1 then - qerror('Invalid FPS cap value: '..cap) -end - -df.global.enabler.fps = capnum diff --git a/scripts/show-unit-syndromes.rb b/scripts/show-unit-syndromes.rb deleted file mode 100644 index 112b33474..000000000 --- a/scripts/show-unit-syndromes.rb +++ /dev/null @@ -1,1035 +0,0 @@ -# Show syndromes affecting units, including duration -# original author: drayath, edited by expwnent -=begin - -show-unit-syndromes -=================== -Show syndromes affecting units and the remaining and maximum duration, along -with (optionally) substantial detail on the effects. - -Use one or more of the following options: - -:help: Show the help message -:showall: Show units even if not affected by any syndrome -:showeffects: Show detailed effects of each syndrome -:showdisplayeffects: Show effects that only change the look of the unit -:selected: Show selected unit -:dwarves: Show dwarves -:livestock: Show livestock -:wildanimals: Show wild animals -:hostile: Show hostiles (e.g. invaders, thieves, forgotten beasts etc) -:world: Show all defined syndromes in the world -:export: ``export:`` sends output to the given file, showing all - syndromes affecting each unit with the maximum and present duration. - -=end - -#TODO: When showing effects on a unit, show the actual change to the unit -# E.g. if +150%, +500 strength show actual total bonus based on the unit stats. -# For this also need to know -# how does size_delays affect the start/peak/end time -# how does size_dilute affect the Severity, does it also affect the phy/mental stat adjustments? -# how does peak affect the Severity, does it also affect the phy/mental stat adjustments? -#TODO: Add interaction info needs to display a bit more data, but the required structures are not yet decoded - -#TODO: Several of the unk_xxx fields have been identified here, and can get some more by comparing the raws with the printed interaction and effect information. Pass these onto the dfhack guys. - -def print_help() - puts "Use one or more of the following options:" - puts " showall: Show units even if not affected by any syndrome" - puts " showeffects: shows detailed effects of each syndrome" - puts " showdisplayeffects: show effects that only change the look of the unit" - puts " ignorehiddencurse: Hides syndromes the user should not be able to know about (TODO)" - puts " selected: Show selected unit" - puts " dwarves: Show dwarves" - puts " livestock: Show livestock" - puts " wildanimals: Show wild animals" - puts " hostile: Show hostiles (e.g. invaders, thieves, forgotten beasts etc)" - puts " world: Show all defined syndromes in the world" - puts " export: Write the output to a file instead of the console." - puts "" - puts "Will show all syndromes affecting each units with the maximum and present duration." -end - -class Output - attr_accessor :fileLogger, :indent_level - - def initialize(filename) - indent_level = "" - if filename==nil - @fileLogger = nil - else - @fileLogger = File.new(filename + ".html", "w") - @fileLogger.puts("") - end - end - - RED = "red" - GREEN = "green" - BLUE = "blue" - DEFAULT = "black" - HIGHLIGHT = "black\" size=\"+1" - - def colorize(text, color_code) - if @fileLogger == nil - return text - else - new_text = "#{text}" - if color_code == HIGHLIGHT - new_text = "" + new_text + "" - end - - return new_text - end - end - - def inactive(text) - if @fileLogger == nil - return "###" + text - else - return "#{text}" - end - end - - def indent() - if @fileLogger == nil - @indent_level = "#{@indent_level} - " - else - @fileLogger.puts("
    ") - end - end - - def unindent() - if @fileLogger == nil - @indent_level = @indent_level.chomp(" - ") - else - @fileLogger.puts("
") - end - end - - def break() - if @fileLogger == nil - puts("\n") - else - @fileLogger.puts("

") - end - end - - def close() - if @fileLogger != nil - @fileLogger.puts("") - @fileLogger.flush - @fileLogger.close - @fileLogger = nil - end - end - - def log(text, color=nil) - if @fileLogger == nil - puts("#{@indent_level}#{text}") - elsif color==nil - @fileLogger.puts(text+"
") - elsif @indent_level == "" - @fileLogger.puts(colorize(text, color)) - else - @fileLogger.puts("
  • " + colorize(text, color)+"
  • ") - end - end -end - -def get_mental_att(att_index) - - case att_index - when 0 - return "Analytical Ability" - when 1 - return "Focus" - when 2 - return "Willpower" - when 3 - return "Creativity" - when 4 - return "Intuition" - when 5 - return "Patience" - when 6 - return "Memory" - when 7 - return "Linguistics" - when 8 - return "Spacial Sense" - when 9 - return "Musicality" - when 10 - return "Kinesthetic Sense" - when 11 - return "Empathy" - when 12 - return "Social Awareness" - else - return "Unknown" - end -end - -def get_physical_att(att_index) - - case att_index - when 0 - return "strength" - when 1 - return "agility" - when 2 - return "toughness" - when 3 - return "endurance" - when 4 - return "recuperation" - when 5 - return "disease resistance" - else - return "unknown" - end -end - -def get_effect_target(target) - - values = [] - - limit = target.key.length - 1 - for i in (0..limit) - - if(target.mode[i].to_s() != "") - - items = "" - - #case target.mode[i].to_s() - #when "BY_TYPE" - # item = "type(" - #when "BY_TOKEN" - # item = "token(" - #when "BY_CATEGORY" - # item = "category(" - #end - - if(target.key[i].to_s()!="") - item = "#{item}#{target.key[i].to_s().capitalize()}" - end - - if target.tissue[i].to_s() != "ALL" - if(target.key[i].to_s()!="" and target.tissue[i].to_s()!="") - item = "#{item}:" - end - - if(target.tissue[i].to_s()!="") - item = "#{item}#{target.tissue[i].to_s().capitalize()}" - end - end - - #item = item + ")" - - values.push(item) - end - - end - - if values.length == 0 or (values.length == 1 and values[0] == "All") - return "" - else - return ", target=" + values.join(", ") - end -end - -def get_att_pairs(values, percents, physical) - - items = [] - - color = Output::DEFAULT - - limit = values.length - 1 - for i in (0..limit) - if (values[i] != 0 or percents[i] != 100) - - if physical - item = "#{get_physical_att(i)}(" - else - item = "#{get_mental_att(i)}(" - end - - if(values[i]!=0) - item = item + "%+d" % values[i] - end - - if (values[i]!=0 and percents[i]!=100) - item = item + ", " - end - - if (percents[i]!=100 or values[i]==0) - item = item + "%d" % percents[i] + "%" - end - - item = item + ")" - - if color != Output::RED and values[i] >= 0 and percents[i] > 100 - color = Output::GREEN - elsif values[i] <0 || percents[i] < 100 - color = Output::RED - end - - items.push(item) - end - end - - return items.join(", "), color -end - -def get_display_name(name, verb) - if name != nil and name != "" - return name.capitalize() - end - - if verb == nil or verb == "" - return "Mystery" - end - - if verb.length > 100 - verb = verb.slice(0, 100).capitalize() - end - - pos = verb.index(".") - if pos == nil - return verb.slice(0, verb.rindex(" ")).capitalize() - else - return verb.slice(0, pos).capitalize() - end -end - -def get_interaction(interaction) - - # name, USAGE_HINT, range, wait period are probably all we really want to show. - - #result = "a=#{interaction.unk_6c} b=#{interaction.unk_7c} c=#{interaction.unk_8c} d=#{interaction.unk_a8} e=#{interaction.unk_c4} f=#{interaction.unk_e4} " - #result = result + "g=#{interaction.unk_e0} h=#{interaction.unk_e4} i=#{interaction.unk_100} j=#{interaction.unk_11c} k=#{interaction.unk_138} l=#{interaction.unk_154} " - #result = result + "m=#{interaction.unk_170} n=#{interaction.unk_18c} o=#{interaction.unk_1a8} p=#{interaction.unk_1c4} q=#{interaction.unk_1e8} r=#{interaction.unk_25c} " - #result = result + "s=#{interaction.unk_278}" - - if interaction.name == "" - name = "mystery" - else - name = interaction.name - end - - return "ability=#{get_display_name(interaction.name, interaction.verb[0])}, delay=#{interaction.usage_delay}, actionType=TODO, range=TODO, maxTargets=TODO" -end - -def get_effect_flags(flags) - - values = [] - - if(flags.SIZE_DELAYS) then values.push("size delays") end - if(flags.SIZE_DILUTES) then values.push("size dilutes") end - if(flags.VASCULAR_ONLY) then values.push("vascular only") end - if(flags.MUSCULAR_ONLY) then values.push("muscles only") end - if(flags.RESISTABLE) then values.push("resistable") end - if(flags.LOCALIZED) then values.push("localized") end - - return values.join(",") -end - -def get_tag1_flags(logger, flags, add) - - values = [] - - good = false - bad = false - - if add - good_color = Output::GREEN - bad_color = Output::RED - else - good_color = Output::RED - bad_color = Output::GREEN - end - - if(flags.EXTRAVISION) - values.push(logger.colorize("extravision", good_color)) - good = true - end - - if(flags.OPPOSED_TO_LIFE) - values.push(logger.colorize("attack the living", bad_color)) - bad = true - end - - if(flags.NOT_LIVING) - values.push(logger.colorize("undead", Output::DEFAULT)) - end - - if(flags.NOEXERT) - values.push(logger.colorize("does not tire", good_color)) - good = true - end - - if(flags.NOPAIN) - values.push(logger.colorize("does not feel pain", good_color)) - good = true - end - - if(flags.NOBREATHE) - values.push(logger.colorize("does not breathe", good_color)) - good = true - end - - if(flags.HAS_BLOOD) - values.push(logger.colorize("has blood", Output::DEFAULT)) - end - - if(flags.NOSTUN) - values.push(logger.colorize("can't be stunned", good_color)) - good = true - end - - if(flags.NONAUSEA) - values.push(logger.colorize("does not get nausea", good_color)) - good = true - end - - if(flags.NO_DIZZINESS) - values.push(logger.colorize("does not get dizzy", good_color)) - good = true - end - - if(flags.NO_FEVERS) - values.push(logger.colorize("does not get fever", good_color)) - good = true - end - - if(flags.TRANCES) - values.push(logger.colorize("can enter trance", good_color)) - good = true - end - - if(flags.NOEMOTION) - values.push(logger.colorize("feels no emotion", good_color)) - good = true - end - - if(flags.LIKES_FIGHTING) - values.push(logger.colorize("like fighting", Output::DEFAULT)) - end - - if(flags.PARALYZEIMMUNE) - values.push(logger.colorize("can't be paralyzed", good_color)) - good = true - end - if(flags.NOFEAR) - values.push(logger.colorize("does not feel fear", good_color)) - good = true - end - - if(flags.NO_EAT) - values.push(logger.colorize("does not eat", good_color)) - good = true - end - - if(flags.NO_DRINK) - values.push(logger.colorize("does not drink", good_color)) - good = true - end - - if(flags.NO_SLEEP) - values.push(logger.colorize("does not sleep", good_color)) - good = true - end - if(flags.MISCHIEVOUS) - values.push(logger.colorize("mischievous", Output::DEFAULT)) - end - - if(flags.NO_PHYS_ATT_GAIN) - values.push(logger.colorize("physical stats cant improve", good_color)) - good = true - end - - if(flags.NO_PHYS_ATT_RUST) - values.push(logger.colorize("physical stats do not rust", good_color)) - good = true - end - - if(flags.NOTHOUGHT) - values.push(logger.colorize("stupid", bad_color)) - bad = true - end - - if(flags.NO_THOUGHT_CENTER_FOR_MOVEMENT) - values.push(logger.colorize("no brain needed to move", good_color)) - good = true - end - - if(flags.CAN_SPEAK) - values.push(logger.colorize("can speak", good_color)) - good = true - end - - if(flags.CAN_LEARN) - values.push(logger.colorize("can learn", good_color)) - good = true - end - - if(flags.UTTERANCES) - values.push(logger.colorize("utterances", Output::DEFAULT)) - end - - if(flags.CRAZED) - values.push(logger.colorize("crazed", bad_color)) - bad = true - end - - if(flags.BLOODSUCKER) - values.push(logger.colorize("drinks blood", bad_color)) - bad = true - end - - if(flags.NO_CONNECTIONS_FOR_MOVEMENT) - values.push(logger.colorize("can move without nerves", good_color)) - good = true - end - - if(flags.SUPERNATURAL) - values.push(logger.colorize("supernatural", good_color)) - good = true - end - - if add - if bad - color = Output::RED - elsif good - color = Output::GREEN - else - color = Output::DEFAULT - end - else - if good - color = Output::RED - elsif bad - color = Output::GREEN - else - color = Output::DEFAULT - end - end - - return values.join(", "), color -end - -def get_tag2_flags(logger, flags, add) - values = [] - - good = false - bad = false - - if add - good_color = Output::GREEN - bad_color = Output::RED - else - good_color = Output::RED - bad_color = Output::GREEN - end - - if(flags.NO_AGING) - good = true - values.push(logger.colorize("does not age", good_color)) - end - - if(flags.MORTAL) - bad = true - values.push(logger.colorize("mortal", bad_color)) - end - - if(flags.STERILE) - values.push(logger.colorize("can't have children", Output::DEFAULT)) - end - - if(flags.FIT_FOR_ANIMATION) - values.push(logger.colorize("can be animated", Output::DEFAULT)) - end - - if(flags.FIT_FOR_RESURRECTION) - good = true - values.push(logger.colorize("can be resurrected", Output::DEFAULT)) - end - - if add - if bad - color = Output::RED - elsif good - color = Output::GREEN - else - color = Output::DEFAULT - end - else - if good - color = Output::RED - elsif bad - color = Output::GREEN - else - color = Output::DEFAULT - end - end - - return values.join(", "), color -end - -def find_creature_name(id, casteid) - creature = df.world.raws.creatures.all.find{ |c| c.creature_id == id } - - if creature == nil - return id, casteid - end - - creature_name = creature.name[0].capitalize() - - if casteid == "DEFAULT" - return creature_name, "" - end - - caste = creature.caste.find{ |c| c.caste_id == casteid } - - if caste == nil - return creature_name, casteid - elsif creature.name[0].downcase() == caste.caste_name[0].downcase() - return creature_name, "" - else - castename = caste.caste_name[0].downcase().chomp(creature.name[0].downcase()).strip() - - if castename.start_with?(creature.name[0]) - castename = castename.slice(creature.name[0].length, castename.length - creature.name[0].length).strip() - end - - if castename.start_with?("of the") - castename = castename.slice("of the".length, castename.length - "of the".length).strip() - end - - return creature_name, castename.downcase() - end -end - -def get_effect(logger, ce, ticks, showdisplayeffects) - - flags = get_effect_flags(ce.flags) - if flags != "" - flags = " (#{flags})" - end - - if ce.end == -1 - duration = " [permanent]" - elsif ce.start >= ce.peak or ce.peak <= 1 - duration = " [#{ce.start}-#{ce.end}]" - else - duration = " [#{ce.start}-#{ce.peak}-#{ce.end}]" - end - - case ce.getType().to_s() - when "PAIN" - name = "Pain" - desc = "power=#{ce.sev}#{get_effect_target(ce.target)}" - color = Output::RED - when "SWELLING" - name = "Swelling" - desc = "power=#{ce.sev}#{get_effect_target(ce.target)}" - color = Output::RED - when "OOZING" - name = "Oozing" - desc = "power=#{ce.sev}#{get_effect_target(ce.target)}" - color = Output::RED - when "BRUISING" - name = "Bruising" - desc = "power=#{ce.sev}#{get_effect_target(ce.target)}" - color = Output::RED - when "BLISTERS" - name = "Blisters" - desc = "power=#{ce.sev}#{get_effect_target(ce.target)}" - color = Output::RED - when "NUMBNESS" - name = "Numbness" - desc = "power=#{ce.sev}#{get_effect_target(ce.target)}" - color = Output::GREEN - when "PARALYSIS" - name = "Paralysis" - desc = "power=#{ce.sev}#{get_effect_target(ce.target)}" - color = Output::RED - when "FEVER" - name = "Fever" - desc = "power=#{ce.sev}" - color = Output::RED - when "BLEEDING" - name = "Bleeding" - desc = "power=#{ce.sev}#{get_effect_target(ce.target)}" - color = Output::RED - when "COUGH_BLOOD" - name = "Cough Blood" - desc = "power=#{ce.sev}" - color = Output::RED - when "VOMIT_BLOOD" - name = "Vomit Blood" - desc = "power=#{ce.sev}" - color = Output::RED - when "NAUSEA" - name = "Nausea" - desc = "power=#{ce.sev}" - color = Output::RED - when "UNCONSCIOUSNESS" - name = "Unconsciousness" - desc = "power=#{ce.sev}" - color = Output::RED - when "NECROSIS" - name = "Necrosis" - desc = "power=#{ce.sev}#{get_effect_target(ce.target)}" - color = Output::RED - when "IMPAIR_FUNCTION" - name = "Impairs" - desc = "power=#{ce.sev}#{get_effect_target(ce.target)}" - color = Output::RED - when "DROWSINESS" - name = "Drowsiness" - desc = "power=#{ce.sev}" - color = Output::RED - when "DIZZINESS" - name = "Dizziness" - desc = "power=#{ce.sev}" - color = Output::RED - when "ADD_TAG" - name = "Add" - tags1 = get_tag1_flags(logger, ce.tags1, true) - tags2 = get_tag2_flags(logger, ce.tags2, true) - desc = "#{tags1[0]},#{tags2[0]}" - - if tags1[1] == Output::RED || tags2[1] == Output::RED - color = Output::RED - elsif tags1[1] == Output::GREEN || tags2[1] == Output::GREEN - color = Output::GREEN - else - color = Output::DEFAULT - end - when "REMOVE_TAG" - name = "Remove" - tags1 = get_tag1_flags(logger, ce.tags1, true) - tags2 = get_tag2_flags(logger, ce.tags2, true) - desc = "#{tags1[0]},#{tags2[0]}" - - if tags1[1] == Output::RED || tags2[1] == Output::RED - color = Output::RED - elsif tags1[1] == Output::GREEN || tags2[1] == Output::GREEN - color = Output::GREEN - else - color = Output::DEFAULT - end - when "DISPLAY_TILE" - if !showdisplayeffects then return "", Output::DEFAULT end - name = "Tile" - desc = "Tile=#{ce.tile}, Colour=#{ce.color}" - color = Output::DEFAULT - when "FLASH_TILE" - if !showdisplayeffects then return "", Output::DEFAULT end - name = "Flash" - color = ce.sym_color >> 8 - tile = ce.sym_color - (color * 256) - desc = "tile = #{tile}, colour=#{color}, time=#{ce.period}, period=#{ce.time}" - color = Output::DEFAULT - when "SPEED_CHANGE" - name = "Physical" - desc = "speed(" - - value = ce.bonus_add - percent = ce.bonus_perc - if(value!=0) - desc = desc + "%+d" % value - end - - if (value!=0 and percent!=100) - desc = desc + ", " - end - - if (percent!=100 or value==0) - desc = desc + "%d" % percent + "%" - end - - desc = desc + ")" - - if value < 0 or percent < 100 - color = Output::RED - elsif value >0 or percent >100 - color = Output::GREEN - else - color = Output::DEFAULT - end - - when "CAN_DO_INTERACTION" - name = "Add interaction" - desc = "#{get_interaction(ce)}" - color = Output::GREEN - when "SKILL_ROLL_ADJUST" - name = "Skill check" - desc = "modifier=#{ce.multiplier}%, chance=#{ce.chance}%" - - if ce.multiplier > 100 - color = Output::GREEN - elsif ce.multiplier < 100 - color = Output::RED - else - color = Output::DEFAULT - end - - when "BODY_TRANSFORMATION" - name = "Transformation" - - if ce.chance > 0 - chance = ", chance=#{ce.chance} " - else - chance = "" - end - - creature_name = find_creature_name(ce.race_str, ce.caste_str) - - if creature_name[1] == "" - desc = "#{creature_name[0]}#{chance}" - else - desc = "#{creature_name[0]}(#{creature_name[1]})#{chance}" - end - - color = Output::BLUE - when "PHYS_ATT_CHANGE" - name = "Physical" - data = get_att_pairs(ce.phys_att_add, ce.phys_att_perc, true) - desc = data[0] - color = data[1] - when "MENT_ATT_CHANGE" - name = "Mental" - data = get_att_pairs(ce.ment_att_add, ce.ment_att_perc, false) - desc = data[0] - color = data[1] - when "MATERIAL_FORCE_MULTIPLIER" - name = "Material force multiplier" - desc = "received damage scaled by #{(ce.fraction_mul * 100 / ce.fraction_div * 100)/100}%" - if ce.fraction_div > ce.fraction_mul - color = Output::GREEN - elsif ce.fraction_div < ce.fraction_mul - color = Output::RED - else - color = Output::DEFAULT - end - - if ce.mat_index >=0 - mat = df.decode_mat(ce.mat_type, ce.mat_index ) - elsif ce.mat_type >= 0 - mat = df.decode_mat(ce.mat_type, 0 ) - else - mat = nil - end - - if mat!= nil - token = mat.token - if token.start_with?("INORGANIC:") - token = token.slice("INORGANIC:".length, token.length - "INORGANIC:".length) - end - - desc = "#{desc} vs #{token.capitalize()}" - end - - when "BODY_MAT_INTERACTION" - # interactionId, SundromeTriggerType - name = "Body material interaction" - desc = "a???=#{ce.unk_6c}, b???=#{ce.unk_88}, c???=#{ce.unk_8c}, d???=#{ce.unk_90}, e???=#{ce.unk_94}" - color = Output::DEFAULT - when "BODY_APPEARANCE_MODIFIER" - if !showdisplayeffects then return "", Output::DEFAULT end - # !!! seems to be missing info class !!! - # should be enum and value - name = "Body Appearance" - desc = "" - color = Output::DEFAULT - when "BP_APPEARANCE_MODIFIER" - if !showdisplayeffects then return "", Output::DEFAULT end - name = "Body part appearance" - desc = "value=#{ce.value} change_type_enum?=#{ce.unk_6c}#{get_effect_target(ce.target)}" - color = Output::DEFAULT - when "DISPLAY_NAME" - if !showdisplayeffects then return "", Output::DEFAULT end - name = "Set display name" - desc = "#{ce.name}" - color = Output::DEFAULT - else - name = "Unknown" - color = Output::HIGHLIGHT - end - - text = "#{name}#{duration}#{flags} #{desc}" - if ticks > 0 and ((ce.start > 0 and ticks < ce.start) or (ce.end > 0 and ticks > ce.end)) - text = logger.inactive(text) - end - - return text, color -end - -print_syndrome = lambda { |logger, syndrome, showeffects, showdisplayeffects| - rawsyndrome = df.world.raws.syndromes.all[syndrome.type] - duration = rawsyndrome.ce.minmax_by{ |ce| ce.end } - - if duration[0].end == -1 - durationStr = "permanent" - else - if duration[0].end == duration[1].end - durationStr = "#{syndrome.ticks} of #{duration[0].end}" - else - durationStr = "#{syndrome.ticks} of #{duration[0].end}-#{duration[1].end}" - end - end - - effects = rawsyndrome.ce.collect { |effect| get_effect(logger, effect, syndrome.ticks, showdisplayeffects) } - - if effects.any?{ |text, color| color==Output::RED } - color = Output::RED - elsif effects.any?{|text, color| color==Output::GREEN } - color = Output::GREEN - else - color = Output::DEFAULT - end - - logger.indent() - logger.log "#{get_display_name(rawsyndrome.syn_name, "")} [#{durationStr}]", color - - if showeffects - logger.indent() - effects.each{ |text, color| if text!="" then logger.log text, color end } - logger.unindent() - end - logger.unindent() -} - -print_raw_syndrome = lambda { |logger, rawsyndrome, showeffects, showdisplayeffects| - - effects = rawsyndrome.ce.collect { |effect| get_effect(logger, effect, -1, showdisplayeffects) } - - if effects.any?{ |item| item[1]==Output::RED } - color = Output::RED - elsif effects.any?{|item| item[1]==Output::GREEN } - color = Output::GREEN - else - color = Output::DEFAULT - end - - logger.indent() - logger.log get_display_name(rawsyndrome.syn_name, ""), color - - if showeffects - logger.indent() - effects.each{ |text, color| if text!="" then logger.log text, color end } - logger.unindent() - end - logger.unindent() -} - -print_syndromes = lambda { |logger, unit, showrace, showall, showeffects, showhiddencurse, showdisplayeffects| - - if showhiddencurse - syndromes = unit.syndromes.active - else - syndromes = unit.syndromes.active - # TODO: syndromes = unit.syndromes.active.select{ |s| visible_syndrome?(unit, s) } - end - - if !syndromes.empty? or showall - if showrace - logger.log "#{df.world.raws.creatures.all[unit.race].name[0]}#{unit.name == '' ? "" : ": "}#{unit.name}", Output::HIGHLIGHT - else - logger.log "#{unit.name}", Output::HIGHLIGHT - end - end - - syndromes.each { |syndrome| print_syndrome[logger, syndrome, showeffects, showdisplayeffects] } -} - -def starts_with?(str, prefix) - prefix = prefix.to_s - str[0, prefix.length] == prefix -end - -showall = false -showeffects = false -selected = false -dwarves = false -livestock = false -wildanimals = false -hostile = false -world = false -showhiddencurse = false -showdisplayeffects = false - -if $script_args.any?{ |arg| arg == "help" or arg == "?" or arg == "-?" } - print_help() -elsif $script_args.empty? - dwarves = true - showeffects = true -else - if $script_args.any?{ |arg| arg == "showall" } then showall=true end - if $script_args.any?{ |arg| arg == "showeffects" } then showeffects=true end - if $script_args.any?{ |arg| arg == "ignorehiddencurse" } then showhiddencurse=true end - if $script_args.any?{ |arg| arg == "showdisplayeffects" } then showdisplayeffects=true end - if $script_args.any?{ |arg| arg == "selected" } then selected=true end - if $script_args.any?{ |arg| arg == "dwarves" } then dwarves=true end - if $script_args.any?{ |arg| arg == "livestock" } then livestock=true end - if $script_args.any?{ |arg| arg == "wildanimals" } then wildanimals=true end - if $script_args.any?{ |arg| arg == "hostile" } then hostile=true end - if $script_args.any?{ |arg| arg == "world" } then world=true end - if $script_args.any?{ |arg| starts_with?(arg, "export:") } - exportfile = $script_args.find{ |arg| starts_with?(arg, "export:") }.gsub("export:", "") - export=true - end -end - -if export - logger = Output.new(exportfile) -else - logger = Output.new(nil) -end - -if selected - print_syndromes[logger, df.unit_find(), true, showall, showeffects, showhiddencurse, showdisplayeffects] - logger.break() -end - -if dwarves - logger.log "Dwarves", Output::HIGHLIGHT - df.unit_citizens.each { |unit| - print_syndromes[logger, unit, false, showall, showeffects, showhiddencurse, showdisplayeffects] - } - logger.break() -end - -if livestock - logger.log "Livestock", Output::HIGHLIGHT - df.world.units.active.find_all { |u| df.unit_category(u) == :Livestock }.each { |unit| - print_syndromes[logger, unit, true, showall, showeffects, showhiddencurse, showdisplayeffects] - } - logger.break() -end - -if wildanimals - logger.log "Wild animals", Output::HIGHLIGHT - df.world.units.active.find_all { |u| df.unit_category(u) == :Other and df.unit_other_category(u) == :Wild }.each { |unit| - print_syndromes[logger, unit, true, showall, showeffects, showhiddencurse, showdisplayeffects] - } - logger.break() -end - -if hostile - logger.log "Hostile units", Output::HIGHLIGHT - df.unit_hostiles.each { |unit| - print_syndromes[logger, unit, true, showall, showeffects, showhiddencurse, showdisplayeffects] - } - logger.break() -end - -if world - logger.log "All syndromes", Output::HIGHLIGHT - df.world.raws.syndromes.all.each { |syndrome| print_raw_syndrome[logger, syndrome, showeffects, showdisplayeffects] } -end - -logger.close() diff --git a/scripts/siren.lua b/scripts/siren.lua deleted file mode 100644 index 5bf821141..000000000 --- a/scripts/siren.lua +++ /dev/null @@ -1,136 +0,0 @@ --- Wakes up the sleeping, ends breaks and parties ---[[=begin - -siren -===== -Wakes up sleeping units, cancels breaks and stops parties either everywhere, -or in the burrows given as arguments. In return, adds bad thoughts about -noise, tiredness and lack of protection. Also, the units with interrupted -breaks will go on break again a lot sooner. The script is intended for -emergencies, e.g. when a siege appears, and all your military is partying. - -=end]] - -local utils = require 'utils' - -local args = {...} -local burrows = {} -local bnames = {} - -if not dfhack.isMapLoaded() then - qerror('Map is not loaded.') -end - -for _,v in ipairs(args) do - local b = dfhack.burrows.findByName(v) - if not b then - qerror('Unknown burrow: '..v) - end - table.insert(bnames, v) - table.insert(burrows, b) -end - -local in_siege = false - -function is_in_burrows(pos) - if #burrows == 0 then - return true - end - for _,v in ipairs(burrows) do - if dfhack.burrows.isAssignedTile(v, pos) then - return true - end - end -end - -function add_thought(unit, emotion, thought) - unit.status.current_soul.personality.emotions:insert('#', { new = true, - type = emotion, - unk2=1, - strength=1, - thought=thought, - subthought=0, - severity=0, - flags=0, - unk7=0, - year=df.global.cur_year, - year_tick=df.global.cur_year_tick}) -end - -function wake_unit(unit) - local job = unit.job.current_job - if not job or job.job_type ~= df.job_type.Sleep then - return - end - - if job.completion_timer > 0 then - unit.counters.unconscious = 0 - add_thought(unit, df.emotion_type.Grouchiness, df.unit_thought_type.Drowsy) - elseif job.completion_timer < 0 then - add_thought(unit, df.emotion_type.Grumpiness, df.unit_thought_type.Drowsy) - end - - job.pos:assign(unit.pos) - - job.completion_timer = 0 - - unit.path.dest:assign(unit.pos) - unit.path.path.x:resize(0) - unit.path.path.y:resize(0) - unit.path.path.z:resize(0) - - unit.counters.job_counter = 0 -end - -function stop_break(unit) - local counter = dfhack.units.getMiscTrait(unit, df.misc_trait_type.OnBreak) - if counter then - counter.id = df.misc_trait_type.TimeSinceBreak - counter.value = 100800 - 30*1200 - add_thought(unit, df.emotion_type.Grumpiness, df.unit_thought_type.Drowsy) - end -end - --- Check siege for thought purpose -for _,v in ipairs(df.global.ui.invasions.list) do - if v.flags.siege and v.flags.active then - in_siege = true - break - end -end - --- Stop rest -for _,v in ipairs(df.global.world.units.active) do - local x,y,z = dfhack.units.getPosition(v) - if x and dfhack.units.isCitizen(v) and is_in_burrows(xyz2pos(x,y,z)) then - if not in_siege and v.military.squad_id < 0 then - add_thought(v, df.emotion_type.Nervousness, df.unit_thought_type.LackProtection) - end - wake_unit(v) - stop_break(v) - end -end - --- Stop parties -for _,v in ipairs(df.global.ui.parties) do - local pos = utils.getBuildingCenter(v.location) - if is_in_burrows(pos) then - v.timer = 0 - for _, u in ipairs(v.units) do - add_thought(unit, df.emotion_type.Grumpiness, df.unit_thought_type.Drowsy) - end - end -end - -local place = 'the halls and tunnels' -if #bnames > 0 then - if #bnames == 1 then - place = bnames[1] - else - place = table.concat(bnames,', ',1,#bnames-1)..' and '..bnames[#bnames] - end -end -dfhack.gui.showAnnouncement( - 'A loud siren sounds throughout '..place..', waking the sleeping and startling the awake.', - COLOR_BROWN, true -) diff --git a/scripts/source.rb b/scripts/source.rb deleted file mode 100644 index e9d7a64f3..000000000 --- a/scripts/source.rb +++ /dev/null @@ -1,114 +0,0 @@ -# create an infinite source/drain of magma/water -=begin - -source -====== -Create an infinite magma or water source or drain on a tile. -For more complex commands, try the `liquids` plugin. - -This script registers a map tile as a liquid source, and every 12 game ticks -that tile receives or remove 1 new unit of flow based on the configuration. - -Place the game cursor where you want to create the source (must be a -flow-passable tile, and not too high in the sky) and call:: - - source add [magma|water] [0-7] - -The number argument is the target liquid level (0 = drain, 7 = source). - -To add more than 1 unit everytime, call the command again on the same spot. - -To delete one source, place the cursor over its tile and use ``source delete``. -To remove all existing sources, call ``source clear``. - -The ``list`` argument shows all existing sources. - -Examples:: - - source add water - water source - source add magma 7 - magma source - source add water 0 - water drain - -=end - -$sources ||= [] - -cur_source = { - :liquid => 'water', - :amount => 7, - :pos => [df.cursor.x, df.cursor.y, df.cursor.z] -} -cmd = 'help' - -$script_args.each { |a| - case a.downcase - when 'water', 'magma' - cur_source[:liquid] = a.downcase - when /^\d+$/ - cur_source[:amount] = a.to_i - when 'add', 'del', 'delete', 'clear', 'help', 'list' - cmd = a.downcase - else - puts "source: unhandled argument #{a}" - end -} - -case cmd -when 'add' - $sources_onupdate ||= df.onupdate_register('sources', 12) { - # called every 12 game ticks (100x a dwarf day) - $sources.each { |s| - if tile = df.map_tile_at(*s[:pos]) and tile.shape_passableflow - # XXX does not check current liquid_type - des = tile.designation - cur = des.flow_size - if cur != s[:amount] - tile.spawn_liquid((cur > s[:amount] ? cur-1 : cur+1), s[:liquid] == 'magma') - end - end - } - if $sources.empty? - df.onupdate_unregister($sources_onupdate) - $sources_onupdate = nil - end - } - - if cur_source[:pos][0] >= 0 - if tile = df.map_tile_at(*cur_source[:pos]) - if tile.shape_passableflow - $sources << cur_source - else - puts "Impassable tile: I'm afraid I can't do that, Dave" - end - else - puts "Unallocated map block - build something here first" - end - else - puts "Please put the game cursor where you want a source" - end - -when 'del', 'delete' - $sources.delete_if { |s| s[:pos] == cur_source[:pos] } - -when 'clear' - $sources.clear - -when 'list' - puts "Source list:", $sources.map { |s| - " #{s[:pos].inspect} #{s[:liquid]} #{s[:amount]}" - } - puts "Current cursor pos: #{[df.cursor.x, df.cursor.y, df.cursor.z].inspect}" if df.cursor.x >= 0 - -else - puts <= 3 and args[3]:sub(1, 1) ~= '-' then - extend(new_args, {'-nick', args[3]}) - start = 4 - end - for i = start, #args do - table.insert(new_args, args[i]) - end -end -if show_command then - print('modtools/create-unit ' .. table.concat(new_args, ' ')) - return -end -dfhack.run_script('modtools/create-unit', table.unpack(new_args)) diff --git a/scripts/startdwarf.rb b/scripts/startdwarf.rb deleted file mode 100644 index a592d5391..000000000 --- a/scripts/startdwarf.rb +++ /dev/null @@ -1,20 +0,0 @@ -# patch start dwarf count -=begin - -startdwarf -========== -Use at the embark screen to embark with the specified number of dwarves. Eg. -``startdwarf 500`` would lead to a severe food shortage and FPS issues, while -``startdwarf 10`` would just allow a few more warm bodies to dig in. -The number must be 7 or greater. - -=end - -nr = $script_args[0].to_i - -raise 'too low' if nr < 7 - -addr = df.get_global_address('start_dwarf_count') -raise 'patch address not available' if addr == 0 -df.memory_patch(addr, [nr].pack('L')) - diff --git a/scripts/starvingdead.rb b/scripts/starvingdead.rb deleted file mode 100644 index 7ee985a7b..000000000 --- a/scripts/starvingdead.rb +++ /dev/null @@ -1,92 +0,0 @@ -# Weaken and eventually destroy undead over time -=begin - -starvingdead -============ -Somewhere between a "mod" and a "fps booster", with a small impact on -vanilla gameplay. It mostly helps prevent undead cascades in the caverns, -where constant combat leads to hundreds of undead roaming the -caverns and destroying your FPS. - -With this script running, all undead that have been on the map for -one month gradually decay, losing strength, speed, and toughness. -After six months, they collapse upon themselves, never to be reanimated. - -Usage: ``starvingdead (start|stop)`` - -=end - -class StarvingDead - - def initialize - @threshold = 1 - @die_threshold = 6 - end - - def process - return false unless @running - month_length = 67200 - if (@undead_count >= 25) - month_length *= 25 / @undead_count - end - - @undead_count = 0 - df.world.units.active.each { |u| - if (u.enemy.undead and not u.flags1.dead) - @undead_count += 1 - if (u.curse.time_on_site > month_length * @threshold) - u.body.physical_attrs.each { |att| - att.value = att.value - (att.value * 0.02) - } - end - - if (u.curse.time_on_site > month_length * @die_threshold) - u.flags1.dead = true - u.curse.rem_tags2.FIT_FOR_ANIMATION = true - end - end - } - end - - def start - @onupdate = df.onupdate_register('starvingdead', 1200, 1200) { process } - @running = true - @undead_count = 0 - - if ($script_args[1] and $script_args[1].gsub(/[^0-9\.]/,'').to_f > 0) - @threshold = $script_args[1].gsub(/[^0-9\.]/,'').to_f - end - - if ($script_args[2] and $script_args[2].gsub(/[^0-9\.]/,'').to_f > 0) - @die_threshold = $script_args[2].gsub(/[^0-9\.]/,'').to_f - end - - puts "Starving Dead starting...weakness starts at #{@threshold} months, true death at #{@die_threshold} months" - end - - def stop - df.onupdate_unregister(@onupdate) - @running = false - end - def status - @running ? 'Running.' : 'Stopped.' - end -end - -case $script_args[0] -when 'start' - if ($StarvingDead) - $StarvingDead.stop - end - $StarvingDead = StarvingDead.new - $StarvingDead.start - -when 'end', 'stop' - $StarvingDead.stop -else - if $StarvingDead - puts $StarvingDead.status - else - puts 'Not loaded.' - end -end diff --git a/scripts/stripcaged.rb b/scripts/stripcaged.rb deleted file mode 100644 index 8cb1a0fa4..000000000 --- a/scripts/stripcaged.rb +++ /dev/null @@ -1,227 +0,0 @@ -# mark stuff inside of cages for dumping. -=begin - -stripcaged -========== -For dumping items inside cages. Will mark selected items for dumping, then -a dwarf may come and actually dump them (or you can use `autodump`). - -Arguments: - -:list: display the list of all cages and their item content on the console -:items: dump items in the cage, excluding stuff worn by caged creatures -:weapons: dump equipped weapons -:armor: dump everything worn by caged creatures (including armor and clothing) -:all: dump everything in the cage, on a creature or not - -Without further arguments, all commands work on all cages and animal traps on -the map. With the ``here`` argument, considers only the in-game selected cage -(or the cage under the game cursor). To target only specific cages, you can -alternatively pass cage IDs as arguments:: - - stripcaged weapons 25321 34228 - -=end - -def plural(nr, name) - # '1 cage' / '4 cages' - "#{nr} #{name}#{'s' if nr > 1}" -end - -def cage_dump_items(list) - count = 0 - count_cage = 0 - list.each { |cage| - pre_count = count - cage.general_refs.each { |ref| - next unless ref.kind_of?(DFHack::GeneralRefContainsItemst) - next if ref.item_tg.flags.dump - count += 1 - ref.item_tg.flags.dump = true - } - count_cage += 1 if pre_count != count - } - - puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}" -end - -def cage_dump_armor(list) - count = 0 - count_cage = 0 - list.each { |cage| - pre_count = count - cage.general_refs.each { |ref| - next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst) - ref.unit_tg.inventory.each { |it| - next if it.mode != :Worn - next if it.item.flags.dump - count += 1 - it.item.flags.dump = true - } - } - count_cage += 1 if pre_count != count - } - - puts "Dumped #{plural(count, 'armor piece')} in #{plural(count_cage, 'cage')}" -end - -def cage_dump_weapons(list) - count = 0 - count_cage = 0 - list.each { |cage| - pre_count = count - cage.general_refs.each { |ref| - next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst) - ref.unit_tg.inventory.each { |it| - next if it.mode != :Weapon - next if it.item.flags.dump - count += 1 - it.item.flags.dump = true - } - } - count_cage += 1 if pre_count != count - } - - puts "Dumped #{plural(count, 'weapon')} in #{plural(count_cage, 'cage')}" -end - -def cage_dump_all(list) - count = 0 - count_cage = 0 - list.each { |cage| - pre_count = count - cage.general_refs.each { |ref| - case ref - when DFHack::GeneralRefContainsItemst - next if ref.item_tg.flags.dump - count += 1 - ref.item_tg.flags.dump = true - when DFHack::GeneralRefContainsUnitst - ref.unit_tg.inventory.each { |it| - next if it.item.flags.dump - count += 1 - it.item.flags.dump = true - } - end - } - count_cage += 1 if pre_count != count - } - - puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}" -end - - -def cage_dump_list(list) - count_total = Hash.new(0) - empty_cages = 0 - list.each { |cage| - count = Hash.new(0) - - cage.general_refs.each { |ref| - case ref - when DFHack::GeneralRefContainsItemst - count[ref.item_tg._rtti_classname] += 1 - when DFHack::GeneralRefContainsUnitst - ref.unit_tg.inventory.each { |it| - count[it.item._rtti_classname] += 1 - } - # TODO vermin ? - else - puts "unhandled ref #{ref.inspect}" if $DEBUG - end - } - - type = case cage - when DFHack::ItemCagest; 'Cage' - when DFHack::ItemAnimaltrapst; 'Animal trap' - else cage._rtti_classname - end - - if count.empty? - empty_cages += 1 - else - puts "#{type} ##{cage.id}: ", count.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" } - end - - count.each { |k, v| count_total[k] += v } - } - - if list.length > 2 - puts '', "Total: ", count_total.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" } - puts "with #{plural(empty_cages, 'empty cage')}" - end -end - - -# handle magic script arguments -here_only = $script_args.delete 'here' -if here_only - it = df.item_find - list = [it] - if not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst) - list = df.world.items.other[:ANY_CAGE_OR_TRAP].find_all { |i| df.at_cursor?(i) } - end - if list.empty? - puts 'Please select a cage' - throw :script_finished - end - -elsif ids = $script_args.find_all { |arg| arg =~ /^\d+$/ } and ids.first - list = [] - ids.each { |id| - $script_args.delete id - if not it = df.item_find(id.to_i) - puts "Invalid item id #{id}" - elsif not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst) - puts "Item ##{id} is not a cage" - list << it - else - list << it - end - } - if list.empty? - puts 'Please use a valid cage id' - throw :script_finished - end - -else - list = df.world.items.other[:ANY_CAGE_OR_TRAP] -end - - -# act -case $script_args[0] -when /^it/i - cage_dump_items(list) -when /^arm/i - cage_dump_armor(list) -when /^wea/i - cage_dump_weapons(list) -when 'all' - cage_dump_all(list) -when 'list' - cage_dump_list(list) -else - puts < 10000 - u.counters2.sleepiness_timer = 1 - end - - # no break - if b = u.status.misc_traits.find { |t| t.id == :OnBreak } - b.value = 500_000 - end - else - $superdwarf_ids.delete id - end - } - end - } - else - puts "Select a creature using 'v'" - end - -when 'del' - if u = df.unit_find - $superdwarf_ids.delete u.id - else - puts "Select a creature using 'v'" - end - -when 'clear' - $superdwarf_ids.clear - -when 'list' - puts "current superdwarves:", $superdwarf_ids.map { |id| df.unit_find(id).name } - -else - puts "Usage:", - " - superdwarf add: give superspeed to currently selected creature", - " - superdwarf del: remove superspeed to current creature", - " - superdwarf clear: remove all superpowers", - " - superdwarf list: list super-dwarves" -end diff --git a/scripts/teleport.lua b/scripts/teleport.lua deleted file mode 100644 index 8c657061c..000000000 --- a/scripts/teleport.lua +++ /dev/null @@ -1,58 +0,0 @@ --- teleports a unit to a location --- author Putnam --- edited by expwnent ---[[=begin - -teleport -======== -Teleports a unit to given coordinates. Examples: - -:teleport -showunitid: prints unitid beneath cursor -:teleport -showpos: prints coordinates beneath cursor -:teleport -unit 1234 -x 56 -y 115 -z 26: - teleports unit 1234 to 56,115,26 - -=end]] - -function teleport(unit,pos) - local unitoccupancy = dfhack.maps.getTileBlock(unit.pos).occupancy[unit.pos.x%16][unit.pos.y%16] - local newoccupancy = dfhack.maps.getTileBlock(pos).occupancy[pos.x%16][pos.y%16] - if newoccupancy.unit then - unit.flags1.on_ground=true - end - unit.pos.x = pos.x - unit.pos.y = pos.y - unit.pos.z = pos.z - if not unit.flags1.on_ground then unitoccupancy.unit = false else unitoccupancy.unit_grounded = false end -end - -utils = require('utils') - -validArgs = validArgs or utils.invert({ - 'unit', - 'x', - 'y', - 'z', - 'showunitid', - 'showpos' -}) - -if moduleMode then - return -end - -local args = utils.processArgs({...}, validArgs) - -if args.showunitid or args.showpos then - if args.showunitid then - print(dfhack.gui.getSelectedUnit(true).id) - else - printall(df.global.cursor) - end -else - local unit = args.unit and df.unit.find(args.unit) or dfhack.gui.getSelectedUnit(true) - local pos = not(not args.x or not args.y or not args.z) and {x=args.x,y=args.y,z=args.z} or {x=df.global.cursor.x,y=df.global.cursor.y,z=df.global.cursor.z} - if not unit then qerror('A unit needs to be selected or specified. Use teleport -showunitid to get a unit\'s ID.') end - if not pos.x or pos.x==-30000 then qerror('A position needs to be highlighted or specified. Use teleport -showpos to get a position\'s exact xyz values.') end - teleport(unit,pos) -end diff --git a/scripts/tidlers.lua b/scripts/tidlers.lua deleted file mode 100644 index 7df509139..000000000 --- a/scripts/tidlers.lua +++ /dev/null @@ -1,10 +0,0 @@ ---Toggle idlers count ---[[=begin - -tidlers -======= -Toggle between all possible positions where the idlers count can be placed. - -=end]] -df.global.d_init.idlers = df.d_init_idlers:next_item(df.global.d_init.idlers) -print('Toggled the display of idlers.') diff --git a/scripts/twaterlvl.lua b/scripts/twaterlvl.lua deleted file mode 100644 index 50f487686..000000000 --- a/scripts/twaterlvl.lua +++ /dev/null @@ -1,11 +0,0 @@ --- Toggle display of water depth ---[[=begin - -twaterlvl -========= -Toggle between displaying/not displaying liquid depth as numbers. - -=end]] - -df.global.d_init.flags1.SHOW_FLOW_AMOUNTS = not df.global.d_init.flags1.SHOW_FLOW_AMOUNTS -print('Water level display toggled.') diff --git a/scripts/undump-buildings.lua b/scripts/undump-buildings.lua deleted file mode 100644 index f4cfe7909..000000000 --- a/scripts/undump-buildings.lua +++ /dev/null @@ -1,36 +0,0 @@ --- Undesignates building base materials for dumping. ---[[=begin - -undump-buildings -================ -Undesignates building base materials for dumping. - -=end]] - -function undump_buildings() - local buildings = df.global.world.buildings.all - local undumped = 0 - for i = 0, #buildings - 1 do - local building = buildings[i] - -- Zones and stockpiles don't have the contained_items field. - if df.building_actual:is_instance(building) then - local items = building.contained_items - for j = 0, #items - 1 do - local contained = items[j] - if contained.use_mode == 2 and contained.item.flags.dump then - -- print(building, contained.item) - undumped = undumped + 1 - contained.item.flags.dump = false - end - end - end - end - - if undumped > 0 then - local s = "s" - if undumped == 1 then s = "" end - print("Undumped "..undumped.." item"..s..".") - end -end - -undump_buildings() diff --git a/scripts/unsuspend.rb b/scripts/unsuspend.rb deleted file mode 100644 index 666969359..000000000 --- a/scripts/unsuspend.rb +++ /dev/null @@ -1,28 +0,0 @@ -# un-suspend construction jobs, one time only -# same as "resume all" -=begin - -unsuspend -========= -Unsuspend jobs in workshops, on a one-off basis. See `autounsuspend` -for regular use. - -=end - -joblist = df.world.job_list.next -count = 0 - -while joblist - job = joblist.item - joblist = joblist.next - - if job.job_type == :ConstructBuilding - if (job.flags.suspend && job.items && job.items[0]) - item = job.items[0].item - job.flags.suspend = false - count += 1 - end - end -end - -puts "Unsuspended #{count} job(s)." diff --git a/scripts/view-item-info.lua b/scripts/view-item-info.lua deleted file mode 100644 index 39614d3bf..000000000 --- a/scripts/view-item-info.lua +++ /dev/null @@ -1,407 +0,0 @@ --- Extended Item Viewscreens --- Shows information on material properties, weapon or armour stats, and more. --- By PeridexisErrant, adapted from nb_item_info by Raidau ---@ enable = true -local help = [[=begin - -view-item-info -============== -A script to extend the item or unit viewscreen with additional information -including a custom description of each item (when available), and properties -such as material statistics, weapon attacks, armor effectiveness, and more. - -The associated script `item-descriptions`.lua supplies custom descriptions -of items. Individual descriptions can be added or overridden by a similar -script :file:`raw/scripts/more-item-descriptions.lua`. Both work as sparse lists, -so missing items simply go undescribed if not defined in the fallback. - -=end]] - -function isInList(list, item, helper) - if not helper then - helper = function(v) return v end - end - for k,v in pairs (list) do - if item == helper(v) then - return true - end - end -end - -if dfhack_flags and dfhack_flags.enable then - enabled = dfhack_flags.enable_state -end - -local args = {...} -local lastframe = df.global.enabler.frame_last -if isInList(args, "help") or isInList(args, "?") then - print(help) - return -elseif isInList(args, "enable") then - enabled = true -elseif isInList(args, "disable") then - enabled = false -end - -function append (list, str, indent) - local str = str or " " - local indent = indent or 0 - table.insert(list, {str, indent * 4}) -end - -function add_lines_to_list(t1,t2) - for i=1,#t2 do - t1[#t1+1] = t2[i] - end - return t1 -end - -function GetMatPlant (item) - if dfhack.matinfo.decode(item).mode == "plant" then - return dfhack.matinfo.decode(item).plant - end -end - -function get_textid (item) - return string.match(tostring(item._type),""):upper() -end - -function compare_iron (mat_prop, iron_prop) - return " "..mat_prop.." ("..math.floor(mat_prop/iron_prop*100).."% of iron)" -end - -function GetMatPropertiesStringList (item) - local mat = dfhack.matinfo.decode(item).material - local list = {} - local deg_U = item.temperature.whole - local deg_C = math.floor((deg_U-10000)*5/9) - append(list,"Temperature: "..deg_C.."\248C ("..deg_U.."U)") - append(list,"Color: "..df.global.world.raws.language.colors[mat.state_color.Solid].name) - local function GetStrainDescription (number) - if tonumber(number) < 1 then return "crystalline" - elseif tonumber(number) < 1000 then return "very stiff" - elseif tonumber(number) < 5001 then return "stiff" - elseif tonumber(number) < 15001 then return "medium" - elseif tonumber(number) < 50000 then return "elastic" - elseif tonumber(number) >= 50000 then return "very elastic" - else return "unknown" end - end - local mat_properties_for = {"BAR", "SMALLGEM", "BOULDER", "ROUGH", - "WOOD", "GEM", "ANVIL", "THREAD", "SHOES", "CLOTH", "ROCK", "WEAPON", "TRAPCOMP", - "ORTHOPEDIC_CAST", "SIEGEAMMO", "SHIELD", "PANTS", "HELM", "GLOVES", "ARMOR", "AMMO"} - if isInList(mat_properties_for, get_textid (item)) then - append(list,"Material name: "..mat.state_name.Solid) - append(list,"Material properties: ") - append(list,"Solid density: "..mat.solid_density..'kg/m^3',1) - local maxedge = mat.strength.max_edge - append(list,"Maximum sharpness: "..maxedge.." ("..maxedge/standard.strength.max_edge*100 .."%)",1) - if mat.molar_mass > 0 then - append(list,"Molar mass: "..mat.molar_mass,1) - end - append(list,"Shear strength:",1) - append(list, "yield:"..compare_iron(mat.strength.yield.SHEAR, standard.strength.yield.SHEAR), 2) - append(list, "fracture:"..compare_iron(mat.strength.fracture.SHEAR, standard.strength.fracture.SHEAR), 2) - local s_strain = mat.strength.strain_at_yield.SHEAR - append(list, "elasticity: "..s_strain.." ("..GetStrainDescription(s_strain)..")", 2) - append(list,"Impact strength:",1) - append(list, "yield:"..compare_iron(mat.strength.yield.IMPACT, standard.strength.yield.IMPACT), 2) - append(list, "fracture:"..compare_iron(mat.strength.fracture.IMPACT, standard.strength.fracture.IMPACT), 2) - local i_strain = mat.strength.strain_at_yield.IMPACT - append(list, "elasticity: "..i_strain.." ("..GetStrainDescription(i_strain)..")", 2) - end - return list -end - -function GetArmorPropertiesStringList (item) - local mat = dfhack.matinfo.decode(item).material - local list = {} - append(list,"Armor properties: ") - append(list,"Thickness: "..item.subtype.props.layer_size,1) - append(list,"Coverage: "..item.subtype.props.coverage.."%",1) - append(list,"Fit for "..df.creature_raw.find(item.maker_race).name[0],1) - return list -end - -function GetShieldPropertiesStringList (item) - local mat = dfhack.matinfo.decode(item).material - local list = {} - append(list,"Shield properties:") - append(list,"Base block chance: "..item.subtype.blockchance,1) - append(list,"Fit for "..df.creature_raw.find(item.maker_race).name[0],1) - return list -end - -function GetWeaponPropertiesStringList (item) - local mat = dfhack.matinfo.decode(item).material - local list = {} - if item._type == df.item_toolst and #item.subtype.attacks < 1 then - return list - end - append(list,"Weapon properties: ") - if item.sharpness > 0 then - append(list,"Sharpness:"..compare_iron(item.sharpness, standard.strength.max_edge),1) - else - append(list,"Not edged",1) - end - if string.len(item.subtype.ranged_ammo) > 0 then - append(list,"Ranged weapon",1) - append(list,"Ammo: "..item.subtype.ranged_ammo:lower(),2) - if item.subtype.shoot_force > 0 then - append(list,"Shoot force: "..item.subtype.shoot_force,2) - end - if item.subtype.shoot_maxvel > 0 then - append(list,"Maximum projectile velocity: "..item.subtype.shoot_maxvel,2) - end - end - append(list,"Required size: "..item.subtype.minimum_size*10,1) - if item.subtype.two_handed*10 > item.subtype.minimum_size*10 then - append(list,"Used as 2-handed until unit size: "..item.subtype.two_handed*10,2) - end - append(list,"Attacks: ",1) - for k,attack in pairs (item.subtype.attacks) do - local name = attack.verb_2nd - if attack.noun ~= "NO_SUB" then - name = name.." with "..attack.noun - end - if attack.edged then - name = name.." (edged)" - else - name = name.." (blunt)" - end - append(list,name,2) - append(list,"Contact area: "..attack.contact,3) - if attack.edged then - append(list,"Penetration: "..attack.penetration,3) - end - append(list,"Velocity multiplier: "..attack.velocity_mult/1000,3) - if attack.flags.bad_multiattack then - append(list,"Bad multiattack",3) - end - append(list,"Prepare "..attack.prepare.." / recover "..attack.recover,3) - end - return list -end - -function GetAmmoPropertiesStringList (item) - local mat = dfhack.matinfo.decode(item).material - local list = {} - if item._type == df.item_toolst and #item.subtype.attacks < 1 then - return list - end - append(list,"Ammo properties: ") - if item.sharpness > 0 then - append(list,"Sharpness: "..item.sharpness,1) - else - append(list,"Not edged",1) - end - append(list,"Attacks: ", 1) - for k,attack in pairs (item.subtype.attacks) do - local name = attack.verb_2nd - if attack.noun ~= "NO_SUB" then - name = name.." with "..attack.noun - end - if attack.edged then - name = name.." (edged)" - else - name = name.." (blunt)" - end - append(list,name,2) - append(list,"Contact area: "..attack.contact,3) - if attack.edged then - append(list,"Penetration: "..attack.penetration,3) - end - append(list,"Velocity multiplier: "..attack.velocity_mult/1000,3) - if attack.flags.bad_multiattack then - append(list,"Bad multiattack",3) - end - append(list,"Prepare "..attack.prepare.." / recover "..attack.recover,3) - end - return list -end - -function edible_string (mat) - local edible_string = "Edible" - if mat.flags.EDIBLE_RAW or mat.flags.EDIBLE_COOKED then - if mat.flags.EDIBLE_RAW then - edible_string = edible_string.." raw" - if mat.flags.EDIBLE_COOKED then - edible_string = edible_string.." and cooked" - end - elseif mat.flags.EDIBLE_COOKED then - edible_string = edible_string.." only when cooked" - end - else - edible_string = "Not edible" - end - return edible_string -end - -function GetReactionProduct (inmat, reaction) - for k,v in pairs (inmat.reaction_product.id) do - if v.value == reaction then - return {inmat.reaction_product.material.mat_type[k], - inmat.reaction_product.material.mat_index[k]} - end - end -end - -function add_react_prod (list, mat, product, str) - local mat_type, mat_index = GetReactionProduct (mat, product) - if mat_type then - local result = dfhack.matinfo.decode(mat_type, mat_index).material.state_name.Liquid - append(list, str..result) - end -end - -function get_plant_reaction_products (mat) - local list = {} - add_react_prod (list, mat, "GROWTH_JUICE_PROD", "Pressed into ") - add_react_prod (list, mat, "PRESS_LIQUID_MAT", "Pressed into ") - add_react_prod (list, mat, "LIQUID_EXTRACTABLE", "Extractable product: ") - add_react_prod (list, mat, "WATER_SOLUTION_PROD", "Can be mixed with water to make ") - add_react_prod (list, mat, "RENDER_MAT", "Rendered into ") - add_react_prod (list, mat, "SOAP_MAT", "Used to make soap") - if GetReactionProduct (mat, "SUGARABLE") then - append(list,"Used to make sugar") - end - if GetReactionProduct (mat, "MILLABLE") or - GetReactionProduct (mat, "GRAIN_MILLABLE") or - GetReactionProduct (mat, "GROWTH_MILLABLE") then - append(list,"Can be milled") - end - if GetReactionProduct(mat, "GRAIN_THRESHABLE") then - append(list,"Grain can be threshed") - end - if GetReactionProduct (mat, "CHEESE_MAT") then - local mat_type, mat_index = GetReactionProduct (mat, "CHEESE_MAT") - append(list,"Used to make "..dfhack.matinfo.decode(mat_type, mat_index).material.state_name.Solid) - end - return list -end - -function GetFoodPropertiesStringList (item) - local mat = dfhack.matinfo.decode(item).material - local list = {{" ", 0}} - append(list,edible_string(mat)) - if item._type == df.item_foodst then - append(list,"This is prepared meal") - return list - end - if mat == dfhack.matinfo.find ("WATER") then - append(list,"Water is drinkable") - return list - end - add_lines_to_list(list, get_plant_reaction_products(mat)) - if item._type == df.item_plantst and GetMatPlant (item) then - local plant = GetMatPlant (item) - for k,v in pairs (plant.material_defs) do - if v ~= -1 and k:find("type_") and not (k:find("type_basic") - or k:find("type_seed") or k:find("type_tree")) then - local targetmat = dfhack.matinfo.decode (v, - plant.material_defs["idx_"..k:match("type_(.+)")]) - local state = "Liquid" - local describe = "Made into " - if k:find("type_mill") - then state = "Powder" describe = "Ground into " - elseif k:find("type_thread") - then state = "Solid" describe = "Woven into " - elseif k:find("type_drink") - then describe = "Brewed into " - elseif k:find("type_extract_barrel") - then describe = "Cask-aged into " - elseif k:find("type_extract_vial") - then describe = "Refined into vials of " - elseif k:find("type_extract_still_vial") - then describe = "Distilled into vials of " end - local st_name = targetmat.material.state_name[state] - append(list,describe..targetmat.material.prefix..''..st_name) - end - end - end - return list -end - -function get_all_uses_strings (item) - local all_lines = {} - local FoodsAndPlants = {df.item_meatst, df.item_globst, - df.item_plantst, df.item_plant_growthst, df.item_liquid_miscst, - df.item_powder_miscst, df.item_cheesest, df.item_foodst} - local ArmourTypes = {df.item_armorst, df.item_pantsst, - df.item_helmst, df.item_glovesst, df.item_shoesst} - if df.global.gamemode == df.game_mode.ADVENTURE and item ~= "COIN" then - add_lines_to_list(all_lines, {{"Value: "..dfhack.items.getValue(item),0}}) - elseif isInList(ArmourTypes, item._type) then - add_lines_to_list(all_lines, GetArmorPropertiesStringList(item)) - elseif item._type == df.item_weaponst or item._type == df.item_toolst then - add_lines_to_list(all_lines, GetWeaponPropertiesStringList(item)) - elseif item._type == df.item_ammost then - add_lines_to_list(all_lines, GetAmmoPropertiesStringList(item)) - elseif item._type == df.item_shieldst then - add_lines_to_list(all_lines, GetShieldPropertiesStringList(item)) - elseif item._type == df.item_seedsst then - local str = math.floor(GetMatPlant(item).growdur/12).." days" - add_lines_to_list(all_lines, {{"Growth time: "..str, 0}}) - elseif isInList(FoodsAndPlants, item._type) then - add_lines_to_list(all_lines, GetFoodPropertiesStringList(item)) - end - if not dfhack.items.isCasteMaterial(df.item_type[get_textid(item)]) then - add_lines_to_list(all_lines, GetMatPropertiesStringList(item)) - end - return all_lines -end - -function get_custom_item_desc (item) - local desc - local ID = get_textid (item) - if ID and dfhack.items.getSubtypeCount(df.item_type[ID]) ~= -1 then - ID = item.subtype.id end - if not ID then return nil end - if dfhack.findScript("item-descriptions") then - desc = dfhack.script_environment("item-descriptions").descriptions[ID] - end - if dfhack.findScript("more-item-descriptions") then - desc = dfhack.script_environment("more-item-descriptions").descriptions[ID] or desc - end - if desc then add_lines_to_list(desc, {""}) end - return desc -end - -function AddUsesString (viewscreen,line,indent) - local str = df.new("string") - str.value = tostring(line) - local indent = indent or 0 - viewscreen.entry_ref:insert('#', nil) - viewscreen.entry_indent:insert('#', indent) - viewscreen.unk_34:insert('#', nil) -- TODO: get this into structures, and fix usage! - viewscreen.entry_string:insert('#', str) - viewscreen.entry_reaction:insert('#', -1) -end - -function dfhack.onStateChange.item_info (code) - vi_label = 'More information (DFHack):' - if not enabled then return end - if code == SC_VIEWSCREEN_CHANGED and dfhack.isWorldLoaded() then - standard = dfhack.matinfo.find("INORGANIC:IRON").material - if not standard then return end - local scr = dfhack.gui.getCurViewscreen() - if scr._type == df.viewscreen_itemst then - if isInList(scr.entry_string, vi_label, function(v) return v.value end) then - return - end - if #scr.entry_string > 0 then - AddUsesString(scr, '') - end - AddUsesString(scr, vi_label) - AddUsesString(scr, '') - local description = get_custom_item_desc (scr.item) or "" - for i = 1, #description do - AddUsesString(scr,description[i]) - end - local all_lines = get_all_uses_strings(scr.item) - for i = 1, #all_lines do - AddUsesString(scr,all_lines[i][1],all_lines[i][2]) - end - scr.caption_uses = true - end - end -end diff --git a/scripts/warn-starving.lua b/scripts/warn-starving.lua deleted file mode 100644 index 12334bf99..000000000 --- a/scripts/warn-starving.lua +++ /dev/null @@ -1,140 +0,0 @@ --- Pause and warn if a unit is starving --- By Meneth32, PeridexisErrant, Lethosor ---@ module = true ---[[=begin - -warn-starving -============= -If any (live) units are starving, very thirsty, or very drowsy, the game will -be paused and a warning shown and logged to the console. Use with the -`repeat` command for regular checks. - -Use ``warn-starving all`` to display a list of all problematic units. - -=end]] - -starvingUnits = starvingUnits or {} -dehydratedUnits = dehydratedUnits or {} -sleepyUnits = sleepyUnits or {} - -function clear() - starvingUnits = {} - dehydratedUnits = {} - sleepyUnits = {} -end - -local gui = require 'gui' -local utils = require 'utils' -local units = df.global.world.units.active - -local args = utils.invert({...}) -if args.all or args.clear then - clear() -end - -warning = defclass(warning, gui.FramedScreen) -warning.ATTRS = { - frame_style = gui.GREY_LINE_FRAME, - frame_title = 'Warning', - frame_width = 20, - frame_height = 18, - frame_inset = 1, - focus_path = 'warn-starving', -} - -function warning:init(args) - self.start = 1 - self.messages = args.messages - self.frame_height = math.min(18, #self.messages) - self.max_start = #self.messages - self.frame_height + 1 - for _, msg in pairs(self.messages) do - self.frame_width = math.max(self.frame_width, #msg + 2) - end - self.frame_width = math.min(df.global.gps.dimx - 2, self.frame_width) -end - -function warning:onRenderBody(p) - for i = self.start, math.min(self.start + self.frame_height - 1, #self.messages) do - p:string(self.messages[i]):newline() - end - if #self.messages > self.frame_height then - if self.start > 1 then - p:seek(self.frame_width - 1, 0):string(string.char(24), COLOR_LIGHTCYAN) -- up - end - if self.start < self.max_start then - p:seek(self.frame_width - 1, self.frame_height - 1):string(string.char(25), COLOR_LIGHTCYAN) -- down - end - end -end - -function warning:onInput(keys) - if keys.LEAVESCREEN or keys.SELECT then - self:dismiss() - elseif keys.CURSOR_UP or keys.STANDARDSCROLL_UP then - self.start = math.max(1, self.start - 1) - elseif keys.CURSOR_DOWN or keys.STANDARDSCROLL_DOWN then - self.start = math.min(self.start + 1, self.max_start) - end -end - -local function findRaceCaste(unit) - local rraw = df.creature_raw.find(unit.race) - return rraw, safe_index(rraw, 'caste', unit.caste) -end - -local function getSexString(sex) - local sexStr = "" - if sex==0 then sexStr=string.char(12) - elseif sex==1 then sexStr=string.char(11) - end - return string.char(40)..sexStr..string.char(41) -end - -local function nameOrSpeciesAndNumber(unit) - if unit.name.has_name then - return dfhack.TranslateName(dfhack.units.getVisibleName(unit))..' '..getSexString(unit.sex),true - else - return 'Unit #'..unit.id..' ('..df.creature_raw.find(unit.race).caste[unit.caste].caste_name[0]..' '..getSexString(unit.sex)..')',false - end -end - -local function checkVariable(var, limit, description, map, unit) - local rraw = findRaceCaste(unit) - local species = rraw.name[0] - local profname = dfhack.units.getProfessionName(unit) - if #profname == 0 then profname = nil end - local name = nameOrSpeciesAndNumber(unit) - if var > limit then - if not map[unit.id] then - map[unit.id] = true - return name .. ", " .. (profname or species) .. " is " .. description .. "!" - end - else - map[unit.id] = false - end - return nil -end - -function doCheck() - local messages = {} - for i=#units-1, 0, -1 do - local unit = units[i] - local rraw = findRaceCaste(unit) - if rraw and not unit.flags1.dead and not dfhack.units.isOpposedToLife(unit) then - table.insert(messages, checkVariable(unit.counters2.hunger_timer, 75000, 'starving', starvingUnits, unit)) - table.insert(messages, checkVariable(unit.counters2.thirst_timer, 50000, 'dehydrated', dehydratedUnits, unit)) - table.insert(messages, checkVariable(unit.counters2.sleepiness_timer, 150000, 'very drowsy', sleepyUnits, unit)) - end - end - if #messages > 0 then - dfhack.color(COLOR_LIGHTMAGENTA) - for _, msg in pairs(messages) do - print(dfhack.df2console(msg)) - end - dfhack.color() - df.global.pause_state = true - warning{messages=messages}:show() - end -end - -if not moduleMode then doCheck() end diff --git a/scripts/weather.lua b/scripts/weather.lua deleted file mode 100644 index a61ab48bd..000000000 --- a/scripts/weather.lua +++ /dev/null @@ -1,46 +0,0 @@ --- Print the weather map or change weather. -local helpstr = [[=begin - -weather -======= -Prints a map of the local weather, or with arguments ``clear``, -``rain``, and ``snow`` changes the weather. - -=end]] - -local args = {...} -local cmd -local val_override = tonumber(args[1]) -if args[1] then - cmd = args[1]:sub(1, 1) -end -if cmd == "h" or cmd == "?" then - print("The current weather is "..df.weather_type[dfhack.world.ReadCurrentWeather()]) - print((helpstr:gsub('=[a-z]+', ''))) -elseif cmd == "c" then - dfhack.world.SetCurrentWeather(df.weather_type.None) - print("The weather has cleared.") -elseif cmd == "r" then - dfhack.world.SetCurrentWeather(df.weather_type.Rain) - print("It is now raining.") -elseif cmd == "s" then - dfhack.world.SetCurrentWeather(df.weather_type.Snow) - print("It is now snowing.") -elseif val_override then - dfhack.world.SetCurrentWeather(val_override) - print("Set weather to " .. val_override) -elseif args[1] then - qerror("Unrecognized argument: " .. args[1]) -else - -- df.global.current_weather is arranged in columns, not rows - kind = {[0]="C", "R", "S"} - print("Weather map (C = clear, R = rain, S = snow):") - for y=0, 4 do - s = "" - for x=0, 4 do - local cur = df.global.current_weather[x][y] - s = s .. (kind[cur] or cur) .. ' ' - end - print(s) - end -end