scripts: switch to windows lineendings

develop
jj 2013-04-21 14:21:53 +02:00
parent d7f5f06d18
commit ba4f649b3c
18 changed files with 1374 additions and 1374 deletions

@ -1,176 +1,176 @@
class AutoFarm class AutoFarm
def initialize def initialize
@thresholds = Hash.new(50) @thresholds = Hash.new(50)
@lastcounts = Hash.new(0) @lastcounts = Hash.new(0)
end end
def setthreshold(id, v) def setthreshold(id, v)
list = df.world.raws.plants.all.find_all { |plt| plt.flags[:SEED] }.map { |plt| plt.id } list = df.world.raws.plants.all.find_all { |plt| plt.flags[:SEED] }.map { |plt| plt.id }
if tok = df.match_rawname(id, list) if tok = df.match_rawname(id, list)
@thresholds[tok] = v.to_i @thresholds[tok] = v.to_i
else else
puts "No plant with id #{id}, try one of " + puts "No plant with id #{id}, try one of " +
list.map { |w| w =~ /[^\w]/ ? w.inspect : w }.sort.join(' ') list.map { |w| w =~ /[^\w]/ ? w.inspect : w }.sort.join(' ')
end end
end end
def setdefault(v) def setdefault(v)
@thresholds.default = v.to_i @thresholds.default = v.to_i
end end
def is_plantable(plant) def is_plantable(plant)
has_seed = plant.flags[:SEED] has_seed = plant.flags[:SEED]
season = df.cur_season season = df.cur_season
harvest = df.cur_season_tick + plant.growdur * 10 harvest = df.cur_season_tick + plant.growdur * 10
will_finish = harvest < 10080 will_finish = harvest < 10080
can_plant = has_seed && plant.flags[season] can_plant = has_seed && plant.flags[season]
can_plant = can_plant && (will_finish || plant.flags[(season+1)%4]) can_plant = can_plant && (will_finish || plant.flags[(season+1)%4])
can_plant can_plant
end end
def find_plantable_plants def find_plantable_plants
plantable = {} plantable = {}
counts = Hash.new(0) counts = Hash.new(0)
df.world.items.other[:SEEDS].each { |i| df.world.items.other[:SEEDS].each { |i|
if (!i.flags.dump && !i.flags.forbid && !i.flags.garbage_collect && if (!i.flags.dump && !i.flags.forbid && !i.flags.garbage_collect &&
!i.flags.hostile && !i.flags.on_fire && !i.flags.rotten && !i.flags.hostile && !i.flags.on_fire && !i.flags.rotten &&
!i.flags.trader && !i.flags.in_building && !i.flags.construction && !i.flags.trader && !i.flags.in_building && !i.flags.construction &&
!i.flags.artifact) !i.flags.artifact)
counts[i.mat_index] += i.stack_size counts[i.mat_index] += i.stack_size
end end
} }
counts.keys.each { |i| counts.keys.each { |i|
if df.ui.tasks.known_plants[i] if df.ui.tasks.known_plants[i]
plant = df.world.raws.plants.all[i] plant = df.world.raws.plants.all[i]
if is_plantable(plant) if is_plantable(plant)
plantable[i] = :Surface if (plant.underground_depth_min == 0 || plant.underground_depth_max == 0) 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) plantable[i] = :Underground if (plant.underground_depth_min > 0 || plant.underground_depth_max > 0)
end end
end end
} }
return plantable return plantable
end end
def set_farms(plants, farms) def set_farms(plants, farms)
return if farms.length == 0 return if farms.length == 0
if plants.length == 0 if plants.length == 0
plants = [-1] plants = [-1]
end end
season = df.cur_season season = df.cur_season
farms.each_with_index { |f, idx| farms.each_with_index { |f, idx|
f.plant_id[season] = plants[idx % plants.length] f.plant_id[season] = plants[idx % plants.length]
} }
end end
def process def process
plantable = find_plantable_plants plantable = find_plantable_plants
@lastcounts = Hash.new(0) @lastcounts = Hash.new(0)
df.world.items.other[:PLANT].each { |i| df.world.items.other[:PLANT].each { |i|
if (!i.flags.dump && !i.flags.forbid && !i.flags.garbage_collect && if (!i.flags.dump && !i.flags.forbid && !i.flags.garbage_collect &&
!i.flags.hostile && !i.flags.on_fire && !i.flags.rotten && !i.flags.hostile && !i.flags.on_fire && !i.flags.rotten &&
!i.flags.trader && !i.flags.in_building && !i.flags.construction && !i.flags.trader && !i.flags.in_building && !i.flags.construction &&
!i.flags.artifact && plantable.has_key?(i.mat_index)) !i.flags.artifact && plantable.has_key?(i.mat_index))
id = df.world.raws.plants.all[i.mat_index].id id = df.world.raws.plants.all[i.mat_index].id
@lastcounts[id] += i.stack_size @lastcounts[id] += i.stack_size
end end
} }
return unless @running return unless @running
plants_s = [] plants_s = []
plants_u = [] plants_u = []
plantable.each_key { |k| plantable.each_key { |k|
plant = df.world.raws.plants.all[k] plant = df.world.raws.plants.all[k]
if (@lastcounts[plant.id] < @thresholds[plant.id]) if (@lastcounts[plant.id] < @thresholds[plant.id])
plants_s.push(k) if plantable[k] == :Surface plants_s.push(k) if plantable[k] == :Surface
plants_u.push(k) if plantable[k] == :Underground plants_u.push(k) if plantable[k] == :Underground
end end
} }
farms_s = [] farms_s = []
farms_u = [] farms_u = []
df.world.buildings.other[:FARM_PLOT].each { |f| df.world.buildings.other[:FARM_PLOT].each { |f|
if (f.flags.exists) if (f.flags.exists)
underground = df.map_designation_at(f.centerx,f.centery,f.z).subterranean underground = df.map_designation_at(f.centerx,f.centery,f.z).subterranean
farms_s.push(f) unless underground farms_s.push(f) unless underground
farms_u.push(f) if underground farms_u.push(f) if underground
end end
} }
set_farms(plants_s, farms_s) set_farms(plants_s, farms_s)
set_farms(plants_u, farms_u) set_farms(plants_u, farms_u)
end end
def start def start
return if @running return if @running
@onupdate = df.onupdate_register('autofarm', 1200) { process } @onupdate = df.onupdate_register('autofarm', 1200) { process }
@running = true @running = true
end end
def stop def stop
df.onupdate_unregister(@onupdate) df.onupdate_unregister(@onupdate)
@running = false @running = false
end end
def status def status
stat = @running ? "Running." : "Stopped." stat = @running ? "Running." : "Stopped."
@lastcounts.each { |k,v| @lastcounts.each { |k,v|
stat << "\n#{k} limit #{@thresholds.fetch(k, 'default')} current #{v}" stat << "\n#{k} limit #{@thresholds.fetch(k, 'default')} current #{v}"
} }
@thresholds.each { |k,v| @thresholds.each { |k,v|
stat << "\n#{k} limit #{v} current 0" unless @lastcounts.has_key?(k) stat << "\n#{k} limit #{v} current 0" unless @lastcounts.has_key?(k)
} }
stat << "\nDefault: #{@thresholds.default}" stat << "\nDefault: #{@thresholds.default}"
stat stat
end end
end end
$AutoFarm ||= AutoFarm.new $AutoFarm ||= AutoFarm.new
case $script_args[0] case $script_args[0]
when 'start', 'enable' when 'start', 'enable'
$AutoFarm.start $AutoFarm.start
puts $AutoFarm.status puts $AutoFarm.status
when 'end', 'stop', 'disable' when 'end', 'stop', 'disable'
$AutoFarm.stop $AutoFarm.stop
puts 'Stopped.' puts 'Stopped.'
when 'default' when 'default'
$AutoFarm.setdefault($script_args[1]) $AutoFarm.setdefault($script_args[1])
when 'threshold' when 'threshold'
t = $script_args[1] t = $script_args[1]
$script_args[2..-1].each {|i| $script_args[2..-1].each {|i|
$AutoFarm.setthreshold(i, t) $AutoFarm.setthreshold(i, t)
} }
when 'delete' when 'delete'
$AutoFarm.stop $AutoFarm.stop
$AutoFarm = nil $AutoFarm = nil
when 'help', '?' when 'help', '?'
puts <<EOS puts <<EOS
Automatically handle crop selection in farm plots based on current plant stocks. Automatically handle crop selection in farm plots based on current plant stocks.
Selects a crop for planting if current stock is below a threshold. Selects a crop for planting if current stock is below a threshold.
Selected crops are dispatched on all farmplots. Selected crops are dispatched on all farmplots.
Usage: Usage:
autofarm start autofarm start
autofarm default 30 autofarm default 30
autofarm threshold 150 helmet_plump tail_pig autofarm threshold 150 helmet_plump tail_pig
EOS EOS
else else
$AutoFarm.process $AutoFarm.process
puts $AutoFarm.status puts $AutoFarm.status
end end

@ -1,58 +1,58 @@
class AutoUnsuspend class AutoUnsuspend
def initialize def initialize
end end
def process def process
return false unless @running return false unless @running
joblist = df.world.job_list.next joblist = df.world.job_list.next
count = 0 count = 0
while joblist while joblist
job = joblist.item job = joblist.item
joblist = joblist.next joblist = joblist.next
if job.job_type == :ConstructBuilding if job.job_type == :ConstructBuilding
if (job.flags.suspend) if (job.flags.suspend)
item = job.items[0].item item = job.items[0].item
job.flags.suspend = false job.flags.suspend = false
count += 1 count += 1
end end
end end
end end
puts "Unsuspended #{count} job(s)." unless count == 0 puts "Unsuspended #{count} job(s)." unless count == 0
end end
def start def start
@onupdate = df.onupdate_register('autounsuspend', 5) { process } @onupdate = df.onupdate_register('autounsuspend', 5) { process }
@running = true @running = true
end end
def stop def stop
df.onupdate_unregister(@onupdate) df.onupdate_unregister(@onupdate)
@running = false @running = false
end end
def status def status
@running ? 'Running.' : 'Stopped.' @running ? 'Running.' : 'Stopped.'
end end
end end
case $script_args[0] case $script_args[0]
when 'start' when 'start'
$AutoUnsuspend = AutoUnsuspend.new unless $AutoUnsuspend $AutoUnsuspend = AutoUnsuspend.new unless $AutoUnsuspend
$AutoUnsuspend.start $AutoUnsuspend.start
when 'end', 'stop' when 'end', 'stop'
$AutoUnsuspend.stop $AutoUnsuspend.stop
else else
if $AutoUnsuspend if $AutoUnsuspend
puts $AutoUnsuspend.status puts $AutoUnsuspend.status
else else
puts 'Not loaded.' puts 'Not loaded.'
end end
end end

@ -1,177 +1,177 @@
# create first necessity items under cursor # create first necessity items under cursor
category = $script_args[0] || 'help' category = $script_args[0] || 'help'
mat_raw = $script_args[1] || 'list' mat_raw = $script_args[1] || 'list'
count = $script_args[2] count = $script_args[2]
category = df.match_rawname(category, ['help', 'bars', 'boulders', 'plants', 'logs', 'webs', 'anvils']) || 'help' category = df.match_rawname(category, ['help', 'bars', 'boulders', 'plants', 'logs', 'webs', 'anvils']) || 'help'
if category == 'help' if category == 'help'
puts <<EOS puts <<EOS
Create first necessity items under the cursor. Create first necessity items under the cursor.
Usage: Usage:
create-items [category] [raws token] [number] create-items [category] [raws token] [number]
Item categories: Item categories:
bars, boulders, plants, logs, webs, anvils bars, boulders, plants, logs, webs, anvils
Raw token: Raw token:
Either a full token (PLANT_MAT:ADLER:WOOD) or the middle part only Either a full token (PLANT_MAT:ADLER:WOOD) or the middle part only
(the missing part is autocompleted depending on the item category) (the missing part is autocompleted depending on the item category)
Use 'list' to show all possibilities Use 'list' to show all possibilities
Exemples: Exemples:
create-items boulders hematite 30 create-items boulders hematite 30
create-items bars CREATURE_MAT:CAT:SOAP 10 create-items bars CREATURE_MAT:CAT:SOAP 10
create-items web cave_giant create-items web cave_giant
create-items plants list create-items plants list
EOS EOS
throw :script_finished throw :script_finished
elsif mat_raw == 'list' elsif mat_raw == 'list'
# allowed with no cursor # allowed with no cursor
elsif df.cursor.x == -30000 elsif df.cursor.x == -30000
puts "Please place the game cursor somewhere" puts "Please place the game cursor somewhere"
throw :script_finished throw :script_finished
elsif !(maptile = df.map_tile_at(df.cursor)) elsif !(maptile = df.map_tile_at(df.cursor))
puts "Error: unallocated map block !" puts "Error: unallocated map block !"
throw :script_finished throw :script_finished
elsif !maptile.shape_passablehigh elsif !maptile.shape_passablehigh
puts "Error: impassible tile !" puts "Error: impassible tile !"
throw :script_finished throw :script_finished
end end
def match_list(tok, list) def match_list(tok, list)
if tok != 'list' if tok != 'list'
tok = df.match_rawname(tok, list) tok = df.match_rawname(tok, list)
if not tok if not tok
puts "Invalid raws token, use one in:" puts "Invalid raws token, use one in:"
tok = 'list' tok = 'list'
end end
end end
if tok == 'list' if tok == 'list'
puts list.map { |w| w =~ /[^\w]/ ? w.inspect : w }.join(' ') puts list.map { |w| w =~ /[^\w]/ ? w.inspect : w }.join(' ')
throw :script_finished throw :script_finished
end end
tok tok
end end
case category case category
when 'bars' when 'bars'
# create metal bar, eg createbar INORGANIC:IRON # create metal bar, eg createbar INORGANIC:IRON
cls = DFHack::ItemBarst cls = DFHack::ItemBarst
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil) if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
list = df.world.raws.inorganics.find_all { |ino| list = df.world.raws.inorganics.find_all { |ino|
ino.material.flags[:IS_METAL] ino.material.flags[:IS_METAL]
}.map { |ino| ino.id } }.map { |ino| ino.id }
mat_raw = match_list(mat_raw, list) mat_raw = match_list(mat_raw, list)
mat_raw = "INORGANIC:#{mat_raw}" mat_raw = "INORGANIC:#{mat_raw}"
puts mat_raw puts mat_raw
end end
customize = lambda { |item| customize = lambda { |item|
item.dimension = 150 item.dimension = 150
item.subtype = -1 item.subtype = -1
} }
when 'boulders' when 'boulders'
cls = DFHack::ItemBoulderst cls = DFHack::ItemBoulderst
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil) if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
list = df.world.raws.inorganics.find_all { |ino| list = df.world.raws.inorganics.find_all { |ino|
ino.material.flags[:IS_STONE] ino.material.flags[:IS_STONE]
}.map { |ino| ino.id } }.map { |ino| ino.id }
mat_raw = match_list(mat_raw, list) mat_raw = match_list(mat_raw, list)
mat_raw = "INORGANIC:#{mat_raw}" mat_raw = "INORGANIC:#{mat_raw}"
puts mat_raw puts mat_raw
end end
when 'plants' when 'plants'
cls = DFHack::ItemPlantst cls = DFHack::ItemPlantst
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil) if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
list = df.world.raws.plants.all.find_all { |plt| list = df.world.raws.plants.all.find_all { |plt|
plt.material.find { |mat| mat.id == 'STRUCTURAL' } plt.material.find { |mat| mat.id == 'STRUCTURAL' }
}.map { |plt| plt.id } }.map { |plt| plt.id }
mat_raw = match_list(mat_raw, list) mat_raw = match_list(mat_raw, list)
mat_raw = "PLANT_MAT:#{mat_raw}:STRUCTURAL" mat_raw = "PLANT_MAT:#{mat_raw}:STRUCTURAL"
puts mat_raw puts mat_raw
end end
when 'logs' when 'logs'
cls = DFHack::ItemWoodst cls = DFHack::ItemWoodst
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil) if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
list = df.world.raws.plants.all.find_all { |plt| list = df.world.raws.plants.all.find_all { |plt|
plt.material.find { |mat| mat.id == 'WOOD' } plt.material.find { |mat| mat.id == 'WOOD' }
}.map { |plt| plt.id } }.map { |plt| plt.id }
mat_raw = match_list(mat_raw, list) mat_raw = match_list(mat_raw, list)
mat_raw = "PLANT_MAT:#{mat_raw}:WOOD" mat_raw = "PLANT_MAT:#{mat_raw}:WOOD"
puts mat_raw puts mat_raw
end end
when 'webs' when 'webs'
cls = DFHack::ItemThreadst cls = DFHack::ItemThreadst
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil) if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
list = df.world.raws.creatures.all.find_all { |cre| list = df.world.raws.creatures.all.find_all { |cre|
cre.material.find { |mat| mat.id == 'SILK' } cre.material.find { |mat| mat.id == 'SILK' }
}.map { |cre| cre.creature_id } }.map { |cre| cre.creature_id }
mat_raw = match_list(mat_raw, list) mat_raw = match_list(mat_raw, list)
mat_raw = "CREATURE_MAT:#{mat_raw}:SILK" mat_raw = "CREATURE_MAT:#{mat_raw}:SILK"
puts mat_raw puts mat_raw
end end
count ||= 1 count ||= 1
customize = lambda { |item| customize = lambda { |item|
item.flags.spider_web = true item.flags.spider_web = true
item.dimension = 15000 # XXX may depend on creature (this is for GCS) item.dimension = 15000 # XXX may depend on creature (this is for GCS)
} }
when 'anvils' when 'anvils'
cls = DFHack::ItemAnvilst cls = DFHack::ItemAnvilst
if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil) if mat_raw !~ /:/ and !(df.decode_mat(mat_raw) rescue nil)
list = df.world.raws.inorganics.find_all { |ino| list = df.world.raws.inorganics.find_all { |ino|
ino.material.flags[:IS_METAL] ino.material.flags[:IS_METAL]
}.map { |ino| ino.id } }.map { |ino| ino.id }
mat_raw = match_list(mat_raw, list) mat_raw = match_list(mat_raw, list)
mat_raw = "INORGANIC:#{mat_raw}" mat_raw = "INORGANIC:#{mat_raw}"
puts mat_raw puts mat_raw
end end
count ||= 1 count ||= 1
end end
mat = df.decode_mat mat_raw mat = df.decode_mat mat_raw
count ||= 20 count ||= 20
count.to_i.times { count.to_i.times {
item = cls.cpp_new item = cls.cpp_new
item.id = df.item_next_id item.id = df.item_next_id
item.stack_size = 1 item.stack_size = 1
item.mat_type = mat.mat_type item.mat_type = mat.mat_type
item.mat_index = mat.mat_index item.mat_index = mat.mat_index
customize[item] if customize customize[item] if customize
df.item_next_id += 1 df.item_next_id += 1
item.categorize(true) item.categorize(true)
df.world.items.all << item df.world.items.all << item
item.pos = df.cursor item.pos = df.cursor
item.flags.on_ground = true item.flags.on_ground = true
df.map_tile_at.mapblock.items << item.id df.map_tile_at.mapblock.items << item.id
df.map_tile_at.occupancy.item = true df.map_tile_at.occupancy.item = true
} }
# move game view, so that the ui menu updates # move game view, so that the ui menu updates
if df.cursor.z > 5 if df.cursor.z > 5
df.curview.feed_keys(:CURSOR_DOWN_Z) df.curview.feed_keys(:CURSOR_DOWN_Z)
df.curview.feed_keys(:CURSOR_UP_Z) df.curview.feed_keys(:CURSOR_UP_Z)
else else
df.curview.feed_keys(:CURSOR_UP_Z) df.curview.feed_keys(:CURSOR_UP_Z)
df.curview.feed_keys(:CURSOR_DOWN_Z) df.curview.feed_keys(:CURSOR_DOWN_Z)
end end

@ -1,67 +1,67 @@
# show death cause of a creature # show death cause of a creature
def display_death_event(e) 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 = "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 << " (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 << " 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 << " using a #{df.world.raws.itemdefs.weapons[e.weapon.item_subtype].name}" if e.weapon.item_type == :WEAPON
str << ", shot by a #{df.world.raws.itemdefs.weapons[e.weapon.bow_item_subtype].name}" if e.weapon.bow_item_type == :WEAPON str << ", shot by a #{df.world.raws.itemdefs.weapons[e.weapon.bow_item_subtype].name}" if e.weapon.bow_item_type == :WEAPON
puts str.chomp(',') + '.' puts str.chomp(',') + '.'
end end
def display_death_unit(u) def display_death_unit(u)
death_info = u.counters.death_tg death_info = u.counters.death_tg
killer = death_info.killer_tg if death_info killer = death_info.killer_tg if death_info
str = "The #{u.race_tg.name[0]}" str = "The #{u.race_tg.name[0]}"
str << " #{u.name}" if u.name.has_name str << " #{u.name}" if u.name.has_name
str << " died" str << " died"
str << " in year #{death_info.event_year}" if death_info 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 << " (cause: #{u.counters.death_cause.to_s.downcase})," if u.counters.death_cause != -1
str << " killed by the #{killer.race_tg.name[0]} #{killer.name}" if killer str << " killed by the #{killer.race_tg.name[0]} #{killer.name}" if killer
puts str.chomp(',') + '.' puts str.chomp(',') + '.'
end end
item = df.item_find(:selected) item = df.item_find(:selected)
unit = df.unit_find(:selected) unit = df.unit_find(:selected)
if !item or !item.kind_of?(DFHack::ItemBodyComponent) if !item or !item.kind_of?(DFHack::ItemBodyComponent)
item = df.world.items.other[:ANY_CORPSE].find { |i| df.at_cursor?(i) } item = df.world.items.other[:ANY_CORPSE].find { |i| df.at_cursor?(i) }
end end
if item and item.kind_of?(DFHack::ItemBodyComponent) if item and item.kind_of?(DFHack::ItemBodyComponent)
hf = item.hist_figure_id hf = item.hist_figure_id
elsif unit elsif unit
hf = unit.hist_figure_id hf = unit.hist_figure_id
end end
if not hf if not hf
puts "Please select a corpse in the loo'k' menu, or an unit in the 'u'nitlist screen" puts "Please select a corpse in the loo'k' menu, or an unit in the 'u'nitlist screen"
elsif hf == -1 elsif hf == -1
if unit ||= item.unit_tg if unit ||= item.unit_tg
display_death_unit(unit) display_death_unit(unit)
else else
puts "Not a historical figure, cannot death find info" puts "Not a historical figure, cannot death find info"
end end
else else
histfig = df.world.history.figures.binsearch(hf) histfig = df.world.history.figures.binsearch(hf)
unit = histfig ? df.unit_find(histfig.unit_id) : nil unit = histfig ? df.unit_find(histfig.unit_id) : nil
if unit and not unit.flags1.dead and not unit.flags3.ghostly if unit and not unit.flags1.dead and not unit.flags3.ghostly
puts "#{unit.name} is not dead yet !" puts "#{unit.name} is not dead yet !"
else else
events = df.world.history.events events = df.world.history.events
(0...events.length).reverse_each { |i| (0...events.length).reverse_each { |i|
e = events[i] e = events[i]
if e.kind_of?(DFHack::HistoryEventHistFigureDiedst) and e.victim_hf == hf if e.kind_of?(DFHack::HistoryEventHistFigureDiedst) and e.victim_hf == hf
display_death_event(e) display_death_event(e)
break break
end end
} }
end end
end end

@ -1,38 +1,38 @@
# designate an area for digging according to a plan in csv format # designate an area for digging according to a plan in csv format
raise "usage: digfort <plan filename>" if not $script_args[0] raise "usage: digfort <plan filename>" if not $script_args[0]
planfile = File.read($script_args[0]) planfile = File.read($script_args[0])
if df.cursor.x == -30000 if df.cursor.x == -30000
raise "place the game cursor to the top-left corner of the design" raise "place the game cursor to the top-left corner of the design"
end end
tiles = planfile.lines.map { |l| tiles = planfile.lines.map { |l|
l.sub(/#.*/, '').split(';').map { |t| t.strip } l.sub(/#.*/, '').split(';').map { |t| t.strip }
} }
x = x0 = df.cursor.x x = x0 = df.cursor.x
y = df.cursor.y y = df.cursor.y
z = df.cursor.z z = df.cursor.z
tiles.each { |line| tiles.each { |line|
next if line.empty? or line == [''] next if line.empty? or line == ['']
line.each { |tile| line.each { |tile|
t = df.map_tile_at(x, y, z) t = df.map_tile_at(x, y, z)
s = t.shape_basic s = t.shape_basic
case tile case tile
when 'd'; t.dig(:Default) if s == :Wall when 'd'; t.dig(:Default) if s == :Wall
when 'u'; t.dig(:UpStair) if s == :Wall when 'u'; t.dig(:UpStair) if s == :Wall
when 'j'; t.dig(:DownStair) if s == :Wall or s == :Floor when 'j'; t.dig(:DownStair) if s == :Wall or s == :Floor
when 'i'; t.dig(:UpDownStair) if s == :Wall when 'i'; t.dig(:UpDownStair) if s == :Wall
when 'h'; t.dig(:Channel) if s == :Wall or s == :Floor when 'h'; t.dig(:Channel) if s == :Wall or s == :Floor
when 'r'; t.dig(:Ramp) if s == :Wall when 'r'; t.dig(:Ramp) if s == :Wall
when 'x'; t.dig(:No) when 'x'; t.dig(:No)
end end
x += 1 x += 1
} }
x = x0 x = x0
y += 1 y += 1
} }
puts 'done' puts 'done'

@ -1,13 +1,13 @@
# remove all aquifers from the map # remove all aquifers from the map
count = 0 count = 0
df.each_map_block { |b| df.each_map_block { |b|
if b.designation[0][0].water_table or b.designation[8][8].water_table if b.designation[0][0].water_table or b.designation[8][8].water_table
count += 1 count += 1
df.each_map_block_z(b.map_pos.z) { |bz| df.each_map_block_z(b.map_pos.z) { |bz|
bz.designation.each { |dx| dx.each { |dy| dy.water_table = false } } bz.designation.each { |dx| dx.each { |dy| dy.water_table = false } }
} }
end end
} }
puts "cleared #{count} aquifer#{'s' if count > 1}" puts "cleared #{count} aquifer#{'s' if count > 1}"

@ -1,135 +1,135 @@
# exterminate creatures # exterminate creatures
# race = name of the race to eradicate, use 'him' to target only the selected creature # race = name of the race to eradicate, use 'him' to target only the selected creature
# use 'undead' to target all undeads # use 'undead' to target all undeads
race = $script_args[0] race = $script_args[0]
# if the 2nd parameter is 'magma', magma rain for the targets instead of instant death # 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) # if it is 'butcher' mark all units for butchering (wont work with hostiles)
kill_by = $script_args[1] kill_by = $script_args[1]
case kill_by case kill_by
when 'magma' when 'magma'
slain = 'burning' slain = 'burning'
when 'slaughter', 'butcher' when 'slaughter', 'butcher'
slain = 'marked for butcher' slain = 'marked for butcher'
when nil when nil
slain = 'slain' slain = 'slain'
else else
race = 'help' race = 'help'
end end
checkunit = lambda { |u| checkunit = lambda { |u|
(u.body.blood_count != 0 or u.body.blood_max == 0) and (u.body.blood_count != 0 or u.body.blood_max == 0) and
not u.flags1.dead and not u.flags1.dead and
not u.flags1.caged and not u.flags1.chained and not u.flags1.caged and not u.flags1.chained and
#not u.flags1.hidden_in_ambush and #not u.flags1.hidden_in_ambush and
not df.map_designation_at(u).hidden not df.map_designation_at(u).hidden
} }
slayit = lambda { |u| slayit = lambda { |u|
case kill_by case kill_by
when 'magma' when 'magma'
# it's getting hot around here # it's getting hot around here
# !!WARNING!! do not call on a magma-safe creature # !!WARNING!! do not call on a magma-safe creature
ouh = df.onupdate_register("exterminate ensure #{u.id}", 1) { ouh = df.onupdate_register("exterminate ensure #{u.id}", 1) {
if u.flags1.dead if u.flags1.dead
df.onupdate_unregister(ouh) df.onupdate_unregister(ouh)
else else
x, y, z = u.pos.x, u.pos.y, u.pos.z 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 z += 1 while tile = df.map_tile_at(x, y, z+1) and
tile.shape_passableflow and tile.shape_passablelow tile.shape_passableflow and tile.shape_passablelow
df.map_tile_at(x, y, z).spawn_magma(7) df.map_tile_at(x, y, z).spawn_magma(7)
end end
} }
when 'butcher', 'slaughter' when 'butcher', 'slaughter'
# mark for slaughter at butcher's shop # mark for slaughter at butcher's shop
u.flags2.slaughter = true u.flags2.slaughter = true
else else
# just make them drop dead # just make them drop dead
u.body.blood_count = 0 u.body.blood_count = 0
# some races dont mind having no blood, ensure they are still taken care of. # some races dont mind having no blood, ensure they are still taken care of.
u.animal.vanish_countdown = 2 u.animal.vanish_countdown = 2
end end
} }
all_races = Hash.new(0) all_races = Hash.new(0)
df.world.units.active.map { |u| df.world.units.active.map { |u|
if checkunit[u] if checkunit[u]
if (u.enemy.undead or if (u.enemy.undead or
(u.curse.add_tags1.OPPOSED_TO_LIFE and not (u.curse.add_tags1.OPPOSED_TO_LIFE and not
u.curse.rem_tags1.OPPOSED_TO_LIFE)) u.curse.rem_tags1.OPPOSED_TO_LIFE))
all_races['Undead'] += 1 all_races['Undead'] += 1
else else
all_races[u.race_tg.creature_id] += 1 all_races[u.race_tg.creature_id] += 1
end end
end end
} }
case race case race
when nil when nil
all_races.sort_by { |race, cnt| [cnt, race] }.each{ |race, cnt| puts " #{race} #{cnt}" } all_races.sort_by { |race, cnt| [cnt, race] }.each{ |race, cnt| puts " #{race} #{cnt}" }
when 'help', '?' when 'help', '?'
puts <<EOS puts <<EOS
Kills all creatures of a given race. Kills all creatures of a given race.
With no argument, lists possible targets with their head count. 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 'him' or 'her', kill only the currently selected creature.
With the special argument 'undead', kill all undead creatures/thralls. 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 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 'magma' will make magma rain on the targets instead.
The special final argument 'butcher' will mark the targets for butchering instead. The special final argument 'butcher' will mark the targets for butchering instead.
Ex: exterminate gob Ex: exterminate gob
exterminate elve magma exterminate elve magma
exterminate him exterminate him
exterminate pig butcher exterminate pig butcher
EOS EOS
when 'him', 'her', 'it', 'that' when 'him', 'her', 'it', 'that'
if him = df.unit_find if him = df.unit_find
case him.race_tg.caste[him.caste].gender case him.race_tg.caste[him.caste].gender
when 0; puts 'its a she !' if race != 'her' when 0; puts 'its a she !' if race != 'her'
when 1; puts 'its a he !' if race != 'him' when 1; puts 'its a he !' if race != 'him'
else; puts 'its an it !' if race != 'it' and race != 'that' else; puts 'its an it !' if race != 'it' and race != 'that'
end end
slayit[him] slayit[him]
else else
puts "Select a target ingame" puts "Select a target ingame"
end end
when /^undead/i when /^undead/i
count = 0 count = 0
df.world.units.active.each { |u| df.world.units.active.each { |u|
if (u.enemy.undead or if (u.enemy.undead or
(u.curse.add_tags1.OPPOSED_TO_LIFE and not (u.curse.add_tags1.OPPOSED_TO_LIFE and not
u.curse.rem_tags1.OPPOSED_TO_LIFE)) and u.curse.rem_tags1.OPPOSED_TO_LIFE)) and
checkunit[u] checkunit[u]
slayit[u] slayit[u]
count += 1 count += 1
end end
} }
puts "#{slain} #{count} undeads" puts "#{slain} #{count} undeads"
else else
raw_race = df.match_rawname(race, all_races.keys) raw_race = df.match_rawname(race, all_races.keys)
if not raw_race if not raw_race
puts "Invalid race, use one of #{all_races.keys.sort.join(' ')}" puts "Invalid race, use one of #{all_races.keys.sort.join(' ')}"
throw :script_finished throw :script_finished
end end
race_nr = df.world.raws.creatures.all.index { |cr| cr.creature_id == raw_race } race_nr = df.world.raws.creatures.all.index { |cr| cr.creature_id == raw_race }
count = 0 count = 0
df.world.units.active.each { |u| df.world.units.active.each { |u|
if u.race == race_nr and checkunit[u] if u.race == race_nr and checkunit[u]
slayit[u] slayit[u]
count += 1 count += 1
end end
} }
puts "#{slain} #{count} #{raw_race}" puts "#{slain} #{count} #{raw_race}"
end end

@ -1,64 +1,64 @@
# script to fix loyalty cascade, when you order your militia to kill friendly units # script to fix loyalty cascade, when you order your militia to kill friendly units
def fixunit(unit) def fixunit(unit)
return if unit.race != df.ui.race_id or unit.civ_id != df.ui.civ_id return if unit.race != df.ui.race_id or unit.civ_id != df.ui.civ_id
links = unit.hist_figure_tg.entity_links links = unit.hist_figure_tg.entity_links
fixed = false fixed = false
# check if the unit is a civ renegade # check if the unit is a civ renegade
if i1 = links.index { |l| if i1 = links.index { |l|
l.kind_of?(DFHack::HistfigEntityLinkFormerMemberst) and l.kind_of?(DFHack::HistfigEntityLinkFormerMemberst) and
l.entity_id == df.ui.civ_id l.entity_id == df.ui.civ_id
} and i2 = links.index { |l| } and i2 = links.index { |l|
l.kind_of?(DFHack::HistfigEntityLinkEnemyst) and l.kind_of?(DFHack::HistfigEntityLinkEnemyst) and
l.entity_id == df.ui.civ_id l.entity_id == df.ui.civ_id
} }
fixed = true fixed = true
i1, i2 = i2, i1 if i1 > i2 i1, i2 = i2, i1 if i1 > i2
links.delete_at i2 links.delete_at i2
links.delete_at i1 links.delete_at i1
links << DFHack::HistfigEntityLinkMemberst.cpp_new(:entity_id => df.ui.civ_id, :link_strength => 100) 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" df.add_announcement "fixloyalty: #{unit.name} is now a member of #{df.ui.civ_tg.name} again"
end end
# check if the unit is a group renegade # check if the unit is a group renegade
if i1 = links.index { |l| if i1 = links.index { |l|
l.kind_of?(DFHack::HistfigEntityLinkFormerMemberst) and l.kind_of?(DFHack::HistfigEntityLinkFormerMemberst) and
l.entity_id == df.ui.group_id l.entity_id == df.ui.group_id
} and i2 = links.index { |l| } and i2 = links.index { |l|
l.kind_of?(DFHack::HistfigEntityLinkEnemyst) and l.kind_of?(DFHack::HistfigEntityLinkEnemyst) and
l.entity_id == df.ui.group_id l.entity_id == df.ui.group_id
} }
fixed = true fixed = true
i1, i2 = i2, i1 if i1 > i2 i1, i2 = i2, i1 if i1 > i2
links.delete_at i2 links.delete_at i2
links.delete_at i1 links.delete_at i1
links << DFHack::HistfigEntityLinkMemberst.cpp_new(:entity_id => df.ui.group_id, :link_strength => 100) 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" df.add_announcement "fixloyalty: #{unit.name} is now a member of #{df.ui.group_tg.name} again"
end end
# fix the 'is an enemy' cache matrix (mark to be recalculated by the game when needed) # 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 if fixed and unit.enemy.enemy_status_slot != -1
i = unit.enemy.enemy_status_slot i = unit.enemy.enemy_status_slot
unit.enemy.enemy_status_slot = -1 unit.enemy.enemy_status_slot = -1
cache = df.world.enemy_status_cache cache = df.world.enemy_status_cache
cache.slot_used[i] = false cache.slot_used[i] = false
cache.rel_map[i].map! { -1 } cache.rel_map[i].map! { -1 }
cache.rel_map.each { |a| a[i] = -1 } cache.rel_map.each { |a| a[i] = -1 }
cache.next_slot = i if cache.next_slot > i cache.next_slot = i if cache.next_slot > i
end end
# return true if we actually fixed the unit # return true if we actually fixed the unit
fixed fixed
end end
count = 0 count = 0
df.unit_citizens.each { |u| df.unit_citizens.each { |u|
count += 1 if fixunit(u) count += 1 if fixunit(u)
} }
if count > 0 if count > 0
puts "loyalty cascade fixed (#{count} dwarves)" puts "loyalty cascade fixed (#{count} dwarves)"
else else
puts "no loyalty cascade found" puts "no loyalty cascade found"
end end

@ -1,25 +1,25 @@
# fix doors that are frozen in 'open' state # fix doors that are frozen in 'open' state
# this may happen after people mess with the game by (incorrectly) teleporting units or items # 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 # a door may stick open if the map occupancy flags are wrong
count = 0 count = 0
df.world.buildings.all.each { |bld| df.world.buildings.all.each { |bld|
# for all doors # for all doors
next if bld._rtti_classname != :building_doorst next if bld._rtti_classname != :building_doorst
# check if it is open # check if it is open
next if bld.close_timer == 0 next if bld.close_timer == 0
# check if occupancy is set # check if occupancy is set
occ = df.map_occupancy_at(bld.x1, bld.y1, bld.z) occ = df.map_occupancy_at(bld.x1, bld.y1, bld.z)
if (occ.unit or occ.unit_grounded) and not if (occ.unit or occ.unit_grounded) and not
# check if an unit is present # 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 } 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 count += 1
occ.unit = occ.unit_grounded = false occ.unit = occ.unit_grounded = false
end end
if occ.item and not df.world.items.all.find { |i| i.pos.x == bld.x1 and i.pos.y == bld.y1 and u.pos.z == bld.z } 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 count += 1
occ.item = false occ.item = false
end end
} }
puts "unstuck #{count} doors" puts "unstuck #{count} doors"

@ -1,49 +1,49 @@
# grow crops in farm plots. ex: growcrops helmet_plump 20 # grow crops in farm plots. ex: growcrops helmet_plump 20
material = $script_args[0] material = $script_args[0]
count_max = $script_args[1].to_i count_max = $script_args[1].to_i
count_max = 100 if count_max == 0 count_max = 100 if count_max == 0
# cache information from the raws # cache information from the raws
@raws_plant_name ||= {} @raws_plant_name ||= {}
@raws_plant_growdur ||= {} @raws_plant_growdur ||= {}
if @raws_plant_name.empty? if @raws_plant_name.empty?
df.world.raws.plants.all.each_with_index { |p, idx| df.world.raws.plants.all.each_with_index { |p, idx|
@raws_plant_name[idx] = p.id @raws_plant_name[idx] = p.id
@raws_plant_growdur[idx] = p.growdur @raws_plant_growdur[idx] = p.growdur
} }
end end
inventory = Hash.new(0) inventory = Hash.new(0)
df.world.items.other[:SEEDS].each { |seed| df.world.items.other[:SEEDS].each { |seed|
next if not seed.flags.in_building next if not seed.flags.in_building
next if not seed.general_refs.find { |ref| ref._rtti_classname == :general_ref_building_holderst } 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] next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index]
inventory[seed.mat_index] += 1 inventory[seed.mat_index] += 1
} }
if !material or material == 'help' or material == 'list' if !material or material == 'help' or material == 'list'
# show a list of available crop types # show a list of available crop types
inventory.sort_by { |mat, c| c }.each { |mat, c| inventory.sort_by { |mat, c| c }.each { |mat, c|
name = df.world.raws.plants.all[mat].id name = df.world.raws.plants.all[mat].id
puts " #{name} #{c}" puts " #{name} #{c}"
} }
else else
mat = df.match_rawname(material, inventory.keys.map { |k| @raws_plant_name[k] }) mat = df.match_rawname(material, inventory.keys.map { |k| @raws_plant_name[k] })
unless wantmat = @raws_plant_name.index(mat) unless wantmat = @raws_plant_name.index(mat)
raise "invalid plant material #{material}" raise "invalid plant material #{material}"
end end
count = 0 count = 0
df.world.items.other[:SEEDS].each { |seed| df.world.items.other[:SEEDS].each { |seed|
next if seed.mat_index != wantmat next if seed.mat_index != wantmat
next if not seed.flags.in_building next if not seed.flags.in_building
next if not seed.general_refs.find { |ref| ref._rtti_classname == :general_ref_building_holderst } 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] next if seed.grow_counter >= @raws_plant_growdur[seed.mat_index]
seed.grow_counter = @raws_plant_growdur[seed.mat_index] seed.grow_counter = @raws_plant_growdur[seed.mat_index]
count += 1 count += 1
} }
puts "Grown #{count} #{mat}" puts "Grown #{count} #{mat}"
end end

@ -1,120 +1,120 @@
# control your levers from the dfhack console # control your levers from the dfhack console
def lever_pull_job(bld) def lever_pull_job(bld)
ref = DFHack::GeneralRefBuildingHolderst.cpp_new ref = DFHack::GeneralRefBuildingHolderst.cpp_new
ref.building_id = bld.id ref.building_id = bld.id
job = DFHack::Job.cpp_new job = DFHack::Job.cpp_new
job.job_type = :PullLever job.job_type = :PullLever
job.pos = [bld.centerx, bld.centery, bld.z] job.pos = [bld.centerx, bld.centery, bld.z]
job.general_refs << ref job.general_refs << ref
bld.jobs << job bld.jobs << job
df.job_link job df.job_link job
puts lever_descr(bld) puts lever_descr(bld)
end end
def lever_pull_cheat(bld) def lever_pull_cheat(bld)
bld.linked_mechanisms.each { |i| bld.linked_mechanisms.each { |i|
i.general_refs.grep(DFHack::GeneralRefBuildingHolderst).each { |r| i.general_refs.grep(DFHack::GeneralRefBuildingHolderst).each { |r|
r.building_tg.setTriggerState(bld.state) r.building_tg.setTriggerState(bld.state)
} }
} }
bld.state = (bld.state == 0 ? 1 : 0) bld.state = (bld.state == 0 ? 1 : 0)
puts lever_descr(bld) puts lever_descr(bld)
end end
def lever_descr(bld, idx=nil) def lever_descr(bld, idx=nil)
ret = [] ret = []
# lever description # lever description
descr = '' descr = ''
descr << "#{idx}: " if idx descr << "#{idx}: " if idx
descr << "lever ##{bld.id} @[#{bld.centerx}, #{bld.centery}, #{bld.z}] #{bld.state == 0 ? '\\' : '/'}" descr << "lever ##{bld.id} @[#{bld.centerx}, #{bld.centery}, #{bld.z}] #{bld.state == 0 ? '\\' : '/'}"
bld.jobs.each { |j| bld.jobs.each { |j|
if j.job_type == :PullLever if j.job_type == :PullLever
flags = '' flags = ''
flags << ', repeat' if j.flags.repeat flags << ', repeat' if j.flags.repeat
flags << ', suspended' if j.flags.suspend flags << ', suspended' if j.flags.suspend
descr << " (pull order#{flags})" descr << " (pull order#{flags})"
end end
} }
bld.linked_mechanisms.map { |i| bld.linked_mechanisms.map { |i|
i.general_refs.grep(DFHack::GeneralRefBuildingHolderst) i.general_refs.grep(DFHack::GeneralRefBuildingHolderst)
}.flatten.each { |r| }.flatten.each { |r|
# linked building description # linked building description
tg = r.building_tg tg = r.building_tg
state = '' state = ''
if tg.respond_to?(:gate_flags) if tg.respond_to?(:gate_flags)
state << (tg.gate_flags.closed ? 'closed' : 'opened') state << (tg.gate_flags.closed ? 'closed' : 'opened')
state << ", closing (#{tg.timer})" if tg.gate_flags.closing state << ", closing (#{tg.timer})" if tg.gate_flags.closing
state << ", opening (#{tg.timer})" if tg.gate_flags.opening state << ", opening (#{tg.timer})" if tg.gate_flags.opening
end end
ret << (descr + " linked to #{tg._rtti_classname} ##{tg.id} @[#{tg.centerx}, #{tg.centery}, #{tg.z}] #{state}") ret << (descr + " linked to #{tg._rtti_classname} ##{tg.id} @[#{tg.centerx}, #{tg.centery}, #{tg.z}] #{state}")
# indent other links # indent other links
descr = descr.gsub(/./, ' ') descr = descr.gsub(/./, ' ')
} }
ret << descr if ret.empty? ret << descr if ret.empty?
ret ret
end end
def lever_list def lever_list
@lever_list = [] @lever_list = []
df.world.buildings.other[:TRAP].find_all { |bld| df.world.buildings.other[:TRAP].find_all { |bld|
bld.trap_type == :Lever bld.trap_type == :Lever
}.sort_by { |bld| bld.id }.each { |bld| }.sort_by { |bld| bld.id }.each { |bld|
puts lever_descr(bld, @lever_list.length) puts lever_descr(bld, @lever_list.length)
@lever_list << bld.id @lever_list << bld.id
} }
end end
case $script_args[0] case $script_args[0]
when 'pull' when 'pull'
cheat = $script_args.delete('--cheat') || $script_args.delete('--now') cheat = $script_args.delete('--cheat') || $script_args.delete('--now')
id = $script_args[1].to_i id = $script_args[1].to_i
id = @lever_list[id] || id id = @lever_list[id] || id
bld = df.building_find(id) bld = df.building_find(id)
raise 'invalid lever id' if not bld raise 'invalid lever id' if not bld
if cheat if cheat
lever_pull_cheat(bld) lever_pull_cheat(bld)
else else
lever_pull_job(bld) lever_pull_job(bld)
end end
when 'list' when 'list'
lever_list lever_list
when /^\d+$/ when /^\d+$/
id = $script_args[0].to_i id = $script_args[0].to_i
id = @lever_list[id] || id id = @lever_list[id] || id
bld = df.building_find(id) bld = df.building_find(id)
raise 'invalid lever id' if not bld raise 'invalid lever id' if not bld
puts lever_descr(bld) puts lever_descr(bld)
else else
puts <<EOS puts <<EOS
Lever control from the dfhack console Lever control from the dfhack console
Usage: Usage:
lever list lever list
shows the list of levers in the fortress, with their id and links shows the list of levers in the fortress, with their id and links
lever pull 42 lever pull 42
order the dwarves to pull lever 42 order the dwarves to pull lever 42
lever pull 42 --cheat lever pull 42 --cheat
magically pull lever 42 immediately magically pull lever 42 immediately
EOS EOS
end end

@ -1,40 +1,40 @@
# pit all caged creatures in a zone # pit all caged creatures in a zone
case $script_args[0] case $script_args[0]
when '?', 'help' when '?', 'help'
puts <<EOS puts <<EOS
Run this script with the cursor on top of a pit/pond activity zone, or with a zone identifier as argument. Run this script with the cursor on top of a pit/pond activity zone, or with a zone identifier as argument.
It will mark all caged creatures on tiles covered by the zone to be dumped. It will mark all caged creatures on tiles covered by the zone to be dumped.
Works best with an animal stockpile on top of the pit/pond zone. Works best with an animal stockpile on top of the pit/pond zone.
EOS EOS
throw :script_finished throw :script_finished
when /(\d+)/ when /(\d+)/
nr = $1.to_i nr = $1.to_i
bld = df.world.buildings.other[:ACTIVITY_ZONE].find { |zone| zone.zone_num == nr } bld = df.world.buildings.other[:ACTIVITY_ZONE].find { |zone| zone.zone_num == nr }
else else
bld = df.world.buildings.other[:ACTIVITY_ZONE].find { |zone| bld = df.world.buildings.other[:ACTIVITY_ZONE].find { |zone|
zone.zone_flags.pit_pond and zone.z == df.cursor.z and zone.zone_flags.pit_pond and zone.z == df.cursor.z and
zone.x1 <= df.cursor.x and zone.x2 >= df.cursor.x and zone.y1 <= df.cursor.y and zone.y2 >= df.cursor.y zone.x1 <= df.cursor.x and zone.x2 >= df.cursor.x and zone.y1 <= df.cursor.y and zone.y2 >= df.cursor.y
} }
end end
if not bld if not bld
puts "Please select a pit/pond zone" puts "Please select a pit/pond zone"
throw :script_finished throw :script_finished
end end
found = 0 found = 0
df.world.items.other[:CAGE].each { |cg| df.world.items.other[:CAGE].each { |cg|
next if not cg.flags.on_ground next if not cg.flags.on_ground
next if cg.pos.z != bld.z or cg.pos.x < bld.x1 or cg.pos.x > bld.x2 or cg.pos.y < bld.y1 or cg.pos.y > bld.y2 next if cg.pos.z != bld.z or cg.pos.x < bld.x1 or cg.pos.x > bld.x2 or cg.pos.y < bld.y1 or cg.pos.y > bld.y2
next if not uref = cg.general_refs.grep(DFHack::GeneralRefContainsUnitst).first next if not uref = cg.general_refs.grep(DFHack::GeneralRefContainsUnitst).first
found += 1 found += 1
u = uref.unit_tg u = uref.unit_tg
puts "Pitting #{u.race_tg.name[0]} #{u.id} #{u.name}" puts "Pitting #{u.race_tg.name[0]} #{u.id} #{u.name}"
u.general_refs << DFHack::GeneralRefBuildingCivzoneAssignedst.cpp_new(:building_id => bld.id) u.general_refs << DFHack::GeneralRefBuildingCivzoneAssignedst.cpp_new(:building_id => bld.id)
bld.assigned_creature << u.id bld.assigned_creature << u.id
} }
puts "No creature available for pitting" if found == 0 puts "No creature available for pitting" if found == 0

@ -1,4 +1,4 @@
# run many dfhack commands separated by ; # run many dfhack commands separated by ;
# ex: multicmd locate-ore IRON ; digv ; digcircle 16 # ex: multicmd locate-ore IRON ; digv ; digcircle 16
$script_args.join(' ').split(/\s*;\s*/).each { |cmd| df.dfhack_run cmd } $script_args.join(' ').split(/\s*;\s*/).each { |cmd| df.dfhack_run cmd }

@ -1,43 +1,43 @@
# remove bad thoughts for the selected unit or the whole fort # remove bad thoughts for the selected unit or the whole fort
dry_run = $script_args.delete('--dry-run') || $script_args.delete('-n') dry_run = $script_args.delete('--dry-run') || $script_args.delete('-n')
$script_args << 'all' if dry_run and $script_args.empty? $script_args << 'all' if dry_run and $script_args.empty?
seenbad = Hash.new(0) seenbad = Hash.new(0)
clear_mind = lambda { |u| clear_mind = lambda { |u|
u.status.recent_events.each { |e| u.status.recent_events.each { |e|
next if DFHack::UnitThoughtType::Value[e.type].to_s[0, 1] != '-' next if DFHack::UnitThoughtType::Value[e.type].to_s[0, 1] != '-'
seenbad[e.type] += 1 seenbad[e.type] += 1
e.age = 0x1000_0000 unless dry_run e.age = 0x1000_0000 unless dry_run
} }
} }
summary = lambda { summary = lambda {
seenbad.sort_by { |thought, cnt| cnt }.each { |thought, cnt| seenbad.sort_by { |thought, cnt| cnt }.each { |thought, cnt|
puts " #{thought} #{cnt}" puts " #{thought} #{cnt}"
} }
count = seenbad.values.inject(0) { |sum, cnt| sum+cnt } count = seenbad.values.inject(0) { |sum, cnt| sum+cnt }
puts "Removed #{count} bad thought#{'s' if count != 1}." if count > 0 and not dry_run puts "Removed #{count} bad thought#{'s' if count != 1}." if count > 0 and not dry_run
} }
case $script_args[0] case $script_args[0]
when 'him' when 'him'
if u = df.unit_find if u = df.unit_find
clear_mind[u] clear_mind[u]
summary[] summary[]
else else
puts 'Please select a dwarf ingame' puts 'Please select a dwarf ingame'
end end
when 'all' when 'all'
df.unit_citizens.each { |uu| df.unit_citizens.each { |uu|
clear_mind[uu] clear_mind[uu]
} }
summary[] summary[]
else else
puts "Usage: removebadthoughts [--dry-run] <him|all>" puts "Usage: removebadthoughts [--dry-run] <him|all>"
end end

