Dwarf Fortress > DF Modding

Combat mechanics (pulping) and material/creature properties [0.40.xx]

(1/15) > >>

Urist Da Vinci:
Previous 0.34.11 thread: http://www.bay12forums.com/smf/index.php?topic=131995.0
There have been several large changes, and perhaps more to come, so I though I would start with a clean thread.

< INSERT HIGH-LEVEL OVERVIEW HERE - for now you can refer to the linked thread above >

New mechanics: Pulping

Pulping appears to work by evaluating the layers in a body part. If each layer meets any one of the following criteria then the body part is pulped:
a) 100% bruised/burned/frostbite/melt/necrosis/blister/boil/freeze/condense (i.e. 10000+ in layer_effect_fraction)
b) 250% dented (i.e. 25000+ in layer_dent_fraction)
c) 100% cut (i.e. 10000+ in layer_cut_fraction) (cut in this case is synonymous with fracture)

Spines, skulls, and perhaps other body parts have the [PREVENTS_PARENT_COLLAPSE] token which prevents the parent body part (such as the head, upper body, or lower body) from being pulped until the sub-part is broken. It appears that only external body parts can be pulped, not internal organs. You will find that boneless body parts that don't contain a spine/skull part will pulp VERY easily (i.e. eyes/ears).

There does not appear to be any distinctions between the combat text descriptions of the pulping, beyond the messages being appropriate to the weapon used (edged, blunt, or creature body part).

EDIT: Updated Ranged Weapon Mechanics

Projectile_velocity = ( SHOOT_FORCE / Weight ) / 20 , capped at the SHOOT_MAXVEL and rounding down
i.e. for a vanilla iron bolt and crossbow: (1000/1.1775)/20 = 42
silver 31, copper 37, wood 200, adamantine 200

As before, momentum = velocity * weight , so iron, copper, and silver end up with almost identical momentums (49). Wood has about 30% of the momentum of most metal bolts and much less cutting ability. These momentums are now on par with melee weapons and human body part attacks. The 0.34.11 "railgun" bolts/arrows had velocities around 1000 and momentum 1000 to 1600.

Comments and questions are welcome. Next, I will probably try to see if the momentum-based mechanics from 0.34.11 were changed with the combat/move speed update.

Urist Da Vinci:
The momentums of melee weapons and creature attacks seem a bit higher, probably something to do with the new attack styles or attack timing. Melee might actually be powerful now compared to ranged attacks, a welcome change from 0.34.11.

Not 100% sure yet, but it looks like armor and defense are mostly the same as in 0.34.11.
Needs a bit more research perhaps? The apparent code (seen as a disassembly) for material properties in combat doesn't appear to have changed from 0.34.11, aside from the known bug fixes.

The apparent code for the input data on the attacking weapon or body part is different. "Heavy" attacks have increased momentum (by how much, I am not sure), and "quick" attacks have decreased momentum. I am curious if the ATTACK_PREPARE_AND_RECOVER token has a direct effect on momentum - this would make the "velocity multiplier" portion of the ATTACK token semi-redundant?

scamtank:
Far be it from me to question your methods, but surely the table tilting of heavy/light/wild attacks screws with more values than just the recovery speeds.

My informal experience with using enormous numbers also suggests that the wind-up doesn't really affect the end result. A super-slow punch is still just like any other punch.

Urist Da Vinci:

--- Quote from: scamtank on August 17, 2014, 03:03:08 am ---Far be it from me to question your methods, but surely the table tilting of heavy/light/wild attacks screws with more values than just the recovery speeds.
...

--- End quote ---

I just found the momentum numbers, and monitoring them shows that:
- Quick attacks hit with exactly 0.5x the momentum of a normal attack
- Heavy attacks hit with exactly 1.5x the momentum of a normal attack
- Wild attacks hit with exactly 1.5x the momentum of a normal attack
- Precise attacks hit with the same momentum of a normal attack

Normal weapon use momentum numbers appear in line with 0.34.11, except that there appears to be an additional x2 multiplier on top of everything if the opponent is prone.

Typical momentum values:
Noob (dabbling) average human slashing with a steel short sword: 67 (as in 0.34.11)
Grand master average human slashing with a steel short sword: 134
Grand master average human slashing a prone opponent with a steel short sword: 268 (in 0.34.11 it was 134!)
Grand master average human heavy slashing a prone opponent with a steel short sword: 402
Grand master size 80000 human heavy slashing a prone opponent with a steel short sword: 441

The 134 value is really just the 67 with some skill and situation bonuses piled on, so it is actually the upper limit for that person. An unskilled person puts out a range of values due to randomness, but the super skilled person appears to consistently put out 2x what they would do on average as a noob. So if you are playing a highly skilled character and want to do more damage, you are limited to getting stronger, getting fatter (really!), using heavy/wild attacks, or getting a better weapon. Infinite skill won't allow creatures to overcome their physical limits.

(My method is to attach a debugger to DF, use DFHack to find the address of something like the weapon raws, and then to set an access breakpoint there. The debugger lets me see the code in assembly language, which can be obtuse)

EDIT: contrary to my suspicions, there are no "inertial dampeners" or momentum shields on the head. If the brain is more protected, it appears to be mostly due to the increased skull relsize from 20 to 200.


--- Quote from: scamtank on August 17, 2014, 03:03:08 am ---...
My informal experience with using enormous numbers also suggests that the wind-up doesn't really affect the end result. A super-slow punch is still just like any other punch.

--- End quote ---

EDIT2: Looks like you are correct. Changing [ATTACK_PREPARE_AND_RECOVER:3:3] to [ATTACK_PREPARE_AND_RECOVER:1:1] doesn't affect the momentum results, allowing you to attack more often at normal strength. No vanilla weapons are like this (all are at least 3:3). 1:1 weapons tend to be very overpowered as you can dodge/parry more often in addition to having more attacks.

Urist Da Vinci:
I haven't looked at this in weeks, but here is where I left off with the combat calculator:


--- Code: (combat.lua) -----Calculates combat info for weapons/armor/creature. DFHack 0.40.08 version

unit=dfhack.gui.getSelectedUnit()
if unit==nil then
print ("No unit under cursor!  Aborting!")
return
end

race=df.global.world.raws.creatures.all[unit.race]
print("Creature size (base/current/racial): ", unit.body.size_info.size_base, unit.body.size_info.size_cur, race.adultsize)
if unit.curse.attr_change ~= nil then
curstrength=((unit.body.physical_attrs.STRENGTH.value * unit.curse.attr_change.phys_att_perc.STRENGTH)/100 +

unit.curse.attr_change.phys_att_add.STRENGTH)
if curstrength > 5000 then curstrength=5000 end
else
curstrength=unit.body.physical_attrs.STRENGTH.value
end
print("Creature strength (base/current): ", unit.body.physical_attrs.STRENGTH.value, curstrength)
print("Wrestle/Charge rating: ", math.floor(curstrength/100+unit.body.size_info.size_cur/100))


print(" ")
print("ITEMS")
print(" ")

for k,v in pairs(unit.inventory) do

--print(v.mode)
--enum-item Hauled 0
--enum-item Weapon 1
--enum-item Worn 2
--enum-item InBody 3
--enum-item Flask 4
--enum-item WrappedAround 5
--enum-item StuckIn 6
--enum-item InMouth 7
--enum-item Shouldered 8
--enum-item SewnInto 9

vitype=df.item_type[v.item:getType()]
--print(vitype) -- why are all items printed??
material=dfhack.matinfo.decode(v.item)
matdata=material.material.strength
vmatname=material.material.state_name.Solid
--print(vmatname, v.item.subtype.name) --WOULD ENABLE THIS BUT BUG ON QUIVERS, OTHER ITEMS W/O SUBTYPES!

vbpart=unit.body.body_plan.body_parts[v.body_part_id]
--print(vbpart.name_singular[0].value) -- why are all items printed??

if vitype=="WEAPON" then
print(vmatname, v.item.subtype.name)
v.item:calculateWeight()
effweight=unit.body.size_info.size_cur/100+v.item.weight*100+v.item.weight_fraction/10000
actweight=v.item.weight*1000+v.item.weight_fraction/1000
if v.item.subtype.flags.HAS_EDGE_ATTACK==true then
print("shear yield/fracture: ", matdata.yield.SHEAR, matdata.fracture.SHEAR)
print("Sharpness: ", v.item.sharpness)
end
print("NAME", "EDGE", "CONTACT", "PNTRT", "WEIGHT", "VEL", "MOMENTUM(+500%/-50%)")
for kk,vv in pairs(v.item.subtype.attacks) do
vvel=unit.body.size_info.size_base * curstrength * vv.velocity_mult/1000/effweight/1000
vmom=vvel*actweight/1000+1
vedge="blunt"
vcut=""
if vv.edged==true then
vedge="edged"
vcut=100
end
print(vv.verb_2nd, vedge, vv.contact, vv.penetration, actweight/1000, math.floor(vvel), math.floor(vmom))
end
actvol=v.item:getVolume()
print("Blunt deflect if layer weight more than:", actvol * matdata.yield.IMPACT / 100 / 500 / 1000)

else
if v.mode==1 then
--item held in hands treated as misc weapon
--1000 velocity mod, power math for contact and penetration
print(vmatname, "(misc weapon)") --v.item.subtype.name quiver bug
actvol=v.item:getVolume()
v.item:calculateWeight()
actweight=v.item.weight*1000+v.item.weight_fraction/1000
effweight=unit.body.size_info.size_cur/100+v.item.weight*100+v.item.weight_fraction/10000
misccontact=math.floor(actvol ^ 0.666)
miscpene=math.floor((actvol*10000) ^ 0.333)
print("NAME", "EDGE", "CONTACT", "PNTRT", "WEIGHT", "VEL", "MOMENTUM(+500%/-50%)")
vvel=unit.body.size_info.size_base * curstrength/effweight/1000
vmom=vvel*actweight/1000+1
vedge="blunt"
print("strike", vedge, misccontact, miscpene, actweight/1000, math.floor(vvel), math.floor(vmom))
print("Blunt deflect if layer weight more than:", actvol * matdata.yield.IMPACT / 100 / 500 / 1000)
print(" ")
end
end


if vitype=="ARMOR" or vitype=="HELM" or vitype=="GLOVES" or vitype=="SHOES" or vitype=="PANTS" then
print(vmatname, v.item.subtype.name)
actvol=v.item:getVolume()
v.item:calculateWeight()
actweight=v.item.weight*1000+v.item.weight_fraction/1000
vbca=actvol*matdata.yield.IMPACT/100/500/10
vbcb=actvol*(matdata.fracture.IMPACT-matdata.yield.IMPACT)/100/500/10
vbcc=actvol*(matdata.fracture.IMPACT-matdata.yield.IMPACT)/100/500/10
deduct=vbca/10
if matdata.strain_at_yield.IMPACT >= 50000 or v.item.subtype.props.flags.STRUCTURAL_ELASTICITY_WOVEN_THREAD==true or

v.item.subtype.props.flags.STRUCTURAL_ELASTICITY_CHAIN_METAL==true or

v.item.subtype.props.flags.STRUCTURAL_ELASTICITY_CHAIN_ALL==true then
vbcb=0
vbcc=0
end
print("Full contact blunt momentum resist: ", math.floor(vbca+vbcb+vbcc))
print("Contact 10 blunt momentum resist: ", math.floor((vbca+vbcb+vbcc)*10/actvol))
print("Unbroken momentum deduction (full,10): ", math.floor(deduct), math.floor(deduct*10/actvol))
print("Volume/contact area/penetration: ", actvol)
print("Weight: ", actweight/1000)
vshyre=matdata.yield.SHEAR
vshfre=matdata.fracture.SHEAR
if v.item.subtype.props.flags.STRUCTURAL_ELASTICITY_WOVEN_THREAD==true and vmatname ~= "leather" then
if vshyre>20000 then vshyre=20000 end
if vshfre>30000 then vshfre=30000 end
end
print("shear yield/fracture: ", vshyre, vshfre)
end

print(" ")
end    --end of unit inventory loop


print("BODY PART ATTACKS")
print("NAME", "EDGE", "SIZE", "CONTACT", "PNTRT", "WEIGHT", "VEL", "MOMENTUM(+500%/-50%)")

for k,v in pairs(unit.body.body_plan.attacks) do
attackpart=unit.body.body_plan.body_parts[v.body_part_idx[0]]
if v.tissue_layer_idx[0] == -1 then
--normal no chosen tissue
layerdata=attackpart.layers[#attackpart.layers-1] --is it always the last layer???
else
--scratch nails etc.
layerdata=attackpart.layers[v.tissue_layer_idx[0]]
end
tisdata=race.tissue[layerdata.tissue_id]
material=dfhack.matinfo.decode(tisdata.mat_type,tisdata.mat_index)
matdata=material.material.strength

sumrelsize=0
for kk,vv in pairs(v.body_part_idx) do
sumrelsize=sumrelsize + unit.body.body_plan.body_parts[vv].relsize
end

partsize = math.floor(unit.body.size_info.size_cur * sumrelsize / unit.body.body_plan.total_relsize)
contact = math.floor((partsize ^ 0.666) * v.contact_perc/100)
attpene = math.floor(partsize * v.penetration_perc/100)
partweight = math.floor(partsize * material.material.solid_density / 100)
vvel = 100 * curstrength / 1000 * v.velocity_modifier / 1000
vmom = vvel * partweight / 1000 + 1

vedge="blunt"
if v.flags.edge==true then
vedge="edged"
end
print(v.name, vedge, partsize, contact, attpene, partweight/1000, math.floor(vvel), math.floor(vmom),

material.material.state_name.Solid)
if v.flags.edge==true then
print(" "," ","shear yield/fracture: ", matdata.yield.SHEAR, matdata.fracture.SHEAR)
end
--print(" ")
end


print(" ")
print("BODY PART DEFENSE")
print("Volume/Contact/Thickness/Material/Blunt_Momentum_Resist/Shear_Yield/Frac")

for k,v in pairs(unit.body.body_plan.body_parts) do
if (v.flags.SMALL==false and v.flags.INTERNAL==false) or v.flags.TOTEMABLE==true or false then

partsize = math.floor(unit.body.size_info.size_base * v.relsize / unit.body.body_plan.total_relsize)
partthick = math.floor((partsize * 10000) ^ 0.333)
contact = math.floor(partsize ^ 0.666)

print(v.name_singular[0].value)

for kk,vv in pairs(v.layers) do

tisdata=race.tissue[vv.tissue_id]
layername = vv.layer_name

material=dfhack.matinfo.decode(tisdata.mat_type,tisdata.mat_index)
--tissue has a mat_state, could it name properly
matdata=material.material.strength

modpartfraction= vv.part_fraction

if tisdata.flags.THICKENS_ON_ENERGY_STORAGE == true then
modpartfraction = unit.counters2.stored_fat * modpartfraction / 2500 / 100
end

if tisdata.flags.THICKENS_ON_STRENGTH == true then
modpartfraction = curstrength * modpartfraction / 1000
end

layervolume = math.floor(partsize * modpartfraction / v.fraction_total)
layerthick = math.floor(partthick * modpartfraction / v.fraction_total)
if layervolume == 0 then
layervolume = 1
end
if layerthick == 0 then
layerthick = 1
end

vbca=layervolume*matdata.yield.IMPACT/100/500/10
vbcb=layervolume*(matdata.fracture.IMPACT-matdata.yield.IMPACT)/100/500/10
vbcc=layervolume*(matdata.fracture.IMPACT-matdata.yield.IMPACT)/100/500/10
deduct= math.floor(vbca/10)
if matdata.strain_at_yield.IMPACT >= 50000 then
vbcb=0
vbcc=0
end
fullbmr= math.floor(vbca+vbcb+vbcc)

print(" ",vv.layer_name, layervolume, contact,

layerthick,material.material.state_name.Solid,fullbmr,matdata.yield.SHEAR,matdata.fracture.SHEAR)

end
end
end

--- End code ---

I think it needs a better UI and way of displaying the output.

Navigation

[0] Message Index

[#] Next page

Go to full version