Remove scripts/ folder
parent
450fcdba31
commit
6258870e2c
@ -1 +0,0 @@
|
||||
Subproject commit 4353c10401ced7aec89f002947d1252f30237789
|
@ -1 +0,0 @@
|
||||
Subproject commit 0a75d5ff69916cf9b3739f4b20d36ab4cfdcf824
|
@ -1 +0,0 @@
|
||||
Subproject commit 704aed4447f27ae802dae6994479ebc9c46568cc
|
@ -1 +0,0 @@
|
||||
Subproject commit 45c78449e71d1ba263044fb00108509088ad0026
|
@ -1 +0,0 @@
|
||||
Subproject commit b337e931b8b7a167ee5ce1ac6b5c3155c291f260
|
@ -1 +0,0 @@
|
||||
Subproject commit 4b6e772654df6805b66f77900a4618bbf9b54dab
|
@ -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
|
||||
)
|
@ -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()
|
@ -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.
|
@ -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 <show|set> <him|all> [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 <show|set> <him|all> [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
|
@ -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
|
@ -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)
|
@ -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 <<EOS
|
||||
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
|
||||
EOS
|
||||
|
||||
else
|
||||
$AutoFarm.process
|
||||
puts $AutoFarm.status
|
||||
end
|
@ -1,79 +0,0 @@
|
||||
-- Run an autolabor command for skill-affected labors.
|
||||
|
||||
--[[=begin
|
||||
|
||||
autolabor-artisans
|
||||
==================
|
||||
Runs an `autolabor` command, for all labors where skill level
|
||||
influences output quality. Examples::
|
||||
|
||||
autolabor-artisans 0 2 3
|
||||
autolabor-artisans disable
|
||||
|
||||
=end]]
|
||||
local artisan_labors = {
|
||||
"CARPENTER",
|
||||
"DETAIL",
|
||||
"MASON",
|
||||
"ARCHITECT",
|
||||
"ANIMALTRAIN",
|
||||
"LEATHER",
|
||||
"WEAVER",
|
||||
"CLOTHESMAKER",
|
||||
"COOK",
|
||||
"FORGE_WEAPON",
|
||||
"FORGE_ARMOR",
|
||||
"FORGE_FURNITURE",
|
||||
"METAL_CRAFT",
|
||||
"CUT_GEM",
|
||||
"ENCRUST_GEM",
|
||||
"WOOD_CRAFT",
|
||||
"STONE_CRAFT",
|
||||
"BONE_CARVE",
|
||||
"GLASSMAKER",
|
||||
"SIEGECRAFT",
|
||||
"BOWYER",
|
||||
"MECHANIC",
|
||||
"DYER",
|
||||
"POTTERY",
|
||||
"WAX_WORKING",
|
||||
}
|
||||
|
||||
local args = {...}
|
||||
|
||||
function make_cmd(labor)
|
||||
local cmd = string.format("autolabor %s", labor)
|
||||
for i, arg in ipairs(args) do
|
||||
cmd = cmd .. " " .. arg
|
||||
end
|
||||
return cmd
|
||||
end
|
||||
|
||||
function run()
|
||||
if #args == 0 or args[1] == "help" then
|
||||
print('Applies an autolabor command to all labors with quality-based output.')
|
||||
print('')
|
||||
print('Examples:')
|
||||
print(' autolabor-artisans 0 2 3')
|
||||
print(' autolabor-artisans disable')
|
||||
return false
|
||||
end
|
||||
|
||||
dfhack.run_command("autolabor enable")
|
||||
|
||||
-- Test with one to make sure the arguments are valid.
|
||||
local cmd = make_cmd(artisan_labors[1])
|
||||
local output, status = dfhack.run_command_silent(cmd)
|
||||
if status ~= CR_OK then
|
||||
qerror("Invalid arguments.", status)
|
||||
return false
|
||||
end
|
||||
|
||||
for i, labor in ipairs(artisan_labors) do
|
||||
dfhack.run_command(make_cmd(labor))
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
run()
|
@ -1,49 +0,0 @@
|
||||
# un-suspend construction jobs, on a recurring basis
|
||||
=begin
|
||||
|
||||
autounsuspend
|
||||
=============
|
||||
Automatically unsuspend jobs in workshops, on a recurring basis.
|
||||
See `unsuspend` for one-off use, or `resume` ``all``.
|
||||
|
||||
=end
|
||||
|
||||
class AutoUnsuspend
|
||||
attr_accessor :running
|
||||
|
||||
def process
|
||||
count = 0
|
||||
df.world.job_list.each { |job|
|
||||
if job.job_type == :ConstructBuilding and job.flags.suspend and df.map_tile_at(job).designation.flow_size <= 1
|
||||
job.flags.suspend = false
|
||||
count += 1
|
||||
end
|
||||
}
|
||||
if count > 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
|
@ -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 <type>``. 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
|
@ -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 <patchname>')
|
||||
end
|
||||
run_command(cmd, name)
|
@ -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
|
@ -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
|
@ -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))
|
@ -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
|
@ -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 <<EOS
|
||||
Create first necessity items under the cursor.
|
||||
Usage:
|
||||
create-items [category] [raws token] [number]
|
||||
|
||||
Item categories:
|
||||
bars, boulders, plants, logs, webs, anvils
|
||||
|
||||
Raw token:
|
||||
Either a full token (PLANT_MAT:ADLER:WOOD) or the middle part only
|
||||
(the missing part is autocompleted depending on the item category)
|
||||
Use 'list' to show all possibilities
|
||||
|
||||
Exemples:
|
||||
create-items boulders hematite 30
|
||||
create-items bars CREATURE_MAT:CAT:SOAP 10
|
||||
create-items web cave_giant
|
||||
create-items plants list
|
||||
|
||||
EOS
|
||||
throw :script_finished
|
||||
|
||||
elsif mat_raw == 'list'
|
||||
# allowed with no cursor
|
||||
|
||||
elsif df.cursor.x == -30000
|
||||
puts "Please place the game cursor somewhere"
|
||||
throw :script_finished
|
||||
|
||||
elsif !(maptile = df.map_tile_at(df.cursor))
|
||||
puts "Error: unallocated map block !"
|
||||
throw :script_finished
|
||||
|
||||
elsif !maptile.shape_passablehigh
|
||||
puts "Error: impassible tile !"
|
||||
throw :script_finished
|
||||
end
|
||||
|
||||
|
||||
def match_list(tok, list)
|
||||
if tok != 'list'
|
||||
tok = df.match_rawname(tok, list)
|
||||
if not tok
|
||||
puts "Invalid raws token, use one in:"
|
||||
tok = 'list'
|
||||
end
|
||||
end
|
||||
if tok == 'list'
|
||||
puts list.map { |w| w =~ /[^\w]/ ? w.inspect : w }.join(' ')
|
||||
throw :script_finished
|
||||
end
|
||||
tok
|
||||
end
|
||||
|
||||
|
||||
case category
|
||||
when 'bars'
|
||||
# create metal bar, eg createbar INORGANIC:IRON
|
||||
cls = DFHack::ItemBarst
|
||||
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
|
||||
list = df.world.raws.inorganics.find_all { |ino|
|
||||
ino.material.flags[:IS_METAL]
|
||||
}.map { |ino| ino.id }
|
||||
mat_raw = match_list(mat_raw, list)
|
||||
mat_raw = "INORGANIC:#{mat_raw}"
|
||||
puts mat_raw
|
||||
end
|
||||
customize = lambda { |item|
|
||||
item.dimension = 150
|
||||
item.subtype = -1
|
||||
}
|
||||
|
||||
when 'boulders'
|
||||
cls = DFHack::ItemBoulderst
|
||||
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
|
||||
list = df.world.raws.inorganics.find_all { |ino|
|
||||
ino.material.flags[:IS_STONE]
|
||||
}.map { |ino| ino.id }
|
||||
mat_raw = match_list(mat_raw, list)
|
||||
mat_raw = "INORGANIC:#{mat_raw}"
|
||||
puts mat_raw
|
||||
end
|
||||
|
||||
when 'plants'
|
||||
cls = DFHack::ItemPlantst
|
||||
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
|
||||
list = df.world.raws.plants.all.find_all { |plt|
|
||||
plt.material.find { |mat| mat.id == 'STRUCTURAL' }
|
||||
}.map { |plt| plt.id }
|
||||
mat_raw = match_list(mat_raw, list)
|
||||
mat_raw = "PLANT_MAT:#{mat_raw}:STRUCTURAL"
|
||||
puts mat_raw
|
||||
end
|
||||
|
||||
when 'logs'
|
||||
cls = DFHack::ItemWoodst
|
||||
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
|
||||
list = df.world.raws.plants.all.find_all { |plt|
|
||||
plt.material.find { |mat| mat.id == 'WOOD' }
|
||||
}.map { |plt| plt.id }
|
||||
mat_raw = match_list(mat_raw, list)
|
||||
mat_raw = "PLANT_MAT:#{mat_raw}:WOOD"
|
||||
puts mat_raw
|
||||
end
|
||||
|
||||
when 'webs'
|
||||
cls = DFHack::ItemThreadst
|
||||
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
|
||||
list = df.world.raws.creatures.all.find_all { |cre|
|
||||
cre.material.find { |mat| mat.id == 'SILK' }
|
||||
}.map { |cre| cre.creature_id }
|
||||
mat_raw = match_list(mat_raw, list)
|
||||
mat_raw = "CREATURE_MAT:#{mat_raw}:SILK"
|
||||
puts mat_raw
|
||||
end
|
||||
count ||= 1
|
||||
customize = lambda { |item|
|
||||
item.flags.spider_web = true
|
||||
item.dimension = 15000 # XXX may depend on creature (this is for GCS)
|
||||
}
|
||||
|
||||
when 'anvils'
|
||||
cls = DFHack::ItemAnvilst
|
||||
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
|
||||
list = df.world.raws.inorganics.find_all { |ino|
|
||||
ino.material.flags[:IS_METAL]
|
||||
}.map { |ino| ino.id }
|
||||
mat_raw = match_list(mat_raw, list)
|
||||
mat_raw = "INORGANIC:#{mat_raw}"
|
||||
puts mat_raw
|
||||
end
|
||||
count ||= 1
|
||||
|
||||
end
|
||||
|
||||
|
||||
mat = df.decode_mat mat_raw
|
||||
|
||||
count ||= 20
|
||||
count.to_i.times {
|
||||
item = cls.cpp_new
|
||||
item.id = df.item_next_id
|
||||
item.stack_size = 1
|
||||
item.mat_type = mat.mat_type
|
||||
item.mat_index = mat.mat_index
|
||||
|
||||
customize[item] if customize
|
||||
|
||||
df.item_next_id += 1
|
||||
item.categorize(true)
|
||||
df.world.items.all << item
|
||||
|
||||
item.pos = df.cursor
|
||||
item.flags.on_ground = true
|
||||
df.map_tile_at.mapblock.items << item.id
|
||||
df.map_tile_at.occupancy.item = true
|
||||
}
|
||||
|
||||
|
||||
# move game view, so that the ui menu updates
|
||||
if df.cursor.z > 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
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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.
|
@ -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
|
@ -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
|
@ -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
|
@ -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)
|
@ -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=<<fillme>>\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()
|
File diff suppressed because it is too large
Load Diff
@ -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
|
@ -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()
|
@ -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()
|
@ -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
|
@ -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
|
@ -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.')
|
@ -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 (<FH>) {
|
||||
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 (<FH>) {
|
||||
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;
|
@ -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)
|
@ -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())
|
@ -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')
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
@ -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 }
|
||||
}
|
@ -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 <<EOS
|
||||
=begin
|
||||
|
||||
devel/spawn-unit-helper
|
||||
=======================
|
||||
Setup stuff to allow arena creature spawn after a mode change.
|
||||
|
||||
With Arena spawn data initialized:
|
||||
|
||||
- enter the :kbd:`k` menu and change mode using
|
||||
``rb_eval df.gametype = :DWARF_ARENA``
|
||||
|
||||
- spawn creatures (:kbd:`c` ingame)
|
||||
|
||||
- revert to game mode using ``rb_eval df.gametype = #{df.gametype.inspect}``
|
||||
|
||||
- To convert spawned creatures to livestock, select each one with
|
||||
the :kbd:`v` menu, and enter ``rb_eval df.unit_find.civ_id = df.ui.civ_id``
|
||||
|
||||
=end
|
||||
EOS
|
@ -1,193 +0,0 @@
|
||||
-- Generates an image using perlin noise
|
||||
--[[=begin
|
||||
|
||||
devel/test-perlin
|
||||
=================
|
||||
Generates an image using multiple octaves of perlin noise.
|
||||
|
||||
=end]]
|
||||
|
||||
local args = {...}
|
||||
local rng = dfhack.random.new(3)
|
||||
|
||||
if #args < 3 then
|
||||
qerror('Usage: devel/test-perlin <fname.pgm> <density> <expr> [-xy <xyscale>] [-z <zscale>]')
|
||||
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)
|
@ -1,10 +0,0 @@
|
||||
# unforbid all items
|
||||
=begin
|
||||
|
||||
devel/unforbidall
|
||||
=================
|
||||
Unforbid all items.
|
||||
|
||||
=end
|
||||
|
||||
df.world.items.all.each { |i| i.flags.forbid = false }
|
@ -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()
|
@ -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
|
||||
|
@ -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 <plan filename>"
|
||||
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'
|
@ -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(...)
|
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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("<?xml version=\"1.0\" encoding='UTF-8'?>\n")
|
||||
file:write("<df_world>\n")
|
||||
file:write("<name>"..dfhack.df2utf(dfhack.TranslateName(df.global.world.world_data.name)).."</name>\n")
|
||||
file:write("<altname>"..dfhack.df2utf(dfhack.TranslateName(df.global.world.world_data.name,1)).."</altname>\n")
|
||||
|
||||
file:write("<landmasses>\n")
|
||||
for landmassK, landmassV in ipairs(df.global.world.world_data.landmasses) do
|
||||
file:write("\t<landmass>\n")
|
||||
file:write("\t\t<id>"..landmassV.index.."</id>\n")
|
||||
file:write("\t\t<name>"..dfhack.df2utf(dfhack.TranslateName(landmassV.name,1)).."</name>\n")
|
||||
file:write("\t\t<coord_1>"..landmassV.min_x..","..landmassV.min_y.."</coord_1>\n")
|
||||
file:write("\t\t<coord_2>"..landmassV.max_x..","..landmassV.max_y.."</coord_2>\n")
|
||||
file:write("\t</landmass>\n")
|
||||
end
|
||||
file:write("</landmasses>\n")
|
||||
|
||||
file:write("<mountain_peaks>\n")
|
||||
for mountainK, mountainV in ipairs(df.global.world.world_data.mountain_peaks) do
|
||||
file:write("\t<mountain_peak>\n")
|
||||
file:write("\t\t<id>"..mountainK.."</id>\n")
|
||||
file:write("\t\t<name>"..dfhack.df2utf(dfhack.TranslateName(mountainV.name,1)).."</name>\n")
|
||||
file:write("\t\t<coords>"..mountainV.pos.x..","..mountainV.pos.y.."</coords>\n")
|
||||
file:write("\t\t<height>"..mountainV.height.."</height>\n")
|
||||
file:write("\t</mountain_peak>\n")
|
||||
end
|
||||
file:write("</mountain_peaks>\n")
|
||||
|
||||
file:write("<regions>\n")
|
||||
for regionK, regionV in ipairs(df.global.world.world_data.regions) do
|
||||
file:write("\t<region>\n")
|
||||
file:write("\t\t<id>"..regionV.index.."</id>\n")
|
||||
file:write("\t\t<coords>")
|
||||
for xK, xVal in ipairs(regionV.region_coords.x) do
|
||||
file:write(xVal..","..regionV.region_coords.y[xK].."|")
|
||||
end
|
||||
file:write("</coords>\n")
|
||||
file:write("\t</region>\n")
|
||||
end
|
||||
file:write("</regions>\n")
|
||||
|
||||
file:write("<underground_regions>\n")
|
||||
for regionK, regionV in ipairs(df.global.world.world_data.underground_regions) do
|
||||
file:write("\t<underground_region>\n")
|
||||
file:write("\t\t<id>"..regionV.index.."</id>\n")
|
||||
file:write("\t\t<coords>")
|
||||
for xK, xVal in ipairs(regionV.region_coords.x) do
|
||||
file:write(xVal..","..regionV.region_coords.y[xK].."|")
|
||||
end
|
||||
file:write("</coords>\n")
|
||||
file:write("\t</underground_region>\n")
|
||||
end
|
||||
file:write("</underground_regions>\n")
|
||||
|
||||
file:write("<sites>\n")
|
||||
for siteK, siteV in ipairs(df.global.world.world_data.sites) do
|
||||
file:write("\t<site>\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).."</"..k..">\n")
|
||||
elseif (k == "buildings") then
|
||||
if (#siteV.buildings > 0) then
|
||||
file:write("\t\t<structures>\n")
|
||||
for buildingK, buildingV in ipairs(siteV.buildings) do
|
||||
file:write("\t\t\t<structure>\n")
|
||||
file:write("\t\t\t\t<id>"..buildingV.id.."</id>\n")
|
||||
file:write("\t\t\t\t<type>"..df_enums.abstract_building_type[buildingV:getType()]:lower().."</type>\n")
|
||||
if (df_enums.abstract_building_type[buildingV:getType()]:lower() ~= "underworld_spire" or table.containskey(buildingV,"name")) then
|
||||
file:write("\t\t\t\t<name>"..dfhack.df2utf(dfhack.TranslateName(buildingV.name, 1)).."</name>\n")
|
||||
file:write("\t\t\t\t<name2>"..dfhack.df2utf(dfhack.TranslateName(buildingV.name)).."</name2>\n")
|
||||
end
|
||||
if (buildingV:getType() == df.abstract_building_type.TEMPLE) then
|
||||
file:write("\t\t\t\t<deity>"..buildingV.deity.."</deity>\n")
|
||||
file:write("\t\t\t\t<religion>"..buildingV.religion.."</religion>\n")
|
||||
end
|
||||
if (buildingV:getType() == df.abstract_building_type.DUNGEON) then
|
||||
file:write("\t\t\t\t<dungeon_type>"..buildingV.dungeon_type.."</dungeon_type>\n")
|
||||
end
|
||||
for inhabitabntK,inhabitabntV in pairs(buildingV.inhabitants) do
|
||||
file:write("\t\t\t\t<inhabitant>"..inhabitabntV.anon_2.."</inhabitant>\n")
|
||||
end
|
||||
file:write("\t\t\t</structure>\n")
|
||||
end
|
||||
file:write("\t\t</structures>\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
file:write("\t</site>\n")
|
||||
end
|
||||
file:write("</sites>\n")
|
||||
|
||||
file:write("<world_constructions>\n")
|
||||
for wcK, wcV in ipairs(df.global.world.world_data.constructions.list) do
|
||||
file:write("\t<world_construction>\n")
|
||||
file:write("\t\t<id>"..wcV.id.."</id>\n")
|
||||
file:write("\t\t<name>"..dfhack.df2utf(dfhack.TranslateName(wcV.name,1)).."</name>\n")
|
||||
file:write("\t\t<type>"..(df_enums.world_construction_type[wcV:getType()]):lower().."</type>\n")
|
||||
file:write("\t\t<coords>")
|
||||
for xK, xVal in ipairs(wcV.square_pos.x) do
|
||||
file:write(xVal..","..wcV.square_pos.y[xK].."|")
|
||||
end
|
||||
file:write("</coords>\n")
|
||||
file:write("\t</world_construction>\n")
|
||||
end
|
||||
file:write("</world_constructions>\n")
|
||||
|
||||
file:write("<artifacts>\n")
|
||||
for artifactK, artifactV in ipairs(df.global.world.artifacts.all) do
|
||||
file:write("\t<artifact>\n")
|
||||
file:write("\t\t<id>"..artifactV.id.."</id>\n")
|
||||
if (artifactV.item:getType() ~= -1) then
|
||||
file:write("\t\t<item_type>"..tostring(df_enums.item_type[artifactV.item:getType()]):lower().."</item_type>\n")
|
||||
if (artifactV.item:getSubtype() ~= -1) then
|
||||
file:write("\t\t<item_subtype>"..artifactV.item.subtype.name.."</item_subtype>\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<writing>"..writingV.."</writing>\n")
|
||||
end
|
||||
elseif impovementV:getType() == df.improvement_type.PAGES then
|
||||
file:write("\t\t<page_count>"..impovementV.count.."</page_count>\n")
|
||||
for writingk,writingV in pairs(impovementV.contents) do
|
||||
file:write("\t\t<writing>"..writingV.."</writing>\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if (table.containskey(artifactV.item,"description")) then
|
||||
file:write("\t\t<item_description>"..dfhack.df2utf(artifactV.item.description:lower()).."</item_description>\n")
|
||||
end
|
||||
if artifactV.item:getMaterial() ~= -1 then
|
||||
file:write("\t\t<mat>"..dfhack.matinfo.toString(dfhack.matinfo.decode(artifactV.item:getMaterial(), artifactV.item:getMaterialIndex())).."</mat>\n")
|
||||
end
|
||||
file:write("\t</artifact>\n")
|
||||
end
|
||||
file:write("</artifacts>\n")
|
||||
|
||||
file:write("<historical_figures>\n")
|
||||
for hfK, hfV in ipairs(df.global.world.history.figures) do
|
||||
file:write("\t<historical_figure>\n")
|
||||
file:write("\t\t<id>"..hfV.id.."</id>\n")
|
||||
file:write("\t\t<sex>"..hfV.sex.."</sex>\n")
|
||||
if hfV.race >= 0 then file:write("\t\t<race>"..df.global.world.raws.creatures.all[hfV.race].name[0].."</race>\n") end
|
||||
file:write("\t</historical_figure>\n")
|
||||
end
|
||||
file:write("</historical_figures>\n")
|
||||
|
||||
file:write("<entity_populations>\n")
|
||||
for entityPopK, entityPopV in ipairs(df.global.world.entity_populations) do
|
||||
file:write("\t<entity_population>\n")
|
||||
file:write("\t\t<id>"..entityPopV.id.."</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<race>"..raceName..":"..entityPopV.counts[raceK].."</race>\n")
|
||||
end
|
||||
file:write("\t\t<civ_id>"..entityPopV.civ_id.."</civ_id>\n")
|
||||
file:write("\t</entity_population>\n")
|
||||
end
|
||||
file:write("</entity_populations>\n")
|
||||
|
||||
file:write("<entities>\n")
|
||||
for entityK, entityV in ipairs(df.global.world.entities.all) do
|
||||
file:write("\t<entity>\n")
|
||||
file:write("\t\t<id>"..entityV.id.."</id>\n")
|
||||
if entityV.race >= 0 then
|
||||
file:write("\t\t<race>"..(df.global.world.raws.creatures.all[entityV.race].creature_id):lower().."</race>\n")
|
||||
end
|
||||
file:write("\t\t<type>"..(df_enums.historical_entity_type[entityV.type]):lower().."</type>\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<worship_id>"..v.."</worship_id>\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
for id, link in pairs(entityV.entity_links) do
|
||||
file:write("\t\t<entity_link>\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]).."</"..k..">\n")
|
||||
else
|
||||
file:write("\t\t\t<"..k..">"..v.."</"..k..">\n")
|
||||
end
|
||||
end
|
||||
file:write("\t\t</entity_link>\n")
|
||||
end
|
||||
for positionK,positionV in pairs(entityV.positions.own) do
|
||||
file:write("\t\t<entity_position>\n")
|
||||
file:write("\t\t\t<id>"..positionV.id.."</id>\n")
|
||||
if positionV.name[0] ~= "" then file:write("\t\t\t<name>"..positionV.name[0].."</name>\n") end
|
||||
if positionV.name_male[0] ~= "" then file:write("\t\t\t<name_male>"..positionV.name_male[0].."</name_male>\n") end
|
||||
if positionV.name_female[0] ~= "" then file:write("\t\t\t<name_female>"..positionV.name_female[0].."</name_female>\n") end
|
||||
if positionV.spouse[0] ~= "" then file:write("\t\t\t<spouse>"..positionV.spouse[0].."</spouse>\n") end
|
||||
if positionV.spouse_male[0] ~= "" then file:write("\t\t\t<spouse_male>"..positionV.spouse_male[0].."</spouse_male>\n") end
|
||||
if positionV.spouse_female[0] ~= "" then file:write("\t\t\t<spouse_female>"..positionV.spouse_female[0].."</spouse_female>\n") end
|
||||
file:write("\t\t</entity_position>\n")
|
||||
end
|
||||
for assignmentK,assignmentV in pairs(entityV.positions.assignments) do
|
||||
file:write("\t\t<entity_position_assignment>\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.."</"..k..">\n")
|
||||
end
|
||||
end
|
||||
file:write("\t\t</entity_position_assignment>\n")
|
||||
end
|
||||
for idx,id in pairs(entityV.histfig_ids) do
|
||||
file:write("\t\t<histfig_id>"..id.."</histfig_id>\n")
|
||||
end
|
||||
for id, link in ipairs(entityV.children) do
|
||||
file:write("\t\t<child>"..link.."</child>\n")
|
||||
end
|
||||
file:write("\t\t<claims>")
|
||||
for xK, xVal in ipairs(entityV.claims.unk2.x) do
|
||||
file:write(xVal..","..entityV.claims.unk2.y[xK].."|")
|
||||
end
|
||||
file:write("\t\t</claims>\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<occasion>\n")
|
||||
file:write("\t\t\t<id>"..occasionV.id.."</id>\n")
|
||||
file:write("\t\t\t<name>"..dfhack.df2utf(dfhack.TranslateName(occasionV.name,1)).."</name>\n")
|
||||
file:write("\t\t\t<event>"..occasionV.event.."</event>\n")
|
||||
for scheduleK, scheduleV in pairs(occasionV.schedule) do
|
||||
file:write("\t\t\t<schedule>\n")
|
||||
file:write("\t\t\t\t<id>"..scheduleK.."</id>\n")
|
||||
file:write("\t\t\t\t<type>"..df_enums.occasion_schedule_type[scheduleV.type]:lower().."</type>\n")
|
||||
if(scheduleV.type == df.occasion_schedule_type.THROWING_COMPETITION) then
|
||||
file:write("\t\t\t\t<item_type>"..df_enums.item_type[scheduleV.reference]:lower().."</item_type>\n")
|
||||
file:write("\t\t\t\t<item_subtype>"..getItemSubTypeName(scheduleV.reference,scheduleV.reference2).."</item_subtype>\n")
|
||||
else
|
||||
file:write("\t\t\t\t<reference>"..scheduleV.reference.."</reference>\n")
|
||||
file:write("\t\t\t\t<reference2>"..scheduleV.reference2.."</reference2>\n")
|
||||
end
|
||||
for featureK, featureV in pairs(scheduleV.features) do
|
||||
file:write("\t\t\t\t<feature>\n")
|
||||
if(df_enums.occasion_schedule_feature[featureV.feature] ~= nil) then
|
||||
file:write("\t\t\t\t\t<type>"..df_enums.occasion_schedule_feature[featureV.feature]:lower().."</type>\n")
|
||||
else
|
||||
file:write("\t\t\t\t\t<type>"..featureV.feature.."</type>\n")
|
||||
end
|
||||
file:write("\t\t\t\t\t<reference>"..featureV.reference.."</reference>\n")
|
||||
file:write("\t\t\t\t</feature>\n")
|
||||
end
|
||||
file:write("\t\t\t</schedule>\n")
|
||||
end
|
||||
file:write("\t\t</occasion>\n")
|
||||
end
|
||||
end
|
||||
file:write("\t</entity>\n")
|
||||
end
|
||||
file:write("</entities>\n")
|
||||
|
||||
file:write("<poetic_forms>\n")
|
||||
for formK, formV in ipairs(df.global.world.poetic_forms.all) do
|
||||
file:write("\t<poetic_form>\n")
|
||||
file:write("\t\t<id>"..formV.id.."</id>\n")
|
||||
file:write("\t\t<name>"..dfhack.df2utf(dfhack.TranslateName(formV.name,1)).."</name>\n")
|
||||
file:write("\t</poetic_form>\n")
|
||||
end
|
||||
file:write("</poetic_forms>\n")
|
||||
|
||||
file:write("<musical_forms>\n")
|
||||
for formK, formV in ipairs(df.global.world.musical_forms.all) do
|
||||
file:write("\t<musical_form>\n")
|
||||
file:write("\t\t<id>"..formV.id.."</id>\n")
|
||||
file:write("\t\t<name>"..dfhack.df2utf(dfhack.TranslateName(formV.name,1)).."</name>\n")
|
||||
file:write("\t</musical_form>\n")
|
||||
end
|
||||
file:write("</musical_forms>\n")
|
||||
|
||||
file:write("<dance_forms>\n")
|
||||
for formK, formV in ipairs(df.global.world.dance_forms.all) do
|
||||
file:write("\t<dance_form>\n")
|
||||
file:write("\t\t<id>"..formV.id.."</id>\n")
|
||||
file:write("\t\t<name>"..dfhack.df2utf(dfhack.TranslateName(formV.name,1)).."</name>\n")
|
||||
file:write("\t</dance_form>\n")
|
||||
end
|
||||
file:write("</dance_forms>\n")
|
||||
|
||||
file:write("<written_contents>\n")
|
||||
for wcK, wcV in ipairs(df.global.world.written_contents.all) do
|
||||
file:write("\t<written_content>\n")
|
||||
file:write("\t\t<id>"..wcV.id.."</id>\n")
|
||||
file:write("\t\t<title>"..wcV.title.."</title>\n")
|
||||
file:write("\t\t<page_start>"..wcV.page_start.."</page_start>\n")
|
||||
file:write("\t\t<page_end>"..wcV.page_end.."</page_end>\n")
|
||||
for refK, refV in pairs(wcV.refs) do
|
||||
file:write("\t\t<reference>\n")
|
||||
file:write("\t\t\t<type>"..df_enums.general_ref_type[refV:getType()].."</type>\n")
|
||||
if refV:getType() == df.general_ref_type.ARTIFACT then file:write("\t\t\t<id>"..refV.artifact_id.."</id>\n") -- artifact
|
||||
elseif refV:getType() == df.general_ref_type.ENTITY then file:write("\t\t\t<id>"..refV.entity_id.."</id>\n") -- entity
|
||||
elseif refV:getType() == df.general_ref_type.HISTORICAL_EVENT then file:write("\t\t\t<id>"..refV.event_id.."</id>\n") -- event
|
||||
elseif refV:getType() == df.general_ref_type.SITE then file:write("\t\t\t<id>"..refV.site_id.."</id>\n") -- site
|
||||
elseif refV:getType() == df.general_ref_type.SUBREGION then file:write("\t\t\t<id>"..refV.region_id.."</id>\n") -- region
|
||||
elseif refV:getType() == df.general_ref_type.HISTORICAL_FIGURE then file:write("\t\t\t<id>"..refV.hist_figure_id.."</id>\n") -- hist figure
|
||||
elseif refV:getType() == df.general_ref_type.WRITTEN_CONTENT then file:write("\t\t\t<id>"..refV.anon_1.."</id>\n")
|
||||
elseif refV:getType() == df.general_ref_type.POETIC_FORM then file:write("\t\t\t<id>"..refV.poetic_form_id.."</id>\n") -- poetic form
|
||||
elseif refV:getType() == df.general_ref_type.MUSICAL_FORM then file:write("\t\t\t<id>"..refV.musical_form_id.."</id>\n") -- musical form
|
||||
elseif refV:getType() == df.general_ref_type.DANCE_FORM then file:write("\t\t\t<id>"..refV.dance_form_id.."</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</reference>\n")
|
||||
end
|
||||
file:write("\t\t<type>"..(df_enums.written_content_type[wcV.type] or wcV.type).."</type>\n")
|
||||
for styleK, styleV in pairs(wcV.styles) do
|
||||
file:write("\t\t<style>"..(df_enums.written_content_style[styleV] or styleV).."</style>\n")
|
||||
end
|
||||
file:write("\t\t<author>"..wcV.author.."</author>\n")
|
||||
file:write("\t</written_content>\n")
|
||||
end
|
||||
file:write("</written_contents>\n")
|
||||
|
||||
file:write("<historical_events>\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<historical_event>\n")
|
||||
file:write("\t\t<id>"..event.id.."</id>\n")
|
||||
file:write("\t\t<type>"..tostring(df_enums.history_event_type[event:getType()]):lower().."</type>\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().."</"..k..">\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<position>"..tostring(entityPositionsV.name[0]):lower().."</position>\n")
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
file:write("\t\t<position>-1</position>\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<position>"..tostring(entityPositionsV.name[0]):lower().."</position>\n")
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
file:write("\t\t<position>-1</position>\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().."</"..k..">\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<position>"..tostring(entityPositionsV.name[0]):lower().."</position>\n")
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
file:write("\t\t<position>-1</position>\n")
|
||||
end
|
||||
elseif event:getType() == df.history_event_type.ADD_HF_HF_LINK and k == "type" then
|
||||
file:write("\t\t<link_type>"..df_enums.histfig_hf_link_type[v]:lower().."</link_type>\n")
|
||||
elseif event:getType() == df.history_event_type.ADD_HF_SITE_LINK and k == "type" then
|
||||
file:write("\t\t<link_type>"..df_enums.histfig_site_link_type[v]:lower().."</link_type>\n")
|
||||
elseif event:getType() == df.history_event_type.REMOVE_HF_SITE_LINK and k == "type" then
|
||||
file:write("\t\t<link_type>"..df_enums.histfig_site_link_type[v]:lower().."</link_type>\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<item_type>"..df_enums.item_type[v]:lower().."</item_type>\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).."</"..k..">\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\t<item_type>food</item_type>\n")
|
||||
file:write("\t\t<"..k..">"..getItemSubTypeName(df.item_type.FOOD,v).."</"..k..">\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<mattype>"..event.mattype.."</mattype>\n")
|
||||
file:write("\t\t<matindex>"..event.matindex.."</matindex>\n")
|
||||
else
|
||||
file:write("\t\t<mat>"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.mattype, event.matindex)).."</mat>\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<mat_type>"..event.mat_type.."</mat_type>\n")
|
||||
file:write("\t\t<mat_index>"..event.mat_index.."</mat_index>\n")
|
||||
else
|
||||
file:write("\t\t<mat>"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.mat_type, event.mat_index)).."</mat>\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<imp_mat_type>"..event.imp_mat_type.."</imp_mat_type>\n")
|
||||
file:write("\t\t<imp_mat_index>"..event.imp_mat_index.."</imp_mat_index>\n")
|
||||
else
|
||||
file:write("\t\t<imp_mat>"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.imp_mat_type, event.imp_mat_index)).."</imp_mat>\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<dye_mat_type>"..event.dye_mat_type.."</dye_mat_type>\n")
|
||||
file:write("\t\t<dye_mat_index>"..event.dye_mat_index.."</dye_mat_index>\n")
|
||||
else
|
||||
file:write("\t\t<dye_mat>"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.dye_mat_type, event.dye_mat_index)).."</dye_mat>\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<topic>"..tostring(df_enums.meeting_topic[v]):lower().."</topic>\n")
|
||||
elseif event:getType() == df.history_event_type.MASTERPIECE_CREATED_ITEM_IMPROVEMENT and k == "improvement_type" then
|
||||
file:write("\t\t<improvement_type>"..df_enums.improvement_type[v]:lower().."</improvement_type>\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.."</"..k..">\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].."</"..k..">\n")
|
||||
end
|
||||
elseif event:getType() == df.history_event_type.BODY_ABUSED and (k == "props") then
|
||||
file:write("\t\t<props_item_type>"..tostring(df_enums.item_type[event.props.item.item_type]):lower().."</props_item_type>\n")
|
||||
file:write("\t\t<props_item_subtype>"..getItemSubTypeName(event.props.item.item_type,event.props.item.item_subtype).."</props_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<props_item_mat_type>"..event.props.item.mat_type.."</props_item_mat_type>\n")
|
||||
file:write("\t\t<props_item_mat_index>"..event.props.item.mat_index.."</props_item_mat_index>\n")
|
||||
else
|
||||
file:write("\t\t<props_item_mat>"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.props.item.mat_type, event.props.item.mat_index)).."</props_item_mat>\n")
|
||||
end
|
||||
end
|
||||
--file:write("\t\t<"..k.."_item_mat_type>"..tostring(event.props.item.mat_type).."</"..k.."_item_mat_index>\n")
|
||||
--file:write("\t\t<"..k.."_item_mat_index>"..tostring(event.props.item.mat_index).."</"..k.."_item_mat_index>\n")
|
||||
file:write("\t\t<"..k.."_pile_type>"..tostring(event.props.pile_type).."</"..k.."_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<identity_name>"..thisIdentity.name.first_name.."</identity_name>\n")
|
||||
file:write("\t\t<identity_race>"..(df.global.world.raws.creatures.all[thisIdentity.race].creature_id):lower().."</identity_race>\n")
|
||||
file:write("\t\t<identity_caste>"..(df.global.world.raws.creatures.all[thisIdentity.race].caste[thisIdentity.caste].caste_id):lower().."</identity_caste>\n")
|
||||
else
|
||||
file:write("\t\t<identity_hf>"..df.global.world.identities.all[v].histfig_id.."</identity_hf>\n")
|
||||
end
|
||||
end
|
||||
elseif event:getType() == df.history_event_type.MASTERPIECE_CREATED_ARCH_CONSTRUCT and k == "building_type" then
|
||||
file:write("\t\t<building_type>"..df_enums.building_type[v]:lower().."</building_type>\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<building_subtype>"..df_enums.furnace_type[v]:lower().."</building_subtype>\n")
|
||||
elseif v > -1 then
|
||||
file:write("\t\t<building_subtype>"..tostring(v).."</building_subtype>\n")
|
||||
end
|
||||
elseif k == "race" then
|
||||
if v > -1 then
|
||||
file:write("\t\t<race>"..df.global.world.raws.creatures.all[v].name[0].."</race>\n")
|
||||
end
|
||||
elseif k == "caste" then
|
||||
if v > -1 then
|
||||
file:write("\t\t<caste>"..(df.global.world.raws.creatures.all[event.race].caste[v].caste_id):lower().."</caste>\n")
|
||||
end
|
||||
elseif k == "interaction" and event:getType() == df.history_event_type.HF_DOES_INTERACTION then
|
||||
file:write("\t\t<interaction_action>"..df.global.world.raws.interactions[v].str[3].value.."</interaction_action>\n")
|
||||
file:write("\t\t<interaction_string>"..df.global.world.raws.interactions[v].str[4].value.."</interaction_string>\n")
|
||||
elseif k == "interaction" and event:getType() == df.history_event_type.HF_LEARNS_SECRET then
|
||||
file:write("\t\t<secret_text>"..df.global.world.raws.interactions[v].str[2].value.."</secret_text>\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.."</"..detailK..">\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<artifact_id>"..refv.artifact_id.."</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().."</"..detailK..">\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).."</"..detailK..">\n")
|
||||
end
|
||||
elseif (detailK == "mattype") then
|
||||
if (detailV > -1) then
|
||||
file:write("\t\t<mat>"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.weapon.mattype, event.weapon.matindex)).."</mat>\n")
|
||||
end
|
||||
elseif (detailK == "matindex") then
|
||||
|
||||
elseif (detailK == "shooter_item") then
|
||||
if detailV > -1 then
|
||||
file:write("\t\t<"..detailK..">"..detailV.."</"..detailK..">\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<shooter_artifact_id>"..refv.artifact_id.."</shooter_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().."</"..detailK..">\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).."</"..detailK..">\n")
|
||||
end
|
||||
elseif (detailK == "shooter_mattype") then
|
||||
if (detailV > -1) then
|
||||
file:write("\t\t<shooter_mat>"..dfhack.matinfo.toString(dfhack.matinfo.decode(event.weapon.shooter_mattype, event.weapon.shooter_matindex)).."</shooter_mat>\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.."</"..detailK..">\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().."</"..k..">\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().."</"..k..">\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].."</"..k..">\n")
|
||||
else
|
||||
file:write("\t\t<"..k..">"..tostring(v).."</"..k..">\n")
|
||||
end
|
||||
end
|
||||
file:write("\t</historical_event>\n")
|
||||
end
|
||||
end
|
||||
file:write("</historical_events>\n")
|
||||
file:write("<historical_event_collections>\n")
|
||||
file:write("</historical_event_collections>\n")
|
||||
file:write("<historical_eras>\n")
|
||||
file:write("</historical_eras>\n")
|
||||
file:write("</df_world>\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
|
@ -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 <<EOS
|
||||
Kills all creatures of a given race.
|
||||
With no argument, lists possible targets with their head count.
|
||||
With the special argument 'him' or 'her', kill only the currently selected creature.
|
||||
With the special argument 'undead', kill all undead creatures/thralls.
|
||||
|
||||
The targets will bleed out on the next game tick, or if they are immune to that, will vanish in a puff of smoke.
|
||||
|
||||
The special final argument 'magma' will make magma rain on the targets instead.
|
||||
The special final argument 'butcher' will mark the targets for butchering instead.
|
||||
|
||||
Ex: exterminate gob
|
||||
exterminate gob:male
|
||||
exterminate elve magma
|
||||
exterminate him
|
||||
exterminate pig butcher
|
||||
EOS
|
||||
|
||||
when 'him', 'her', 'it', 'that'
|
||||
if him = df.unit_find
|
||||
case him.race_tg.caste[him.caste].gender
|
||||
when 0; puts 'its a she !' if race != 'her'
|
||||
when 1; puts 'its a he !' if race != 'him'
|
||||
else; puts 'its an it !' if race != 'it' and race != 'that'
|
||||
end
|
||||
slayit[him]
|
||||
else
|
||||
puts "Select a target ingame"
|
||||
end
|
||||
|
||||
when /^undead/i
|
||||
count = 0
|
||||
df.world.units.active.each { |u|
|
||||
if (u.enemy.undead or
|
||||
(u.curse.add_tags1.OPPOSED_TO_LIFE and not
|
||||
u.curse.rem_tags1.OPPOSED_TO_LIFE)) and
|
||||
checkunit[u]
|
||||
slayit[u]
|
||||
count += 1
|
||||
end
|
||||
}
|
||||
puts "#{slain} #{count} undeads"
|
||||
|
||||
else
|
||||
if race.index(':')
|
||||
race, caste = race.split(':')
|
||||
end
|
||||
|
||||
raw_race = df.match_rawname(race, all_races.keys)
|
||||
if not raw_race
|
||||
puts "Invalid race, use one of #{all_races.keys.sort.join(' ')}"
|
||||
throw :script_finished
|
||||
end
|
||||
|
||||
race_nr = df.world.raws.creatures.all.index { |cr| cr.creature_id == raw_race }
|
||||
|
||||
if caste
|
||||
all_castes = df.world.raws.creatures.all[race_nr].caste.map { |c| c.caste_id }
|
||||
raw_caste = df.match_rawname(caste, all_castes)
|
||||
if not raw_caste
|
||||
puts "Invalid caste, use one of #{all_castes.sort.join(' ')}"
|
||||
throw :script_finished
|
||||
end
|
||||
caste_nr = all_castes.index(raw_caste)
|
||||
end
|
||||
|
||||
count = 0
|
||||
df.world.units.active.each { |u|
|
||||
if u.race == race_nr and checkunit[u]
|
||||
next if caste_nr and u.caste != caste_nr
|
||||
slayit[u]
|
||||
count += 1
|
||||
end
|
||||
}
|
||||
puts "#{slain} #{count} #{raw_caste} #{raw_race}"
|
||||
|
||||
end
|
@ -1,75 +0,0 @@
|
||||
-- List or manage map features & enable magma furnaces
|
||||
local help = [[=begin
|
||||
|
||||
feature
|
||||
=======
|
||||
Enables management of map features.
|
||||
|
||||
* Discovering a magma feature (magma pool, volcano, magma sea, or curious
|
||||
underground structure) permits magma workshops and furnaces to be built.
|
||||
* Discovering a cavern layer causes plants (trees, shrubs, and grass) from
|
||||
that cavern to grow within your fortress.
|
||||
|
||||
Options:
|
||||
|
||||
:list: Lists all map features in your current embark by index.
|
||||
:magma: Enable magma furnaces (discovers a random magma feature).
|
||||
:show X: Marks the selected map feature as discovered.
|
||||
:hide X: Marks the selected map feature as undiscovered.
|
||||
|
||||
=end]]
|
||||
|
||||
local map_features = df.global.world.features.map_features
|
||||
|
||||
function toggle_feature(idx, discovered)
|
||||
idx = tonumber(idx)
|
||||
if idx < 0 or idx >= #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
|
@ -1,122 +0,0 @@
|
||||
-- makes creatures [in]fertile, by modifying orientation
|
||||
-- usage: fix-ster [fert|ster] [all|animals|only:<creature>]
|
||||
-- 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:<creature>]
|
||||
|
||||
``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:<creature>`` 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 <fert|ster> [all|animals|only:<creature>]")
|
||||
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:<creature> 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
|
@ -1 +0,0 @@
|
||||
``fix/*`` scripts fix various bugs and issues, some of them obscure.
|
@ -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
|
||||
|
@ -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
|
@ -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)
|
@ -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.")
|
@ -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.')
|
@ -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)
|
@ -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.")
|
@ -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)
|
@ -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
|
@ -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.")
|
@ -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.')
|
@ -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 <tweak>`.
|
||||
|
||||
=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
|
@ -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"
|
@ -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()
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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.
|
File diff suppressed because it is too large
Load Diff
@ -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_amount<slot.job_item.quantity
|
||||
end
|
||||
function jobitemEditor:can_remove()
|
||||
local slot=self:get_slot()
|
||||
return #slot.items>0
|
||||
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="<no item>"})
|
||||
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<v.job_item.quantity do
|
||||
local added=false
|
||||
for _,it in ipairs(v.choices) do
|
||||
if not self.assigned[it.id] then
|
||||
self:add_item_to_slot(v,it)
|
||||
added=true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not added then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function jobitemEditor:add_item_to_slot(slot,item)
|
||||
table.insert(slot.items,item)
|
||||
slot.filled_amount=slot.filled_amount+item:getTotalDimension()
|
||||
self.assigned[item.id]=true
|
||||
update_slot_text(slot)
|
||||
self.subviews.itemList:setChoices(self.slots)
|
||||
end
|
||||
function jobitemEditor:remove_item()
|
||||
local slot=self:get_slot()
|
||||
for k,v in pairs(slot.items) do
|
||||
self.assigned[v.id]=nil
|
||||
end
|
||||
slot.items={}
|
||||
slot.filled_amount=0
|
||||
update_slot_text(slot)
|
||||
self.subviews.itemList:setChoices(self.slots)
|
||||
end
|
||||
function jobitemEditor:fill()
|
||||
self.slots={}
|
||||
for k,v in pairs(self.items) do
|
||||
local job_item
|
||||
|
||||
if self.job then
|
||||
job_item=self.job.job_items[k]
|
||||
else
|
||||
job_item=self.job_items[k]
|
||||
end
|
||||
|
||||
table.insert(self.slots,{job_item=job_item, id=k, items={},choices=v,filled_amount=0,slot_id=#self.slots})
|
||||
update_slot_text(self.slots[#self.slots])
|
||||
end
|
||||
self.subviews.itemList:setChoices(self.slots)
|
||||
end
|
||||
|
||||
function jobitemEditor:jobValid()
|
||||
for k,v in pairs(self.slots) do
|
||||
if v.filled_amount<v.job_item.quantity then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
function jobitemEditor:commit()
|
||||
if self.job then
|
||||
for _,slot in pairs(self.slots) do
|
||||
for _1,cur_item in pairs(slot.items) do
|
||||
self.job.items:insert("#",{new=true,item=cur_item,role=df.job_item_ref.T_role.Reagent,job_item_idx=slot.id})
|
||||
end
|
||||
end
|
||||
end
|
||||
self:dismiss()
|
||||
if self.on_okay then self.on_okay(self.slots) end
|
||||
end
|
||||
function jobitemEditor:onDestroy()
|
||||
if self.on_close then
|
||||
self.on_close()
|
||||
end
|
||||
end
|
||||
function showItemEditor(job,item_selections)
|
||||
jobitemEditor{
|
||||
job = job,
|
||||
items = item_selections,
|
||||
on_close = gscript.qresume(nil),
|
||||
on_okay = gscript.mkresume(true)
|
||||
--on_cancel=gscript.mkresume(false)
|
||||
}:show()
|
||||
|
||||
return gscript.wait()
|
||||
end
|
||||
|
||||
return _ENV
|
@ -1,209 +0,0 @@
|
||||
-- Assign weapon racks to squads (needs binpatch)
|
||||
--[[=begin
|
||||
|
||||
gui/assign-rack
|
||||
===============
|
||||
`This script requires a binpatch <binpatches/needs-patch>`, 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
|
@ -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()
|
@ -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
|
@ -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
|
@ -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)<trg_layer.size+sb.layer_size then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
function AddLayering(body_part,item)
|
||||
if not DoesHaveSubtype(item) then
|
||||
return
|
||||
end
|
||||
local sb=item.subtype.props
|
||||
local trg_layer=body_part.layers[sb.layer]
|
||||
trg_layer.permit=math.min(trg_layer.permit,sb.layer_permit)
|
||||
trg_layer.size=trg_layer.size+sb.layer_size
|
||||
end
|
||||
function AddIfFits(body_equip,unit,item)
|
||||
--TODO shaped items
|
||||
|
||||
local need_flag
|
||||
for k,v in pairs(permited_equips) do
|
||||
if k:is_instance(item) then
|
||||
need_flag=v
|
||||
break
|
||||
end
|
||||
end
|
||||
if need_flag==nil then
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
for k,bp in pairs(body_equip) do
|
||||
local handedness_ok=true
|
||||
if df.item_glovesst:is_instance(item) then
|
||||
if bp.bp.flags["LEFT"]~=item.handedness[1] then
|
||||
handedness_ok=false
|
||||
end
|
||||
end
|
||||
if bp.bp.flags[need_flag] and LayeringPermits(bp,item) and handedness_ok then
|
||||
if dfhack.items.moveToInventory(item,unit,2,k) then
|
||||
AddLayering(bp,item)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
function EnumGrasps(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
|
||||
if v.flags.GRASP then
|
||||
--table.insert(ret,{k,v})
|
||||
ret[k]={v}
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
function EnumEmptyGrasps(unit)
|
||||
local grasps=EnumGrasps(unit.race,unit.caste)
|
||||
local ret={}
|
||||
for k,v in pairs(unit.inventory) do
|
||||
if grasps[v.body_part_id] and v.mode==1 then
|
||||
grasps[v.body_part_id][2]=true
|
||||
end
|
||||
end
|
||||
for k,v in pairs(grasps) do
|
||||
if not v[2] then
|
||||
table.insert(ret,k)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
function GetBackpack(unit)
|
||||
for k,v in pairs(unit.inventory) do
|
||||
|
||||
if df.item_backpackst:is_instance(v.item) then
|
||||
return v.item
|
||||
end
|
||||
end
|
||||
end
|
||||
function AddBackpackItems(backpack,items)
|
||||
if backpack then
|
||||
local bitems=dfhack.items.getContainedItems(backpack)
|
||||
for k,v in pairs(bitems) do
|
||||
table.insert(items,v)
|
||||
end
|
||||
end
|
||||
end
|
||||
function GetItemsAtPos(pos)
|
||||
local ret={}
|
||||
for k,v in pairs(df.global.world.items.all) do
|
||||
if v.flags.on_ground and v.pos.x==pos.x and v.pos.y==pos.y and v.pos.z==pos.z then
|
||||
table.insert(ret,v)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
function isAnyOfEquipable(item)
|
||||
for k,v in pairs(permited_equips) do
|
||||
if k:is_instance(item) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
function FilterByEquipable(items)
|
||||
local ret={}
|
||||
for k,v in pairs(items) do
|
||||
if isAnyOfEquipable(v) then
|
||||
table.insert(ret,v)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
function FilterBySize(items,race_id) --TODO add logic for compatible races
|
||||
local ret={}
|
||||
for k,v in pairs(items) do
|
||||
if v.maker_race==race_id then
|
||||
table.insert(ret,v)
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
--local companions=??
|
||||
local orders={
|
||||
{name="move",f=function (unit_list,pos)
|
||||
if not CheckCursor(pos) then
|
||||
return false
|
||||
end
|
||||
for k,v in pairs(unit_list) do
|
||||
v.path.dest:assign(pos)
|
||||
end
|
||||
|
||||
return true
|
||||
end},
|
||||
{name="equip",f=function (unit_list)
|
||||
--search in inventory(hands/backpack/ground) and equip everything
|
||||
--lot's of stuff to think: layering, which item goes where, which goes first, which body parts are missing, body sizes etc...
|
||||
--dfhack.items.moveToInventory(item,unit,use_mode,body_part)
|
||||
for k,unit in pairs(unit_list) do
|
||||
local items=GetItemsAtPos(unit.pos)
|
||||
--todo make a table join function or sth... submit it to the lua list!
|
||||
AddBackpackItems(GetBackpack(unit),items)
|
||||
items=FilterByEquipable(items)
|
||||
FilterBySize(items,unit.race)
|
||||
local body_parts=EnumBodyEquipable(unit.race,unit.caste)
|
||||
ReadCurrentEquiped(body_parts,unit)
|
||||
for it_num,item in pairs(items) do
|
||||
AddIfFits(body_parts,unit,item)
|
||||
end
|
||||
end
|
||||
end},
|
||||
{name="pick-up",f=function (unit_list)
|
||||
--pick everything up (first into hands (if empty) then backpack (if have and has space?))
|
||||
for k,v in pairs(unit_list) do
|
||||
local items=GetItemsAtPos(v.pos)
|
||||
local grasps=EnumEmptyGrasps(v)
|
||||
-- TODO sort with weapon/shield on top of list!
|
||||
--add to grasps, then add to backpack (sanely? i.e. weapons/shields into hands then stuff)
|
||||
--or add to backpack if have, only then check grasps (faster equiping)
|
||||
while #grasps >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()
|
@ -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()
|
@ -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
|
@ -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
|
||||
|
@ -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
|
@ -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 <lua command> - 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="<no item>",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
|
||||
|
@ -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="<unlearned>",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="<none>",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="<unnamed>:$ID",
|
||||
name_other="<other(-1)>",
|
||||
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 "<other (-1)>"
|
||||
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 "<invalid>"
|
||||
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 "<unnamed>:$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(": <none>")
|
||||
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=": <none>",
|
||||
}
|
||||
}
|
||||
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 "<unnamed wound>"
|
||||
end
|
||||
function format_wound( list_id,wound, unit)
|
||||
|
||||
local name="<unnamed wound>"
|
||||
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()
|
@ -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()
|
@ -1,8 +0,0 @@
|
||||
--@ alias = 'gui/create-item'
|
||||
--[[=begin
|
||||
|
||||
gui/hack-wish
|
||||
=============
|
||||
An alias for `gui/create-item`. Deprecated.
|
||||
|
||||
=end]]
|
@ -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()
|
@ -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()
|
@ -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)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue