Someone asked me if I could offer an elemental damage system (fire, cold, poison, energy, physical) that was complete for both weapon and spell based combat... so here it is:
Step 1: Do NOT enable the COMBAT_USE_RESISTANCE or COMBAT_SPECIALDAMAGE flags in the sphere.ini file.
Step 2: Make sure all monsters have appropriate "resistances" set in their @Create triggers. For example:
Code:
RESCOLD={15 25}
RESENERGY={15 25}
RESFIRE={15 25}
RESPOISON={15 25}
These numbers are percentages of resistance to each type of damage, and ideally they should add up to less than 100. If the total of these 4 settings is less than 100, the remainder is considered resistance to physical damage.
Step 3: To make a monster cause elemental damage when they hit someone, add these tags to their @Create trigger. For example:
Code:
TAG.ColdDamagePercent=20
TAG.EnergyDamagePercent=20
TAG.FireDamagePercent=20
TAG.PoisonDamagePercent=20
TAG.PhysicalDamagePercent=20
Note, like the resistances, these are also percentages and should not exceed 100. Also, if they add up to less than 100, the remainder is considered physical.
Each monster (and player) needs to have the following event code:
Code:
[FUNCTION f_combatlog]
IF (<EVAL <DEF.CombatLogging>> >= <ARGV[0]>)
SERV.LOG <ARGV[1]>
ENDIF
[DEFNAME combat_system]
// To enable higher and higher logging of the combat system, set this number as high as 4
CombatLogging=0
[EVENTS e_combat_effects_npcs]
// This event is installed on all players and NPCs
// The combat modifier tags are all assumed to be on the player/NPC
ON=@GetHit
//
// Default is the person getting hit
// SRC is the source of the damage
// ARGN1 is the amount of damage to be dealt. This value can be rewritten.
// This is before AR and other variables
// ARGN2 is the type of damage dealt
//
// NOTE: ARGN1 is the max possible damage to be dealt ... the target armor
// and other possible damage reducing effects will alter the actual
// damage result.
//Prevent damage to self... especially since elementals drop fire and walk into it all the time!
IF (<UID>==<SRC>) && !(<ARGN2> & <DEF.dam_god>)
f_CombatLog 1 "GetHit: <SRC.NAME>'s (<SRC>) damage to self was blocked."
RETURN 1
ENDIF
// //Prevent damage when attacker is inside a guarded zone and defender is out
// IF (<SRC.REGION.FLAGS> & region_flag_guarded) && !(<REGION.FLAGS> & region_flag_guarded)
// f_CombatLog 1 "GetHit: damage blocked due to region flag mismatch."
// RETURN 0
// ENDIF
// //Show damage your pet does as though you did the damage (FIXME: this stopped working!)
// IF (<SRC.OWNER>)
// TRYSRC <SRC.OWNER> SRC.SENDPACKET 0bf W11 W022 01 D<UID> <EVAL <ARGN1>>
// ENDIF
f_CombatLog 1 "GetHit Begins: <NAME>(<UID>) gets hit by <SRC.NAME>(<SRC>) using a <ARGO.NAME>(<ARGO.UID>)"
f_CombatLog 1 "- Before Mod: Max Damage=<EVAL <ARGN1>> Type=<HVAL <ARGN2>> Difficulty=<EVAL <ARGN3>>"
//Pass godlike damage on without processing it:
IF (<ARGN2> & <DEF.dam_god>)
f_CombatLog 1 "GetHit: God damage: <NAME>(<UID>) gets hit for <ARGN1> damage by <SRC.NAME>(<SRC>) using a <ARGO.NAME>(<ARGO.UID>)"
RETURN 0
ENDIF
//Handle damage resulting from spells (this assumes the SPELL definitions have SPELLFLAG_HARM and SPELLFLAG_DAMAGE FLAGS properly set)
IF (<ARGN2> & <DEF.dam_magic>)
f_CombatLog 2 "-- Magic damage detected"
ARGN2=<DEF.dam_magic>
LOCAL.TotalMagicDamage=<ARGN1>
ELSE
f_CombatLog 2 "-- No magic damage detected"
IF (<ARGN2> & <DEF.dam_physical>) || (<ARGN2> & <DEF.dam_general>) || (<ARGN2> & <DEF.dam_slash>) || (<ARGN2> & <DEF.dam_pierce>)
LOCAL.TotalPhysicalDamage=<ARGN1>
ENDIF
IF (<ARGN2> & <DEF.dam_poison>) || (<ARGN2> & <DEF.dam_acidic>)
LOCAL.TotalPoisonDamage=<ARGN1>
ENDIF
IF (<ARGN2> & <DEF.dam_fire>)
LOCAL.TotalFireDamage=<ARGN1>
ENDIF
IF (<ARGN2> & <DEF.dam_lightning>) || (<ARGN2> & <DEF.dam_drain>) || (<ARGN2> & <DEF.dam_energy>)
LOCAL.TotalEnergyDamage=<ARGN1>
ENDIF
IF (<ARGN2> & <DEF.dam_cold>) || (<ARGN2> & <DEF.dam_cold_old>) || (<ARGN2> & <DEF.dam_cold_new>)
LOCAL.TotalColdDamage=<ARGN1>
ENDIF
ARGN2=<DEF.dam_physical>
LOCAL.TotalDamagePercent=<EVAL <LOCAL.TotalPhysicalDamage>+<LOCAL.TotalPoisonDamage>+<LOCAL.TotalFireDamage>+<LOCAL.TotalEnergyDamage>+<LOCAL.TotalColdDamage>+<LOCAL.TotalMagicDamage>>
IF (<LOCAL.TotalPhysicalDamage>)
LOCAL.TotalPhysicalDamage=<EVAL (<LOCAL.TotalPhysicalDamage> * <LOCAL.TotalPhysicalDamage>) / <LOCAL.TotalDamagePercent>>
ENDIF
IF (<LOCAL.TotalFireDamage>)
LOCAL.TotalFireDamage=<EVAL (<LOCAL.TotalFireDamage> * <LOCAL.TotalFireDamage>) / <LOCAL.TotalDamagePercent>>
ENDIF
IF (<LOCAL.TotalColdDamage>)
LOCAL.TotalColdDamage=<EVAL (<LOCAL.TotalColdDamage> * <LOCAL.TotalColdDamage>) / <LOCAL.TotalDamagePercent>>
ENDIF
IF (<LOCAL.TotalEnergyDamage>)
LOCAL.TotalEnergyDamage=<EVAL (<LOCAL.TotalEnergyDamage> * <LOCAL.TotalEnergyDamage>) / <LOCAL.TotalDamagePercent>>
ENDIF
IF (<LOCAL.TotalPoisonDamage>)
LOCAL.TotalPoisonDamage=<EVAL (<LOCAL.TotalPoisonDamage> * <LOCAL.TotalPoisonDamage>) / <LOCAL.TotalDamagePercent>>
ENDIF
LOCAL.TotalDamage=<EVAL <LOCAL.TotalPhysicalDamage>+<LOCAL.TotalFireDamage>+<LOCAL.TotalColdDamage>+<LOCAL.TotalEnergyDamage>+<LOCAL.TotalPoisonDamage>>
f_CombatLog 3 "--> If damage flags are used, damage gets spread like this:"
f_CombatLog 3 "--> Total Physical damage: <dLOCAL.TotalPhysicalDamage>"
f_CombatLog 3 "--> Total Fire damage: <dLOCAL.TotalFireDamage>"
f_CombatLog 3 "--> Total Cold damage: <dLOCAL.TotalColdDamage>"
f_CombatLog 3 "--> Total Energy damage: <dLOCAL.TotalEnergyDamage>"
f_CombatLog 3 "--> Total Poison damage: <dLOCAL.TotalPoisonDamage>"
f_CombatLog 3 "--> Grand Total damage: <dLOCAL.TotalDamage>"
//END Calculations using damage flags...
//BEGIN Calculations using attacker tags...
LOCAL.TotalPhysicalDamage=
LOCAL.TotalPoisonDamage=
LOCAL.TotalFireDamage=
LOCAL.TotalEnergyDamage=
LOCAL.TotalColdDamage=
IF (<SRC.TAG0.PhysicalDamagePercent>)
LOCAL.TotalPhysicalDamage=<SRC.TAG.PhysicalDamagePercent>
ENDIF
IF (<SRC.TAG0.PoisonDamagePercent>)
LOCAL.TotalPoisonDamage=<SRC.TAG.PoisonDamagePercent>
ENDIF
IF (<SRC.TAG0.FireDamagePercent>)
LOCAL.TotalFireDamage=<SRC.TAG0.FireDamagePercent>
ENDIF
IF (<SRC.TAG0.EnergyDamagePercent>)
LOCAL.TotalEnergyDamage=<SRC.TAG0.EnergyDamagePercent>
ENDIF
IF (<SRC.TAG0.ColdDamagePercent>)
LOCAL.TotalColdDamage=<SRC.TAG0.ColdDamagePercent>
ENDIF
LOCAL.TotalDamagePercent=<EVAL <LOCAL.TotalPhysicalDamage>+<LOCAL.TotalPoisonDamage>+<LOCAL.TotalFireDamage>+<LOCAL.TotalEnergyDamage>+<LOCAL.TotalColdDamage>+<LOCAL.TotalMagicDamage>>
IF (<LOCAL.TotalDamagePercent> < 100)
LOCAL.TotalPhysicalDamage=<EVAL 100 - <LOCAL.TotalDamagePercent>>
LOCAL.TotalDamagePercent += <EVAL 100 - <LOCAL.TotalDamagePercent>>
ENDIF
IF (<LOCAL.TotalPhysicalDamage>)
LOCAL.TotalPhysicalDamage=<EVAL (<ARGN1> * 100 * <LOCAL.TotalPhysicalDamage>) / (<LOCAL.TotalDamagePercent> * 100)>
ENDIF
IF (<LOCAL.TotalFireDamage>)
LOCAL.TotalFireDamage=<EVAL (<ARGN1> * 100 * <LOCAL.TotalFireDamage>) / (<LOCAL.TotalDamagePercent> * 100)>
ENDIF
IF (<LOCAL.TotalColdDamage>)
LOCAL.TotalColdDamage=<EVAL (<ARGN1> * 100 * <LOCAL.TotalColdDamage>) / (<LOCAL.TotalDamagePercent> * 100)>
ENDIF
IF (<LOCAL.TotalEnergyDamage>)
LOCAL.TotalEnergyDamage=<EVAL (<ARGN1> * 100 * <LOCAL.TotalEnergyDamage>) / (<LOCAL.TotalDamagePercent> * 100)>
ENDIF
IF (<LOCAL.TotalPoisonDamage>)
LOCAL.TotalPoisonDamage=<EVAL (<ARGN1> * 100 * <LOCAL.TotalPoisonDamage>) / (<LOCAL.TotalDamagePercent> * 100)>
ENDIF
LOCAL.TotalDamage=<EVAL <LOCAL.TotalPhysicalDamage>+<LOCAL.TotalFireDamage>+<LOCAL.TotalColdDamage>+<LOCAL.TotalEnergyDamage>+<LOCAL.TotalPoisonDamage>+<LOCAL.TotalMagicDamage>>
f_CombatLog 3 "--> If attacker tags are used, damage gets spread like this:"
f_CombatLog 3 "--> Total Physical damage: <dLOCAL.TotalPhysicalDamage>"
f_CombatLog 3 "--> Total Fire damage: <dLOCAL.TotalFireDamage>"
f_CombatLog 3 "--> Total Cold damage: <dLOCAL.TotalColdDamage>"
f_CombatLog 3 "--> Total Energy damage: <dLOCAL.TotalEnergyDamage>"
f_CombatLog 3 "--> Total Poison damage: <dLOCAL.TotalPoisonDamage>"
f_CombatLog 3 "--> Grand Total damage: <dLOCAL.TotalDamage>"
//END Calculations using damage flags...
ENDIF
IF (<LOCAL.TotalMagicDamage>)
//FIXME: Allow magic damage to be resisted
LOCAL.Chance=<EVAL (<MAGICRESISTANCE> - <SRC.MAGERY>) / 10>
IF (<LOCAL.Chance> < <EVAL <DEF.MinMagicResistChance>>)
LOCAL.Chance=<EVAL <DEF.MinMagicResistChance>>
ENDIF
IF (<R100> < <EVAL <LOCAL.Chance>>)
SRC.SYSMESSAGE @color_text <NAME> resisted the magic in your attack!
f_CombatLog 2 "-- Magic resistance skill effect."
f_CombatLog 3 "--- Initial Magic damage: <dLOCAL.TotalMagicDamage> (reduced by <dLOCAL.Chance> percent)"
LOCAL.TotalMagicDamage -= <EVAL (<LOCAL.TotalMagicDamage> * <LOCAL.Chance>) / 100>
f_CombatLog 3 "--- Resulting Magic damage: <dLOCAL.TotalMagicDamage>"
ENDIF
ELSE
IF (<LOCAL.TotalPhysicalDamage>)
IF (<AR> > 70) && !(<NPC>)
LOCAL.EffectivePhysicalResist=70
f_CombatLog 3 "--- Effective Physical Resist dropped from <AR> to <dLOCAL.EffectivePhysicalResist>"
ELSE
LOCAL.EffectivePhysicalResist=<AR>
ENDIF
f_CombatLog 3 "--- Initial Physical damage: <dLOCAL.TotalPhysicalDamage> (reduced by <dLOCAL.EffectivePhysicalResist> percent)"
LOCAL.TotalPhysicalDamage -= <EVAL (<LOCAL.TotalPhysicalDamage> * <LOCAL.EffectivePhysicalResist>) / 100>
f_CombatLog 3 "--- Resulting Physical damage: <dLOCAL.TotalPhysicalDamage>"
IF (<TAG0.ReflectPhysicalDamage>)
IF (<TAG0.ReflectPhysicalDamage> > <EVAL <DEF.MaxReflectPhysicalDamage>>)
LOCAL.Chance=<DEF.MaxReflectPhysicalDamage>
ELSE
LOCAL.Chance=<TAG0.ReflectPhysicalDamage>
ENDIF
LOCAL.ReflectResult=<EVAL (<LOCAL.TotalPhysicalDamage> * <LOCAL.Chance>) / 100>
IF (<LOCAL.ReflectResult> > 0)
f_CombatLog 2 "-- ReflectPhysicalDamage effect: <dLOCAL.ReflectResult> damage back to <SRC.NAME>"
SRC.DAMAGE <LOCAL.ReflectResult> <DEF.dam_physical> <UID>
ENDIF
//FIXME: Test what happens when both combatants have reflection!!!
ENDIF
ENDIF
IF (<LOCAL.TotalFireDamage>)
IF (<RESFire> > 70) && !(<NPC>)
LOCAL.EffectiveFireResist=70
f_CombatLog 3 "--- Effective Fire Resist dropped from <dRESFire> to <dLOCAL.EffectiveFireResist>"
ELSE
LOCAL.EffectiveFireResist=<RESFire>
ENDIF
f_CombatLog 3 "--- Initial Fire damage: <dLOCAL.TotalFireDamage> (reduced by <dLOCAL.EffectiveFireResist> percent)"
LOCAL.TotalFireDamage -= <EVAL (<LOCAL.TotalFireDamage> * <LOCAL.EffectiveFireResist>) / <LOCAL.TotalDamagePercent>>
f_CombatLog 3 "--- Resulting Fire damage: <dLOCAL.TotalFireDamage>"
ENDIF
IF (<LOCAL.TotalColdDamage>)
IF (<RESCold> > 70) && !(<NPC>)
LOCAL.EffectiveColdResist=70
f_CombatLog 3 "--- Effective Cold Resist dropped from <dRESCold> to <dLOCAL.EffectiveColdResist>"
ELSE
LOCAL.EffectiveColdResist=<RESCold>
ENDIF
f_CombatLog 3 "--- Initial Cold damage: <dLOCAL.TotalColdDamage> (reduced by <dLOCAL.EffectiveColdResist> percent)"
LOCAL.TotalColdDamage -= <EVAL (<LOCAL.TotalColdDamage> * <LOCAL.EffectiveColdResist>) / <LOCAL.TotalDamagePercent>>
f_CombatLog 3 "--- Resulting Cold damage: <dLOCAL.TotalColdDamage>"
ENDIF
IF (<LOCAL.TotalEnergyDamage>)
IF (<RESEnergy> > 70) && !(<NPC>)
LOCAL.EffectiveEnergyResist=70
IF (<RESEnergy> > 75) && (<f_isElf>)
LOCAL.EffectiveEnergyResist=75
ELSE
LOCAL.EffectiveEnergyResist=<RESEnergy>
ENDIF
f_CombatLog 3 "--- Effective Energy Resist dropped from <dRESEnergy> to <dLOCAL.EffectiveEnergyResist>"
ELSE
LOCAL.EffectiveEnergyResist=<RESEnergy>
ENDIF
f_CombatLog 3 "--- Initial Energy damage: <dLOCAL.TotalEnergyDamage> (reduced by <dLOCAL.EffectiveEnergyResist> percent)"
LOCAL.TotalEnergyDamage -= <EVAL (<LOCAL.TotalEnergyDamage> * <LOCAL.EffectiveEnergyResist>) / <LOCAL.TotalDamagePercent>>
f_CombatLog 3 "--- Resulting Energy damage: <dLOCAL.TotalEnergyDamage>"
ENDIF
IF (<LOCAL.TotalPoisonDamage>)
IF (<RESPoison> > 70) && !(<NPC>)
LOCAL.EffectivePoisonResist=70
f_CombatLog 3 "--- Effective Poison Resist dropped from <dRESPoison> to <dLOCAL.EffectivePoisonResist>"
ELSE
LOCAL.EffectivePoisonResist=<RESPoison>
ENDIF
f_CombatLog 3 "--- Initial Poison damage: <dLOCAL.TotalPoisonDamage> (reduced by <dLOCAL.EffectivePoisonResist> percent)"
LOCAL.TotalPoisonDamage -= <EVAL (<LOCAL.TotalPoisonDamage> * <LOCAL.EffectivePoisonResist>) / <LOCAL.TotalDamagePercent>>
f_CombatLog 3 "--- Resulting Poison damage: <dLOCAL.TotalPoisonDamage>"
ENDIF
ENDIF
LOCAL.TotalDamage=<EVAL <LOCAL.TotalPhysicalDamage>+<LOCAL.TotalFireDamage>+<LOCAL.TotalColdDamage>+<LOCAL.TotalEnergyDamage>+<LOCAL.TotalPoisonDamage>+<LOCAL.TotalMagicDamage>>
f_CombatLog 3 "--> Damage after modifications are made:"
f_CombatLog 3 "--> Total Physical damage: <dLOCAL.TotalPhysicalDamage>"
f_CombatLog 3 "--> Total Fire damage: <dLOCAL.TotalFireDamage>"
f_CombatLog 3 "--> Total Cold damage: <dLOCAL.TotalColdDamage>"
f_CombatLog 3 "--> Total Energy damage: <dLOCAL.TotalEnergyDamage>"
f_CombatLog 3 "--> Total Poison damage: <dLOCAL.TotalPoisonDamage>"
f_CombatLog 3 "--> Total Magic damage: <dLOCAL.TotalMagicDamage>"
f_CombatLog 3 "--> Grand Total damage: <dLOCAL.TotalDamage>"
ARGN1=<EVAL <LOCAL.TotalDamage>>
f_CombatLog 1 "- After Mods: Max Damage=<EVAL <ARGN1>> Type=<HVAL <ARGN2>> Difficulty=<EVAL <ARGN3>>"