@ -1,83 +1,83 @@
# create an infinite magma/water source/drain at the cursor # create an infinite magma/water source/drain at the cursor
$sources ||= [] $sources ||= []
cur_source = { cur_source = {
:liquid => 'water', :liquid => 'water',
:amount => 7, :amount => 7,
:pos => [df.cursor.x, df.cursor.y, df.cursor.z] :pos => [df.cursor.x, df.cursor.y, df.cursor.z]
} }
cmd = 'help' cmd = 'help'
$script_args.each { |a| $script_args.each { |a|
case a.downcase case a.downcase
when 'water', 'magma' when 'water', 'magma'
cur_source[:liquid] = a.downcase cur_source[:liquid] = a.downcase
when /^\d+$/ when /^\d+$/
cur_source[:amount] = a.to_i cur_source[:amount] = a.to_i
when 'add', 'del', 'delete', 'clear', 'help', 'list' when 'add', 'del', 'delete', 'clear', 'help', 'list'
cmd = a.downcase cmd = a.downcase
else else
puts "source: unhandled argument #{a}" puts "source: unhandled argument #{a}"
end end
} }
case cmd case cmd
when 'add' when 'add'
$sources_onupdate ||= df.onupdate_register('sources', 12) { $sources_onupdate ||= df.onupdate_register('sources', 12) {
# called every 12 game ticks (100x a dwarf day) # called every 12 game ticks (100x a dwarf day)
$sources.each { |s| $sources.each { |s|
if tile = df.map_tile_at(*s[:pos]) and tile.shape_passableflow if tile = df.map_tile_at(*s[:pos]) and tile.shape_passableflow
# XXX does not check current liquid_type # XXX does not check current liquid_type
des = tile.designation des = tile.designation
cur = des.flow_size cur = des.flow_size
if cur != s[:amount] if cur != s[:amount]
tile.spawn_liquid((cur > s[:amount] ? cur-1 : cur+1), s[:liquid] == 'magma') tile.spawn_liquid((cur > s[:amount] ? cur-1 : cur+1), s[:liquid] == 'magma')
end end
end end
} }
if $sources.empty? if $sources.empty?
df.onupdate_unregister($sources_onupdate) df.onupdate_unregister($sources_onupdate)
$sources_onupdate = nil $sources_onupdate = nil
end end
} }
if cur_source[:pos][0] >= 0 if cur_source[:pos][0] >= 0
if tile = df.map_tile_at(*cur_source[:pos]) if tile = df.map_tile_at(*cur_source[:pos])
if tile.shape_passableflow if tile.shape_passableflow
$sources << cur_source $sources << cur_source
else else
puts "Impassable tile: I'm afraid I can't do that, Dave" puts "Impassable tile: I'm afraid I can't do that, Dave"
end end
else else
puts "Unallocated map block - build something here first" puts "Unallocated map block - build something here first"
end end
else else
puts "Please put the game cursor where you want a source" puts "Please put the game cursor where you want a source"
end end
when 'del', 'delete' when 'del', 'delete'
$sources.delete_if { |s| s[:pos] == cur_source[:pos] } $sources.delete_if { |s| s[:pos] == cur_source[:pos] }
when 'clear' when 'clear'
$sources.clear $sources.clear
when 'list' when 'list'
puts "Source list:", $sources.map { |s| puts "Source list:", $sources.map { |s|
" #{s[:pos].inspect} #{s[:liquid]} #{s[:amount]}" " #{s[:pos].inspect} #{s[:liquid]} #{s[:amount]}"
} }
puts "Current cursor pos: #{[df.cursor.x, df.cursor.y, df.cursor.z].inspect}" if df.cursor.x >= 0 puts "Current cursor pos: #{[df.cursor.x, df.cursor.y, df.cursor.z].inspect}" if df.cursor.x >= 0
else else
puts <<EOS puts <<EOS
Creates a new infinite liquid source at the cursor. Creates a new infinite liquid source at the cursor.
Examples: Examples:
source add water - create a water source under cursor source add water - create a water source under cursor
source add water 0 - create a water drain source add water 0 - create a water drain
source add magma 5 - create a magma source, up to 5/7 deep source add magma 5 - create a magma source, up to 5/7 deep
source delete - delete source under cursor source delete - delete source under cursor
source clear - remove all sources source clear - remove all sources
source list source list
EOS EOS
end end

