Questions about the wrapper script.1. I wanted to, but you changed syntax a lot, kept working on it, and then I went away on tour.
1. Does anyone other than myself use the 'wrapper' script?
2. If so, what are your opinions on how the system currently works? There are a couple options to try and improve usability. (For instance instead of having to do -profession required:CARPENTER or -profession immune:MASON I could make a separate rprofession and iprofession).
3. I would like to add more features (including checking for body parts, inventory items, etc...). But I am wondering when it becomes too much. I unfortunately can't think of a better way to allow more custom targeting options.
4. Upper or lowercase? That is to say, currently if you want to require a target to have at least 2000 strength you would say -physical min:STRENGTH:2000. Should that instead be MIN:STRENGTH:2000? Something else entirely?
5. Any other suggestions?
Questions about the wrapper script.
1. Does anyone other than myself use the 'wrapper' script?
2. If so, what are your opinions on how the system currently works? There are a couple options to try and improve usability. (For instance instead of having to do -profession required:CARPENTER or -profession immune:MASON I could make a separate rprofession and iprofession).
3. I would like to add more features (including checking for body parts, inventory items, etc...). But I am wondering when it becomes too much. I unfortunately can't think of a better way to allow more custom targeting options.
4. Upper or lowercase? That is to say, currently if you want to require a target to have at least 2000 strength you would say -physical min:STRENGTH:2000. Should that instead be MIN:STRENGTH:2000? Something else entirely?
5. Any other suggestions?
Posting to watch.
Have you switched the !BLAH system over to the \\BLAH system to make it more uniform with modtools stuff?
EnchancedItems - similar to EnchancedCreatures, except for items. Will also display an items history.I've seen a script like this flying around, but it was never finished.
QuoteEnchancedItems - similar to EnchancedCreatures, except for items. Will also display an items history.I've seen a script like this flying around, but it was never finished.
Call me boring, but shouldnt the sharpness decrease, the more the weapon is used? (except on magical weapons)
Posting to watch.
Have you switched the !BLAH system over to the \\BLAH system to make it more uniform with modtools stuff?
Yes, I did have a question about that though, and more specifically about the entire utils.processArgs thing. A couple questions actually;
1. Is processArgs recursive? What I mean is when using something like modtools/interaction-trigger does it read through everything in the -command argument? Or does it just lump it all into one thing?
2. Similarly, if I can't use the same \\BLAH that the other modtools things use correct? Because it would be overwritten?
3. Should it always be two \? Is there ever a time you would only use one \?
EDIT: Thanks for the feedback. My current plan is to rewrite the wrapper script to make it more intelligeble for people who want to see what is going on, and to make it easily addable to in the future. The rewrite shouldn't change the syntax from what it was before, and I am actually going to allow for both upper or lower case (since that is super easy to do). I am currently planning on breaking -profession, -noble, and -entity into a required and immune section (from #2 in the previous post)
-radius x,y,z
or-radius [ x y z ]
Well I found a way to successfully implement a dispel type system, the only issue is that is adds quite a bit more entries into persistent storage. I will have to do some tests to see how much space they are taking up now. I use it for a crap ton of things.
EDIT: Here is a question, in updating my scripts I noticed I used a lot of things like -radius x,y,z and -offset x,y,z. These were used because I just did a direct translation from the previous system of using SYN_CLASSes. But I could change them to -radius [ x y z ]. So the question is, which one is preferred?Code: [Select]-radius x,y,z
orCode: [Select]-radius [ x y z ]
raw/scripts taps into raws so that they load in a script calling function for eventful or something like that, and the other is stand alone where someone can call on them by typing out their name.
Roses: I've been integrating the script library into DFHack. What's the difference between the hack/scripts directory and the raw/scripts directory?
Roses: I've been integrating the script library into DFHack. What's the difference between the hack/scripts directory and the raw/scripts directory?My understanding is that DFHack searches raw/scripts for a named script first then hack/scripts, to allow a mod to overload a specific script's name if desired for some reason. Though I agree with the logic for this, in a specific case I wanted the exact opposite behavior (include a fallback with the mod but use the probably-more-up-to-date general version if it's installed) and there is no way to explicitly call hack/scripts.
That's how it's supposed to work but I think I noticed a few scripts that were in both places so I was confused.Seriously: Stop abusing script systems!
I'm not sure there's a simple way to extend the script finding system to do that. You might be able to do it manually by checking for the existence / modify date of hack/scripts/whatever and calling it if it's newer than data/save/adsf/raw/scripts/whatever.
Squire
Knight
Guardian
Fighter
Warrior
Slayer
Thief
Rogue
Stalker
Archer
Marksman
Sniper
Assassin
Warden
Gladiator
Ranger
Mage
Arch-Mage
Elementalist
Adept
Mystic
Arcanist
Cleric
Priest
Bishop
Necromancer
Occultist
Ovate
Druid
High Druid
Shaman
Sage
Geomancer
Disciple
Erudite
Psion
Rune Smith
Holy Mage
Archon
Earth Master
Artificer
Alchemist
Seer
Warlock
Oracle
Defiler
Keeper
Corrupter
Beast Master
Eldritch Knight
Arcane Warrior
Paladin
Death Knight
Monk
Warshaper
Avenger
Mind Blade
Berserker
Magic Arrow
Eldritch Shadow
Trickster
Spellslayer
Vigilante
Demon Hunter
Bard
Strider
Mind Hunter
I like the mix of D&D-feel and other sorts of games in there.
Arcane Shield
Arcane Weapon
Magic Shell
Protection from Melee
Protection from Range
Airward
Arcane Armor
Earthward
Fireward
Iceward
Metalward
Smokeward
Stormward
Waterward
Arcane Fortification
Arcane Wall
Arcaneward
Divineward
Elementalward
Mentalward
Natureward
Resist Magic
Weapon Immunity
Impenetrable Barrier
Magic Immunity
Arcane Missile
Arcane Surge
Attract
Hold
Repel
Arcane Blast
Arcane Burst
Arcane Explosion
Arcane Fury
Arcane Spear
Arcane Volley
Bend Light
Flip Gravity
Gravity
Levitate
Repulse
Arcane Eruption
Arcane Orb
Crush
Element
Pulsar
Quasar
Shock Wave
Blackhole
Supernova
Disengage
Distort Space
Enlarge
Shrink
Vertigo
Alter Reality
Bend Space
Disenchant
Displace
Limbo
Passwall
Portal
Recall Hero
Summon Familiar
Arcane Elemental
Counter Magic
Displacement Field
Magic Vortex
Tear Space
Unsummon
Void
Warp Reality
Wormhole
Arcane Gate
Bind Elemental
Accelerate Healing
Aging Touch
Blink
Haste
Slow
Youth
Accelerate Syndrome
Age Armor
Age Item
Age Weapon
Aging Breath
Delay Syndrome
Distort Time
Mass Haste
Mass Slow
Teleport
Eternal Youth
Reverse Time
Stop
Time Bubble
Time Stop
Warp
Predict Future
Quick
Reflect
Agony
Curse
Dark
Dark Gaze
Dark Sword
Harm
Bind Undead
Bone Armor
Contagion
Corpse Explosion
Creeping Dark
Death Knell
Drain Life
Enfeeble
Nightmare
Pray
Raise Dead
Bind Demon
Create Undead
Dark Pact
Death Word
Desecrate
Divine Blessing
Enthrall
Necrosis
Shackle
Summon Fiend
Death
Vampirism
Bless
Heal
Holy Weapon
Light
Smite
Ward
Barrier
Blinding Light
Cleanse
Consecrate
Fortify
Holy Armor
Holy Aura
Pray
Regenerate
Renew
Turn Undead
Dispel
Divine Blessing
Esuna
Greater Heal
Holy Word
Mass Heal
Rejuvenate
Sanctify Chaos
Sanctify Undead
Serephic Ally
Holy
Resurrect
Blur
Shadow
Shadow Blade
Shadow Bolt
Shadow Walk
Atone
Darkness
Daylight
Greater Shadow
Shadow Armor
Shadow Decoy
Shadow Steed
Shadowsight
Sleep
Vanish
Vengeance
Grand Shadow
Moonlight
Re-Raise
Sacrifice
Sanctuary
Shade
Shadow Beast
Divine Power
Eternal Shade
Repulse
Void Blade
Void Bolt
Void Sphere
Void Walk
Void Ward
Demi
Destruction
Drain
Hold
Soul Bind
Siphon
Void Armor
Void Barrier
Void Blast
Abyss
Banish
Darkward
Lightward
Scourge
Ultima
Void Beast
Void Storm
Doom
Omega
Air Bubble
Air Dart
Blind
Rushing Wind
Whipping Wind
Binding Winds
Gust
Harden Air
Pressing Air
Ventilate
Wind Blast
Wind Shield
Wind Walk
Air Elemental
Burst of Wind
Deflecting Winds
Displace
Howling Wind
Suffocate
Tempest
Vacuum
Wall of Air
Cyclone
Flight
Tornado
Bind
Earthen Ward
Harden
Stone Daggers
Stone Fist
Stone Weapon
Boulder
Earth Armor
Grasping Earth
Seismic Wave
Stalactite
Stalagmite
Stoneskin
Earth Elemental
Earthen Bulwark
Petrify
Sand Storm
Stone Aegis
Sunder Earth
Wall of Stone
Disintegrate
Earthquake
Rockslide
Shelter
Stone Form
Burn
Fire Dart
Flame Weapon
Scorch
Burning Hands
Combust
Fire Armor
Fire Jet
Flame Blast
Flame Strike
Flame Tongue
Ignite
Scorching Ray
Blaze
Conflagration
Fire Ball
Fire Elemental
Flaming Sphere
Incinerate
Wall of Fire
Dragon Breath
Flare
Inferno
Meteor Storm
Cone of Cold
Frostbite
Ice Darts
Ice Weapon
Slowing Ice
Chains of Ice
Cold Snap
Frostburn
Ice Armor
Ice Lash
Ice Spike
Ray of Frost
Arctic Wind
Freeze
Ice Barrier
Ice Elemental
Iceball
Orb of Cold
Polar Ray
Wall of Ice
Arctic Tempest
Avalanche
Blizzard
Frost Nova
Keen Edge
Magic Sword
Metal Shards
Metal Skin
Temper
Animate Armor
Animate Weapon
Armor Break
Metal Fist
Mighty Guard
Repair Metal
Rust Armor
Rust Weapon
Weaken Armor
Weaken Weapon
Weapon Break
Blade Barrier
Chill Metal
Diamond Shell
Heat Metal
Metal Elemental
Molten Spray
Disintegrate Equipment
Quicken
Shatter
Fog Walk
Smog
Smoke Dart
Smoke Screen
Steam Blast
Stinking Cloud
Choke
Cloak of Smoke
Evaporate
Haze
Jet of Steam
Miasma
Mind Fog
Obscure
Poison Mist
Steam Cloud
Acid Fog
Caustic Smoke
Crushing Fog
Fog Cloud
Smoke Elemental
Smoke Terror
Bewildering Fog
Cloud Kill
Incendiary Cloud
Charge
Flash
Lightning Bolt
Shock
Shocking Touch
Spark
Storm Weapon
Disorient
Lightning
Lightning Spear
Lightning Stab
Storm Armor
Super Charge
Thunder Clap
Ball Lightning
Call Lightning
Chain Lightning
Drain Energy
Infuse
Paralyze
Storm Elemental
Thunderbolt
Lightning Shield
Lightning Storm
Storm Cloud
Clean
Healing Stream
Soak
Surge
Water Bolt
Cleansing Rain
Create Water
Cure Infection
Dehydrate
Extinguish
Hydrate
Tidal Wave
Water Orb
Drown
Healing Mist
Torrent
Typhoon
Wall of Water
Water Elemental
Water Shell
Water Spout
Waterfall
Flood
Maelstrom
Rushing Water
Apathy
Bliss
Calm
Fear
Grief
Hate
Courage
Drain Emotion
Fury
Gloom
Pleasure
Pride
Sadness
Terror
Break
Charm
Depression
Ecstasy
Enrage
Horror
Lust
Vigilance
Despair
Dominate
Tranquility
Decoy
Ghost Sound
Mental Blades
Phantom Dart
Sound Burst
Apparition
Blur
Delusion
Disguise
Hypnotic Pattern
Phantom Spear
Phantom Trap
Simulacrum
True Sight
Color Spray
Death Vision
Destroy Illusion
Illusory Wall
Invisibility
Mass Blur
Mirror Image
Phantom Warriors
Copy Self
Mass Invisibility
Phantom Beast
Blind
Deafen
Mind Slash
Psi Blades
Psionic Bolt
Turmoil
Disable
Empty Mind
Hold Mind
Jumble Mind
Mind Flay
Mind Leech
Psionic Blast
Psionic Gift
Share Mind
Telekinesis
Hive Mind
Mental Duel
Mind Trap
Shatter Mind
Silence
Steal Mind
Mind Rot
Mind Storm
Stop Mind
Blank
Craze
Daze
Strengthen Will
Thought Lash
Bend Will
Confusion
Guard Thoughts
Hysteria
Read Thoughts
Scour
Sleep
Steel Will
Stun
Thought Eater
Dementia
Devour Thoughts
Drain Will
Greater Stun
Mass Confusion
Thought Prison
Torment
Break Will
Deep Sleep
Insanity
Animal Agility
Animal Endurance
Animal Strength
Animal Toughness
Animal Bane
Animal Ferocity
Animal Physicality
Calm Animals
Charm Animal
Empower Animal
Envenom
Magic Fang
Mend Animal
Purge Animal
Companion
Enrage Animals
Frenzy
Greater Mending
Recall Companion
Savage Maw
Share Soul
Summon Animal
Summon Swarm
Animal Form
Revive Companion
Geomancy
Soften Earth
Stone Spikes
Burrow
Change Terrain
Earth Bind
Geostrike
Pitfall
Stoneskin
Tremor
Call Earth
Cleanse Land
Corrupt Land
Cracks Call
Hungering Earth
Move Earth
Stone Form
Earth Gate
Cure
Natures Eye
Neutralize Poison
Tree Stride
Warp Wood
Antidote
Barkskin
Cloak of Petals
Entangle
Poison
Restoration
Rot
Awaken Trees
Command Plants
Degenerate
Grow Plants
Ironwood
Natures Wrath
Regenerate
Summon Shambler
Toxin
Wall of Thorns
Blight
Create Guardian
Steeloak
Guardian Spirit
Spirit Walk
Spirit Weapon
Strength of Spirits
Ancestral Armor
Awaken
Calm Spirit
Commune with Spirits
Faerie Fire
Heal
Life Link
Spirit Wave
Totem
Bind Spirit
Call Ancestors
Chain Heal
Cleanse Spirits
Meditate
Spirit Drain
Spirit Link
Spirit Shriek
Spirit Tear
Summon Spirit
Reincarnate
Spirit Form
spells[spell] = spell
Quotespells[spell] = spell
spells is undefined, line 445 of functions/tables.lua
it's defined 2 lines later
functions/unit.lua, function changeSkill, utils is used but never actually defined
tonumber(spellTable.RequiredLevel) <= currentClassLevel in functions/class.lua function changeClass gives me a compare-string-to-number error; should probably put currentClassLevel in a tonumber as well
persistTable.GlobalTable.roses.SpellTable is never defined
i'll probably try to set up the newest version of fortbent as a testbed for the class system... especially since i'm not altogether aware of any other mods using it
EDIT: Why am i posting here, i'll make a pull request once i get the class system working
I think the assumptions that the EntityTable is based on are entirely wrong as of 0.42.04, since I can't seem to get it to work no matter what I do. Trying to fix it doesn't seem like a good idea, since I don't know what it's trying to do.
I know that most of your effort is going toward updating, but have you made any progress on add-attack (or attack-add as it's called in the OP)? That way I won't need to worry about creatures biting with their tails :)
Appreciated... the bigger issue is making sure that the creatures get into a proper fight, but even just the one valid attack makes my mod much better than it was before you told me about this script.I know that most of your effort is going toward updating, but have you made any progress on add-attack (or attack-add as it's called in the OP)? That way I won't need to worry about creatures biting with their tails :)
I have made some progress with that, but haven't fixed that exact bug yet. But that is on the list.
Appreciated... the bigger issue is making sure that the creatures get into a proper fight, but even just the one valid attack makes my mod much better than it was before you told me about this script.I know that most of your effort is going toward updating, but have you made any progress on add-attack (or attack-add as it's called in the OP)? That way I won't need to worry about creatures biting with their tails :)
I have made some progress with that, but haven't fixed that exact bug yet. But that is on the list.
I find that loud error messages by default are a better policy than guessing at what the calling script meant. It helps debugging.In that case, make it pick a random attack only when told to do so, perhaps a -random flag. If -random is present with an explicit directive, it fills in the parameters not specified by the calling script. For example, a SCRATCH attack with didn't specify a particular limb.
unit/attack -attacker \\UNIT_ID -defender \\UNIT_ID
-target BP_CATEGORY (body part to target e.g. -target UPPERBODY)
-attack ATTACK (type of attack e.g. -attack BITE)
-velocity # (velocity of the attack e.g. -velocity 1000)
-hitchance # (hit chance of the attack e.g. -hitchance 100)
-weapon Equipped (uses equipped weapon to attack with)
-weapon Fake -material MAT_ID -quality # (this will create a fake weapon that lasts for a small duration, long enough for the attack to be carried out, and then disappears e.g. -weapon Fake -material INORGANIC:STEEL -quality 1. Note, this works because the game doesn't actually check where the weapon is or who is holding it when performing an attack)
-delay # (how many ticks to wait before the attack is made e.g. -delay 5. Defaults to 1)
-number # (how many attacks to make e.g. -number 5. Defaults to 1)
I like the system you have there. Does this completely bypass dodging, or fit normally within the combat system?
I've never seen an attack with the current script get dodged.
Keep in mind, I've only ever used the current script to start a fight between units that aren't in combat. It might be hard (or impossible) to dodge that kind of "sneak attack."I like the system you have there. Does this completely bypass dodging, or fit normally within the combat system?
I've never seen an attack with the current script get dodged.
Good questions, and I don't know. I think the hit chance plays into dodging. Meaning that setting hit chance = 100 completely bypasses dodging. But I will test it to see if dodging is taking into account before or after hit chance is defined.
Keep in mind, I've only ever used the current script to start a fight between units that aren't in combat. It might be hard (or impossible) to dodge that kind of "sneak attack."I like the system you have there. Does this completely bypass dodging, or fit normally within the combat system?
I've never seen an attack with the current script get dodged.
Good questions, and I don't know. I think the hit chance plays into dodging. Meaning that setting hit chance = 100 completely bypasses dodging. But I will test it to see if dodging is taking into account before or after hit chance is defined.
local utils = require 'utils'
validArgs = validArgs or utils.invert({
'help',
'defender',
'attacker',
'target',
'attack',
'velocity',
'hitchance',
'weapon',
'delay',
'number',
})
local args = utils.processArgs({...}, validArgs)
if args.defender and tonumber(args.defender) then
defender = df.unit.find(tonumber(args.defender))
else
print('No defender selected')
return
end
if args.attacker and tonumber(args.attacker) then
attacker = df.unit.find(tonumber(args.attacker))
else
print('No attacker selected')
return
end
attack = nil
target = nil
if not args.target then
local rand = dfhack.random.new()
local totwght = 0
local weights = {}
weights[0] = 0
local n = 1
for _,targets in pairs(defender.body.body_plan.body_parts) do
totwght = totwght + targets.relsize
weights[n] = weights[n-1] + targets.relsize
n = n + 1
end
while not target do
pick = rand:random(totwght)
for i = 1,n do
if pick >= weights[i-1] and pick < weights[i] then
target = i-1
break
end
end
if defender.body.components.body_part_status[target].missing then target = nil end
end
else
for i,targets in pairs(defender.body.body_plan.body_parts) do
if targets.token == args.target then
target = i
break
end
end
end
if not target then
print('No appropriate target found')
return
end
delay = args.delay or 1
number = args.number or 1
hitchance = args.hitchance or 100
if args.weapon then
local item = nil
if args.weapon == 'Equipped' then
for _,items in pairs(attacker.inventory) do
if items.mode == 1 then
item = items.item
break
end
end
if not item then
print('No Equipped Weapon')
return
end
end
if not args.attack then
local rand = dfhack.random.new()
local totwght = 0
local weights = {}
weights[0] = 0
local n = 1
for _,attacks in pairs(item.subtype.attacks) do
totwght = totwght + 1
weights[n] = weights[n-1] + 1
n = n + 1
end
pick = rand:random(totwght)
for i = 1,n do
if pick >= weights[i-1] and pick < weights[i] then
attack = i-1
vel_mod = item.subtype.attacks[attack].velocity_mult
break
end
end
else
for i,attacks in pairs(item.subtype.attacks) do
if attacks.verb_2nd == args.attack or attacks.verb_3rd == args.attack then
attack = i
vel_mod = item.subtype.attacks[attack].velocity_mult
break
end
end
end
if not attack then
print('No appropriate attack found')
return
end
if args.velocity then
velocity = tonumber(args.velocity)
else
material = dfhack.matinfo.decode(item.mat_type,item.mat_index).material
weight = math.floor(item.subtype.size*material.solid_density/100000)
weight_fraction = item.subtype.size*material.solid_density*10 - weight*1000000
effweight=attacker.body.size_info.size_cur/100+weight*100+weight_fraction/10000
velocity = attacker.body.size_info.size_base * dfhack.units.getPhysicalAttrValue(attacker,0) * vel_mod/1000/effweight/1000
if velocity == 0 then velocity = 1 end
end
j = 0
while j < number do
action = df.unit_action:new()
action.id = attacker.next_action_id
attacker.next_action_id = attacker.next_action_id + 1
action.type = 1
attack_action = action.data.attack
attack_action.target_unit_id = defender.id
attack_action.attack_item_id = item.id
attack_action.target_body_part_id = target
attack_action.attack_body_part_id = -1
attack_action.unk_30 = velocity
attack_action.attack_id = attack
attack_action.flags = 7
attack_action.unk_28 = hitchance
attack_action.unk_2c = 100
attack_action.unk_38 = 100
attack_action.unk_3c = 100
attack_action.timer1 = delay
attack_action.timer2 = delay
for i,x in pairs(attack_action.unk_4) do
attack_action.unk_4[i] = 7
end
attack_action.unk_4.wrestle_type = -1
attacker.actions:insert('#',action)
j = j + 1
end
else
if not args.attack then
local rand = dfhack.random.new()
local totwght = 0
local weights = {}
weights[0] = 0
local n = 1
for _,attacks in pairs(attacker.body.body_plan.attacks) do
if attacks.flags.main then
totwght = totwght + 100
weights[n] = weights[n-1] + 100
else
totwght = totwght + 1
weights[n] = weights[n-1] + 1
end
n = n + 1
end
while not attack do
pick = rand:random(totwght)
for i = 1,n do
if pick >= weights[i-1] and pick < weights[i] then
attack = i-1
vel_mod = attacker.body.body_plan.attacks[attack].velocity_modifier
break
end
end
if attacker.body.components.body_part_status[attacker.body.body_plan.attacks[attack].body_part_idx[0]].missing then attack = nil end
end
else
for i,attacks in pairs(attacker.body.body_plan.attacks) do
if attacks.name == args.attack then
attack = i
vel_mod = attacker.body.body_plan.attacks[attack].velocity_modifier
break
end
end
end
if not attack then
print('No appropriate attack found')
return
end
if args.velocity then
velocity = tonumber(args.velocity)
else
velocity = 100 * dfhack.units.getPhysicalAttrValue(attacker,0) / 1000 * vel_mod / 1000
end
j = 0
while j < number do
action = df.unit_action:new()
action.id = attacker.next_action_id
attacker.next_action_id = attacker.next_action_id + 1
action.type = 1
attack_action = action.data.attack
attack_action.target_unit_id = defender.id
attack_action.attack_item_id = -1
attack_action.target_body_part_id = target
attack_action.attack_body_part_id = attacker.body.body_plan.attacks[attack].body_part_idx[0]
attack_action.unk_30 = velocity
attack_action.attack_id = attack
attack_action.flags = 7
attack_action.unk_28 = hitchance
attack_action.unk_2c = 100
attack_action.unk_38 = 100
attack_action.unk_3c = 100
attack_action.timer1 = delay
attack_action.timer2 = delay
for i,x in pairs(attack_action.unk_4) do
attack_action.unk_4[i] = 7
end
attack_action.unk_4.wrestle_type = -1
attacker.actions:insert('#',action)
j = j + 1
end
end
unit/attack -attacker 0 -defender 1
unit/attack -attacker 0 -defender 1 -target UB
unit/attack -attacker 0 -defender 1 -attack PUNCH
unit/attack -attacker 0 -defender 1 -target RH -weapon Equipped
A couple small updates for the attack script due to the naming of unknown attributes:
attack_action.unk_30 -> attack_action.attack_velocity
attack_action.unk_3c -> attack_action.attack_accuracy
(My guess is that these were named based on your work.)
ATTACK
BIOME
CASTE
EXTRACT
INTERACTION
MATERIAL
TYPE
SUBTYPE
HEAD
TORSO
LEG
ARM
HAND
FOOT
EYE
EAR
NOSE
MOUTH
ORGANS
SKELETAL
ATTACHMENT_HEAD
ATTACHMENT_TORSO
ATTACHMENT_LIMBS
ATTACHMENT_MISC
[TEMPLATE:-type-:-ID-]
{DESCRIPTION:-desc-}
{NAME:-names-}
{ATTACKS:-attacks-}
{ARGS:-args-}
{TOKENS:-tokens-}
{LINK:-tokens-}
{EXCEPT:-tokens-}
{PERCENT:-tokens-}
{BODY:-body parts-}
-raws-
[TEMPLATE:EXTRACT:VENOM_1]
{DESCRIPTION:It has a painful venom.}
{ARGS:SEV,END}
{TOKENS:TOXIC_CREATURE}
{PERCENT:VENOM}
{LINK:VENOM}
{EXCEPT:TOXIC_CREATURE}
[USE_MATERIAL_TEMPLATE:VENOM:CREATURE_EXTRACT_TEMPLATE]
[STATE_NAME:ALL_SOLID:frozen #NAME venom]
[STATE_ADJ:ALL_SOLID:frozen #NAME venom]
[STATE_NAME:LIQUID:#NAME venom]
[STATE_ADJ:LIQUID:#NAME venom]
[STATE_NAME:GAS:boiling #NAME venom]
[STATE_ADJ:GAS:boiling #NAME venom]
[PREFIX:NONE]
[ENTERS_BLOOD]
[REACTION_CLASS:VENOM]
[SYNDROME]
[SYN_NAME:#NAME venom]
[SYN_INJECTED]
[SYN_CONTACT]
[CE_PAIN:SEV:#ARG1:PROB:100:RESISTABLE:SIZE_DILUTES:LOCALIZED:VASCULAR_ONLY:START:0:PEAK:10:END:#ARG2]
[EXTRACT:LOCAL_CREATURE_MAT:VENOM]
TOKENS vs. LINK vs. EXCEPT vs. PERCENT#VERMIN - Checks if the creature is the correct size for vermin (defined by Size: Vermin)
#TINY - Checks if the creature is the correct size for tiny vermin (defined by Size: Tiny)
#TRADER - Checks if the creature is the correct size for a trading animal (defined by Size: Trade)
#MALE - Used for defining male castes in TEMPLATE:CASTE - LINK
#FEMALE - Used for defining female castes in TEMPLATE:CASTE - LINK
#DESC - Used to fill in the creature description when creating raws, very little use for this as the script currently creates a description for each caste already
#NAME - Used to fill the the creatue name when creating raws, very useful, allows for naming of things directly in the templates
#ARG1, #ARG2, #ARG3, etc... - Used to fill in the arguments provided in TEMPLATE - ARGS
#SWIMMING_GAITS - If this tag is present in a creature (no matter which template the creature recieved it from) will alter the gaits, flipping the WALK and SWIM gaits
#ONLY_SWIMMING - Same effect as above, but removes all other gaits (WALK, CLIMB, CRAWL, FLY)
#FLYING_GAITS - If this tag is present in a creature (no matter which template the creature recieved it from) will alter the gaits, moving WALK to FLY and CRAWL to WALK
#ONLY_FLYING - Same effect as above, but removes all other gaits (WALK, CLIMB, CRAWL, SWIM)
#NOARMS - Removes the CLIMB gait
#NOLEGS - Removes the WALK gait
[CREATURE:--]
[NAME:#NAME]
[CREATURE_TILE:--]
[COLOR:--:--:--]
=> TYPE template -raws- go here
=> SUBTYPE templates -raws- go here
=> BIOME template -raws- go here
=> all numbers generated in Step 1: go here (e.g. [MAX_AGE:--:--], [BODY_SIZE:--:--:--], [CHILD:--], etc...)
[APPLY_CREATURE_VARIATION:#SPEED]
[BODY:#BODY]
=> MATERIAL template -raws- go here
=> EXTRACT templates -raws- go here
=> INTERACTION templates -raws- go here
=> ATTACK templates -raws- go here
=> CASTE template -raws- go here, additionally [DESCRIPTION:#DESC] and [CASTE_NAME:#CASTE_NAME] are added here as well
So I finally decided to dust off the random creature creation script that I had created before. I have added a GUI and many other features. See the GUI below, and read how the script works. It is almost ready for release, with just a few more options that need to be moved from the black box of the code into easily configurable options. Let me know what you think!Roses: the middleware that you are building with the Civilization, Class, Spells and Event Systems have in my opinion the touch of genius.
-snip-Thank you for the kind words. I had originally thought of making the systems in a more intuitive language, but since they are explicitly for DF I decided to go with the raw templates. Currently you can actually have as many class/civilization/events files as you want. It looks for all files labeled classes_*.txt etc... And reads them in. You are right that if I wanted to make it more like the raws I should put an [OBJECT:] header and such, but I always found that that is the most unfriendly part of the raws.
the funny part is that i've thought about moving the whole fortbent class system to one of my own design just so that I don't have to rely on any dependencies, since I'm fully capable of that; main thing stopping is that it mostly work at this point (though it has errors every single time anything dies) and there's just a ridiculously huge amount of classes I made, which may be why the game hitches for something like 300-700 milliseconds for every class that I assign.
Like, tbh, I'm by far the worst example of the greatness of the raws system, I basically just ignore it entirely this point except for the very most basic of things. I made an entire mouse UI in the new SCP mod before even considering whether I could've done it with reactions (I can now, since descriptions and categories were added, but at the time it was infeasible), for example.
the funny part is that i've thought about moving the whole fortbent class system to one of my own design just so that I don't have to rely on any dependencies, since I'm fully capable of that; main thing stopping is that it mostly work at this point (though it has errors every single time anything dies) and there's just a ridiculously huge amount of classes I made, which may be why the game hitches for something like 300-700 milliseconds for every class that I assign.Putnam you are one of the most capable Lua modders in our community next to Roses, IndigoFenix, Expwnent, Lethosor, Warmists,Milo Christiansen, Quietust or many others.
... I had originally thought of making the systems in a more intuitive language, but since they are explicitly for DF I decided to go with the raw templates...That almost every DF modder out there is able to use in some way, using the familiar "lingua franca" of DF raws to give access to the new functionalities and systems (civilizations, classes, spells, events, ...) you have democratized and uniformized the access to everybody. You are removing the entry barriers.
...Currently you can actually have as many class/civilization/events files as you want. It looks for all files labeled classes_*.txt etc... And reads them in....Nice. :) Docs are in need of some love then?
...You are right that if I wanted to make it more like the raws I should put an [OBJECT:] header and such, but I always found that that is the most unfriendly part of the raws...Sure! Your system does not need that, you could even ignore [OBJECT:] things as decorators. But adding them is a way to evite confusion and make your DF raw extensions indistinguishable from those of vanilla. Because the casual modder does not need to know that some raw files needs [OBJECT:] and others no... ;D Best to relieve people of the burden of something that only can be at the origine of problems.
--fix-histfigs.lua
local figs = df.global.world.history.figures
for i=0,#figs-1,1 do
local v = figs[i]
if v.id<0 then
--print(v.id..' '..v.name.first_name..' '..v.name.language)
v.name.language = 0
end
end
What happens when the Thoughts and Feelings (or any other) category starts to stretch past the end of the page? Does it get truncated or does it scroll?
PSA: the most recent (beta) DFHack release, DFHack 0.42.06-alpha1, has a crash bug that will affect many of these scripts. Anything that uses persistent data will likely make the game crash when commissioning something related to a historical figure. Running this script before doing that should fix the problem temporarily:
Now I just need to figure out how to alphabetize it.You mean sorting?
Or get it acquired by Google's parent company?Now I just need to figure out how to alphabetize it.You mean sorting?
Now I just need to figure out how to alphabetize it.You mean sorting?
What you get when you open up the unit viewer with the currently provided classes_example.txt and spells_example.txtOMG. I love that you are still working on this, and this looks fantastic for players. :)Spoiler (click to show/hide)
Now I just need to figure out how to alphabetize it.
What you get when you open up the unit viewer with the currently provided classes_example.txt and spells_example.txtOMG. I love that you are still working on this, and this looks fantastic for players. :)Spoiler (click to show/hide)
Now I just need to figure out how to alphabetize it.
Now this is all in the brainstorming phase so far (don't have much time for anything besides thesis writing right now), so let me know if any of you have any suggestions or comments.I suggest making it a chapter of your thesis :)
Have the actual limits being scienced yet? I imagine that intermediate steps of some formulas could overflow if size or a stat was too high. And that would all change when DF goes 64-bit.
I didn't mean the stats' hard caps would change from 64-bit, but that the intermediate-result-overflow constraints might vanish. But there's already a lot more science on this topic than I realized, so it might be trivial to test those chokepoints when a 64-bit build comes out.Have the actual limits being scienced yet? I imagine that intermediate steps of some formulas could overflow if size or a stat was too high. And that would all change when DF goes 64-bit.
1. Yes.
2. Yeah, 0.40.01 gave a sort of soft cap on strength dependent on unit size and carry weight; for example, having more than ~2,000,000 strength on a human-sized creature will make it so that that creature will immediately become as slow as possible upon picking anything up. Other than that, stuff just tends to work.
3. I don't think it will. DF going 64-bit doesn't mean all the uint_32s that make up attributes are going to magically become 64-bit. AFAIK even if he just uses "int" it still won't do that, "long" is the 64-bit integer type IIRC.
AFAIK even if he just uses "int" it still won't do that, "long" is the 64-bit integer type IIRC.It's compiler and OS dependent. The only standard requirement is that short ≤ int ≤ long.
class.lua:191: attempt to index field 'Physical' (a nil value)
EDIT: I have no idea why this is happening.
--Add/Subtract permanent level bonuses
for _,attr in pairs(class.LevelBonus.Physical._children) do
local amount = class.LevelBonus.Physical[attr]
dfhack.script_environment('functions/unit').changeAttribute(unit,attr,amount,0,'track')
end
for _,attr in pairs(class.LevelBonus.Mental._children) do
local amount = class.LevelBonus.Mental[attr]
dfhack.script_environment('functions/unit').changeAttribute(unit,attr,amount,0,'track')
end
for _,skill in pairs(class.LevelBonus.Skill._children) do
local amount = class.LevelBonus.Skill[skill]
dfhack.script_environment('functions/unit').changeSkill(unit,skill,amount,0,'track')
end
for _,trait in pairs(class.LevelBonus.Trait._children) do
local amount = class.LevelBonus.Trait[trait]
dfhack.script_environment('functions/unit').changeTrait(unit,trait,amount,0,'track')
end
--Add/Subtract permanent level bonuses
if class.LevelBonus.Physical then
for _,attr in pairs(class.LevelBonus.Physical._children) do
local amount = class.LevelBonus.Physical[attr]
dfhack.script_environment('functions/unit').changeAttribute(unit,attr,amount,0,'track')
end
end
if class.LevelBonus.Mental then
for _,attr in pairs(class.LevelBonus.Mental._children) do
local amount = class.LevelBonus.Mental[attr]
dfhack.script_environment('functions/unit').changeAttribute(unit,attr,amount,0,'track')
end
end
if class.LevelBonus.Skill then
for _,skill in pairs(class.LevelBonus.Skill._children) do
local amount = class.LevelBonus.Skill[skill]
dfhack.script_environment('functions/unit').changeSkill(unit,skill,amount,0,'track')
end
end
if class.LevelBonus.Trait then
for _,trait in pairs(class.LevelBonus.Trait._children) do
local amount = class.LevelBonus.Trait[trait]
dfhack.script_environment('functions/unit').changeTrait(unit,trait,amount,0,'track')
end
end
So I really didn't feel this deserved another post anywhere, and I didn't want to clutter up anyone elses threads. Does anyone know of a utility that allows for easily taking sprites from multiple different sheets and creating a new sheet out of them? I have found tools that let combine entire sheets together, but I was hoping to be able to pick and choose individual sprites from a given sheet.Not sure if Quiet-Sun's Tool (http://www.bay12forums.com/smf/index.php?topic=156151.0) does what you are looking for, but it's the only thing I know of that's close (other than straight-up image tools).
It's easy enough with GIMP by just opening them as layers and removing the pieces you don't want.So I really didn't feel this deserved another post anywhere, and I didn't want to clutter up anyone elses threads. Does anyone know of a utility that allows for easily taking sprites from multiple different sheets and creating a new sheet out of them? I have found tools that let combine entire sheets together, but I was hoping to be able to pick and choose individual sprites from a given sheet.Not sure if Quiet-Sun's Tool (http://www.bay12forums.com/smf/index.php?topic=156151.0) does what you are looking for, but it's the only thing I know of that's close (other than straight-up image tools).
I rewrote it to use json instead of persist-table. It's loads faster now. Main problem I can find is that I can't get it to save on quicksaves.
I rewrote it to use json instead of persist-table. It's loads faster now. Main problem I can find is that I can't get it to save on quicksaves.
I hate to break it to you but I'm 99% done with replacing all histfig persistent data with json in a way that's even backwards-compatible with persist-table. It automatically converts to json and uses the same persist-table interface and works for (almost all) plugins too.
{
"Attributes": {
"AGILITY": {
"Base": "817",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"ANALYTICAL_ABILITY": {
"Base": "1472",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CREATIVITY": {
"Base": "2126",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"DISEASE_RESISTANCE": {
"Base": "1945",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"EMPATHY": {
"Base": "1192",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"ENDURANCE": {
"Base": "1060",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"FOCUS": {
"Base": "4649",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"INTUITION": {
"Base": "757",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"KINESTHETIC_SENSE": {
"Base": "1314",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"LINGUISTIC_ABILITY": {
"Base": "804",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MEMORY": {
"Base": "796",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MUSICALITY": {
"Base": "817",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PATIENCE": {
"Base": "1240",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"RECUPERATION": {
"Base": "1111",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SOCIAL_AWARENESS": {
"Base": "1663",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SPATIAL_SENSE": {
"Base": "1373",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"STRENGTH": {
"Base": "1161",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"TOUGHNESS": {
"Base": "472",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"WILLPOWER": {
"Base": "1523",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
}
},
"Classes": {
"BARD_OF_BLOOD": {
"Experience": "0",
"Level": "0"
},
"BARD_OF_BREATH": {
"Experience": "0",
"Level": "0"
},
"BARD_OF_DOOM": {
"Experience": "0",
"Level": "0"
},
"BARD_OF_HEART": {
"Experience": "0",
"Level": "0"
},
"BARD_OF_HOPE": {
"Experience": "0",
"Level": "0"
},
"BARD_OF_LIFE": {
"Experience": "0",
"Level": "0"
},
"BARD_OF_LIGHT": {
"Experience": "0",
"Level": "0"
},
"BARD_OF_MIND": {
"Experience": "0",
"Level": "0"
},
"BARD_OF_RAGE": {
"Experience": "0",
"Level": "0"
},
"BARD_OF_SPACE": {
"Experience": "0",
"Level": "0"
},
"BARD_OF_TIME": {
"Experience": "0",
"Level": "0"
},
"BARD_OF_VOID": {
"Experience": "0",
"Level": "0"
},
"Current": {
"Name": "PRINCE_OF_DOOM",
"SkillExp": "0",
"TotalExp": "0"
},
"HEIR_OF_BLOOD": {
"Experience": "0",
"Level": "0"
},
"HEIR_OF_BREATH": {
"Experience": "0",
"Level": "0"
},
"HEIR_OF_DOOM": {
"Experience": "0",
"Level": "0"
},
"HEIR_OF_HEART": {
"Experience": "0",
"Level": "0"
},
"HEIR_OF_HOPE": {
"Experience": "0",
"Level": "0"
},
"HEIR_OF_LIFE": {
"Experience": "0",
"Level": "0"
},
"HEIR_OF_LIGHT": {
"Experience": "0",
"Level": "0"
},
"HEIR_OF_MIND": {
"Experience": "0",
"Level": "0"
},
"HEIR_OF_RAGE": {
"Experience": "0",
"Level": "0"
},
"HEIR_OF_SPACE": {
"Experience": "0",
"Level": "0"
},
"HEIR_OF_TIME": {
"Experience": "0",
"Level": "0"
},
"HEIR_OF_VOID": {
"Experience": "0",
"Level": "0"
},
"KNIGHT_OF_BLOOD": {
"Experience": "0",
"Level": "0"
},
"KNIGHT_OF_BREATH": {
"Experience": "0",
"Level": "0"
},
"KNIGHT_OF_DOOM": {
"Experience": "0",
"Level": "0"
},
"KNIGHT_OF_HEART": {
"Experience": "0",
"Level": "0"
},
"KNIGHT_OF_HOPE": {
"Experience": "0",
"Level": "0"
},
"KNIGHT_OF_LIFE": {
"Experience": "0",
"Level": "0"
},
"KNIGHT_OF_LIGHT": {
"Experience": "0",
"Level": "0"
},
"KNIGHT_OF_MIND": {
"Experience": "0",
"Level": "0"
},
"KNIGHT_OF_RAGE": {
"Experience": "0",
"Level": "0"
},
"KNIGHT_OF_SPACE": {
"Experience": "0",
"Level": "0"
},
"KNIGHT_OF_TIME": {
"Experience": "0",
"Level": "0"
},
"KNIGHT_OF_VOID": {
"Experience": "0",
"Level": "0"
},
"MAGE_OF_BLOOD": {
"Experience": "0",
"Level": "0"
},
"MAGE_OF_BREATH": {
"Experience": "0",
"Level": "0"
},
"MAGE_OF_DOOM": {
"Experience": "0",
"Level": "0"
},
"MAGE_OF_HEART": {
"Experience": "0",
"Level": "0"
},
"MAGE_OF_HOPE": {
"Experience": "0",
"Level": "0"
},
"MAGE_OF_LIFE": {
"Experience": "0",
"Level": "0"
},
"MAGE_OF_LIGHT": {
"Experience": "0",
"Level": "0"
},
"MAGE_OF_MIND": {
"Experience": "0",
"Level": "0"
},
"MAGE_OF_RAGE": {
"Experience": "0",
"Level": "0"
},
"MAGE_OF_SPACE": {
"Experience": "0",
"Level": "0"
},
"MAGE_OF_TIME": {
"Experience": "0",
"Level": "0"
},
"MAGE_OF_VOID": {
"Experience": "0",
"Level": "0"
},
"MAID_OF_BLOOD": {
"Experience": "0",
"Level": "0"
},
"MAID_OF_BREATH": {
"Experience": "0",
"Level": "0"
},
"MAID_OF_DOOM": {
"Experience": "0",
"Level": "0"
},
"MAID_OF_HEART": {
"Experience": "0",
"Level": "0"
},
"MAID_OF_HOPE": {
"Experience": "0",
"Level": "0"
},
"MAID_OF_LIFE": {
"Experience": "0",
"Level": "0"
},
"MAID_OF_LIGHT": {
"Experience": "0",
"Level": "0"
},
"MAID_OF_MIND": {
"Experience": "0",
"Level": "0"
},
"MAID_OF_RAGE": {
"Experience": "0",
"Level": "0"
},
"MAID_OF_SPACE": {
"Experience": "0",
"Level": "0"
},
"MAID_OF_TIME": {
"Experience": "0",
"Level": "0"
},
"MAID_OF_VOID": {
"Experience": "0",
"Level": "0"
},
"PAGE_OF_BLOOD": {
"Experience": "0",
"Level": "0"
},
"PAGE_OF_BREATH": {
"Experience": "0",
"Level": "0"
},
"PAGE_OF_DOOM": {
"Experience": "0",
"Level": "0"
},
"PAGE_OF_HEART": {
"Experience": "0",
"Level": "0"
},
"PAGE_OF_HOPE": {
"Experience": "0",
"Level": "0"
},
"PAGE_OF_LIFE": {
"Experience": "0",
"Level": "0"
},
"PAGE_OF_LIGHT": {
"Experience": "0",
"Level": "0"
},
"PAGE_OF_MIND": {
"Experience": "0",
"Level": "0"
},
"PAGE_OF_RAGE": {
"Experience": "0",
"Level": "0"
},
"PAGE_OF_SPACE": {
"Experience": "0",
"Level": "0"
},
"PAGE_OF_TIME": {
"Experience": "0",
"Level": "0"
},
"PAGE_OF_VOID": {
"Experience": "0",
"Level": "0"
},
"PRINCE_OF_BLOOD": {
"Experience": "0",
"Level": "0"
},
"PRINCE_OF_BREATH": {
"Experience": "0",
"Level": "0"
},
"PRINCE_OF_DOOM": {
"Experience": "0",
"Level": "0"
},
"PRINCE_OF_HEART": {
"Experience": "0",
"Level": "0"
},
"PRINCE_OF_HOPE": {
"Experience": "0",
"Level": "0"
},
"PRINCE_OF_LIFE": {
"Experience": "0",
"Level": "0"
},
"PRINCE_OF_LIGHT": {
"Experience": "0",
"Level": "0"
},
"PRINCE_OF_MIND": {
"Experience": "0",
"Level": "0"
},
"PRINCE_OF_RAGE": {
"Experience": "0",
"Level": "0"
},
"PRINCE_OF_SPACE": {
"Experience": "0",
"Level": "0"
},
"PRINCE_OF_TIME": {
"Experience": "0",
"Level": "0"
},
"PRINCE_OF_VOID": {
"Experience": "0",
"Level": "0"
},
"ROGUE_OF_BLOOD": {
"Experience": "0",
"Level": "0"
},
"ROGUE_OF_BREATH": {
"Experience": "0",
"Level": "0"
},
"ROGUE_OF_DOOM": {
"Experience": "0",
"Level": "0"
},
"ROGUE_OF_HEART": {
"Experience": "0",
"Level": "0"
},
"ROGUE_OF_HOPE": {
"Experience": "0",
"Level": "0"
},
"ROGUE_OF_LIFE": {
"Experience": "0",
"Level": "0"
},
"ROGUE_OF_LIGHT": {
"Experience": "0",
"Level": "0"
},
"ROGUE_OF_MIND": {
"Experience": "0",
"Level": "0"
},
"ROGUE_OF_RAGE": {
"Experience": "0",
"Level": "0"
},
"ROGUE_OF_SPACE": {
"Experience": "0",
"Level": "0"
},
"ROGUE_OF_TIME": {
"Experience": "0",
"Level": "0"
},
"ROGUE_OF_VOID": {
"Experience": "0",
"Level": "0"
},
"SEER_OF_BLOOD": {
"Experience": "0",
"Level": "0"
},
"SEER_OF_BREATH": {
"Experience": "0",
"Level": "0"
},
"SEER_OF_DOOM": {
"Experience": "0",
"Level": "0"
},
"SEER_OF_HEART": {
"Experience": "0",
"Level": "0"
},
"SEER_OF_HOPE": {
"Experience": "0",
"Level": "0"
},
"SEER_OF_LIFE": {
"Experience": "0",
"Level": "0"
},
"SEER_OF_LIGHT": {
"Experience": "0",
"Level": "0"
},
"SEER_OF_MIND": {
"Experience": "0",
"Level": "0"
},
"SEER_OF_RAGE": {
"Experience": "0",
"Level": "0"
},
"SEER_OF_SPACE": {
"Experience": "0",
"Level": "0"
},
"SEER_OF_TIME": {
"Experience": "0",
"Level": "0"
},
"SEER_OF_VOID": {
"Experience": "0",
"Level": "0"
},
"SYLPH_OF_BLOOD": {
"Experience": "0",
"Level": "0"
},
"SYLPH_OF_BREATH": {
"Experience": "0",
"Level": "0"
},
"SYLPH_OF_DOOM": {
"Experience": "0",
"Level": "0"
},
"SYLPH_OF_HEART": {
"Experience": "0",
"Level": "0"
},
"SYLPH_OF_HOPE": {
"Experience": "0",
"Level": "0"
},
"SYLPH_OF_LIFE": {
"Experience": "0",
"Level": "0"
},
"SYLPH_OF_LIGHT": {
"Experience": "0",
"Level": "0"
},
"SYLPH_OF_MIND": {
"Experience": "0",
"Level": "0"
},
"SYLPH_OF_RAGE": {
"Experience": "0",
"Level": "0"
},
"SYLPH_OF_SPACE": {
"Experience": "0",
"Level": "0"
},
"SYLPH_OF_TIME": {
"Experience": "0",
"Level": "0"
},
"SYLPH_OF_VOID": {
"Experience": "0",
"Level": "0"
},
"THIEF_OF_BLOOD": {
"Experience": "0",
"Level": "0"
},
"THIEF_OF_BREATH": {
"Experience": "0",
"Level": "0"
},
"THIEF_OF_DOOM": {
"Experience": "0",
"Level": "0"
},
"THIEF_OF_HEART": {
"Experience": "0",
"Level": "0"
},
"THIEF_OF_HOPE": {
"Experience": "0",
"Level": "0"
},
"THIEF_OF_LIFE": {
"Experience": "0",
"Level": "0"
},
"THIEF_OF_LIGHT": {
"Experience": "0",
"Level": "0"
},
"THIEF_OF_MIND": {
"Experience": "0",
"Level": "0"
},
"THIEF_OF_RAGE": {
"Experience": "0",
"Level": "0"
},
"THIEF_OF_SPACE": {
"Experience": "0",
"Level": "0"
},
"THIEF_OF_TIME": {
"Experience": "0",
"Level": "0"
},
"THIEF_OF_VOID": {
"Experience": "0",
"Level": "0"
},
"WITCH_OF_BLOOD": {
"Experience": "0",
"Level": "0"
},
"WITCH_OF_BREATH": {
"Experience": "0",
"Level": "0"
},
"WITCH_OF_DOOM": {
"Experience": "0",
"Level": "0"
},
"WITCH_OF_HEART": {
"Experience": "0",
"Level": "0"
},
"WITCH_OF_HOPE": {
"Experience": "0",
"Level": "0"
},
"WITCH_OF_LIFE": {
"Experience": "0",
"Level": "0"
},
"WITCH_OF_LIGHT": {
"Experience": "0",
"Level": "0"
},
"WITCH_OF_MIND": {
"Experience": "0",
"Level": "0"
},
"WITCH_OF_RAGE": {
"Experience": "0",
"Level": "0"
},
"WITCH_OF_SPACE": {
"Experience": "0",
"Level": "0"
},
"WITCH_OF_TIME": {
"Experience": "0",
"Level": "0"
},
"WITCH_OF_VOID": {
"Experience": "0",
"Level": "0"
}
},
"Skills": {
"ALCHEMY": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"ANIMALCARE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"ANIMALTRAIN": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"APPRAISAL": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"ARMOR": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"ASTRONOMY": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"AXE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"BALANCE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"BEEKEEPING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"BITE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"BLOWGUN": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"BONECARVE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"BOOKBINDING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"BOW": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"BOWYER": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"BREWING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"BUTCHER": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CARPENTRY": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CHEESEMAKING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CHEMISTRY": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CLIMBING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CLOTHESMAKING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"COMEDY": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CONCENTRATION": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CONSOLE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CONVERSATION": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"COOK": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"COORDINATION": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CRITICAL_THINKING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CROSSBOW": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CRUTCH_WALK": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"CUTGEM": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"DAGGER": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"DANCE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"DESIGNBUILDING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"DETAILSTONE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"DIAGNOSE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"DISCIPLINE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"DISSECT_FISH": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"DISSECT_VERMIN": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"DODGING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"DRESS_WOUNDS": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"DYER": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"ENCRUSTGEM": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"EXTRACT_STRAND": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"FISH": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"FLATTERY": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"FLUID_ENGINEER": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"FORGE_ARMOR": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"FORGE_FURNITURE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"FORGE_WEAPON": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"GELD": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"GEOGRAPHY": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"GLASSMAKER": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"GLAZING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"GRASP_STRIKE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"HAMMER": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"HERBALISM": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"INTIMIDATION": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"JUDGING_INTENT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"KNAPPING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"KNOWLEDGE_ACQUISITION": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"LEADERSHIP": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"LEATHERWORK": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"LOGIC": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"LYE_MAKING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"LYING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MACE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MAGIC_NATURE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MAKE_MUSIC": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MASONRY": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MATHEMATICS": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MECHANICS": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MELEE_COMBAT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"METALCRAFT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MILITARY_TACTICS": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MILK": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MILLING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"MINING": {
"Base": "2",
"Change": "0",
"Class": "0",
"Current": "2",
"Item": "0",
"StatusEffects": []
},
"MISC_WEAPON": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"NEGOTIATION": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"OPERATE_PUMP": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"OPTICS_ENGINEER": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"ORGANIZATION": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PACIFY": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PAPERMAKING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PERSUASION": {
"Base": "0",
"Change": "0",
"Class": "0",
"Current": "0",
"Item": "0",
"StatusEffects": []
},
"PIKE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PLANT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PLAY_KEYBOARD_INSTRUMENT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PLAY_PERCUSSION_INSTRUMENT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PLAY_STRINGED_INSTRUMENT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PLAY_WIND_INSTRUMENT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"POETRY": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"POTASH_MAKING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"POTTERY": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PRESSING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PROCESSFISH": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PROCESSPLANTS": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"PROSE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"RANGED_COMBAT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"READING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"RECORD_KEEPING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SET_BONE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SHEARING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SHIELD": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SIEGECRAFT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SIEGEOPERATE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SING_MUSIC": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SITUATIONAL_AWARENESS": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SMELT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SNEAK": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SOAP_MAKING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SPEAKING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SPEAR": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SPINNING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"STANCE_STRIKE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"STONECRAFT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SURGERY": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SUTURE": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SWIMMING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"SWORD": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"TANNER": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"TEACHING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"THROW": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"TRACKING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"TRAPPING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"WAX_WORKING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"WEAVING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"WHIP": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"WOODCRAFT": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"WOODCUTTING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"WOOD_BURNING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"WRESTLING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
},
"WRITING": {
"Base": "0",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
}
},
"Spells": {
"Active": []
},
"Stats": {
"Deaths": "0",
"Kills": "0"
},
"Traits": {
"ABSTRACT_INCLINED": {
"Base": "57",
"Change": "0",
"Class": "0",
"Item": "0",
"StatusEffects": []
Skill and attribute and class? I noticed that issue. Each unit was 34 kb and i'm willing to bet that nothing I would do could've increased or decrease that number (except maybe add/remove classes, obv). Thought it was real weird that it stored all 144 classes in every single unit regardless of whether the unit was going to switch. My json conversion is complete except for the quick/autosave issue, but that's big enough that I won't bother releasing.
Example:
<snipped>
And then it goes on to describe every other trait much the same way. It didn't even fit into the post.
Yeah, I found it fascinating. Then it turned out I had to actually cut the persist-table into multiple files to get it to work correctly lol
ClassTable.json is 399 KB alone, each unit is 34 KB
unit/attribute-change -unit \\UNIT_ID -fixed 100 -attribute STRENGTH
unit/attribute-change -unit \\UNIT_ID -percent [ 10 10 10 ] -attribute [ ENDURANCE TOUGHNESS WILLPOWER ] -dur 3600
unit/attribute-change -unit \\UNIT_ID -set 5000 -attribute WILLPOWER -dur 1000
would become unit/attribute-change -unit \\UNIT_ID -mode fixed -amount 100 -attribute STRENGTH
unit/attribute-change -unit \\UNIT_ID -mode percent -amount [ 10 10 10 ] -attribute [ ENDURANCE TOUGHNESS WILLPOWER ] -dur 3600
unit/attribute-change -unit \\UNIT_ID -mode set -amount 5000 -attribute WILLPOWER -dur 1000
A couple weeks behind schedule, but the update is out. The Class System along with Spell and Feat SubSystems is now included in my collection. Note that the ReadMe is still being worked on and will be included at a later time, but working examples of the entire system and subsystems are included. If you have any questions please feel free to ask.Nice. I'll check it out later.
Alright, so wrapping up my next release (which will also have a couple extra scripts that I created for Meph to use):D Thank you
base/roses-init -classSystem [ Feats Spells ]
In our onLoad.init file. You can then use the following scripts to utilize the systemHey, how exactly do I install your scripts? As in, where do I put each folder? Does the scripts folder go inside /raw , for instance?Yes, the scripts folder goes inside the /raw folder.
EDIT: I think I figured it out.
EDIT 2: If I might make a request, could you allow for classes to also do personality, transformations and size changes?I can add that to the system, but it is sort of already possible. In the file inorganic_class_names.txt, along with the [CE_DISPLAY_NAME] syndrome effect you can add any other syndrome effects you want. For example, instead of
[SYNDROME]
[SYN_NAME:WARRIOR]
[SYN_CLASS:CLASS_NAME]
[CE_DISPLAY_NAME:NAME:warrior:warriors:warrior:START:0]
You can put [SYNDROME]
[SYN_NAME:WARRIOR]
[SYN_CLASS:CLASS_NAME]
[CE_DISPLAY_NAME:NAME:warrior:warriors:warrior:START:0]
[CE_BODY_APPEARANCE_MODIFIER:HEIGHT:200]
EDIT 3: What is the correct syntax for AUTO_UPGRADE? [AUTO_UPGRADE:CLASSNAME]? [AUTO_UPGRADE:CLASSNAME:LEVEL]? Also, do feats work? I hope I am not being a bother, but this is not covered in the read me file.You are not being a bother at all, it is my fault for not making an updated ReadMe. Yes feats work, and the correct syntax is [AUTO_UPGRADE:CLASSNAME], one your class reaches the max level of that class it will automatically change classes to the auto upgrade one.
I don't know how possible, or practical, this is so disregard if that's the case, but could you make it so that there's an option to use creature materials when it comes to the names and spells? The reason for this is that any inorganic, regardless if it has [SPECIAL] or any other token, can be picked as the material for a worldgen slab, resulting in weird stuff like "warrior name slab".Any STONE_TEMPLATE using inorganic is being used for slabs as far as I know.
I thought of a workaround this (namely, making the inorganic a copy of a vanilla one minus the environment), but the would avoid some trouble.
If that's the case, it makes things far easier. Ignore my request then.I don't know how possible, or practical, this is so disregard if that's the case, but could you make it so that there's an option to use creature materials when it comes to the names and spells? The reason for this is that any inorganic, regardless if it has [SPECIAL] or any other token, can be picked as the material for a worldgen slab, resulting in weird stuff like "warrior name slab".Any STONE_TEMPLATE using inorganic is being used for slabs as far as I know.
I thought of a workaround this (namely, making the inorganic a copy of a vanilla one minus the environment), but the would avoid some trouble.
Is it possible to restrict classes to specific creature classes? Or to prevent creatures with specific syn classes from having them, like with regular interactions?
If not, could you add that possibility? I want specific castes of a creature (representing their nobles) to rise through tiers of knighthood (which will use your system), but prevent other castes (peasants) or creatures from doing the same.
While caste restriction works for me, it would be nice if you could add creature classes and syndrome classes if you have the time.Is it possible to restrict classes to specific creature classes? Or to prevent creatures with specific syn classes from having them, like with regular interactions?
If not, could you add that possibility? I want specific castes of a creature (representing their nobles) to rise through tiers of knighthood (which will use your system), but prevent other castes (peasants) or creatures from doing the same.
[REQUIREMENT_CREATURE:CREATURE_NAME:CASTE_NAME] can be used to restrict it to a specific caste of a creature. I can add the ability to restrict it to certain creature classes and syndrome classes.
The way you asked that question is a bit confusing, but I think I know what you mean. Once a world is generated civilizations are separate from the entity file. They are all their own individual civilization that tames their own creatures, has its own entity positions (in vanilla they can actually vary between civilizations. Humans don't start out with law givers they make them at some point, and I think there can be some varying names) and any editing of the entity files does not affect them. Which is why I've been trying to always use entity to refer to the files and civilization for civilizations.I can confirm that civilizations have distinct identities beyond the entity definition, much the same way units have distinct identities beyond their creature definition.
Now just in case my current interpretation is wrong and my initial one is actually correct, then having multiple MOUNTAIN entities is duplicating raws which is not a viable option for a mod that makes any sense.
[CIVILIZATION:MOUNTAIN]
...
...
...
I then assign that Civilization to all populations from the matching entity, but each civilization gets it's own unique Civilization. This is done by basically making several copies of the same Civilization and applying them across each civilization . This means that each civilization advances separately from the others. But I could change it so that each civilization that belongs to a given entity all belong to the same Civilization, so that if you give one them access to steel, they all get it.creature class
syndrome class
syndrome name
race
caste
body part
flag (i.e. skeleton, ambusher_hidden, etc...)
token (i.e. MEGABEAST, FLIER, etc...)
noble position
profession
skill level
attribute level
traits
speed
age
entity
pathfinding
station
I am also planning on adding targeting configuration for a units inventory, but if there is anything else people would like to see just let me know and I will see if I can add it in.
I won't lie, the fact that very few people, if any, use these scripts I write has made it difficult to continue working, but I know that that is largely my fault in making the whole system more complicated and harder to understand.Its not a fault to be a perfectionist. ;)
modtools/skill-change -skill MASONRY -mode add -granularity experience -unit 0 -value 500
wrapper -sourceUnit 0 -checkUnit -script [ modtools/skill-change -skill MASONRY -mode add -granularity experience -unit SOURCE_UNIT_ID -value 500 ]
'requiredClass',
'requiredCreature',
'requiredSyndrome',
'requiredToken',
'requiredNoble',
'requiredProfession',
'requiredEntity',
'immuneClass',
'immuneCreature',
'immuneSyndrome',
'immuneToken',
'immuneNoble',
'immuneProfession',
'immuneEntity',
'maxAttribute',
'minAttribute',
'gtAttribute',
'ltAttribute',
'maxSkill',
'minSkill',
'gtSkill',
'ltSkill',
'maxTrait',
'minTrait',
'gtTrait',
'ltTrait',
'maxAge',
'minAge',
'gtAge',
'ltAge',
'maxSpeed',
'minSpeed',
'gtSpeed',
'ltSpeed',
wrapper -sourceUnit 0 -checkUnit -minSkill MASONRY:1 -maxSkill MASONRY:5 -script [ modtools/skill-change -skill MASONRY -mode add -granularity experience -unit SOURCE_UNIT_ID -value 500 ]
wrapper -sourceUnit 0 -checkUnit -radius [ 10 10 2 ] -script [ modtools/skill-change -skill MASONRY -mode add -granularity experience -unit SOURCE_UNIT_ID -value 500 ]
wrapper -sourceUnit 0 -checkUnit -radius [ 10 10 2 ] -maxTargets 5 -script [ modtools/skill-change -skill MASONRY -mode add -granularity experience -unit SOURCE_UNIT_ID -value 500 ]
wrapper -sourceUnit 0 -checkUnit Friendly -radius [ 10 10 2 ] -script [ modtools/skill-change -skill MASONRY -mode add -granularity experience -unit SOURCE_UNIT_ID -value 500 ]
wrapper -sourceUnit 0 -checkUnit -value [ source.attribute.strength ] -script [ modtools/skill-change -skill MASONRY -mode add -granularity experience -unit SOURCE_UNIT_ID -value VALUE_1 ]
wrapper -sourceUnit 0 -checkUnit -value [ (source.attribute.strength+source.attribute.toughness)/2 ] -script [ modtools/skill-change -skill MASONRY -mode add -granularity experience -unit SOURCE_UNIT_ID -value VALUE_1 ]
[CLASS:WARRIOR]
[NAME:warrior:warriors]
[LEVELS:5]
[EXP:50:100:175:275:400]
[LEVELING_BONUS:PHYSICAL:STRENGTH:10]
[LEVELING_BONUS:PHYSICAL:TOUGHNESS:10]
[BONUS_PHYS:STRENGTH:25:50:75:100:150]
[BONUS_PHYS:TOUGHNESS:25:50:75:100:150]
[SPELL:RALLY:1]
[SPELL_AUTO_LEARN]
[SPELL:BATTLE_SHOUT:3]
[SPELL_AUTO_LEARN]
[SPELL:LEADERSHIP:5]
[SPELL_AUTO_LEARN]
I will assume you mean the leveling bonus since the temporary bonus is already level dependent. Yes, I can make the leveling bonus function the same way the temporary bonus works. If there is just one number it will apply that increase at every level, if you wanted it to only increase at the odd levels you could do[LEVELING_BONUS:PHYSICAL:STRENGTH:10:0:10:0:10]
I'll put it on my list of changes to make.
Traits:
NOPAIN
vs.Traits:
Does not feel pain
Traits:
Does not feel pain [NOPAIN]
local utils=require 'utils'
local gui = require 'gui'
validArgs = --[[validArgs or]]utils.invert({
'help',
'unit',
'corpse',
'location',
'kill',
})
local args = utils.processArgs({...}, validArgs)
if args.help then
print('')
return
end
corpse = nil
if args.unit and tonumber(args.unit) then
unit = df.unit.find(tonumber(args.unit))
if not unit then return end
if unit.flags1.dead then
for _,id in pairs(unit.corpse_parts) do
item = df.item.find(id)
if df.item_corpsest:is_instance(item) and not item.body.components.body_part_status[0].missing and item.corpse_flags.unbutchered then
corpse = item
break
end
end
else
if args.kill then
print('-kill not currently working')
else
print('Unit is still alive and has not been ordered -kill')
end
end
elseif args.corpse and tonumber(args.corpse) then
item = df.item.find(tonumber(args.corpse))
if not item then return end
if df.item_corpsest:is_instance(item) then
if not item.body.components.body_part_status[0].missing and item.corpse_flags.unbutchered then
corpse = item
end
end
elseif args.location then
locx = tonumber(args.location[1])
locy = tonumber(args.location[2])
locz = tonumber(args.location[3])
block = dfhack.maps.ensureTileBlock(locx,locy,locz)
if block.occupancy[locx%16][locy%16].item then
for _,id in pairs(block.items) do
item = df.item.find(id)
if df.item_corpsest:is_instance(item) then
if item.pos.x == locx and item.pos.y == locy and item.pos.z == locz then
if not item.body.components.body_part_status[0].missing and item.corpse_flags.unbutchered then
corpse = item
break
end
end
end
end
end
end
if not corpse then return end
local view_x = df.global.window_x
local view_y = df.global.window_y
local view_z = df.global.window_z
local curViewscreen = dfhack.gui.getCurViewscreen()
local dwarfmodeScreen = df.viewscreen_dwarfmodest:new()
curViewscreen.child = dwarfmodeScreen
dwarfmodeScreen.parent = curViewscreen
local oldMode = df.global.ui.main.mode
df.global.ui.main.mode = df.ui_sidebar_mode.LookAround
local old_gametype = df.global.gametype
df.global.gametype = df.game_type.DWARF_ARENA
df.global.cursor.x = corpse.pos.x
df.global.cursor.y = corpse.pos.y
df.global.cursor.z = corpse.pos.z
for i,_ in pairs(df.global.ui_look_list.items) do
df.global.ui_look_cursor = i
if dfhack.gui.getCurFocus() == 'dwarfmode/LookAround/Item' then
if corpse.id == dfhack.gui.getSelectedItem().id then
break
end
end
end
gui.simulateInput(dfhack.gui.getCurViewscreen(), 'D_LOOK_ARENA_ADV_MODE')
df.global.gametype = old_gametype
curViewscreen.child = nil
dwarfmodeScreen:delete()
df.global.ui.main.mode = oldMode
df.global.window_x = view_x
df.global.window_y = view_y
df.global.window_z = view_z
On the bestiary: I think a neat idea might be a list of what interactions a creature is capable of. Doesn't help much in vanilla, but sure does with mods.
[EQUATION:FIRE_DAMAGE_1:(50+10*SOURCE.ATTRIBUTE.PRIMARY/1000-5*TARGET.ATTRIBUTE.PRIMARY/1000)*(100-TARGET.RESISTANCE.!_TDDS+SOURCE.STAT.!_SKILL_PENETRATION)/100*(1+math.max(0,math.floor(math.min(1,EQUATION.MAGICAL_CRIT_CHANCE/math.random(100))))*(EQUATION.MAGICAL_CRIT_BONUS-100)/100)]
I'm experimenting with customizable equations. Basically if you don't like the way my scripts or systems do something, you can easily change it. Don't like how I compute hit chance for script attacks?[EQUATION:PHYSICAL_HIT_CHANCE:STAT.HIT_CHANCE+STAT.!_HIT_CHANCE+5*SKILL.WEAPON]
You can change it! The only problem is some equations (like the first one) can be a bit difficult to read. (Don't worry about what the values actually are, that is all well documented)
[NAME:pink skinned bald blob:pink skinned bald blobs:pink skinned bald blob]
[DESCRIPTION:A common animal found in the wilderness., It lives in deep underground places. A hairless leathery creature with a single part body,
a large head and no eyes, a simple nose, a beak, and large ears. It has two tentacles attached to it's upper body with claws and six tentacles attached to it's lower body with clubs.
It Is only found underground. It cleans itself when dirty. Maximum Size: 19kg ]
[NAME:fluffed purple eyed fowl:fluffed purple eyed fowls:fluffed purple eyed fowl]
[DESCRIPTION:A common animal found in the wilderness. A feathered creature with a single part body, a head a single horn and four eyes, no nose, a beak, and pointed ears.
It has two arms and hands with four fingers each and two legs with hooves. It Is found in hardy places. It often greets other animals by head bumping them. It cleans itself when dirty. Maximum Size: 22kg ]
[NAME:fen centipede:fen centipedes:fen centipede]
[DESCRIPTION:A common animal found in the wilderness. A hairy creature with a two part body, two heads two tusks and two eyes, a snout, a beak, and no ears.
It has four arms with large pincers and one hundred legs and feet with four claws each. It Can be found in any temperate wetland. Maximum Size: 20kg ]
[NAME:tropical claw-fowl:tropical claw-fowls:tropical claw-fowl]
[DESCRIPTION:A common animal found in the wilderness. A feathered creature with a two part body, a head four horns and no eyes, no nose, a beak, and no ears. It has two wings and two legs and feet with four claws each. It Can be found in any dense tropical location. Maximum Size: 4kg ]
1. notepad++
2. i've already done that with fortbent, it shouldn't be a problem
killer_id = tonumber(target.relations.last_attacker_id)
replace with new line: killer_id = tonumber(target.relationship_ids.LastAttacker)
Awesome stuff Roses. Possibly expect PMs pestering you for info.
awesome work Rose. My only question, is there a central location for documentation? I've been scanning scripts and I have an "idea" of what is going, but not enough to "know" what's going on. Its hard to understand how they mesh together.
one error: the top of base/roses-init: it states to put it in DFHack.init... but that is outside of the range where persist-tables can operate, so it errors on the first actual line of code. Shouldn't it be put at the top of onLoad.init, when a map is actually loaded and persist-tables can operate?
well I forked your github to my pc, this morning... decided to update all the scripts in MDF to the most recent version... now chasing down the few errors i know exist because of the data-structural change from 43.03 to 43.05. I can send some pull requests on things I spot as I go through here, possibly even pass it through a DFHack version check so that both codes can exist side by side without error, so its forward/backward compatible. Its not many errors, at the moment, but I'm sure I will find more as I test out the script region.
Additionally, I type as fast as I read, so I could start creating usage blocks at the start of the scripts, based on what I see in them; then you can just suggest corrections. I read lua code and dfhack structure fairly well and it would be a learning experience. adding such documentation, may assist people in understanding what is going on inside the script and what they need called for different things. Dealing with MDF... I've discovered inter-dependencies that I didn't know existed. I could do it in my downtime around my other projects (you got to take breaks, lol).
my decision was more future-proofing and accountability than anything; i wanted to be able to quickly remember exactly what does what and where so that i could fix things if a problem shows up. I still use some of the other stuff, like the select-all-units-in-circle and makeprojectile things.
Weirdly enough, I think I wrote my own persist-timeout even though I'm not sure that that was ever necessary. I'm not entirely sure why I did.
So I am picking up from where I left off and just now realizing that I left several of my scripts in a quasi-unplayable state (actually the scripts are fine it's the systems that suffer) which is probably why Putnam (the main user of the Class System) was forced to create his own system for Fortbent (also my Class System provided a lot more bells and whistles than Fortbent needed which make Putnam's decision to make his own even more underdstandable). I apologize that I've been absent and that my work was left in a subpar state. Over the next couple of weeks I plan on making sure everything I have release is working correctly, and also building on the infrastructure I already have to make my next release(s) more substantial. I'll be updating my first post as well over the next week or so, but I wanted to illustrate some of the advantages that a modder would get from using some of my scripts over others that are already provided. So here is a little PR piece.
Many scripts (and even some builtin functions of DFHack) provide options for changing a units attributes. So why would I include a script for changing attributes in my collection? The main problem I had with the current scripts is that they all simple added or removed a set amount of an attribute. My scripts allow adding and removing, as well as multiplying/dividing and even setting to a specific value. In addition, the modification of an attribute can be associated with a specific persistent value. This means that you can have multiple attribute increasing effects (e.g. effect A that increases Strength and effect B that increases Agility) and have them be removed by the same script. This "dispel" option is a big reason that I decided to duplicate many of the functions that current scripts provide as I wanted there to be "dispel" spells that could remove ANY attribute/skill increase in an effective and efficient way.
This tracking system is currently already working, but is so closely tied to my Class System that it makes it practically unusable by any other modder. That is why, over the next few weeks, I am playing on decoupling my various scripts from my various systems. A modder should be able to use any individual part of my work, without worrying that the parts they don't want will interfere. In addition, over the next month, I will be releasing my current mod with 9+ playable races, 40+ classes and 1000+ spells that will utilize all of my scripts and systems to their fullest so that other modders can get an idea of the power of some of what I have done (I've been really bad at showcasing exactly what I have done offers modders).
On a side note, I really want to thank Putnam and Meph, both of who have used a lot of what I have done in their own work, and who have adapted what I have done to make it actually usable. They have given me a lot of ideas to process over the next chunk of time about features vs usability and providing a product that doesn't fail 90% of the time.
item/equip
item/unequip
unit/create
unit/destroy
unit/emotion-change
unit/wound-change
Class System - Spell SubSystem
Enhanced System - Buildings
Enhanced System - Reactions
I found an issue in flow/source.lua
snipped
I found an issue in flow/source.lua
snipped
Great to know, I guess my test of the script would never have found that since all I do is try to place them, check that they were placed, then try and remove them and check that they were removed, don't think I ever tried adding again after one was removed. I'll make this change. Thank you very much
For some reason I just noticed your last line of your previous message. If you are familiar with Git and want to submit a pull request that would be great! Would save me having to go through the same fixes
If it's a big hassle you don't need to worry about it. I think you gave me enough information to make it pretty quick (I know it can be difficult to navigate some of my stuff)
Just wanted to let you know that the building subtype-change script still works in dfhack-alpha for 44.02 without any issues. :)
One of the curses of DF adventurer mode is inability to manually create titles (earned after 5 significant kills). Is there such a functionality in this plugin?Sorry for the late reply. I'm actually not sure about that, I've never really played adventure mode, so I don't even know how most of my scripts would fair, let alone how the title information is stored. But, as per my above message, I will look into it over the holidays and have a script for you in the new year that lets you create titles
[ITEM:FORCE_SHIELD]
[ON_EQUIP]
[SKILL_CHANGE:SHIELD:5]
[ON_BLOCK]
[CHANCE:50]
[SCRIPT:unit/propel -unitSource ATTACKER_ID -unitTarget DEFENDER_ID -velocity [ 25 25 0 ] -mode relative]
[ITEM:CLOAK_OF_SPEED]
[ON_DODGE]
[CHANCE:100]
[DEFENDER_SYNDROME_ADD:SPEED_UP]
These sound fantastic for adventure mode. :)
[BUILDING:MULTI_STORY_CATHEDRAL]
[OUTSIDE_ONLY]
[WALLS:BUILDING_MAT:STONE]
[ROOF:BUILDING_MAT:STONE]
[FLOORS:BUILDING_MAT:STONE]
[LEVELS:5]
[PILLAR:1:1]
[LEVEL:1:MULTI_STORY_CATHEDRAL]
[LEVEL:2:CATHEDRAL_LEVEL_2]
[LEVEL:3:CATHEDRAL_LEVEL_3]
[LEVEL:4:CATHEDRAL_LEVEL_4]
[LEVEL:5:CATHEDRAL_TOP_LEVEL]
When you start construction of the MULTI_STORY_CATHEDRAL building it will check to make sure there is enough room for all five levels and that it is outside. It will then construct floors, a roof, and walls out of whatever stone material is being used in the base construction of the building (if the building material being used isn't stone it will default to a local stone material). Walls and roof are optional, the PILLAR argument states which position you want to act as a pillar that holds the whole thing up. If you are using walls it is not necessary. Each level needs to have it's own BUILDING_WORKSHOP raw entry, as it is basically just constructing buildings on buildings. I'm hoping to tie the construction of different levels to the build stages of the base building, but right now it just completes all of them at once. I haven't tested out building destroyers and multistory buildings yet, but I'm guessing it will fail miserably, as I barely have deconstruction working.[BUILDING:MIST_GENERATOR_BASE]
[SPAWN_FLOW:MIST:100:10]
Which is really just a shortcut for a building that runs a script[BUILDING:MIST_GENERATOR_SCRIPT]
[SCRIPT:flow/random-pos -location BUILDING_LOC -flow MIST -density 100:10]
[[trigger/action.lua usage
arguments:
-help
print this help message
-clear
clear all registered triggers
-actionType
trigger the command when this action takes place
valid values:
Attack -- This triggers whenever a unit makes an attack, whether or not the attack hits or misses
Block -- This triggers whenever a unit blocks an attack
Dodge -- DOES NOT CURRENTLY WORK, DODGES AREN'T ACTIONS
Equip -- Stolen from modtools/item-trigger, triggers when a unit equips an item
Move -- This triggers whenever a unit moves
Parry -- This triggers whenever a unit parries an attack
Unequip -- Stolen from modtools/item-trigger, triggers when a unit unequips an item
Wound -- Stolen from modtools/item-trigger, triggers when a unit wounds another unit
ANNOUNCEMENT_TYPE -- NOT CURRENTLY IMPLEMENTED
-item type
trigger the command for items of this type
examples:
ITEM_WEAPON_PICK
-material mat
trigger the commmand on items with the given material
examples
INORGANIC:IRON
CREATURE:DWARF:BRAIN
PLANT:MUSHROOM_HELMET_PLUMP:DRINK
-contaminant mat
trigger the command on items with a given material contaminant
examples
INORGANIC:IRON
CREATURE:DWARF:BRAIN
PLANT:MUSHROOM_HELMET_PLUMP:DRINK
-creature creature
trigger the command on creature performing the action
examples:
DWARF:ALL
ELF:FEMALE
-attack type
trigger the command when a certain attack is performed
obviously only works for the attack actionType
-command [ commandStrs ]
specify the command to be executed
commandStrs
UNIT_ID
ATTACKER_ID
BLOCKER_ID
PARRIER_ID
DEFENDER_ID
BLOCKED_UNIT_ID
PARRIED_UNIT_ID
ITEM_MATERIAL
ITEM_TOKEN
ITEM_ID
CONTAMINANT_MATERIAL
anything -> anything
]])
Unlike item-trigger this script checks that all three requirements are met before triggering a script. This means you could make an action trigger which only works if a male elf attacks with a mithril short sword coated in dwarf braintrigger/action -actionType Attack -creature ELF:MALE -item ITEM_WEAPON_SWORD_SHORT -material INORGANIC:MITHRIL -contaminant CREATURE:DWARF:BRAIN -command [ blah ]
You can also trigger a script any time a specific unit movestrigger/action -actionType Move -creature DRAGON:ALL -command [ shake the ground or something ]
Note that -actionType Dodge doesn't currently work because the game doesn't actually register the dodge as an action even though there is a dodge action type. The work around for this is allowing triggers off of combat reports as well. This will open up many new action types (list below), right now this is disabled but I do have a prototype version working, just need to find a full proof way to get the required information from the reports <enum-item name='COMBAT_TWIST_WEAPON'/>
<enum-item name='COMBAT_LET_ITEM_DROP'/>
<enum-item name='COMBAT_START_CHARGE'/>
<enum-item name='COMBAT_SURPRISE_CHARGE'/>
<enum-item name='COMBAT_JUMP_DODGE_PROJ'/>
<enum-item name='COMBAT_JUMP_DODGE_STRIKE'/>
<enum-item name='COMBAT_DODGE'/>
<enum-item name='COMBAT_COUNTERSTRIKE'/>
<enum-item name='COMBAT_BLOCK'/>
<enum-item name='COMBAT_PARRY'/>
<enum-item name='COMBAT_CHARGE_COLLISION'/>
<enum-item name='COMBAT_CHARGE_DEFENDER_TUMBLES'/>
<enum-item name='COMBAT_CHARGE_DEFENDER_KNOCKED_OVER'/>
<enum-item name='COMBAT_CHARGE_ATTACKER_TUMBLES'/>
<enum-item name='COMBAT_CHARGE_ATTACKER_BOUNCE_BACK'/>
<enum-item name='COMBAT_CHARGE_TANGLE_TOGETHER'/>
<enum-item name='COMBAT_CHARGE_TANGLE_TUMBLE'/>
<enum-item name='COMBAT_CHARGE_RUSH_BY'/>
<enum-item name='COMBAT_CHARGE_MANAGE_STOP'/>
<enum-item name='COMBAT_CHARGE_OBSTACLE_SLAM'/>
<enum-item name='COMBAT_WRESTLE_LOCK'/>
<enum-item name='COMBAT_WRESTLE_CHOKEHOLD'/>
<enum-item name='COMBAT_WRESTLE_TAKEDOWN'/>
<enum-item name='COMBAT_WRESTLE_THROW'/>
<enum-item name='COMBAT_WRESTLE_RELEASE_LOCK'/>
<enum-item name='COMBAT_WRESTLE_RELEASE_CHOKE'/>
<enum-item name='COMBAT_WRESTLE_RELEASE_GRIP'/>
<enum-item name='COMBAT_WRESTLE_STRUGGLE'/>
<enum-item name='COMBAT_WRESTLE_RELEASE_LATCH'/>
<enum-item name='COMBAT_WRESTLE_STRANGLE_KO'/>
<enum-item name='COMBAT_WRESTLE_ADJUST_GRIP'/>
<enum-item name='COMBAT_GRAB_TEAR'/>
<enum-item name='COMBAT_STRIKE_DETAILS'/>
<enum-item name='COMBAT_STRIKE_DETAILS_2'/>
<enum-item name='COMBAT_EVENT_ENRAGED'/>
<enum-item name='COMBAT_EVENT_STUCKIN'/>
<enum-item name='COMBAT_EVENT_LATCH_BP'/>
<enum-item name='COMBAT_EVENT_LATCH_GENERAL'/>
<enum-item name='COMBAT_EVENT_PROPELLED_AWAY'/>
<enum-item name='COMBAT_EVENT_KNOCKED_OUT'/>
<enum-item name='COMBAT_EVENT_STUNNED'/>
<enum-item name='COMBAT_EVENT_WINDED'/>
Arguments::
-clear
clears the list of registered buildings
-building BUILDING_TOKEN
specify the id of the building
-location OUTSIDE or INSIDE
state whether the building needs to be built outside or inside
-zLevels #
the number of z levels needed for the building
-requiredBuilding [ ID:# ]
the id and number of a building(s) that needs to already be built to continue
-forbiddenBuilding [ ID:# ]
the id and number of a building(s) that cannot be already built to continue
-maxNumber #
the maximum number of this type of building
-requiredWater #
the amount of water next to the building that is required to build
it will check every tile between x1-1->x2+1, y1-1->y2+1, and z-1->z
-requiredMagma #
the amount of water next to the building that is required to build
it will check every tile between x1-1->x2+1, y1-1->y2+1, and z-1->z
-command [ commandStrs ]
specify the command to be executed if the building passes the checks
commandStrs
BUILDING_ID
BUILDING_TOKEN
BUILDING_LOCATION
anything -> anything
This script is fairly self explanatory, be careful with using the zLevel check as it is very specific, there must be that much open space above the building, if even a small tree branch is there it will abort the building.Arguments::
-clear
unregister all reaction hooks
-reactionName name
specify the name of the reaction
-trigger Trigger Type
specify when to trigger the script
Valid Values:
onStart
onFinish
onProduct
-delay #
how long to make the reaction take
only works on -trigger onStart
-requiredWater #
amount of water the reaction needs, will cancel reaction if the water isn't present
removes this amount of water from the surrounding tiles
-requiredMagma #
same as -requiredWater except for with magma
-syndrome name
specify the name of the syndrome to be applied to the targets
-allowNonworkerTargets
allow other units in the same building to be targetted by
either the script or the syndrome
-allowMultipleTargets
allow multiple targets to the script or syndrome
if absent:
if running a script, only one target will be used
if applying a syndrome, then only one target will be infected
-resetPolicy policy
the policy in the case that the syndrome is already present
policy
NewInstance (default)
DoNothing
ResetDuration
AddDuration
-command [ commandStrs ]
specify the command to be run on the target(s)
special args
WORKER_ID
TARGET_ID
BUILDING_ID
LOCATION
REACTION_NAME
JOB_ID
INPUT_ITEMS
OUTPUT_ITEMS
anything -> anything
With the inclusion of the -trigger option this script mimics the behavior of the combined scripts. Use -trigger onFinish to replicate modtools/reaction-trigger and use -trigger onProduct to replicate modtools/reaction-product-trigger. The major additions from this script is the onStart trigger and delay and requiredWater/Magma. A couple examples for illustration. First, a reaction that takes a full day, regardless of skill, to complete. Note that the timer for this starts as soon as the unit begins the work so it includes the amount of time spent fetching items needed for the reaction, at a later time I will make it only start the timer once the unit stops fetching and starts working.trigger/reaction -reaction SOME_REACTION_YOU_WANT_TO_TAKE_A_WHOLE_DAY -delay 3200 -command [ you don't need a command if you don't want one ]
Next up is a reaction that requires 1 water and 1 magma to run and makes steam. Note the water and magma will be removed from the surrounding tiles.trigger/reaction -reaction MAKE_STEAM -requiredWater 1 -requiredMagma 1 -command [ flow/random-pos -flow STEAM -location LOCATION ]
Other than the above examples the script should work the same as the two modtools equivalents.[BUILDING:TEST_BUILDING_1]
[NAME:Test Building] < Name and Description are only for the journal-gui scripts
[DESCRIPTION:Testing all the things]
[INSIDE_ONLY]
[MAX_AMOUNT:4]
[REQUIRED_WATER:7]
[REQUIRED_MAGMA:3]
[REQUIRED_BUILDING:TEST_BUILDING_2:1]
[FORBIDDEN_BUILDING:TEST_BUILDING_3:2]
[MULTI_STORY:4]
[SCRIPT:devel/print-args [ BUILDING_ID BUILDING_TOKEN BUILDING_LOCATION ]:500] < This script will be run every 500 ticks for as long as the building remains constructed
You will see that most of the the options correspond directly to what is in the trigger/building script. This allows you to write out what each building is supposed to do/require and not have to worry about placing script calls in onLoad.init[CREATURE:ELF:MALE]
[NAME:Test Enhanced Creature (ELF:MALE)]
[DESCRIPTION:This is a test enhanced creature, checks that all tokens are processed correctly]
[BODY_SIZE:1000:10000:100000:200000:1000] < Will change the size of the creature based on age, BABY:CHILD:ADULT:MAX:VARIANCE
[ATTRIBUTE:AGILITY:5000:5500:6000:6500:7000:7500:8000] < Will set agility to between 5000 and 8000 using the same weighting as the standard raws
[NATURAL_SKILL:AXE:2:5] < Will set units natural axe skill to between 2 and 5
[STAT:SPELL_CASTING:50:75] < Stats and resistances are used in my other scripts, they work just like skills and attributes
[RESISTANCE:MAGICAL:25:50]
[CLASS:TEST_CLASS_1:2:50] < Will assign TEST_CLASS_1 at level 2 with 50% of it's spells chosen randomly, note that multiple CLASS tokens will be chosen from randomly
[INTERACTION:TEST_SPELL_2:50] < 50% chance the unit will learn TEST_SPELL_2
[INTERACTION:TEST_SPELL_3:50] < 50% chance the unit will learn TEST_SPELL_3
This entry will update any male elf on the map every 100 ticks. Once they are "enhanced" they won't be enhanced again so you don't have to worry about multiple enhancements for the same unit (note MALE could be replaced with ALL to handle all elf units).[ITEM:ITEM_WEAPON_SWORD_SHORT]
[NAME:Test Enhanced Item (ITEM_WEAPON_SWORD_SHORT)]
[DESCRIPTION:This is a test enhanced item, checks that all tokens are processed correctly]
[ON_EQUIP] < This means the following effects occur when the item is equipped (and are reveresed when unequipped)
[ATTRIBUTE_CHANGE:STRENGTH:-250] < Subtracts 250 strength from the unit
[SKILL_CHANGE:SWORD:10] < Adds 10 levels to the units sword skill
[TRAIT_CHANGE:ANGER:50] < Adds 50 to the units anger trait
[STAT_CHANGE:PHYSICAL_HIT_CHANCE:-10] < Stats and resistances are used in other scripts, they work the same as skills and attributes
[RESISTANCE_CHANGE:FIRE:-25]
[INTERACTION_ADD:TEST_SPELL_2] < Adds the TEST_SPELL_2 interaction to the unit
[SYNDROME_ADD:TEST_SYNDROME_1] < Adds the syndrome TEST_SYNDROME_1
[ON_ATTACK:25] < This means the following effects occur on an attack with the item 25% of the time. Effects are the same as ON_EQUIP but are prefaced by ATTACKER_ or DEFENDER_
[ATTACKER_ATTRIBUTE_CHANGE:STRENGTH:25]
[ATTACKER_SKILL_CHANGE:SWORD:-1]
[ATTACKER_TRAIT_CHANGE:ANGER:-5]
[ATTACKER_STAT_CHANGE:PHYSICAL_HIT_CHANCE:1]
[ATTACKER_RESISTANCE_CHANGE:FIRE:5]
[ATTACKER_INTERACTION_ADD:TEST_SPELL_3]
[ATTACKER_SYNDROME_ADD:TEST_SYNDROME_2]
[ATTACKER_CHANGE_DUR:100] < The length of time for the changes to last. Note that in this particular case multple stacks of these changes can and will be applied to the attacker
[ON_BLOCK:5] < This means the following effects occur on a block with the item 5% of the time. Effects are the same as ON_EQUIP but are prefaced by ATTACKER_ or DEFENDER_
[DEFENDER_ATTRIBUTE_CHANGE:AGILITY:25]
[DEFENDER_SKILL_CHANGE:DODGE:-1]
[DEFENDER_TRAIT_CHANGE:ANGER:5]
[DEFENDER_STAT_CHANGE:PHYSICAL_CRIT_CHANCE:5]
[DEFENDER_RESISTANCE_CHANGE:WATER:-5]
[DEFENDER_INTERACTION_ADD:TEST_SPELL_1]
[DEFENDER_SYNDROME_ADD:TEST_SYNDROME_3]
[DEFENDER_CHANGE_DUR:100]
[SCRIPT:devel/print-args [ SOURCE_ID TARGET_ID ITEM_ID ITEM_TOKEN ]:50] < This script will be run 50% of the time the ON_BLOCK is triggered (so 50% of 5% of the blocks)
[ON_PARRY:100] < This means the following effects occur on a parry with the item 100% of the time. Effects are the same as ON_EQUIP but are prefaced by ATTACKER_ or DEFENDER_
[ATTACKER_ATTRIBUTE_CHANGE:STRENGTH:-25]
[ATTACKER_SKILL_CHANGE:SWORD:1]
[ATTACKER_TRAIT_CHANGE:ANGER:5]
[ATTACKER_STAT_CHANGE:PHYSICAL_HIT_CHANCE:-1]
[ATTACKER_RESISTANCE_CHANGE:FIRE:-5]
[ATTACKER_INTERACTION_ADD:TEST_SPELL_3]
[ATTACKER_SYNDROME_ADD:TEST_SYNDROME_2]
[ATTACKER_CHANGE_DUR:100]
The Materials SubSystem has the exact same format exept [ITEM:ITEM_WEAPON_SWORD_SHORT] gets changed to [MATERIAL:INORGANIC:STEEL], [MATERIAL:CREATURE:DRAGON:BONE], or [MATERIAL:PLANT:SINGLE-GRAIN_WHEAT:DRINK] etc...[REACTION:TEST_REACTION_1]
[NAME:Test Reaction 1]
[DESCRIPTION:Testing various Enhanced Reaction triggers]
[ON_START]
[SKILL:MINING] < Skill associated with this reaction
[BASE_DURATION:1000] < Base duration (or delay in the trigger/reaction script) for the reaction to last
[DURATION_REDUCTION:50:10] < Reduction in the base duration based on skill. This means for each 1 level of MINING the duration of the reaction decreases by 50 ticks down to a minimum of 10 ticks
[SCRIPT:devel/print-args [ UNIT_ID TARGET_ID BUILDING_ID LOCATION INPUT_ITEMS OUTPUT_ITEMS REACTION_NAME ]] < This script will be run on the start of the reaction, note that INPUT_ITEMS and OUTPUT_ITEMS will not be populated since they aren't available to onStart reaction triggers
[ON_PRODUCT]
[SCRIPT:devel/print-args [ UNIT_ID TARGET_ID BUILDING_ID LOCATION INPUT_ITEMS OUTPUT_ITEMS REACTION_NAME ]]
[ON_FINISH]
[SCRIPT:devel/print-args [ UNIT_ID TARGET_ID BUILDING_ID LOCATION INPUT_ITEMS OUTPUT_ITEMS REACTION_NAME ]]
Currently there aren't as many fun features for this subsystem as there with the others. But that will change with time.[ITEM:ITEM_AMMO_ARROW_SPLITSHOT]
[NAME:Split Shot Arrow]
[Description:An arrow that splits into multiple when fired]
[ON_PROJECTILE_FIRED]
[SCRIPT:item/projectile -unitSource UNIT_ID -unitTarget TARGET_ID -item ITEM_TOKEN -material MATERIAL_TOKEN -number 2 -velocity PROJECTILE_VELOCITY -hitchance PROJECTILE_HITCHANCE]
[ITEM_AMMO:ITEM_AMMO_ARROWS_SPLITSHOT]
[NAME:split shot arrow:split shot arrows]
[CLASS:ARROW]
[SIZE:150]
[ATTACK:EDGE:5:1000:stab:stabs:NO_SUB:1000]
[ATTACK_PREPARE_AND_RECOVER:3:3]
[ITEM:ITEM_AMMO_ARROWS_SPLITSHOT]
[NAME:Split Shot Arrow]
[Description:An arrow that splits into multiple when fired]
[ON_PROJECTILE_FIRED]
[SCRIPT:item/projectile -unitSource UNIT_ID -unitTarget TARGET_ID -item ITEM_TOKEN -material MATERIAL_TOKEN -number 2 -velocity PROJECTILE_VELOCITY -hitchance PROJECTILE_HITCHANCE]
[ITEM_AMMO:ITEM_AMMO_ARROWS_SPLITSHOT]
[NAME:split shot arrow:split shot arrows]
{Description:An arrow that splits into multiple when fired}
[CLASS:ARROW]
[SIZE:150]
[ATTACK:EDGE:5:1000:stab:stabs:NO_SUB:1000]
[ATTACK_PREPARE_AND_RECOVER:3:3]
{ON_PROJECTILE_FIRED}
{SCRIPT:item/projectile -unitSource UNIT_ID -unitTarget TARGET_ID -item ITEM_TOKEN -material MATERIAL_TOKEN -number 2 -velocity PROJECTILE_VELOCITY -hitchance PROJECTILE_HITCHANCE}
Eitems_test
[OBJECT:ENHANCED_ITEM]
[ITEM:ITEM_WEAPON_PICK]
[NAME:Test Enhanced Item 1 (ITEM_WEAPON_PICK)]
[DESCRIPTION:This is a test enhanced item, check ON_EQUIP SKILL_CHANGE]
[ON_EQUIP]
[SKILL_CHANGE:AXE:15]
[ON_ATTACK:25]
[SCRIPT:devel/print-args [ SOURCE_ID TARGET_ID ITEM_ID ITEM_TOKEN ]:50]
[ITEM:ITEM_WEAPON_AXE_BATTLE]
[NAME:Test Enhanced Item 2 (ITEM_WEAPON_AXE_BATTLE)]
[DESCRIPTION:This is a test enhanced item, checks ON_EQUIP INTERACTION_ADD]
[ON_EQUIP]
[INTERACTION_ADD:TEST_SPELL_1]
Now the first group of lines is only included to match with default raws, the lua parser doesn't start reading until it finds [ITEM: on line 5. By using the same names as the default raws (namely ITEM_WEAPON_PICK and ITEM_WEAPON_AXE_BATTLE) the system knows to link create a link between the two.[BUILDING_WORKSHOP:FOUNTAIN]
[NAME:Fountain]
[NAME_COLOR:7:0:1]
[DIM:1:1]
[WORK_LOCATION:1:1]
[BUILD_LABOR:MECHANIC]
[BUILD_KEY:CUSTOM_P]
[BLOCK:1:0]
[TILE:0:1:207]
[COLOR:0:1:0:7:0]
[TILE:1:1:207]
[COLOR:1:1:MAT]
{OUTSIDE_ONLY}
{REQUIRED_WATER:4}
{SCRIPT:flow/source -location BUILDING_LOCATION -source 200 -flow MIST -check 10:0}
[BUILD_ITEM:2:TRAPPARTS:NONE:NONE:NONE][CAN_USE_ARTIFACT]
[ITEM_WEAPON:ITEM_WEAPON_SPEAR]
[NAME:spear:spears]
{DESCRIPTION:A standard spear}
[SIZE:400]
[SKILL:SPEAR]
[TWO_HANDED:47500]
[MINIMUM_SIZE:5000]
[MATERIAL_SIZE:3]
{CRIT_CHANCE:10}
{CRIT_BONUS:150}
{DAMAGE_TYPE:PIERCING}
{WEAPON_CLASS:SPEAR}
[ATTACK:EDGE:20:10000:stab:stabs:NO_SUB:1000]
[ATTACK_PREPARE_AND_RECOVER:3:3]
[ATTACK:BLUNT:10000:6000:bash:bashes:shaft:1250]
[ATTACK_PREPARE_AND_RECOVER:3:3]
[BUILDING_WORKSHOP:TEST_BUILDING_1]
[NAME:Test Building 1]
{DESCRIPTION:Testing various Enhanced Building triggers}
[NAME_COLOR:7:0:1]
[DIM:1:1]
[WORK_LOCATION:1:1]
[BUILD_LABOR:MECHANIC]
[BLOCK:1:0]
[TILE:0:1:207]
[COLOR:0:1:0:7:0]
[TILE:1:1:207]
[COLOR:0:1:0:7:0]
[BUILD_ITEM:1:NONE:NONE:NONE:NONE][BUILDMAT]
{OUTSIDE_ONLY}
{SCRIPT:devel/print-args [ BUILDING_ID BUILDING_TOKEN BUILDING_LOCATION ]:500}
[ITEM_WEAPON:ITEM_WEAPON_PICK]
[NAME:Enhanced Pick]
{DESCRIPTION:This is a test enhanced pick}
[SIZE:500]
[SKILL:MINING]
[TWO_HANDED:47500]
[MINIMUM_SIZE:42500]
[MATERIAL_SIZE:4]
[ATTACK:EDGE:100:4000:strike:strikes:NO_SUB:2000]
[ATTACK_PREPARE_AND_RECOVER:3:3]
{ON_EQUIP}
{SKILL_CHANGE:MINING:15}
{ON_ATTACK:25}
{SCRIPT:devel/print-args [ SOURCE_ID TARGET_ID ITEM_ID ITEM_TOKEN ]:50}
[REACTION:BASIC_MINING]
[NAME:shallow mining]
{DESCRIPTION:dig up rocks from the mine shaft}
[BUILDING:MINE_SHAFT:NONE]
[SKILL:MINING]
[PRODUCT:100:1:BOULDER:NONE:INORGANIC:NONE]
{ON_START}
{BASE_DURATION:1000} -- In-game ticks the reaction takes for someone with 0 skill
{DURATION_REDUCTION:50:10} -- Lowers duration by 50 in-game ticks per skill level down to a minimum of 10
Why does Urist, Female Dwarf, have a male appearance? Is that the placeholder at work?
Her hair is copper with a touch of gray. It is very long, wavy, quite dense, and is tied in a pony tail.
compared to the default stringHer quite dense hair is wavy. Her very long hair is tied in a pony tail. Her hair is copper with a touch of gray.
Kinda feel like that screen should have orientation on it.
['main'] = {
num_cols = 3,
num_rows = 3,
col_pads = 4,
row_pads = 1,
widths = {{40,40,40},
{40,40,40},
{40,40,40}},
heights = {{6, 10,60},
{10,10, 0},
{10,10, 0}},
fill = {'BaseInfo','Description','AppearanceBasic',
'WorshipBasic','HealthBasic',nil,
'RelationshipsBasic','AttributesBasic',nil}},
Once you get around to adding skills, could there be an indicator whether or not the associated labor is active? Might be kind of useful, IDK.
Are you going to add color coding to the Values and Traits (numbers) based on which range they're in? (Or rather, is it possible to setup using the UI config?)
Is there a view that has labor selection, so you don't have to [v]iew the unit or find them in Dwarf Therapist / Manipulator?
But can you toggle the labors somewhere convenient?
{DESCRIPTION:a grand ovate magenta fungus, with light blue cap. it grows in the summer, autumn, and winter in desert badlands. it has short spreading dark magenta daises. the daises smell pungent. the daises can be used to mill a dark magenta dye}
[NAME:grand ovate magenta desert fungus]
[NAME_PLURAL:grand ovate magenta desert fungi]
[ADJ:grand ovate magenta desert fungus]
[PICKED_TILE:58][PICKED_COLOR:5:0:0][SHRUB_COLOR:5:0:0]
[VALUE:2]
[FREQUENCY:50]
[CLUSTERSIZE:5]
[SUMMER][FALL][WINTER]
[WET]
[BIOME:DESERT_BADLAND]
[PREFSTRING:light blue cap]
[PREFSTRING:short spreading dark magenta daises]
[PREFSTRING:white petals]
[PREFSTRING:silver stigma]
[PREFSTRING:dark magenta dye]
# Structure Details
[USE_MATERIAL_TEMPLATE:STRUCTURAL:STRUCTURAL_PLANT_TEMPLATE]
[MATERIAL_REACTION_PRODUCT:SEED_MAT:LOCAL_PLANT_MAT:SEED]
[MATERIAL_REACTION_PRODUCT:POWDER_MAT:LOCAL_PLANT_MAT:MILL]
[BASIC_MAT:LOCAL_PLANT_MAT:STRUCTURAL]
# Seed Details
[USE_MATERIAL_TEMPLATE:SEED:SEED_TEMPLATE]
[MATERIAL_VALUE:1]
[EDIBLE_VERMIN]
[SEED:grand ovate magenta desert fungus seed:0:0:0:LOCAL_PLANT_MAT:SEED]
# Growth Details
[USE_MATERIAL_TEMPLATE:FLOWER:FLOWER_TEMPLATE]
[STATE_COLOR:ALL:#COLOR_STATE_GROWTH_FLOWER]
[DISPLAY_COLOR:5:0:0]
[EDIBLE_VERMIN]
[MATERIAL_REACTION_PRODUCT:POWDER_MAT:LOCAL_PLANT_MAT:MILL]
[GROWTH:FLOWER]
[GROWTH_NAME:short spreading dark magenta daises]
[GROWTH_ITEM:PLANT_GROWTH:NONE:LOCAL_PLANT_MAT:FLOWER]
[GROWTH_TIMING:60000:119999]
[GROWTH_PRINT:5:5:5:0:0:60000:119999:2]
# Product Details
[USE_MATERIAL_TEMPLATE:MILL:PLANT_POWDER_TEMPLATE]
[STATE_NAME_ADJ:ALL_SOLID:dark magenta dye]
[STATE_COLOR:#COLOR_STATE_PRODUCT_MILL]
[DISPLAY_COLOR:5:0:0]
[REACTION_CLASS:PLANT_MILL]
[MILL:LOCAL_PLANT_MAT:MILL]
{DESCRIPTION:a crawling scaly ivory algae. it grows in the spring and summer in sandy deserts. it has giant square saffron soft fruit, and short whorled gold leaves. the soft fruit smell musky and taste sweet and have round and tiny seeds. the leaves smell pleasant and have pungent edges. the soft fruit can be used to mill a saffron dye. the soft fruit can be used to brew a sweet spirit}
[NAME:crawling scaly ivory desert algae]
[NAME_PLURAL:crawling scaly ivory desert algae]
[ADJ:crawling scaly ivory desert algae]
[PICKED_TILE:58][PICKED_COLOR:7:0:1][SHRUB_COLOR:7:0:1]
[VALUE:2]
[FREQUENCY:50]
[CLUSTERSIZE:5]
[SPRING][SUMMER]
[WET]
[BIOME:DESERT_SAND]
[PREFSTRING:giant square saffron soft fruit]
[PREFSTRING:short whorled gold leaves]
[PREFSTRING:red veins]
[PREFSTRING:saffron dye]
[PREFSTRING:sweet spirit]
# Structure Details
[USE_MATERIAL_TEMPLATE:STRUCTURAL:STRUCTURAL_PLANT_TEMPLATE]
[MATERIAL_REACTION_PRODUCT:SEED_MAT:LOCAL_PLANT_MAT:SEED]
[MATERIAL_REACTION_PRODUCT:POWDER_MAT:LOCAL_PLANT_MAT:MILL]
[MATERIAL_REACTION_PRODUCT:DRINK_MAT:LOCAL_PLANT_MAT:DRINK]
[BASIC_MAT:LOCAL_PLANT_MAT:STRUCTURAL]
# Seed Details
[USE_MATERIAL_TEMPLATE:SEED:SEED_TEMPLATE]
[MATERIAL_VALUE:1]
[EDIBLE_VERMIN]
[SEED:crawling scaly ivory desert algae seed:0:0:0:LOCAL_PLANT_MAT:SEED]
# Growth Details
[USE_MATERIAL_TEMPLATE:SOFT_SHELL:FRUIT_TEMPLATE]
[EDIBLE_VERMIN]
[EDIBLE_RAW]
[EDIBLE_COOKED]
[STOCKPILE_PLANT_GROWTH]
[MATERIAL_REACTION_PRODUCT:SEED_MAT:LOCAL_PLANT_MAT:SEED]
[MATERIAL_REACTION_PRODUCT:POWDER_MAT:LOCAL_PLANT_MAT:MILL]
[MATERIAL_REACTION_PRODUCT:DRINK_MAT:LOCAL_PLANT_MAT:DRINK]
[USE_MATERIAL_TEMPLATE:LEAF:LEAF_TEMPLATE]
[STATE_COLOR:ALL:#COLOR_STATE_GROWTH_LEAF]
[DISPLAY_COLOR:6:0:1]
[EDIBLE_VERMIN]
[GROWTH:SOFT_SHELL]
[GROWTH_NAME:giant square saffron soft fruit]
[GROWTH_ITEM:PLANT_GROWTH:NONE:LOCAL_PLANT_MAT:SOFT_SHELL]
[GROWTH_TIMING:120000:200000]
[GROWTH_DROPS_OFF]
[GROWTH_PRINT:'%':'%':6:0:1:120000:200000:3]
[GROWTH_HAS_SEED]
[GROWTH:LEAF]
[GROWTH_NAME:short whorled gold leaves]
[GROWTH_ITEM:PLANT_GROWTH:NONE:LOCAL_PLANT_MAT:LEAF]
[GROWTH_PRINT:0:6:6:0:1:ALL:1]
# Product Details
[USE_MATERIAL_TEMPLATE:MILL:PLANT_POWDER_TEMPLATE]
[STATE_NAME_ADJ:ALL_SOLID:saffron dye]
[STATE_COLOR:#COLOR_STATE_PRODUCT_MILL]
[DISPLAY_COLOR:6:0:1]
[REACTION_CLASS:PLANT_MILL]
[USE_MATERIAL_TEMPLATE:DRINK:PLANT_ALCOHOL_TEMPLATE]
[STATE_NAME_ADJ:ALL_SOLID:frozen sweet spirit]
[STATE_NAME_ADJ:LIQUID:sweet spirit]
[STATE_NAME_ADJ:GAS:boiling sweet spirit]
[DISPLAY_COLOR:6:0:1]
[REACTION_CLASS:PLANT_BREW]
[EDIBLE_VERMIN]
[EDIBLE_RAW]
[EDIBLE_COOKED]
[MILL:LOCAL_PLANT_MAT:MILL]
[DRINK:LOCAL_PLANT_MAT:DRINK]
Created 9907 plants out of 10000 in 65.0 seconds
category 2
MUNDANE : 98.9%
MAGICAL : 1.1%
type 7
SAVAGE : 36.0%
WILD : 33.2%
DOMESTIC : 29.7%
ARCANE : 0.3%
NATURE : 0.3%
ELEMENTAL : 0.3%
DIVINE : 0.2%
subtype 6
TREE : 21.7%
BUSH : 20.5%
GRASS : 18.9%
VINE : 16.0%
FUNGUS : 12.5%
MOSS : 10.4%
growths 8
LEAF : 26.0%
HERB : 21.0%
FLOWER : 18.6%
POD : 18.2%
SOFT_SHELL : 16.9%
GRAIN : 16.2%
HARD_SHELL : 14.4%
UNDERGROUND : 11.2%
products 5
MILL : 68.4%
BREW : 68.0%
THRESH : 16.5%
EXTRACT : 16.0%
PULP : 10.7%
biomes 51
SHRUBLAND_TROPICAL : 341 3.4%
DESERT_ROCK : 335 3.4%
MOUNTAIN : 327 3.3%
SUBTERRANEAN_CHASM : 326 3.3%
MARSH_TEMPERATE_FRESHWATER : 326 3.3%
GLACIER : 322 3.3%
SAVANNA_TEMPERATE : 317 3.2%
SWAMP_TEMPERATE_FRESHWATER : 313 3.2%
SWAMP_TROPICAL_FRESHWATER : 312 3.1%
FOREST_TAIGA : 309 3.1%
DESERT_BADLAND : 309 3.1%
FOREST_TEMPERATE_BROADLEAF : 306 3.1%
MARSH_TROPICAL_FRESHWATER : 305 3.1%
SUBTERRANEAN_LAVA : 304 3.1%
SWAMP_TEMPERATE_SALTWATER : 303 3.1%
MARSH_TEMPERATE_SALTWATER : 302 3.0%
FOREST_TROPICAL_DRY_BROADLEAF : 300 3.0%
SAVANNA_TROPICAL : 298 3.0%
FOREST_TEMPERATE_CONIFER : 298 3.0%
GRASSLAND_TROPICAL : 293 3.0%
TUNDRA : 291 2.9%
DESERT_SAND : 290 2.9%
MARSH_TROPICAL_SALTWATER : 287 2.9%
FOREST_TROPICAL_MOIST_BROADLEAF : 286 2.9%
SWAMP_TROPICAL_SALTWATER : 284 2.9%
SHRUBLAND_TEMPERATE : 284 2.9%
GRASSLAND_TEMPERATE : 280 2.8%
SWAMP_MANGROVE : 275 2.8%
SUBTERRANEAN_WATER : 275 2.8%
FOREST_TROPICAL_CONIFER : 251 2.5%
POOL_TEMPERATE_BRACKISHWATER : 52 0.5%
POOL_TROPICAL_FRESHWATER : 48 0.5%
LAKE_TEMPERATE_BRACKISHWATER : 48 0.5%
RIVER_TEMPERATE_BRACKISHWATER : 47 0.5%
OCEAN_TEMPERATE : 46 0.5%
RIVER_TROPICAL_BRACKISHWATER : 45 0.5%
OCEAN_TROPICAL : 44 0.4%
RIVER_TROPICAL_FRESHWATER : 43 0.4%
RIVER_TEMPERATE_SALTWATER : 42 0.4%
LAKE_TEMPERATE_SALTWATER : 42 0.4%
RIVER_TEMPERATE_FRESHWATER : 41 0.4%
OCEAN_ARCTIC : 41 0.4%
LAKE_TROPICAL_SALTWATER : 40 0.4%
LAKE_TROPICAL_FRESHWATER : 40 0.4%
LAKE_TROPICAL_BRACKISHWATER : 40 0.4%
POOL_TEMPERATE_SALTWATER : 38 0.4%
POOL_TEMPERATE_FRESHWATER : 35 0.4%
RIVER_TROPICAL_SALTWATER : 31 0.3%
POOL_TROPICAL_SALTWATER : 31 0.3%
POOL_TROPICAL_BRACKISHWATER : 31 0.3%
LAKE_TEMPERATE_FRESHWATER : 31 0.3%
"6PART_ARM": {"__comment": "shoulder, upper arm, elbow, lower arm, wrist, hand (2,4,6)",
"TYPE": "ARM_BASIC",
"WEIGHT": 100,
"MATERIAL": ["SURFACE","INSULATION","MEAT","STRUCTURE"],
"DIGITS": {"MIN": 1, "REQUIRED": ["FINGERS"]},
"ATTACK": ["PUNCH"],
"REPEAT": {
"2": [1.0,
["L","left","[LEFT]"],["R","right","[RIGHT]"]],
"4": [0.5,
["UL","upper left","[LEFT]"],["UR","upper right","[RIGHT]"],
["LL","lower left","[LEFT]"],["LR","lower right","[RIGHT]"]],
"6": [0.1,
["TL","top left","[LEFT]"], ["TR","top right","[RIGHT]"],
["ML","mid left","[LEFT]"], ["MR","mid right","[RIGHT]"],
["BL","bottom left","[LEFT]"],["BR","bottom right","[RIGHT]"]]
},
"EXTERNAL": {
"BODY": [
"[BP:^1UA_J:^2 shoulder:STP] [CATEGORY:ARM_JOINT]^3 [JOINT][SMALL][INTERNAL][DEFAULT_RELSIZE:100][CONTYPE:UPPERBODY]",
"[BP:^1UA:^2 upper arm:STP] [CATEGORY:ARM_UPPER]^3 [LIMB] [DEFAULT_RELSIZE:200][CON:^1UA_J]",
"[BP:^1LA_J:^2 elbow:STP] [CATEGORY:ARM_JOINT]^3 [JOINT][SMALL][INTERNAL][DEFAULT_RELSIZE:20] [CON:^1UA]",
"[BP:^1LA:^2 lower arm:STP] [CATEGORY:ARM_LOWER]^3 [LIMB] [DEFAULT_RELSIZE:200][CON:^1LA_J]",
"[BP:^1H_J:^2 wrist:STP] [CATEGORY:ARM_JOINT]^3 [JOINT][SMALL][INTERNAL][DEFAULT_RELSIZE:20] [CON:^1LA]",
"[BP:^1HAND:^2 hand:STP] [CATEGORY:HAND] ^3 [GRASP] [DEFAULT_RELSIZE:80] [CON:^1H_J]"],
"BODY_DETAIL_PLAN": [
"[BP_LAYERS:BY_CATEGORY:ARM_JOINT:STRUCTURE:4:MEAT:1]",
"[BP_LAYERS:BY_CATEGORY:ARM_UPPER:STRUCTURE:25:MEAT:25:INSULATION:5:SURFACE:1]",
"[BP_LAYERS:BY_CATEGORY:ARM_LOWER:STRUCTURE:25:MEAT:25:INSULATION:5:SURFACE:1]",
"[BP_LAYERS:BY_CATEGORY:HAND:STRUCTURE:25:MEAT:25:INSULATION:5:SURFACE:1]"]
},
"RAWS": {
"BODY": ["{#KEY}"],
"BDP": ["[BODY_DETAIL_PLAN:#KEY]"]
}
}
RANDOM WORLD CREATOR STARTING
Reading templates/raws.json
Created 1000 syndromes out of 1000 in 5.3 seconds
Created 1000 environments out of 1000 in 4.1 seconds
Created 1000 materials out of 1000 in 2.6 seconds
Created 1159 inorganics out of 1000 in 6.1 seconds
Created 1000 plants out of 1000 in 10.5 seconds
No weapons created
Created 1000 creatures out of 1000 in 48.8 seconds
No buildings created
No reactions created
No entities created
Writing EXTERNAL raws
Hiya, i've mainly come to ask a question regarding the wrapping script, i wouldn't particularly call it a suggestion for a plugin or command but rather just a check-up on the archaic form of yoinking to summon creatures to your desired location which has been used on numerous DFhack things in the past.
Since the new interactions for 47.xx creates brand new entities that don't rely on a nessecity to exist but only to be defined (a body required with [DOES_NOT_EXIST]) and few other perfunctory arguement rules like length of persistency of the summon & whether the unit has historical relevance, do you think wrapper would be a appropriate rubyscript tool for these kind of desired DFhack triggers?
Your 'Stone Mine' proof of concept design was very interesting. If i might add in accordance with [SKILL_ROLL_RANGE] which would allow units to use workshops such as pseudo gyms for a longer amount of time until the job is completed or suspended with extra-experience or attribute outcome. The future of the scripts shown here will be definitely PTW.
{FIRE_RATE:base rate:skill increase:max rate}
in the ranged items raws (currently can only be specified in the actual ranged item, not the ammo), where{FIRE_RATE:1:0:1}
or a weapon that is really slow to fire unless you have skill with it{FIRE_RATE:500:50:1}
<...>
Note that this only changes the time between shots, the unit will still act between shots just like they do now. Also worth noting, firing every tick will drain ammo like mad. Since most times the game is playing at >20 frames (frame = tick) per second, it will empty a quiver quite quickly. <...>
{SPREAD_SHOT:# of projectiles:spread distance:per target distance}
So if you were to put{SPREAD_SHOT:5:1:4}
onto a crossbow, every time you fired a crossbow bolt, it would create 5 bolts (but only costing a single bolt) with a target offset of 1 tile for every 4 tiles away the target is from the shooter. If you set the spread distance to 0 all created projectiles will have the exact same target position.{BURST_FIRE:# of shots}
So if you were to put{BURST_FIRE:4}
onto a crossbow, the unit would fire four projectiles in four ticks, and then wait the normal fire rate delay to fire again. If you also use the FIRE_RATE command the normal delay will be replaced with the FIRE_RATE delay.{ON_EQUIP}
{SCRIPT:unit/change-attribute -unit HOLDER -attribute [ STRENGTH +200 ]:50}
you use{ON_EQUIP}
{ATTRIBUTE_CHANGE:STRENGTH:200:50}
Again, these are identical, but the later is easier to read and understand in my opinion
Looking good! I imagine that many modders would be interested in this if they were aware of its existence and understood how to use it; I sometimes wonder whether the sequestration of utilities into this subforum has decreased their general visibility as compared to when everything was mixed together in the 'Modding' section. It would probably help to have a couple of eyecatching usage examples in the first few posts to draw interest.Yes I agree, examples are definitely required. I just keep adding more features instead of writing them! I will work on getting some good ones together
With regards to {ON_EQUIP} in the enhancedItems system, I haven't checked your code, but if it works like modtools/item-trigger you need to be aware that this script used to have a problem of triggering the 'onEquip' effect regardless of whether the target item was being hauled, worn, wielded, stabbed into a unit, etc (so a sword intended to transform wielders into giants would also have a transform units hauling it to the stockpile or opponents in the middle of a battle, for example). This may or may not be intended behaviour in your system, but I had added the option to specify the desired equipment mode(s) to give item-trigger users more control.Good point, I remember that was an issue the last time I worked on this system. ON_EQUIP should definitely only work when the unit is wearing or wielding the item. I will add a mode check for that. Similarly I should add a ON_HAULED and ON_STUCKIN (or something) so that you can trigger those effects as well.
Does enhancedItems not include {ON_UNEQUIP} or is it assumed that unequipping an item removes the {ON_EQUIP} effect? If the latter is correct, allowing users to make {ON_EQUIP} effects permanent may be useful in some cases. A separate {ON_UNEQUIP} option would be useful for effects unique to unequipping (a cursed sock which kills the wearer when removed, for example).Another good point. Originally I had the idea that all ON_EQUIP effects would simply be removed when the unit unequipped the item, but in the latest update I have added an ON_UNEQUIP so that the the effects are indeed seperate. It also makes the coding easier as I know longer have to figure out what the opposite of everything is.
Looking good! I imagine that many modders would be interested in this if they were aware of its existence and understood how to use it; I sometimes wonder whether the sequestration of utilities into this subforum has decreased their general visibility as compared to when everything was mixed together in the 'Modding' section. It would probably help to have a couple of eyecatching usage examples in the first few posts to draw interest.
<...>
Looking good! I imagine that many modders would be interested in this if they were aware of its existence and understood how to use it; I sometimes wonder whether the sequestration of utilities into this subforum has decreased their general visibility as compared to when everything was mixed together in the 'Modding' section. It would probably help to have a couple of eyecatching usage examples in the first few posts to draw interest.
<...>
Yeah i was naive thinking that modders would use straight lua (as it's IMHO easier to learn than df raws) for modding instead of any hacks on top of modding system. That being said i think dfhack and RSS (i.e. this) has limited visibility and needs a some sort community showcase or a way to show off features in general. I had an idea to make a sort-of newspaper with each release but lately DF is way down in the list of thing i have time and patience for.
Some fun new stuff I'm working with;
1. Reactions that create miasma and smoke when completed
2. Reactions that take variable amounts of time and produce variable amounts of output
3. Reactions that remove a unit from play for an amount of time
4. Multistory buildings
Still not quite ready for a release, but hopefully within the next week I'll have the Enhanced Buildings and Items fleshed out, and the initial Enhanced Reactions system in place, along with some cool examples.
EDIT: While I'm thinking about it, are there certain things people would like to see in the Enhanced Buildings, Items, and Reactions systems? Things that I can build in before a release. Feel free to request anything you think might fit, if it seems to out of scope or to much work I'll just put it aside for now. (An example might be wanting a trigger for Enhanced Items ON_DODGE, this would be a great suggestion, however, triggering something on a dodge is much more complicated than triggering on other actions due to the way DF handles it, which is why this isn't included yet)
-snip- Also there would be mandatory maintenance and support part as anything that does not eat/sleep/bleed is boring and must have drawbacks
-snip- Also there would be mandatory maintenance and support part as anything that does not eat/sleep/bleed is boring and must have drawbacks
Feel like the big issue with golems is if you give them emotions or not, if you don't they probably work fine until one goes extremely distracted and crafting with them might get shoddy I heard, if you do give them emotions then they act like an immortal fort citizen that is affected by corpses and stress can cause them to go mad so you end up having to find a way to de-stress the golems.
which already feels like maintenance.
-snip- Also there would be mandatory maintenance and support part as anything that does not eat/sleep/bleed is boring and must have drawbacks
Feel like the big issue with golems is if you give them emotions or not, if you don't they probably work fine until one goes extremely distracted and crafting with them might get shoddy I heard, if you do give them emotions then they act like an immortal fort citizen that is affected by corpses and stress can cause them to go mad so you end up having to find a way to de-stress the golems.
which already feels like maintenance.
You could set up "pylon" buildings that periodically de-stress golems automatically when they walk by them. For even more flavor you could make them naturally super weak and slow, then have the pylons give them a massive strength and speed boost which lasts a certain amount of time, so you would need them to almost always be close to the building
-snip- Also there would be mandatory maintenance and support part as anything that does not eat/sleep/bleed is boring and must have drawbacks
Feel like the big issue with golems is if you give them emotions or not, if you don't they probably work fine until one goes extremely distracted and crafting with them might get shoddy I heard, if you do give them emotions then they act like an immortal fort citizen that is affected by corpses and stress can cause them to go mad so you end up having to find a way to de-stress the golems.
which already feels like maintenance.
You could set up "pylon" buildings that periodically de-stress golems automatically when they walk by them. For even more flavor you could make them naturally super weak and slow, then have the pylons give them a massive strength and speed boost which lasts a certain amount of time, so you would need them to almost always be close to the building
Wouldn't it be like alcohol dependency? Dwarf golems drinking without aperture, and getting eventual decline of neglect for not standing near pylons, or visiting a tavern for a mechanical refill even if they can't party (can't choke because no lungs, also easiest way to get served drinks until full least in current version is to be unable to speak and take part in socialising)
Plumphelmet-men visitors just stand in the corner sipping their drinks, its not really known how they're supposed to have fun.
so I just got an adv mode idea given how stealth works and how it's invisible to the player but not to everyone else. is it possible to make Dfhack npcs that spawn in and hide out of site to do stuff you can't just force with dfhack yet.
like making an unit named Aimed Shot that hits like a space station and sets up aimed attacks that pierce and probably sticks into the person you told Aim to hit.
you could even set up how much force and damage the attacks are you could even assign the same historical figure to Aimed Shot so any kills they get would transfer over to the player.
this script idea would only work as long as the whole sneaking thing isn't fixed and probably break if someone uses any reveal any hidden units scripts.
so you're saying one could slap an attack on the unit modify it and have it target themselves?
well then that's one work around to get control of which body part you want to shoot.
[lua]# unit = reqscript("functions/unit").getUnit(752)
[lua]# ~unit
table: 000001F81AE9CDA0
id = 752
_unit = <unit: 000001F81AE7E0C0> -- The old return of df.unit.find(id)
Flags = table: 000001F81AE9CFE0 -- Each of these are metatables that access the desired information
Counters = table: 000001F81AE9D060
Attributes = table: 000001F81AE9CEE0
Skills = table: 000001F81AE9CF20
[lua]# !unit.name == unit._unit.name
true
[lua]# ~unit.name
<language_name: 000001F81AE7E0C8>
first_name =
nickname =
words = <int32_t[]: 000001F81AE7E108>
parts_of_speech = <part_of_speech[]: 000001F81AE7E124>
language = -1
unknown = -1
has_name = false
[lua]# ~unit.Attributes.STRENGTH
table: 000001F81B143420
token = STRENGTH
type = Physical
_unit = <unit: 000001F81AE7E0C0>
_attribute = <unit_attribute: 000001F81AE7E6A4>
[lua]# ~unit.Attributes.WILLPOWER
table: 000001F81BE4C8A0
token = WILLPOWER
type = Mental
_unit = <unit: 000001F81AE7E0C0>
_attribute = <unit_attribute: 000001F819EDBB64>
[lua]# ~unit.getAge()
230.81025793651
[lua]# ~dfhack.units.getAge(unit._unit)
230.81025793651
{PRODUCT:100:150:FLOW:MIASMA:NONE:NONE}
to the reaction means that the reaction will produce a 150 density miasma flow with 100% probability, and{PRODUCT:50:7:LIQUID:WATER:NONE:NONE}
will produce a 7 depth tile of water with a 50% probability.{ON_DEATH}
{SCRIPT:map/spawn-flow -unit UNIT_ID -type Fire -density 100:100}
OR just put{EXPLODES_ON_DEATH}
[ATTACK:KICK:BODYPART:BY_TYPE:STANCE]
[ATTACK_SKILL:STANCE_STRIKE]
[ATTACK_VERB:kick:kicks]
[ATTACK_CONTACT_PERC:100]
[ATTACK_PREPARE_AND_RECOVER:4:4]
[ATTACK_FLAG_WITH]
[ATTACK_PRIORITY:SECOND]
[ATTACK_FLAG_BAD_MULTIATTACK]
we can add some extra tokens to do some fancy things [ATTACK:KICK:BODYPART:BY_TYPE:STANCE]
[ATTACK_SKILL:STANCE_STRIKE]
[ATTACK_VERB:kick:kicks]
[ATTACK_CONTACT_PERC:100]
[ATTACK_PREPARE_AND_RECOVER:4:4]
[ATTACK_FLAG_WITH]
[ATTACK_PRIORITY:SECOND]
[ATTACK_FLAG_BAD_MULTIATTACK]
{ON_ATTACK}
{SCRIPT:do-something}
{ON_HIT}
{SCRIPT:do-something}
{ON_WOUND}
{SCRIPT:do-something}
How to use itRegarding this list, couldn't the third step (core/initialize) be skipped by adding init.lua (https://docs.dfhack.org/en/latest/docs/Lua%20API.html#save-init-script) into raws for the purpose of sharing existing saves?
- Copy the Scripts/raw/scripts folder into your DF raw folder (hopefully there are no conflicts with any other scripts you have installed there)
- Copy the Scripts/raw/systems folder into your DF raw folder (directly into the raw folder, not the raw/scripts folder)
- Put core/initialize in your onLoad init file. If you want more verbose output you can use -v repeatedly (up to -vvvvv)
- All available systems should now be working, and all scripts should now be callable through any other methods you want
Encouraging to see the system develop. The advantage of rawified approach in keeping the entire thing in one place is quite nice for compartmentalizing, despite what limits it may hold.
If I were to make a suggestion, {ON_EXISTENCE} (or _LIFE?) i.e. something to run when the unit enters map for the first time (through being born or migrating on-site) for ex. assigning animalmen to player fort and announcing migrants, though I think you currently can do a lot by just self-applied syndrome.How to use itRegarding this list, couldn't the third step (core/initialize) be skipped by adding init.lua (https://docs.dfhack.org/en/latest/docs/Lua%20API.html#save-init-script) into raws for the purpose of sharing existing saves?
- Copy the Scripts/raw/scripts folder into your DF raw folder (hopefully there are no conflicts with any other scripts you have installed there)
- Copy the Scripts/raw/systems folder into your DF raw folder (directly into the raw folder, not the raw/scripts folder)
- Put core/initialize in your onLoad init file. If you want more verbose output you can use -v repeatedly (up to -vvvvv)
- All available systems should now be working, and all scripts should now be callable through any other methods you want
Roses,
I'm trying to bring back old script for cage-thrower that cages target.
So far I've managed to restore projectiles into cages and they do capture targets.
But, the hunter or attackers keep at the target. Apparently there has been a change between then and now on how units store their targets.
After searching through forums I've noticed you have a script for creating an attack and clearing actions from units. Any ideas how to remove current action target from unit? :/
Just for clarification, what exactly happens? The cage gets thrown correctly, and the target gets put in the cage correctly, but the unit keeps attacking the caged target? More than one attack, or just a single extra attack?There was once this bug where squads ordered to kill target followed target after it was caught by cage trap.
Figured out was the problem. You didn't have a plural name for the example items. It should've been
power digger:power diggers.
But that's honestly a minor thing.
I got a question are the skills and attributes changes attached to the item invisible? Or are they suppose to show up in you the player's stats?
Just for clarification, what exactly happens? The cage gets thrown correctly, and the target gets put in the cage correctly, but the unit keeps attacking the caged target? More than one attack, or just a single extra attack?There was once this bug where squads ordered to kill target followed target after it was caught by cage trap.
It's the same - hunter wants to hunt/return kill, squads ordered to kill stay at that order and follow caged target.
Here is the script, I will refactor it a bit later, if I deal with this follow issue :S
local allUnits = nil
allUnits = df.global.world.units.all
--make other units stop following it
for i=#allUnits-1,0,-1 do -- search list in reverse
newu = allUnits[i]
if newu.opponent.unit_id == unit then
newu.following = nil
newu.enemy = nil
newu.opponent.unit_id = nil
newu.job.hunt_target = nil
end
end
Looking at the code briefly I see this part, which looks like it's trying to do what you want, but the if statement will never read true because it is comparing an id to a unit struct
Try changing if newu.opponent.unit_id == unit then to if newu.opponent.unit_id == unit.id then and see if that works.
newu.job.current_job = nil
although I would be a little concerned that that might leave a hanging job on the list and cause issues if it happens to much. But if that does fix it, then it would be simple enough to properly remove the job instead of just unassigning it.
Darn, was hoping it would be a simple fix. One other simple thing you could try would be to change the units path goal newu.path.goal = -1, that might reset them. You can also try to remove any jobs they might currently have withCode: [Select]newu.job.current_job = nil
although I would be a little concerned that that might leave a hanging job on the list and cause issues if it happens to much. But if that does fix it, then it would be simple enough to properly remove the job instead of just unassigning it.
<...>
Wow. Thanks.<...>
You need to mark that map tile as empty of creatures- see here: https://github.com/DFHack/scripts/blob/master/teleport.lua#L32
...
local eventful = require 'plugins.eventful'
function getItemType(item)
if item:getSubtype() ~= -1 and dfhack.items.getSubtypeDef(item:getType(), item:getSubtype()) then
return dfhack.items.getSubtypeDef(item:getType(),item:getSubtype()).id
else
return df.item_type[item:getType()]
end
end
function createCage(material, quality)
local item = df.item_cagest:new()
item.id = df.global.item_next_id
df.global.world.items.all:insert('#',item)
df.global.item_next_id = df.global.item_next_id+1
item:setMaterial(material.type)
item:setMaterialIndex(material.index)
item:setQuality(quality)
item:categorize(true)
item.flags.removed = true
return item
end
function getCaptureProbability(unit)
local damageFactor = 1
if unit.counters.winded > 0 then
damageFactor = damageFactor/1.5
end
if unit.counters.stunned > 0 then
damageFactor = damageFactor/1.5
end
if unit.counters.unconscious > 0 then
damageFactor = damageFactor/2
end
if unit.counters.webbed > 0 then
damageFactor = damageFactor/1.5
end
if unit.counters.nausea > 0 then
damageFactor = damageFactor/1.5
end
local bloodFrac = 1
local bloodMax = unit.body.blood_max
if bloodMax ~= 0 then
bloodFrac = unit.body.blood_count/bloodMax
end
return 100 - ((unit.body.size_info.size_cur*10 ^ 1/3)*bloodFrac*damageFactor)
end
function cageUnit(cage, unit)
local containedIn = df.general_ref_contained_in_itemst:new()
containedIn.item_id = cage.id
unit.general_refs:insert('#', containedIn)
unit.flags1.caged = true
unit.flags1.on_ground = false
local tileOccupancy = dfhack.maps.getTileBlock(unit.pos).occupancy[unit.pos.x%16][unit.pos.y%16]
tileOccupancy.unit = false -- would probably be better to check whether there are other units on the same tile before disabling these
tileOccupancy.unit_grounded = false
local containsUnit = df.general_ref_contains_unitst:new()
containsUnit.unit_id = unit.id
cage.general_refs:insert('#', containsUnit)
cage.flags.container = true
end
eventful.onProjItemCheckImpact.capture = function(projectile)
local firedItem = projectile.item
if not firedItem then
return
end
if string.find(getItemType(firedItem), "_PROJCAGE", 9) then -- replace as necessary
local material = dfhack.matinfo.decode(firedItem)
if not material then -- shouldn't happen
return
end
local targetUnit
for _, u in ipairs(df.global.world.units.active) do
if u ~= projectile.firer then -- prevent firer from capturing themself
if same_xyz(u.pos, projectile.cur_pos) then -- find a unit at the same location as the projectile
targetUnit = u
break -- only a single unit will be captured
end
end
end
if not targetUnit then -- nothing to capture at the impact location
return
end
local cage = createCage(material, firedItem.quality)
dfhack.items.moveToGround(cage, targetUnit.pos)
if math.random(100) > getCaptureProbability(targetUnit) then -- removing this will guarantee capture
cageUnit(cage, targetUnit)
end
projectile.flags.to_be_deleted = true -- ensures that the fired item doesn't persist
-- make other units stop following the captured unit:
local allUnits = df.global.world.units.all
for i = #allUnits-1, 0, -1 do
local unit = allUnits[i]
if unit.following == targetUnit then
unit.following = nil
end
if unit.opponent.unit_id == targetUnit.id then
unit.opponent.unit_id = -1
unit.path.goal = -1
unit.job.current_job = nil
unit.job.hunt_target = nil
end
end
end
end
Tidied up your script whilst looking into the "phantom unit" issue. It also appears to occur with creatures that are captured via regular cage traps, so I'd say that this is a more of a problem with the way DFHack generates the "units under the cursor" display (cursor support isn't a vanilla feature), as opposed to anything specifically wrong with this script.Whoah. Thanks.
Looks like the script incorporates a Pokemon-esque mechanic which makes capturing easier the more damaged the target is. I didn't examine it closely, but it might need some fine-tuning as the bulk of my test units were caught on first attempt.True, I think original author (was it IndigoPhoenix) had something like that in mind. I've actually seen capture failed few times, but on bigger animals, so it might just work, somewhat :)
For future reference, enclosing code in a "code" block as opposed to a "spoiler" makes it easier for others to read and copy. Also, this particular thread might not be the most appropriate place to ask for scripting advice; opening a new one or continuing the discussion in the general DFHack thread would make it easier for others to notice your queries whilst preventing accidental derailing.I'll keep that in mind. I thought maybe posting large blocks of unclean code wasn't such a smart idea ;)
Hi Roses! Welcome back! I've been seeing your name in the code a lot, but we've never met. I've been making a lot of changes recently to quickfort (https://docs.dfhack.org/en/latest/docs/_auto/base.html#quickfort) (there's one in DFHack now, not the external Python one), buildingplan (https://docs.dfhack.org/en/latest/docs/Plugins.html#buildingplan), and blueprint (https://docs.dfhack.org/en/latest/docs/Plugins.html#blueprint).
I'm also working on a few utility plugins that might be useful for you -- dig-dug (https://github.com/DFHack/dfhack/pull/1853) to dig out sections of rock according to dig designations and build-now (https://github.com/DFHack/dfhack/issues/1857) (or something.. I haven't really thought of a name yet) for instantly completing construction jobs. My use case is automated regression testing -- apply a blueprint file with quickfort, use dig-dug and build-now to make things happen without dwarven intervention, create blueprint files from the resulting game map with blueprint, and compare the input and output files -- but you might be able to use them for making general changes to the game map.
There's also a database of building metadata (https://github.com/DFHack/scripts/blob/master/internal/quickfort/build.lua#L237) in quickfort that we might be able to factor out and share.
plant.lua looks like exactly what I need to simulate tree chopping. I was worried that I'd just have to ignore trees entirely if I couldn't figure it out!
y_column = math.floor(base.y/16)
y_column-y_column%3
when I'm looking atmovsx r12d, r8w ; y coord into r12
mov eax, 2AAAAAABh
imul r12d
mov edi, r12d
movzx ecx, r12w
sar edx, 3
mov eax, edx
shr eax, 1Fh
add edx, eax
lea eax, [rdx+rdx*2]
shl eax, 4
sub edi, eax
sub cx, di
mov [rsp+30h+arg_8], edi
is very helpful!map_block_column = df.global.world.map.column_index[(math.floor(base.x / 48) * 48) / 16][(math.floor(base.y / 48) * 48) / 16]
which avoids %3.
plant.lua looks like exactly what I need to simulate tree chopping. I was worried that I'd just have to ignore trees entirely if I couldn't figure it out! Do you mind if I add that to either dig-dug itself or the dfhack core library?
tiletypes (https://docs.dfhack.org/en/latest/docs/Plugins.html#tiletypes) might also be useful to force a tile into a particular shape/configuration. I'm writing dig-dug because I specifically want the tile tile transformations to follow the rules of "organic" digging.
If you think you could use the quickfort buildings db, I can work on putting it behind an API so it's queryable by multiple indices (e.g. by type,subtype,custom instead of its current "ui keystroke sequence" map key
plant.lua looks like exactly what I need to simulate tree chopping. I was worried that I'd just have to ignore trees entirely if I couldn't figure it out!
I don't understand everything that's going on in the script, but it doesn't look like it will cause items to fall out of the removed tree, or water to recalculate. In other words, it's equivalent to using a ballista bolt.
dfhack.items.makeProjectile can be used to cause an item to fall, and liquids plugin probably has the proper way to update fluids. Should be simple enough to implement.
I've been preparing to disassemble the exact code for tree chopping by starting with understanding the less complex function for finding a plant at a tile (that the tree chopping function calls.) Knowing I'm probably seeing:Code: [Select]y_column = math.floor(base.y/16)
when I'm looking at
y_column-y_column%3Code: [Select]movsx r12d, r8w ; y coord into r12
is very helpful!
mov eax, 2AAAAAABh
imul r12d
mov edi, r12d
movzx ecx, r12w
sar edx, 3
mov eax, edx
shr eax, 1Fh
add edx, eax
lea eax, [rdx+rdx*2]
shl eax, 4
sub edi, eax
sub cx, di
mov [rsp+30h+arg_8], edi
Edit: Actually, looks like the above assembly calculates math.floor(base.x / 48) * 48 and base.y%48 (which it later uses with dim_y to completely avoid using extent_north, etc.)
The optimized code used by DF is:Code: [Select]map_block_column = df.global.world.map.column_index[(math.floor(base.x / 48) * 48) / 16][(math.floor(base.y / 48) * 48) / 16]
which avoids %3.
It definitely wouldn't be hard to loop over any items in the tree and turn them into projectiles and loop over any water tiles and add the update flows flag.
It definitely wouldn't be hard to loop over any items in the tree and turn them into projectiles and loop over any water tiles and add the update flows flag.
As far as I know, you can just enable updates in the tile where the plant coordinate was, and it will end up updating the whole body of water. (Unless the liquids command is doing more than just setting the single tile's flag.) Although you'd probably want to set the flag where each solid tree tile was, just in case there are setups where that matters.
I suppose the inside/outside or light/dark flags (can't remember their exact names) are also probably updated, but I'm sure there are more that I am not thinking about.
Something more may need to be done there, like setting it to the respective x/y tiles in the z-level above the tree.This is what I do for the (in-progress) dig-now plugin (https://github.com/DFHack/dfhack/pull/1853/files#diff-2d6a3ff76af0fba5cca4b4c49bd1a415925ea68332464d4431a33b03b53d54d3R36) so light can filter down through newly dug ramps/channels. Code is in C++ but we could probably move it to the core library and expose it via the Lua API.
df::tile_designation td = map.designationAt(pos);
if (!map.ensureBlockAt(DFCoord(pos.x, pos.y, pos.z+1))) {
// only the sky above
td.bits.light = true;
td.bits.outside = true;
td.bits.subterranean = false;
}
int32_t zlevel = pos.z;
df::tiletype_shape shape =
tileShape(map.tiletypeAt(DFCoord(pos.x, pos.y, zlevel)));
while ((shape == df::tiletype_shape::EMPTY
|| shape == df::tiletype_shape::RAMP_TOP)
&& map.ensureBlockAt(DFCoord(pos.x, pos.y, --zlevel))) {
DFCoord pos_below(pos.x, pos.y, zlevel);
df::tile_designation td_below = map.designationAt(pos_below);
if (td_below.bits.light == td.bits.light
&& td_below.bits.outside == td.bits.outside
&& td_below.bits.subterranean == td.bits.subterranean)
break;
td_below.bits.light = td.bits.light;
td_below.bits.outside = td.bits.outside;
td_below.bits.subterranean = td.bits.subterranean;
map.setDesignationAt(pos_below, td_below);
shape = tileShape(map.tiletypeAt(pos_below));
}
-- Erase plant from correct map column
map_block_column = df.global.world.map.column_index[base.x//48*3][base.y//48*3]
for i,plant in pairs(map_block_column.plants) do
if plant.pos == base then
map_block_column.plants:erase(i)
break
end
end
"//" is integer division, which avoids the need for "math.floor()"....
local z1 = tree.pos.z
local z2 = tree.pos.z + tree.tree_info.body_height - 1
local z_root1 = tree.pos.z - 1
local z_root2 = tree.pos.z - tree.tree_info.root_depth
for x = x1,x2 do
for y = y1,y2 do
for z = z1,z2 do
pos = {x=x,y=y,z=z}
body = tree.tree_info.body[pos.z-z]:_displace((pos.y - y) * tree.tree_info.dim_x + (pos.x - x))
if body.trunk or body.twigs or body.branches or body.connection_north then
positions[#positions+1] = pos
end
end
for z = z_root2,z_root1 do
pos = {x=x,y=y,z=z}
root = tree.tree_info.root[pos.z - z - 1]:_displace((pos.y - y) * tree.tree_info.dim_x + (pos.x - x))
if root.trunk then
positions[#positions+1] = pos
end
end
end
...
I also simplified the if statements for body checks.Ah, you are correct, yeah that doesn't make much sense for underground trees.I suppose the inside/outside or light/dark flags (can't remember their exact names) are also probably updated, but I'm sure there are more that I am not thinking about.
I don't think trees affect subterranean/above ground or light/dark flags, just outside/inside (like buildings.) Looks like your script always sets the tile to outside, which doesn't make sense for cavern trees. Something more may need to be done there, like setting it to the respective x/y tiles in the z-level above the tree.
Here's a simplification of the code for erasing the plant from the map column:That's a lot cleaner. Honestly I don't even know where my previous code of figuring out the column blocks and such.Code: [Select]-- Erase plant from correct map column
"//" is integer division, which avoids the need for "math.floor()".
map_block_column = df.global.world.map.column_index[base.x//48*3][base.y//48*3]
for i,plant in pairs(map_block_column.plants) do
if plant.pos == base then
map_block_column.plants:erase(i)
break
end
end
Note that "n//m" equals "n - n%m", because it's the non-remainder portion.
Dividing by 48 instead of 3 (since 48 = 3 * 16) takes care of the column conversion.
(It's worth pointing out that "base.x//48" is a mid-level tile coord. MLT's are 3*3 tile block columns, so "base.x//48*3" is a tile block column coord. It all checks out.)
Reused "map_block_column" instead of calculating it again.
I think I originally wrote this because I was only interested in removing trees in such a way that I could build buildings where they were, so it isn't surprising to me that a bunch of what I did was so piecemeal (and that I ignored roots entirely). This all looks good, and I'm glad other people are looking into this stuff.
Some shroom trees use just "body.connection_north" to indicate part of their cap, so you'll need to add that to "getPositions()". You also used z1,y1,x1 in the displace logic instead of the loop variables z,y,x.
You're probably going to need to remove roots, unless that's handled automatically somehow. "tree.roots" is similar to "tree.body", except it's upside-down. It's indexed from 0 to "tree.root_depth - 1". Presence of a root is indicated by "trunk" flag. Should be as simple as adding a second z loop after the first one. (Technically, you could get away with just "tree.roots[0]" in the current version of DF, but a loop is forward compatible for root_depth > 1.)
Changes:Code: [Select]...
I also simplified the if statements for body checks.
local z1 = tree.pos.z
local z2 = tree.pos.z + tree.tree_info.body_height - 1
local z_root1 = tree.pos.z
local z_root2 = tree.pos.z + tree.tree_info.root_depth - 1
for x = x1,x2 do
for y = y1,y2 do
for z = z1,z2 do
pos = {x=x,y=y,z=z}
body = tree.tree_info.body[pos.z-z]:_displace((pos.y - y) * tree.tree_info.dim_x + (pos.x - x))
if body.trunk or body.twigs or body.branches or body.connection_north then
positions[#positions+1] = pos
end
end
for z = z_root2,z_root1 do
pos = {x=x,y=y,z=z}
root = tree.tree_info.root[pos.z-z]:_displace((pos.y - y) * tree.tree_info.dim_x + (pos.x - x))
if root.trunk then
positions[#positions+1] = pos
end
end
end
...
All that's left is to account for "position.z < base.z" for the tiletypes. Assuming roots can't grow in stone (need to test this,) then all you have to do is convert those positions to soil walls (tiletype 261.)
For the base, the floor tiletype should chose randomly from {348,349,350,351} instead of just 350 for tile variations. (That was probably already on the to-do list.)
[DFHack]# :lua ~(7//2)
3
[DFHack]# :lua ~(7-7%2)
6
All that being said, I am super happy people are looking into this stuff, like I said before, I only messed around with this so that I could delete trees in order to build buildings, in particular my multi-story buildings. But if we can get a proper tree removal/script tree chopping going I could definitely see some custom civs using a version of it instead of built-in tree chopping