develop
commit
df6355a092
@ -1,176 +1,176 @@
|
||||
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.known_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'
|
||||
$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
|
||||
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.known_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,58 +1,58 @@
|
||||
class AutoUnsuspend
|
||||
|
||||
def initialize
|
||||
end
|
||||
|
||||
def process
|
||||
return false unless @running
|
||||
joblist = df.world.job_list.next
|
||||
count = 0
|
||||
|
||||
while joblist
|
||||
job = joblist.item
|
||||
joblist = joblist.next
|
||||
|
||||
if job.job_type == :ConstructBuilding
|
||||
if (job.flags.suspend)
|
||||
item = job.items[0].item
|
||||
job.flags.suspend = false
|
||||
count += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
puts "Unsuspended #{count} job(s)." unless count == 0
|
||||
|
||||
end
|
||||
|
||||
def start
|
||||
@onupdate = df.onupdate_register('autounsuspend', 5) { process }
|
||||
@running = true
|
||||
end
|
||||
|
||||
def stop
|
||||
df.onupdate_unregister(@onupdate)
|
||||
@running = false
|
||||
end
|
||||
|
||||
def status
|
||||
@running ? 'Running.' : 'Stopped.'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
case $script_args[0]
|
||||
when 'start'
|
||||
$AutoUnsuspend = AutoUnsuspend.new unless $AutoUnsuspend
|
||||
$AutoUnsuspend.start
|
||||
|
||||
when 'end', 'stop'
|
||||
$AutoUnsuspend.stop
|
||||
|
||||
else
|
||||
if $AutoUnsuspend
|
||||
puts $AutoUnsuspend.status
|
||||
else
|
||||
puts 'Not loaded.'
|
||||
end
|
||||
end
|
||||
class AutoUnsuspend
|
||||
|
||||
def initialize
|
||||
end
|
||||
|
||||
def process
|
||||
return false unless @running
|
||||
joblist = df.world.job_list.next
|
||||
count = 0
|
||||
|
||||
while joblist
|
||||
job = joblist.item
|
||||
joblist = joblist.next
|
||||
|
||||
if job.job_type == :ConstructBuilding
|
||||
if (job.flags.suspend)
|
||||
item = job.items[0].item
|
||||
job.flags.suspend = false
|
||||
count += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
puts "Unsuspended #{count} job(s)." unless count == 0
|
||||
|
||||
end
|
||||
|
||||
def start
|
||||
@onupdate = df.onupdate_register('autounsuspend', 5) { process }
|
||||
@running = true
|
||||
end
|
||||
|
||||
def stop
|
||||
df.onupdate_unregister(@onupdate)
|
||||
@running = false
|
||||
end
|
||||
|
||||
def status
|
||||
@running ? 'Running.' : 'Stopped.'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
case $script_args[0]
|
||||
when 'start'
|
||||
$AutoUnsuspend = AutoUnsuspend.new unless $AutoUnsuspend
|
||||
$AutoUnsuspend.start
|
||||
|
||||
when 'end', 'stop'
|
||||
$AutoUnsuspend.stop
|
||||
|
||||
else
|
||||
if $AutoUnsuspend
|
||||
puts $AutoUnsuspend.status
|
||||
else
|
||||
puts 'Not loaded.'
|
||||
end
|
||||
end
|
||||
|
@ -1,177 +1,177 @@
|
||||
# create first necessity items under cursor
|
||||
|
||||
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
|
||||
# create first necessity items under cursor
|
||||
|
||||
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,67 +1,67 @@
|
||||
# show death cause of a creature
|
||||
|
||||
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.bow_item_subtype].name}" if e.weapon.bow_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
|
||||
|
||||
# show death cause of a creature
|
||||
|
||||
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.bow_item_subtype].name}" if e.weapon.bow_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
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
# list indexes in world.item.other[] where current selected item appears
|
||||
|
||||
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 }
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
# unforbid all items
|
||||
|
||||
df.world.items.all.each { |i| i.flags.forbid = false }
|
@ -1,38 +1,38 @@
|
||||
# designate an area for digging according to a plan in csv format
|
||||
|
||||
raise "usage: digfort <plan filename>" if not $script_args[0]
|
||||
planfile = File.read($script_args[0])
|
||||
|
||||
if df.cursor.x == -30000
|
||||
raise "place the game cursor to the top-left corner of the design"
|
||||
end
|
||||
|
||||
tiles = planfile.lines.map { |l|
|
||||
l.sub(/#.*/, '').split(';').map { |t| t.strip }
|
||||
}
|
||||
|
||||
x = x0 = df.cursor.x
|
||||
y = df.cursor.y
|
||||
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 = x0
|
||||
y += 1
|
||||
}
|
||||
|
||||
puts 'done'
|
||||
# designate an area for digging according to a plan in csv format
|
||||
|
||||
raise "usage: digfort <plan filename>" if not $script_args[0]
|
||||
planfile = File.read($script_args[0])
|
||||
|
||||
if df.cursor.x == -30000
|
||||
raise "place the game cursor to the top-left corner of the design"
|
||||
end
|
||||
|
||||
tiles = planfile.lines.map { |l|
|
||||
l.sub(/#.*/, '').split(';').map { |t| t.strip }
|
||||
}
|
||||
|
||||
x = x0 = df.cursor.x
|
||||
y = df.cursor.y
|
||||
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 = x0
|
||||
y += 1
|
||||
}
|
||||
|
||||
puts 'done'
|
||||
|
@ -1,11 +1,13 @@
|
||||
# remove all aquifers from the map
|
||||
|
||||
count = 0
|
||||
df.each_map_block { |b|
|
||||
if b.designation[0][0].water_table or b.designation[15][15].water_table
|
||||
count += 1
|
||||
b.designation.each { |dx| dx.each { |dy| dy.water_table = false } }
|
||||
end
|
||||
}
|
||||
|
||||
puts "cleared #{count} map blocks"
|
||||
# remove all aquifers from the map
|
||||
|
||||
count = 0
|
||||
df.each_map_block { |b|
|
||||
if b.designation[0][0].water_table or b.designation[8][8].water_table
|
||||
count += 1
|
||||
df.each_map_block_z(b.map_pos.z) { |bz|
|
||||
bz.designation.each { |dx| dx.each { |dy| dy.water_table = false } }
|
||||
}
|
||||
end
|
||||
}
|
||||
|
||||
puts "cleared #{count} aquifer#{'s' if count > 1}"
|
||||
|
@ -1,112 +1,135 @@
|
||||
# exterminate creatures
|
||||
|
||||
# race = name of the race to eradicate, use 'him' to target only the selected creature
|
||||
# use 'undead' to target all undeads
|
||||
race = $script_args[0]
|
||||
|
||||
# if the 2nd parameter is 'magma', magma rain for the targets instead of instant death
|
||||
magma = ($script_args[1] == 'magma')
|
||||
|
||||
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|
|
||||
if not magma
|
||||
# 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
|
||||
else
|
||||
# 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
|
||||
}
|
||||
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.
|
||||
|
||||
Ex: exterminate gob
|
||||
exterminate elve magma
|
||||
exterminate him
|
||||
EOS
|
||||
|
||||
when 'him', 'her'
|
||||
if him = df.unit_find
|
||||
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
|
||||
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 }
|
||||
|
||||
count = 0
|
||||
df.world.units.active.each { |u|
|
||||
if u.race == race_nr and checkunit[u]
|
||||
slayit[u]
|
||||
count += 1
|
||||
end
|
||||
}
|
||||
puts "slain #{count} #{raw_race}"
|
||||
|
||||
end
|
||||
# exterminate creatures
|
||||
|
||||
# race = name of the race to eradicate, use 'him' to target only the selected creature
|
||||
# use 'undead' to target all undeads
|
||||
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 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
|
||||
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 }
|
||||
|
||||
count = 0
|
||||
df.world.units.active.each { |u|
|
||||
if u.race == race_nr and checkunit[u]
|
||||
slayit[u]
|
||||
count += 1
|
||||
end
|
||||
}
|
||||
puts "#{slain} #{count} #{raw_race}"
|
||||
|
||||
end
|
||||
|
@ -1,64 +1,64 @@
|
||||
# script to fix loyalty cascade, when you order your militia to kill friendly units
|
||||
|
||||
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
|
||||
# script to fix loyalty cascade, when you order your militia to kill friendly units
|
||||
|
||||
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,20 +1,25 @@
|
||||
# fix doors that are frozen in 'open' state
|
||||
|
||||
# door is stuck in open state if the map occupancy flag incorrectly indicates
|
||||
# that an unit is present (and creatures will prone to pass through)
|
||||
|
||||
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)
|
||||
next if not occ.unit
|
||||
# check if an unit is present
|
||||
next if 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 = false
|
||||
}
|
||||
puts "unstuck #{count} doors"
|
||||
# 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
|
||||
|
||||
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,49 +1,49 @@
|
||||
# grow crops in farm plots. ex: growcrops helmet_plump 20
|
||||
|
||||
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
|
||||
# grow crops in farm plots. ex: growcrops helmet_plump 20
|
||||
|
||||
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,120 +1,120 @@
|
||||
# control your levers from the dfhack console
|
||||
|
||||
def lever_pull_job(bld)
|
||||
ref = DFHack::GeneralRefBuildingHolderst.cpp_new
|
||||
ref.building_id = bld.id
|
||||
|
||||
job = DFHack::Job.cpp_new
|
||||
job.job_type = :PullLever
|
||||
job.pos = [bld.centerx, bld.centery, bld.z]
|
||||
job.general_refs << ref
|
||||
bld.jobs << job
|
||||
df.job_link job
|
||||
|
||||
puts lever_descr(bld)
|
||||
end
|
||||
|
||||
def lever_pull_cheat(bld)
|
||||
bld.linked_mechanisms.each { |i|
|
||||
i.general_refs.grep(DFHack::GeneralRefBuildingHolderst).each { |r|
|
||||
r.building_tg.setTriggerState(bld.state)
|
||||
}
|
||||
}
|
||||
|
||||
bld.state = (bld.state == 0 ? 1 : 0)
|
||||
|
||||
puts lever_descr(bld)
|
||||
end
|
||||
|
||||
def lever_descr(bld, idx=nil)
|
||||
ret = []
|
||||
|
||||
# lever description
|
||||
descr = ''
|
||||
descr << "#{idx}: " if idx
|
||||
descr << "lever ##{bld.id} @[#{bld.centerx}, #{bld.centery}, #{bld.z}] #{bld.state == 0 ? '\\' : '/'}"
|
||||
bld.jobs.each { |j|
|
||||
if j.job_type == :PullLever
|
||||
flags = ''
|
||||
flags << ', repeat' if j.flags.repeat
|
||||
flags << ', suspended' if j.flags.suspend
|
||||
descr << " (pull order#{flags})"
|
||||
end
|
||||
}
|
||||
|
||||
bld.linked_mechanisms.map { |i|
|
||||
i.general_refs.grep(DFHack::GeneralRefBuildingHolderst)
|
||||
}.flatten.each { |r|
|
||||
# linked building description
|
||||
tg = r.building_tg
|
||||
state = ''
|
||||
if tg.respond_to?(:gate_flags)
|
||||
state << (tg.gate_flags.closed ? 'closed' : 'opened')
|
||||
state << ", closing (#{tg.timer})" if tg.gate_flags.closing
|
||||
state << ", opening (#{tg.timer})" if tg.gate_flags.opening
|
||||
end
|
||||
|
||||
ret << (descr + " linked to #{tg._rtti_classname} ##{tg.id} @[#{tg.centerx}, #{tg.centery}, #{tg.z}] #{state}")
|
||||
|
||||
# indent other links
|
||||
descr = descr.gsub(/./, ' ')
|
||||
}
|
||||
|
||||
ret << descr if ret.empty?
|
||||
|
||||
ret
|
||||
end
|
||||
|
||||
def lever_list
|
||||
@lever_list = []
|
||||
df.world.buildings.other[:TRAP].find_all { |bld|
|
||||
bld.trap_type == :Lever
|
||||
}.sort_by { |bld| bld.id }.each { |bld|
|
||||
puts lever_descr(bld, @lever_list.length)
|
||||
@lever_list << bld.id
|
||||
}
|
||||
end
|
||||
|
||||
case $script_args[0]
|
||||
when 'pull'
|
||||
cheat = $script_args.delete('--cheat') || $script_args.delete('--now')
|
||||
|
||||
id = $script_args[1].to_i
|
||||
id = @lever_list[id] || id
|
||||
bld = df.building_find(id)
|
||||
raise 'invalid lever id' if not bld
|
||||
|
||||
if cheat
|
||||
lever_pull_cheat(bld)
|
||||
else
|
||||
lever_pull_job(bld)
|
||||
end
|
||||
|
||||
when 'list'
|
||||
lever_list
|
||||
|
||||
when /^\d+$/
|
||||
id = $script_args[0].to_i
|
||||
id = @lever_list[id] || id
|
||||
bld = df.building_find(id)
|
||||
raise 'invalid lever id' if not bld
|
||||
|
||||
puts lever_descr(bld)
|
||||
|
||||
else
|
||||
puts <<EOS
|
||||
Lever control from the dfhack console
|
||||
|
||||
Usage:
|
||||
lever list
|
||||
shows the list of levers in the fortress, with their id and links
|
||||
|
||||
lever pull 42
|
||||
order the dwarves to pull lever 42
|
||||
|
||||
lever pull 42 --cheat
|
||||
magically pull lever 42 immediately
|
||||
EOS
|
||||
|
||||
end
|
||||
|
||||
# control your levers from the dfhack console
|
||||
|
||||
def lever_pull_job(bld)
|
||||
ref = DFHack::GeneralRefBuildingHolderst.cpp_new
|
||||
ref.building_id = bld.id
|
||||
|
||||
job = DFHack::Job.cpp_new
|
||||
job.job_type = :PullLever
|
||||
job.pos = [bld.centerx, bld.centery, bld.z]
|
||||
job.general_refs << ref
|
||||
bld.jobs << job
|
||||
df.job_link job
|
||||
|
||||
puts lever_descr(bld)
|
||||
end
|
||||
|
||||
def lever_pull_cheat(bld)
|
||||
bld.linked_mechanisms.each { |i|
|
||||
i.general_refs.grep(DFHack::GeneralRefBuildingHolderst).each { |r|
|
||||
r.building_tg.setTriggerState(bld.state)
|
||||
}
|
||||
}
|
||||
|
||||
bld.state = (bld.state == 0 ? 1 : 0)
|
||||
|
||||
puts lever_descr(bld)
|
||||
end
|
||||
|
||||
def lever_descr(bld, idx=nil)
|
||||
ret = []
|
||||
|
||||
# lever description
|
||||
descr = ''
|
||||
descr << "#{idx}: " if idx
|
||||
descr << "lever ##{bld.id} @[#{bld.centerx}, #{bld.centery}, #{bld.z}] #{bld.state == 0 ? '\\' : '/'}"
|
||||
bld.jobs.each { |j|
|
||||
if j.job_type == :PullLever
|
||||
flags = ''
|
||||
flags << ', repeat' if j.flags.repeat
|
||||
flags << ', suspended' if j.flags.suspend
|
||||
descr << " (pull order#{flags})"
|
||||
end
|
||||
}
|
||||
|
||||
bld.linked_mechanisms.map { |i|
|
||||
i.general_refs.grep(DFHack::GeneralRefBuildingHolderst)
|
||||
}.flatten.each { |r|
|
||||
# linked building description
|
||||
tg = r.building_tg
|
||||
state = ''
|
||||
if tg.respond_to?(:gate_flags)
|
||||
state << (tg.gate_flags.closed ? 'closed' : 'opened')
|
||||
state << ", closing (#{tg.timer})" if tg.gate_flags.closing
|
||||
state << ", opening (#{tg.timer})" if tg.gate_flags.opening
|
||||
end
|
||||
|
||||
ret << (descr + " linked to #{tg._rtti_classname} ##{tg.id} @[#{tg.centerx}, #{tg.centery}, #{tg.z}] #{state}")
|
||||
|
||||
# indent other links
|
||||
descr = descr.gsub(/./, ' ')
|
||||
}
|
||||
|
||||
ret << descr if ret.empty?
|
||||
|
||||
ret
|
||||
end
|
||||
|
||||
def lever_list
|
||||
@lever_list = []
|
||||
df.world.buildings.other[:TRAP].find_all { |bld|
|
||||
bld.trap_type == :Lever
|
||||
}.sort_by { |bld| bld.id }.each { |bld|
|
||||
puts lever_descr(bld, @lever_list.length)
|
||||
@lever_list << bld.id
|
||||
}
|
||||
end
|
||||
|
||||
case $script_args[0]
|
||||
when 'pull'
|
||||
cheat = $script_args.delete('--cheat') || $script_args.delete('--now')
|
||||
|
||||
id = $script_args[1].to_i
|
||||
id = @lever_list[id] || id
|
||||
bld = df.building_find(id)
|
||||
raise 'invalid lever id' if not bld
|
||||
|
||||
if cheat
|
||||
lever_pull_cheat(bld)
|
||||
else
|
||||
lever_pull_job(bld)
|
||||
end
|
||||
|
||||
when 'list'
|
||||
lever_list
|
||||
|
||||
when /^\d+$/
|
||||
id = $script_args[0].to_i
|
||||
id = @lever_list[id] || id
|
||||
bld = df.building_find(id)
|
||||
raise 'invalid lever id' if not bld
|
||||
|
||||
puts lever_descr(bld)
|
||||
|
||||
else
|
||||
puts <<EOS
|
||||
Lever control from the dfhack console
|
||||
|
||||
Usage:
|
||||
lever list
|
||||
shows the list of levers in the fortress, with their id and links
|
||||
|
||||
lever pull 42
|
||||
order the dwarves to pull lever 42
|
||||
|
||||
lever pull 42 --cheat
|
||||
magically pull lever 42 immediately
|
||||
EOS
|
||||
|
||||
end
|
||||
|
||||
|
@ -0,0 +1,84 @@
|
||||
# scan the map for ore veins
|
||||
|
||||
target_ore = $script_args[0]
|
||||
|
||||
def find_all_ore_veins
|
||||
puts 'scanning map...'
|
||||
$ore_veins = {}
|
||||
seen_mat = {}
|
||||
df.each_map_block { |block|
|
||||
block.block_events.grep(DFHack::BlockSquareEventMineralst).each { |vein|
|
||||
mat_index = vein.inorganic_mat
|
||||
if not seen_mat[mat_index] or $ore_veins[mat_index]
|
||||
seen_mat[mat_index] = true
|
||||
if df.world.raws.inorganics[mat_index].flags[:METAL_ORE]
|
||||
$ore_veins[mat_index] ||= []
|
||||
$ore_veins[mat_index] << [block.map_pos.x, block.map_pos.y, block.map_pos.z]
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
df.onstatechange_register_once { |st|
|
||||
if st == :MAP_LOADED
|
||||
$ore_veins = nil # invalidate veins cache
|
||||
true
|
||||
end
|
||||
}
|
||||
|
||||
$ore_veins
|
||||
end
|
||||
|
||||
$ore_veins ||= find_all_ore_veins
|
||||
|
||||
if not target_ore or target_ore == 'help'
|
||||
puts <<EOS
|
||||
Scan the map to find one random tile of unmined ore.
|
||||
It will center the game view on that tile and mark it for digging.
|
||||
Only works with metal ores.
|
||||
|
||||
Usage:
|
||||
locate_ore list list all existing vein materials (including mined ones)
|
||||
locate_ore hematite find one tile of unmined hematite ore
|
||||
locate_ore iron find one tile of unmined ore you can smelt into iron
|
||||
EOS
|
||||
|
||||
elsif target_ore and mats = $ore_veins.keys.find_all { |k|
|
||||
ino = df.world.raws.inorganics[k]
|
||||
ino.id =~ /#{target_ore}/i or ino.metal_ore.mat_index.find { |m|
|
||||
df.world.raws.inorganics[m].id =~ /#{target_ore}/i
|
||||
}
|
||||
} and not mats.empty?
|
||||
pos = nil
|
||||
dxs = (0..15).sort_by { rand }
|
||||
dys = (0..15).sort_by { rand }
|
||||
if found_mat = mats.sort_by { rand }.find { |mat|
|
||||
$ore_veins[mat].sort_by { rand }.find { |bx, by, bz|
|
||||
dys.find { |dy|
|
||||
dxs.find { |dx|
|
||||
tile = df.map_tile_at(bx+dx, by+dy, bz)
|
||||
if tile.tilemat == :MINERAL and tile.designation.dig == :No and tile.shape == :WALL and
|
||||
tile.mat_index_vein == mat and
|
||||
# ignore map borders
|
||||
bx+dx > 0 and bx+dx < df.world.map.x_count-1 and by+dy > 0 and by+dy < df.world.map.y_count-1
|
||||
pos = [bx+dx, by+dy, bz]
|
||||
end
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
df.center_viewscreen(*pos)
|
||||
df.map_tile_at(*pos).dig
|
||||
puts "Here is some #{df.world.raws.inorganics[found_mat].id}"
|
||||
else
|
||||
puts "Cannot find unmined #{mats.map { |mat| df.world.raws.inorganics[mat].id }.join(', ')}"
|
||||
end
|
||||
|
||||
else
|
||||
puts "Available ores:", $ore_veins.sort_by { |mat, pos| pos.length }.map { |mat, pos|
|
||||
ore = df.world.raws.inorganics[mat]
|
||||
metals = ore.metal_ore.mat_index.map { |m| df.world.raws.inorganics[m] }
|
||||
' ' + ore.id.downcase + ' (' + metals.map { |m| m.id.downcase }.join(', ') + ')'
|
||||
}
|
||||
|
||||
end
|
@ -1,56 +0,0 @@
|
||||
# create an infinite magma source at the cursor
|
||||
|
||||
$magma_sources ||= []
|
||||
|
||||
case $script_args[0]
|
||||
when 'here'
|
||||
$magma_onupdate ||= df.onupdate_register('magmasource', 12) {
|
||||
# called every 12 game ticks (100x a dwarf day)
|
||||
if $magma_sources.empty?
|
||||
df.onupdate_unregister($magma_onupdate)
|
||||
$magma_onupdate = nil
|
||||
end
|
||||
|
||||
$magma_sources.each { |x, y, z|
|
||||
if tile = df.map_tile_at(x, y, z) and tile.shape_passableflow
|
||||
des = tile.designation
|
||||
tile.spawn_magma(des.flow_size + 1) if des.flow_size < 7
|
||||
end
|
||||
}
|
||||
}
|
||||
|
||||
if df.cursor.x != -30000
|
||||
if tile = df.map_tile_at(df.cursor)
|
||||
if tile.shape_passableflow
|
||||
$magma_sources << [df.cursor.x, df.cursor.y, df.cursor.z]
|
||||
else
|
||||
puts "Impassable tile: I'm afraid I can't do that, Dave"
|
||||
end
|
||||
else
|
||||
puts "Unallocated map block - build something here first"
|
||||
end
|
||||
else
|
||||
puts "Please put the game cursor where you want a magma source"
|
||||
end
|
||||
|
||||
when 'delete-here'
|
||||
$magma_sources.delete [df.cursor.x, df.cursor.y, df.cursor.z]
|
||||
|
||||
when 'stop'
|
||||
$magma_sources.clear
|
||||
|
||||
else
|
||||
puts <<EOS
|
||||
Creates a new infinite magma source at the cursor.
|
||||
|
||||
Arguments:
|
||||
here - create a new source at the current cursor position
|
||||
(call multiple times for higher flow)
|
||||
delete-here - delete the source under the cursor
|
||||
stop - delete all created magma sources
|
||||
EOS
|
||||
|
||||
if $magma_sources.first
|
||||
puts '', 'Current magma sources:', $magma_sources.map { |s| " #{s.inspect}" }
|
||||
end
|
||||
end
|
@ -1,40 +1,40 @@
|
||||
# pit all caged creatures in a zone
|
||||
|
||||
case $script_args[0]
|
||||
when '?', 'help'
|
||||
puts <<EOS
|
||||
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.
|
||||
Works best with an animal stockpile on top of the pit/pond zone.
|
||||
EOS
|
||||
throw :script_finished
|
||||
|
||||
when /(\d+)/
|
||||
nr = $1.to_i
|
||||
bld = df.world.buildings.other[:ACTIVITY_ZONE].find { |zone| zone.zone_num == nr }
|
||||
|
||||
else
|
||||
bld = df.world.buildings.other[:ACTIVITY_ZONE].find { |zone|
|
||||
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
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
if not bld
|
||||
puts "Please select a pit/pond zone"
|
||||
throw :script_finished
|
||||
end
|
||||
|
||||
found = 0
|
||||
df.world.items.other[:CAGE].each { |cg|
|
||||
next if not cg.flags.on_ground
|
||||
next if cg.pos.z != bld.z or cg.pos.x < bld.x1 or cg.pos.x > bld.x2 or cg.pos.y < bld.y1 or cg.pos.y > bld.y2
|
||||
next if not uref = cg.general_refs.grep(DFHack::GeneralRefContainsUnitst).first
|
||||
found += 1
|
||||
u = uref.unit_tg
|
||||
puts "Pitting #{u.race_tg.name[0]} #{u.id} #{u.name}"
|
||||
u.general_refs << DFHack::GeneralRefBuildingCivzoneAssignedst.cpp_new(:building_id => bld.id)
|
||||
bld.assigned_creature << u.id
|
||||
}
|
||||
puts "No creature available for pitting" if found == 0
|
||||
# pit all caged creatures in a zone
|
||||
|
||||
case $script_args[0]
|
||||
when '?', 'help'
|
||||
puts <<EOS
|
||||
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.
|
||||
Works best with an animal stockpile on top of the pit/pond zone.
|
||||
EOS
|
||||
throw :script_finished
|
||||
|
||||
when /(\d+)/
|
||||
nr = $1.to_i
|
||||
bld = df.world.buildings.other[:ACTIVITY_ZONE].find { |zone| zone.zone_num == nr }
|
||||
|
||||
else
|
||||
bld = df.world.buildings.other[:ACTIVITY_ZONE].find { |zone|
|
||||
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
|
||||
}
|
||||
|
||||
end
|
||||
|
||||
if not bld
|
||||
puts "Please select a pit/pond zone"
|
||||
throw :script_finished
|
||||
end
|
||||
|
||||
found = 0
|
||||
df.world.items.other[:CAGE].each { |cg|
|
||||
next if not cg.flags.on_ground
|
||||
next if cg.pos.z != bld.z or cg.pos.x < bld.x1 or cg.pos.x > bld.x2 or cg.pos.y < bld.y1 or cg.pos.y > bld.y2
|
||||
next if not uref = cg.general_refs.grep(DFHack::GeneralRefContainsUnitst).first
|
||||
found += 1
|
||||
u = uref.unit_tg
|
||||
puts "Pitting #{u.race_tg.name[0]} #{u.id} #{u.name}"
|
||||
u.general_refs << DFHack::GeneralRefBuildingCivzoneAssignedst.cpp_new(:building_id => bld.id)
|
||||
bld.assigned_creature << u.id
|
||||
}
|
||||
puts "No creature available for pitting" if found == 0
|
||||
|
@ -0,0 +1,4 @@
|
||||
# run many dfhack commands separated by ;
|
||||
# ex: multicmd locate-ore IRON ; digv ; digcircle 16
|
||||
|
||||
$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
|
||||
|
||||
dry_run = $script_args.delete('--dry-run') || $script_args.delete('-n')
|
||||
|
||||
$script_args << 'all' if dry_run and $script_args.empty?
|
||||
|
||||
seenbad = Hash.new(0)
|
||||
|
||||
clear_mind = lambda { |u|
|
||||
u.status.recent_events.each { |e|
|
||||
next if DFHack::UnitThoughtType::Value[e.type].to_s[0, 1] != '-'
|
||||
seenbad[e.type] += 1
|
||||
e.age = 0x1000_0000 unless dry_run
|
||||
}
|
||||
}
|
||||
|
||||
summary = lambda {
|
||||
seenbad.sort_by { |thought, cnt| cnt }.each { |thought, cnt|
|
||||
puts " #{thought} #{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
|
||||
}
|
||||
|
||||
case $script_args[0]
|
||||
when 'him'
|
||||
if u = df.unit_find
|
||||
clear_mind[u]
|
||||
summary[]
|
||||
else
|
||||
puts 'Please select a dwarf ingame'
|
||||
end
|
||||
|
||||
when 'all'
|
||||
df.unit_citizens.each { |uu|
|
||||
clear_mind[uu]
|
||||
}
|
||||
summary[]
|
||||
|
||||
else
|
||||
puts "Usage: removebadthoughts [--dry-run] <him|all>"
|
||||
|
||||
end
|
||||
# remove bad thoughts for the selected unit or the whole fort
|
||||
|
||||
dry_run = $script_args.delete('--dry-run') || $script_args.delete('-n')
|
||||
|
||||
$script_args << 'all' if dry_run and $script_args.empty?
|
||||
|
||||
seenbad = Hash.new(0)
|
||||
|
||||
clear_mind = lambda { |u|
|
||||
u.status.recent_events.each { |e|
|
||||
next if DFHack::UnitThoughtType::Value[e.type].to_s[0, 1] != '-'
|
||||
seenbad[e.type] += 1
|
||||
e.age = 0x1000_0000 unless dry_run
|
||||
}
|
||||
}
|
||||
|
||||
summary = lambda {
|
||||
seenbad.sort_by { |thought, cnt| cnt }.each { |thought, cnt|
|
||||
puts " #{thought} #{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
|
||||
}
|
||||
|
||||
case $script_args[0]
|
||||
when 'him'
|
||||
if u = df.unit_find
|
||||
clear_mind[u]
|
||||
summary[]
|
||||
else
|
||||
puts 'Please select a dwarf ingame'
|
||||
end
|
||||
|
||||
when 'all'
|
||||
df.unit_citizens.each { |uu|
|
||||
clear_mind[uu]
|
||||
}
|
||||
summary[]
|
||||
|
||||
else
|
||||
puts "Usage: removebadthoughts [--dry-run] <him|all>"
|
||||
|
||||
end
|
||||
|
@ -0,0 +1,83 @@
|
||||
# create an infinite magma/water source/drain at the cursor
|
||||
|
||||
$sources ||= []
|
||||
|
||||
cur_source = {
|
||||
:liquid => 'water',
|
||||
:amount => 7,
|
||||
:pos => [df.cursor.x, df.cursor.y, df.cursor.z]
|
||||
}
|
||||
cmd = 'help'
|
||||
|
||||
$script_args.each { |a|
|
||||
case a.downcase
|
||||
when 'water', 'magma'
|
||||
cur_source[:liquid] = a.downcase
|
||||
when /^\d+$/
|
||||
cur_source[:amount] = a.to_i
|
||||
when 'add', 'del', 'delete', 'clear', 'help', 'list'
|
||||
cmd = a.downcase
|
||||
else
|
||||
puts "source: unhandled argument #{a}"
|
||||
end
|
||||
}
|
||||
|
||||
case cmd
|
||||
when 'add'
|
||||
$sources_onupdate ||= df.onupdate_register('sources', 12) {
|
||||
# called every 12 game ticks (100x a dwarf day)
|
||||
$sources.each { |s|
|
||||
if tile = df.map_tile_at(*s[:pos]) and tile.shape_passableflow
|
||||
# XXX does not check current liquid_type
|
||||
des = tile.designation
|
||||
cur = des.flow_size
|
||||
if cur != s[:amount]
|
||||
tile.spawn_liquid((cur > s[:amount] ? cur-1 : cur+1), s[:liquid] == 'magma')
|
||||
end
|
||||
end
|
||||
}
|
||||
if $sources.empty?
|
||||
df.onupdate_unregister($sources_onupdate)
|
||||
$sources_onupdate = nil
|
||||
end
|
||||
}
|
||||
|
||||
if cur_source[:pos][0] >= 0
|
||||
if tile = df.map_tile_at(*cur_source[:pos])
|
||||
if tile.shape_passableflow
|
||||
$sources << cur_source
|
||||
else
|
||||
puts "Impassable tile: I'm afraid I can't do that, Dave"
|
||||
end
|
||||
else
|
||||
puts "Unallocated map block - build something here first"
|
||||
end
|
||||
else
|
||||
puts "Please put the game cursor where you want a source"
|
||||
end
|
||||
|
||||
when 'del', 'delete'
|
||||
$sources.delete_if { |s| s[:pos] == cur_source[:pos] }
|
||||
|
||||
when 'clear'
|
||||
$sources.clear
|
||||
|
||||
when 'list'
|
||||
puts "Source list:", $sources.map { |s|
|
||||
" #{s[:pos].inspect} #{s[:liquid]} #{s[:amount]}"
|
||||
}
|
||||
puts "Current cursor pos: #{[df.cursor.x, df.cursor.y, df.cursor.z].inspect}" if df.cursor.x >= 0
|
||||
|
||||
else
|
||||
puts <<EOS
|
||||
Creates a new infinite liquid source at the cursor.
|
||||
|
||||
Examples:
|
||||
source add water - create a water source under cursor
|
||||
source add water 0 - create a water drain
|
||||
source add magma 5 - create a magma source, up to 5/7 deep
|
||||
source delete - delete source under cursor
|
||||
source clear - remove all sources
|
||||
source list
|
||||
EOS
|
||||
end
|
@ -1,204 +1,204 @@
|
||||
# mark stuff inside of cages for dumping.
|
||||
|
||||
def plural(nr, name)
|
||||
# '1 cage' / '4 cages'
|
||||
"#{nr} #{name}#{'s' if nr > 1}"
|
||||
end
|
||||
|
||||
def cage_dump_items(list)
|
||||
count = 0
|
||||
count_cage = 0
|
||||
list.each { |cage|
|
||||
pre_count = count
|
||||
cage.general_refs.each { |ref|
|
||||
next unless ref.kind_of?(DFHack::GeneralRefContainsItemst)
|
||||
next if ref.item_tg.flags.dump
|
||||
count += 1
|
||||
ref.item_tg.flags.dump = true
|
||||
}
|
||||
count_cage += 1 if pre_count != count
|
||||
}
|
||||
|
||||
puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}"
|
||||
end
|
||||
|
||||
def cage_dump_armor(list)
|
||||
count = 0
|
||||
count_cage = 0
|
||||
list.each { |cage|
|
||||
pre_count = count
|
||||
cage.general_refs.each { |ref|
|
||||
next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst)
|
||||
ref.unit_tg.inventory.each { |it|
|
||||
next if it.mode != :Worn
|
||||
next if it.item.flags.dump
|
||||
count += 1
|
||||
it.item.flags.dump = true
|
||||
}
|
||||
}
|
||||
count_cage += 1 if pre_count != count
|
||||
}
|
||||
|
||||
puts "Dumped #{plural(count, 'armor piece')} in #{plural(count_cage, 'cage')}"
|
||||
end
|
||||
|
||||
def cage_dump_weapons(list)
|
||||
count = 0
|
||||
count_cage = 0
|
||||
list.each { |cage|
|
||||
pre_count = count
|
||||
cage.general_refs.each { |ref|
|
||||
next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst)
|
||||
ref.unit_tg.inventory.each { |it|
|
||||
next if it.mode != :Weapon
|
||||
next if it.item.flags.dump
|
||||
count += 1
|
||||
it.item.flags.dump = true
|
||||
}
|
||||
}
|
||||
count_cage += 1 if pre_count != count
|
||||
}
|
||||
|
||||
puts "Dumped #{plural(count, 'weapon')} in #{plural(count_cage, 'cage')}"
|
||||
end
|
||||
|
||||
def cage_dump_all(list)
|
||||
count = 0
|
||||
count_cage = 0
|
||||
list.each { |cage|
|
||||
pre_count = count
|
||||
cage.general_refs.each { |ref|
|
||||
case ref
|
||||
when DFHack::GeneralRefContainsItemst
|
||||
next if ref.item_tg.flags.dump
|
||||
count += 1
|
||||
ref.item_tg.flags.dump = true
|
||||
when DFHack::GeneralRefContainsUnitst
|
||||
ref.unit_tg.inventory.each { |it|
|
||||
next if it.item.flags.dump
|
||||
count += 1
|
||||
it.item.flags.dump = true
|
||||
}
|
||||
end
|
||||
}
|
||||
count_cage += 1 if pre_count != count
|
||||
}
|
||||
|
||||
puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}"
|
||||
end
|
||||
|
||||
|
||||
def cage_dump_list(list)
|
||||
count_total = Hash.new(0)
|
||||
empty_cages = 0
|
||||
list.each { |cage|
|
||||
count = Hash.new(0)
|
||||
|
||||
cage.general_refs.each { |ref|
|
||||
case ref
|
||||
when DFHack::GeneralRefContainsItemst
|
||||
count[ref.item_tg._rtti_classname] += 1
|
||||
when DFHack::GeneralRefContainsUnitst
|
||||
ref.unit_tg.inventory.each { |it|
|
||||
count[it.item._rtti_classname] += 1
|
||||
}
|
||||
# TODO vermin ?
|
||||
else
|
||||
puts "unhandled ref #{ref.inspect}" if $DEBUG
|
||||
end
|
||||
}
|
||||
|
||||
type = case cage
|
||||
when DFHack::ItemCagest; 'Cage'
|
||||
when DFHack::ItemAnimaltrapst; 'Animal trap'
|
||||
else cage._rtti_classname
|
||||
end
|
||||
|
||||
if count.empty?
|
||||
empty_cages += 1
|
||||
else
|
||||
puts "#{type} ##{cage.id}: ", count.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" }
|
||||
end
|
||||
|
||||
count.each { |k, v| count_total[k] += v }
|
||||
}
|
||||
|
||||
if list.length > 2
|
||||
puts '', "Total: ", count_total.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" }
|
||||
puts "with #{plural(empty_cages, 'empty cage')}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# handle magic script arguments
|
||||
here_only = $script_args.delete 'here'
|
||||
if here_only
|
||||
it = df.item_find
|
||||
list = [it]
|
||||
if not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst)
|
||||
list = df.world.items.other[:ANY_CAGE_OR_TRAP].find_all { |i| df.at_cursor?(i) }
|
||||
end
|
||||
if list.empty?
|
||||
puts 'Please select a cage'
|
||||
throw :script_finished
|
||||
end
|
||||
|
||||
elsif ids = $script_args.find_all { |arg| arg =~ /^\d+$/ } and ids.first
|
||||
list = []
|
||||
ids.each { |id|
|
||||
$script_args.delete id
|
||||
if not it = df.item_find(id.to_i)
|
||||
puts "Invalid item id #{id}"
|
||||
elsif not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst)
|
||||
puts "Item ##{id} is not a cage"
|
||||
list << it
|
||||
else
|
||||
list << it
|
||||
end
|
||||
}
|
||||
if list.empty?
|
||||
puts 'Please use a valid cage id'
|
||||
throw :script_finished
|
||||
end
|
||||
|
||||
else
|
||||
list = df.world.items.other[:ANY_CAGE_OR_TRAP]
|
||||
end
|
||||
|
||||
|
||||
# act
|
||||
case $script_args[0]
|
||||
when /^it/i
|
||||
cage_dump_items(list)
|
||||
when /^arm/i
|
||||
cage_dump_armor(list)
|
||||
when /^wea/i
|
||||
cage_dump_weapons(list)
|
||||
when 'all'
|
||||
cage_dump_all(list)
|
||||
when 'list'
|
||||
cage_dump_list(list)
|
||||
else
|
||||
puts <<EOS
|
||||
Marks items inside all cages for dumping.
|
||||
Add 'here' to dump stuff only for selected cage.
|
||||
Add a cage id to dump stuff for this cage only.
|
||||
|
||||
See 'autodump' to actually dump stuff.
|
||||
|
||||
Usage:
|
||||
stripcaged items
|
||||
dump items directly in cages (eg seeds after training)
|
||||
|
||||
stripcaged [armor|weapons] here
|
||||
dump armor or weapons of caged creatures in selected cage
|
||||
|
||||
stripcaged all 28 29
|
||||
dump every item in cage id 28 and 29, along with every item worn by creatures in there too
|
||||
|
||||
stripcaged list
|
||||
show content of the cages
|
||||
|
||||
EOS
|
||||
|
||||
end
|
||||
# mark stuff inside of cages for dumping.
|
||||
|
||||
def plural(nr, name)
|
||||
# '1 cage' / '4 cages'
|
||||
"#{nr} #{name}#{'s' if nr > 1}"
|
||||
end
|
||||
|
||||
def cage_dump_items(list)
|
||||
count = 0
|
||||
count_cage = 0
|
||||
list.each { |cage|
|
||||
pre_count = count
|
||||
cage.general_refs.each { |ref|
|
||||
next unless ref.kind_of?(DFHack::GeneralRefContainsItemst)
|
||||
next if ref.item_tg.flags.dump
|
||||
count += 1
|
||||
ref.item_tg.flags.dump = true
|
||||
}
|
||||
count_cage += 1 if pre_count != count
|
||||
}
|
||||
|
||||
puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}"
|
||||
end
|
||||
|
||||
def cage_dump_armor(list)
|
||||
count = 0
|
||||
count_cage = 0
|
||||
list.each { |cage|
|
||||
pre_count = count
|
||||
cage.general_refs.each { |ref|
|
||||
next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst)
|
||||
ref.unit_tg.inventory.each { |it|
|
||||
next if it.mode != :Worn
|
||||
next if it.item.flags.dump
|
||||
count += 1
|
||||
it.item.flags.dump = true
|
||||
}
|
||||
}
|
||||
count_cage += 1 if pre_count != count
|
||||
}
|
||||
|
||||
puts "Dumped #{plural(count, 'armor piece')} in #{plural(count_cage, 'cage')}"
|
||||
end
|
||||
|
||||
def cage_dump_weapons(list)
|
||||
count = 0
|
||||
count_cage = 0
|
||||
list.each { |cage|
|
||||
pre_count = count
|
||||
cage.general_refs.each { |ref|
|
||||
next unless ref.kind_of?(DFHack::GeneralRefContainsUnitst)
|
||||
ref.unit_tg.inventory.each { |it|
|
||||
next if it.mode != :Weapon
|
||||
next if it.item.flags.dump
|
||||
count += 1
|
||||
it.item.flags.dump = true
|
||||
}
|
||||
}
|
||||
count_cage += 1 if pre_count != count
|
||||
}
|
||||
|
||||
puts "Dumped #{plural(count, 'weapon')} in #{plural(count_cage, 'cage')}"
|
||||
end
|
||||
|
||||
def cage_dump_all(list)
|
||||
count = 0
|
||||
count_cage = 0
|
||||
list.each { |cage|
|
||||
pre_count = count
|
||||
cage.general_refs.each { |ref|
|
||||
case ref
|
||||
when DFHack::GeneralRefContainsItemst
|
||||
next if ref.item_tg.flags.dump
|
||||
count += 1
|
||||
ref.item_tg.flags.dump = true
|
||||
when DFHack::GeneralRefContainsUnitst
|
||||
ref.unit_tg.inventory.each { |it|
|
||||
next if it.item.flags.dump
|
||||
count += 1
|
||||
it.item.flags.dump = true
|
||||
}
|
||||
end
|
||||
}
|
||||
count_cage += 1 if pre_count != count
|
||||
}
|
||||
|
||||
puts "Dumped #{plural(count, 'item')} in #{plural(count_cage, 'cage')}"
|
||||
end
|
||||
|
||||
|
||||
def cage_dump_list(list)
|
||||
count_total = Hash.new(0)
|
||||
empty_cages = 0
|
||||
list.each { |cage|
|
||||
count = Hash.new(0)
|
||||
|
||||
cage.general_refs.each { |ref|
|
||||
case ref
|
||||
when DFHack::GeneralRefContainsItemst
|
||||
count[ref.item_tg._rtti_classname] += 1
|
||||
when DFHack::GeneralRefContainsUnitst
|
||||
ref.unit_tg.inventory.each { |it|
|
||||
count[it.item._rtti_classname] += 1
|
||||
}
|
||||
# TODO vermin ?
|
||||
else
|
||||
puts "unhandled ref #{ref.inspect}" if $DEBUG
|
||||
end
|
||||
}
|
||||
|
||||
type = case cage
|
||||
when DFHack::ItemCagest; 'Cage'
|
||||
when DFHack::ItemAnimaltrapst; 'Animal trap'
|
||||
else cage._rtti_classname
|
||||
end
|
||||
|
||||
if count.empty?
|
||||
empty_cages += 1
|
||||
else
|
||||
puts "#{type} ##{cage.id}: ", count.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" }
|
||||
end
|
||||
|
||||
count.each { |k, v| count_total[k] += v }
|
||||
}
|
||||
|
||||
if list.length > 2
|
||||
puts '', "Total: ", count_total.sort_by { |k, v| v }.map { |k, v| " #{v} #{k}" }
|
||||
puts "with #{plural(empty_cages, 'empty cage')}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# handle magic script arguments
|
||||
here_only = $script_args.delete 'here'
|
||||
if here_only
|
||||
it = df.item_find
|
||||
list = [it]
|
||||
if not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst)
|
||||
list = df.world.items.other[:ANY_CAGE_OR_TRAP].find_all { |i| df.at_cursor?(i) }
|
||||
end
|
||||
if list.empty?
|
||||
puts 'Please select a cage'
|
||||
throw :script_finished
|
||||
end
|
||||
|
||||
elsif ids = $script_args.find_all { |arg| arg =~ /^\d+$/ } and ids.first
|
||||
list = []
|
||||
ids.each { |id|
|
||||
$script_args.delete id
|
||||
if not it = df.item_find(id.to_i)
|
||||
puts "Invalid item id #{id}"
|
||||
elsif not it.kind_of?(DFHack::ItemCagest) and not it.kind_of?(DFHack::ItemAnimaltrapst)
|
||||
puts "Item ##{id} is not a cage"
|
||||
list << it
|
||||
else
|
||||
list << it
|
||||
end
|
||||
}
|
||||
if list.empty?
|
||||
puts 'Please use a valid cage id'
|
||||
throw :script_finished
|
||||
end
|
||||
|
||||
else
|
||||
list = df.world.items.other[:ANY_CAGE_OR_TRAP]
|
||||
end
|
||||
|
||||
|
||||
# act
|
||||
case $script_args[0]
|
||||
when /^it/i
|
||||
cage_dump_items(list)
|
||||
when /^arm/i
|
||||
cage_dump_armor(list)
|
||||
when /^wea/i
|
||||
cage_dump_weapons(list)
|
||||
when 'all'
|
||||
cage_dump_all(list)
|
||||
when 'list'
|
||||
cage_dump_list(list)
|
||||
else
|
||||
puts <<EOS
|
||||
Marks items inside all cages for dumping.
|
||||
Add 'here' to dump stuff only for selected cage.
|
||||
Add a cage id to dump stuff for this cage only.
|
||||
|
||||
See 'autodump' to actually dump stuff.
|
||||
|
||||
Usage:
|
||||
stripcaged items
|
||||
dump items directly in cages (eg seeds after training)
|
||||
|
||||
stripcaged [armor|weapons] here
|
||||
dump armor or weapons of caged creatures in selected cage
|
||||
|
||||
stripcaged all 28 29
|
||||
dump every item in cage id 28 and 29, along with every item worn by creatures in there too
|
||||
|
||||
stripcaged list
|
||||
show content of the cages
|
||||
|
||||
EOS
|
||||
|
||||
end
|
||||
|
@ -1,61 +1,61 @@
|
||||
# give super-dwarven speed to an unit
|
||||
|
||||
$superdwarf_onupdate ||= nil
|
||||
$superdwarf_ids ||= []
|
||||
|
||||
case $script_args[0]
|
||||
when 'add'
|
||||
if u = df.unit_find
|
||||
$superdwarf_ids |= [u.id]
|
||||
|
||||
$superdwarf_onupdate ||= df.onupdate_register('superdwarf', 1) {
|
||||
if $superdwarf_ids.empty?
|
||||
df.onupdate_unregister($superdwarf_onupdate)
|
||||
$superdwarf_onupdate = nil
|
||||
else
|
||||
$superdwarf_ids.each { |id|
|
||||
if u = df.unit_find(id) and not u.flags1.dead
|
||||
# faster walk/work
|
||||
if u.counters.job_counter > 0
|
||||
u.counters.job_counter = 0
|
||||
end
|
||||
|
||||
# no sleep
|
||||
if u.counters2.sleepiness_timer > 10000
|
||||
u.counters2.sleepiness_timer = 1
|
||||
end
|
||||
|
||||
# no break
|
||||
if b = u.status.misc_traits.find { |t| t.id == :OnBreak }
|
||||
b.value = 500_000
|
||||
end
|
||||
else
|
||||
$superdwarf_ids.delete id
|
||||
end
|
||||
}
|
||||
end
|
||||
}
|
||||
else
|
||||
puts "Select a creature using 'v'"
|
||||
end
|
||||
|
||||
when 'del'
|
||||
if u = df.unit_find
|
||||
$superdwarf_ids.delete u.id
|
||||
else
|
||||
puts "Select a creature using 'v'"
|
||||
end
|
||||
|
||||
when 'clear'
|
||||
$superdwarf_ids.clear
|
||||
|
||||
when 'list'
|
||||
puts "current superdwarves:", $superdwarf_ids.map { |id| df.unit_find(id).name }
|
||||
|
||||
else
|
||||
puts "Usage:",
|
||||
" - superdwarf add: give superspeed to currently selected creature",
|
||||
" - superdwarf del: remove superspeed to current creature",
|
||||
" - superdwarf clear: remove all superpowers",
|
||||
" - superdwarf list: list super-dwarves"
|
||||
end
|
||||
# give super-dwarven speed to an unit
|
||||
|
||||
$superdwarf_onupdate ||= nil
|
||||
$superdwarf_ids ||= []
|
||||
|
||||
case $script_args[0]
|
||||
when 'add'
|
||||
if u = df.unit_find
|
||||
$superdwarf_ids |= [u.id]
|
||||
|
||||
$superdwarf_onupdate ||= df.onupdate_register('superdwarf', 1) {
|
||||
if $superdwarf_ids.empty?
|
||||
df.onupdate_unregister($superdwarf_onupdate)
|
||||
$superdwarf_onupdate = nil
|
||||
else
|
||||
$superdwarf_ids.each { |id|
|
||||
if u = df.unit_find(id) and not u.flags1.dead
|
||||
# faster walk/work
|
||||
if u.counters.job_counter > 0
|
||||
u.counters.job_counter = 0
|
||||
end
|
||||
|
||||
# no sleep
|
||||
if u.counters2.sleepiness_timer > 10000
|
||||
u.counters2.sleepiness_timer = 1
|
||||
end
|
||||
|
||||
# no break
|
||||
if b = u.status.misc_traits.find { |t| t.id == :OnBreak }
|
||||
b.value = 500_000
|
||||
end
|
||||
else
|
||||
$superdwarf_ids.delete id
|
||||
end
|
||||
}
|
||||
end
|
||||
}
|
||||
else
|
||||
puts "Select a creature using 'v'"
|
||||
end
|
||||
|
||||
when 'del'
|
||||
if u = df.unit_find
|
||||
$superdwarf_ids.delete u.id
|
||||
else
|
||||
puts "Select a creature using 'v'"
|
||||
end
|
||||
|
||||
when 'clear'
|
||||
$superdwarf_ids.clear
|
||||
|
||||
when 'list'
|
||||
puts "current superdwarves:", $superdwarf_ids.map { |id| df.unit_find(id).name }
|
||||
|
||||
else
|
||||
puts "Usage:",
|
||||
" - superdwarf add: give superspeed to currently selected creature",
|
||||
" - superdwarf del: remove superspeed to current creature",
|
||||
" - superdwarf clear: remove all superpowers",
|
||||
" - superdwarf list: list super-dwarves"
|
||||
end
|
||||
|
@ -1,17 +1,17 @@
|
||||
joblist = df.world.job_list.next
|
||||
count = 0
|
||||
|
||||
while joblist
|
||||
job = joblist.item
|
||||
joblist = joblist.next
|
||||
|
||||
if job.job_type == :ConstructBuilding
|
||||
if (job.flags.suspend && job.items && job.items[0])
|
||||
item = job.items[0].item
|
||||
job.flags.suspend = false
|
||||
count += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
puts "Unsuspended #{count} job(s)."
|
||||
joblist = df.world.job_list.next
|
||||
count = 0
|
||||
|
||||
while joblist
|
||||
job = joblist.item
|
||||
joblist = joblist.next
|
||||
|
||||
if job.job_type == :ConstructBuilding
|
||||
if (job.flags.suspend && job.items && job.items[0])
|
||||
item = job.items[0].item
|
||||
job.flags.suspend = false
|
||||
count += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
puts "Unsuspended #{count} job(s)."
|
||||
|
Loading…
Reference in New Issue