@ -1,204 +1,204 @@
# mark stuff inside of cages for dumping. # mark stuff inside of cages for dumping.
def plural(nr, name) def plural(nr, name)
# '1 cage' / '4 cages' # '1 cage' / '4 cages'
"#{nr} #{name}#{'s' if nr > 1}" "#{nr} #{name}#{'s' if nr > 1}"
end end
def cage_dump_items(list) def cage_dump_items(list)
count = 0 count = 0
count_cage = 0 count_cage = 0
list.each { |cage| list.each { |cage|
pre_count = count pre_count = count
cage.general_refs.each { |ref| cage.general_refs.each { |ref|
next unless ref.kind_of?(DFHack::GeneralRefContainsItemst) next unless ref.kind_of?(DFHack::GeneralRefContainsItemst)
next if ref.item_tg.flags.dump next if ref.item_tg.flags.dump
count += 1 count += 1
ref.item_tg.flags.dump = true ref.item_tg.flags.dump = true
} }
count_cage += 1 if pre_count != count count_cage += 1 if pre_count != count
} }
puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}" puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}"
end end
def cage_dump_armor(list) def cage_dump_armor(list)
count = 0 count = 0
count_cage = 0 count_cage = 0
list.each { |cage| list.each { |cage|
pre_count = count pre_count = count
cage.general_refs.each { |ref| cage.general_refs.each { |ref|
next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst) next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst)
ref.unit_tg.inventory.each { |it| ref.unit_tg.inventory.each { |it|
next if it.mode != :Worn next if it.mode != :Worn
next if it.item.flags.dump next if it.item.flags.dump
count += 1 count += 1
it.item.flags.dump = true it.item.flags.dump = true
} }
} }
count_cage += 1 if pre_count != count count_cage += 1 if pre_count != count
} }
puts "Dumped #{plural(count, 'armor piece')} in #{plural(count_cage, 'cage')}" puts "Dumped #{plural(count, 'armor piece')} in #{plural(count_cage, 'cage')}"
end end
def cage_dump_weapons(list) def cage_dump_weapons(list)
count = 0 count = 0
count_cage = 0 count_cage = 0
list.each { |cage| list.each { |cage|
pre_count = count pre_count = count
cage.general_refs.each { |ref| cage.general_refs.each { |ref|
next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst) next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst)
ref.unit_tg.inventory.each { |it| ref.unit_tg.inventory.each { |it|
next if it.mode != :Weapon next if it.mode != :Weapon
next if it.item.flags.dump next if it.item.flags.dump
count += 1 count += 1
it.item.flags.dump = true it.item.flags.dump = true
} }
} }
count_cage += 1 if pre_count != count count_cage += 1 if pre_count != count
} }
puts "Dumped #{plural(count, 'weapon')} in #{plural(count_cage, 'cage')}" puts "Dumped #{plural(count, 'weapon')} in #{plural(count_cage, 'cage')}"
end end
def cage_dump_all(list) def cage_dump_all(list)
count = 0 count = 0
count_cage = 0 count_cage = 0
list.each { |cage| list.each { |cage|
pre_count = count pre_count = count
cage.general_refs.each { |ref| cage.general_refs.each { |ref|
case ref case ref
when DFHack::GeneralRefContainsItemst when DFHack::GeneralRefContainsItemst
next if ref.item_tg.flags.dump next if ref.item_tg.flags.dump
count += 1 count += 1
ref.item_tg.flags.dump = true ref.item_tg.flags.dump = true
when DFHack::GeneralRefContainsUnitst when DFHack::GeneralRefContainsUnitst
ref.unit_tg.inventory.each { |it| ref.unit_tg.inventory.each { |it|
next if it.item.flags.dump next if it.item.flags.dump
count += 1 count += 1
it.item.flags.dump = true it.item.flags.dump = true
} }
end end
} }
count_cage += 1 if pre_count != count count_cage += 1 if pre_count != count
} }
puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}" puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}"
end end
def cage_dump_list(list) def cage_dump_list(list)
count_total = Hash.new(0) count_total = Hash.new(0)
empty_cages = 0 empty_cages = 0
list.each { |cage| list.each { |cage|
count = Hash.new(0) count = Hash.new(0)
cage.general_refs.each { |ref| cage.general_refs.each { |ref|
case ref case ref
when DFHack::GeneralRefContainsItemst when DFHack::GeneralRefContainsItemst
count[ref.item_tg._rtti_classname] += 1 count[ref.item_tg._rtti_classname] += 1
when DFHack::GeneralRefContainsUnitst when DFHack::GeneralRefContainsUnitst
ref.unit_tg.inventory.each { |it| ref.unit_tg.inventory.each { |it|
count[it.item._rtti_classname] += 1 count[it.item._rtti_classname] += 1
} }
# TODO vermin ? # TODO vermin ?
else else
puts "unhandled ref #{ref.inspect}" if $DEBUG puts "unhandled ref #{ref.inspect}" if $DEBUG
end end
} }
type = case cage type = case cage
when DFHack::ItemCagest; 'Cage' when DFHack::ItemCagest; 'Cage'
when DFHack::ItemAnimaltrapst; 'Animal trap' when DFHack::ItemAnimaltrapst; 'Animal trap'
else cage._rtti_classname else cage._rtti_classname
end end
if count.empty? if count.empty?
empty_cages += 1 empty_cages += 1
else else
puts "#{type} ##{cage.id}: ", count.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" } puts "#{type} ##{cage.id}: ", count.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" }
end end
count.each { |k, v| count_total[k] += v } count.each { |k, v| count_total[k] += v }
} }
if list.length > 2 if list.length > 2
puts '', "Total: ", count_total.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" } puts '', "Total: ", count_total.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" }
puts "with #{plural(empty_cages, 'empty cage')}" puts "with #{plural(empty_cages, 'empty cage')}"
end end
end end
# handle magic script arguments # handle magic script arguments
here_only = $script_args.delete 'here' here_only = $script_args.delete 'here'
if here_only if here_only
it = df.item_find it = df.item_find
list = [it] list = [it]
if not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst) if not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst)
list = df.world.items.other[:ANY_CAGE_OR_TRAP].find_all { |i| df.at_cursor?(i) } list = df.world.items.other[:ANY_CAGE_OR_TRAP].find_all { |i| df.at_cursor?(i) }
end end
if list.empty? if list.empty?
puts 'Please select a cage' puts 'Please select a cage'
throw :script_finished throw :script_finished
end end
elsif ids = $script_args.find_all { |arg| arg =~ /^\d+$/ } and ids.first elsif ids = $script_args.find_all { |arg| arg =~ /^\d+$/ } and ids.first
list = [] list = []
ids.each { |id| ids.each { |id|
$script_args.delete id $script_args.delete id
if not it = df.item_find(id.to_i) if not it = df.item_find(id.to_i)
puts "Invalid item id #{id}" puts "Invalid item id #{id}"
elsif not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst) elsif not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst)
puts "Item ##{id} is not a cage" puts "Item ##{id} is not a cage"
list << it list << it
else else
list << it list << it
end end
} }
if list.empty? if list.empty?
puts 'Please use a valid cage id' puts 'Please use a valid cage id'
throw :script_finished throw :script_finished
end end
else else
list = df.world.items.other[:ANY_CAGE_OR_TRAP] list = df.world.items.other[:ANY_CAGE_OR_TRAP]
end end
# act # act
case $script_args[0] case $script_args[0]
when /^it/i when /^it/i
cage_dump_items(list) cage_dump_items(list)
when /^arm/i when /^arm/i
cage_dump_armor(list) cage_dump_armor(list)
when /^wea/i when /^wea/i
cage_dump_weapons(list) cage_dump_weapons(list)
when 'all' when 'all'
cage_dump_all(list) cage_dump_all(list)
when 'list' when 'list'
cage_dump_list(list) cage_dump_list(list)
else else
puts <<EOS puts <<EOS
Marks items inside all cages for dumping. Marks items inside all cages for dumping.
Add 'here' to dump stuff only for selected cage. Add 'here' to dump stuff only for selected cage.
Add a cage id to dump stuff for this cage only. Add a cage id to dump stuff for this cage only.
See 'autodump' to actually dump stuff. See 'autodump' to actually dump stuff.
Usage: Usage:
stripcaged items stripcaged items
dump items directly in cages (eg seeds after training) dump items directly in cages (eg seeds after training)
stripcaged [armor|weapons] here stripcaged [armor|weapons] here
dump armor or weapons of caged creatures in selected cage dump armor or weapons of caged creatures in selected cage
stripcaged all 28 29 stripcaged all 28 29
dump every item in cage id 28 and 29, along with every item worn by creatures in there too dump every item in cage id 28 and 29, along with every item worn by creatures in there too
stripcaged list stripcaged list
show content of the cages show content of the cages
EOS EOS
end end

@ -1,61 +1,61 @@
# give super-dwarven speed to an unit # give super-dwarven speed to an unit
$superdwarf_onupdate ||= nil $superdwarf_onupdate ||= nil
$superdwarf_ids ||= [] $superdwarf_ids ||= []
case $script_args[0] case $script_args[0]
when 'add' when 'add'
if u = df.unit_find if u = df.unit_find
$superdwarf_ids |= [u.id] $superdwarf_ids |= [u.id]
$superdwarf_onupdate ||= df.onupdate_register('superdwarf', 1) { $superdwarf_onupdate ||= df.onupdate_register('superdwarf', 1) {
if $superdwarf_ids.empty? if $superdwarf_ids.empty?
df.onupdate_unregister($superdwarf_onupdate) df.onupdate_unregister($superdwarf_onupdate)
$superdwarf_onupdate = nil $superdwarf_onupdate = nil
else else
$superdwarf_ids.each { |id| $superdwarf_ids.each { |id|
if u = df.unit_find(id) and not u.flags1.dead if u = df.unit_find(id) and not u.flags1.dead
# faster walk/work # faster walk/work
if u.counters.job_counter > 0 if u.counters.job_counter > 0
u.counters.job_counter = 0 u.counters.job_counter = 0
end end
# no sleep # no sleep
if u.counters2.sleepiness_timer > 10000 if u.counters2.sleepiness_timer > 10000
u.counters2.sleepiness_timer = 1 u.counters2.sleepiness_timer = 1
end end
# no break # no break
if b = u.status.misc_traits.find { |t| t.id == :OnBreak } if b = u.status.misc_traits.find { |t| t.id == :OnBreak }
b.value = 500_000 b.value = 500_000
end end
else else
$superdwarf_ids.delete id $superdwarf_ids.delete id
end end
} }
end end
} }
else else
puts "Select a creature using 'v'" puts "Select a creature using 'v'"
end end
when 'del' when 'del'
if u = df.unit_find if u = df.unit_find
$superdwarf_ids.delete u.id $superdwarf_ids.delete u.id
else else
puts "Select a creature using 'v'" puts "Select a creature using 'v'"
end end
when 'clear' when 'clear'
$superdwarf_ids.clear $superdwarf_ids.clear
when 'list' when 'list'
puts "current superdwarves:", $superdwarf_ids.map { |id| df.unit_find(id).name } puts "current superdwarves:", $superdwarf_ids.map { |id| df.unit_find(id).name }
else else
puts "Usage:", puts "Usage:",
" - superdwarf add: give superspeed to currently selected creature", " - superdwarf add: give superspeed to currently selected creature",
" - superdwarf del: remove superspeed to current creature", " - superdwarf del: remove superspeed to current creature",
" - superdwarf clear: remove all superpowers", " - superdwarf clear: remove all superpowers",
" - superdwarf list: list super-dwarves" " - superdwarf list: list super-dwarves"
end end

@ -1,17 +1,17 @@
joblist = df.world.job_list.next joblist = df.world.job_list.next
count = 0 count = 0
while joblist while joblist
job = joblist.item job = joblist.item
joblist = joblist.next joblist = joblist.next
if job.job_type == :ConstructBuilding if job.job_type == :ConstructBuilding
if (job.flags.suspend && job.items && job.items[0]) if (job.flags.suspend && job.items && job.items[0])
item = job.items[0].item item = job.items[0].item
job.flags.suspend = false job.flags.suspend = false
count += 1 count += 1
end end
end end
end end
puts "Unsuspended #{count} job(s)." puts "Unsuspended #{count} job(s)."