diff --git a/artifacts/example_mods/ComputeDamage/gl_compute_damage.ssl b/artifacts/example_mods/ComputeDamage/gl_compute_damage.ssl index 44d32934..02ef3225 100644 --- a/artifacts/example_mods/ComputeDamage/gl_compute_damage.ssl +++ b/artifacts/example_mods/ComputeDamage/gl_compute_damage.ssl @@ -1,28 +1,61 @@ /* - Example algorithm of how the game engine calculates combat damage + This script re-implements damage calculation of the original game. + Use it to implement your own damage formula! */ -#include "..\headers\define.h" -#include "..\headers\command.h" -#include "..\headers\sfall\sfall.h" -#include "..\headers\sfall\define_extra.h" +#include "../sfall/define_lite.h" +#include "../sfall/define_extra.h" +#include "../sfall/sfall.h" procedure start; -procedure compute_damage_F2; -procedure item_w_damage_hook; +procedure combatdamage_handler; +procedure itemdamage_handler; procedure item_w_subtype(variable weapon, variable hit_mode); procedure get_ammo_value(variable weapon, variable param); -variable item_w_damage; +variable + item_damage_min, + item_damage_max, + item_damage_weapon, + item_damage_attacker; procedure start begin if game_loaded then begin - register_hook_proc(HOOK_COMBATDAMAGE, compute_damage_F2); - register_hook_proc(HOOK_ITEMDAMAGE, item_w_damage_hook); + register_hook_proc(HOOK_COMBATDAMAGE, combatdamage_handler); + register_hook_proc(HOOK_ITEMDAMAGE, itemdamage_handler); end end -procedure compute_damage_F2 begin +/** + HOOK_COMBATDAMAGE + + Runs when: + + Game calculates how much damage each target will get. This includes primary target as well as all extras (explosions and bursts). This happens BEFORE the actual attack animation. + AI decides whether it is safe to use area attack (burst, grenades), if he might hit friendlies. + Does not run for misses, or non-combat damage like dynamite explosions. + + Critter arg0 - The target + Critter arg1 - The attacker + int arg2 - The amount of damage to the target + int arg3 - The amount of damage to the attacker + int arg4 - The special effect flags for the target (use bwand DAM_* to check specific flags) + int arg5 - The special effect flags for the attacker (use bwand DAM_* to check specific flags) + int arg6 - The weapon used in the attack + int arg7 - The bodypart that was struck + int arg8 - Damage Multiplier (this is divided by 2, so a value of 3 does 1.5x damage, and 8 does 4x damage. Usually it's 2; for critical hits, the value is taken from the critical table; with Silent Death perk and the corresponding attack conditions, the value will be doubled) + int arg9 - Number of bullets actually hit the target (1 for melee attacks) + int arg10 - The amount of knockback to the target + int arg11 - Attack Type (see ATKTYPE_* constants) + mixed arg12 - computed attack data (see C_ATTACK_* for offsets and use get/set_object_data functions to get/set the data) + + int ret0 - The damage to the target + int ret1 - The damage to the attacker + int ret2 - The special effect flags for the target + int ret3 - The special effect flags for the attacker + int ret4 - The amount of knockback to the target + */ +procedure combatdamage_handler begin variable dmg_type, weapon_perk, dmg_thresh, dmg_resist, weapon_subtype, bonus_ranged, difficulty, i, dmg_mult, dmg_div, damage; variable target, flags, knockback, amount; @@ -40,6 +73,11 @@ procedure compute_damage_F2 begin amountKnockback := get_sfall_arg, hit_mode := get_sfall_arg; + if (ctdSource != item_damage_attacker or weapon != item_damage_weapon) then begin + debug_msg("! ERROR ! compute_damage: Expected attacker and weapon differs!"); + return; + end + if (flagsSource bwand DAM_HIT) then begin target := ctdTarget; flags := flagsTarget; @@ -77,7 +115,7 @@ procedure compute_damage_F2 begin end weapon_subtype := item_w_subtype(weapon, hit_mode); // item_w_subtype_ - if (ctdSource != dude_obj) or (weapon_subtype != WEAPON_TYPE_GUNS) then + if (ctdSource != dude_obj) or (weapon_subtype != WEAPON_TYPE_RANGED) then bonus_ranged := 0; else bonus_ranged := has_trait(TRAIT_PERK, ctdSource, PERK_bonus_ranged_damage) * 2; @@ -100,7 +138,7 @@ procedure compute_damage_F2 begin dmg_div := get_ammo_value(weapon, PROTO_AM_DMG_DIV); // item_w_dam_div_ (Dmg mod B) for (i := 1; i <= rounds; i++) begin - damage := (item_w_damage + bonus_ranged) * dmg_mult; // item_w_damage_ (raw_damage) + damage := (random(item_damage_min, item_damage_max) + bonus_ranged) * dmg_mult; // item_w_damage_ (raw_damage) if dmg_div then damage /= dmg_div; damage := (((damage / 2) * difficulty) / 100) - dmg_thresh; @@ -156,38 +194,46 @@ procedure compute_damage_F2 begin set_sfall_return(amountKnockback); end -procedure item_w_damage_hook begin - variable - item_w_damage_min := get_sfall_arg, - item_w_damage_max := get_sfall_arg; - //weapon := get_sfall_arg, - //source := get_sfall_arg, + +/* + HOOK_ITEMDAMAGE + Runs when retrieving the damage rating of the player's used weapon. (Which may be their fists.) + + int arg0 - The default min damage + int arg1 - The default max damage + Item arg2 - The weapon used (0 if unarmed) + Critter arg3 - The critter doing the attacking + int arg4 - The type of attack + int arg5 - non-zero if this is an attack using a melee weapon + + int ret0 - Either the damage to be used, if ret1 isn't given, or the new minimum damage if it is + int ret1 - The new maximum damage +*/ +procedure itemdamage_handler begin + item_damage_min := get_sfall_arg; + item_damage_max := get_sfall_arg; + item_damage_weapon := get_sfall_arg; + item_damage_attacker := get_sfall_arg; //hit_mode := get_sfall_arg, //isMelee := get_sfall_arg; - item_w_damage := (item_w_damage_min + (item_w_damage_max - item_w_damage_min) / 2); - display_msg("item_w_damage_ = " + item_w_damage); - - set_sfall_return(item_w_damage); + display_msg("itemdamage: " + item_damage_min + "-" + item_damage_max); end procedure item_w_subtype(variable weapon, variable hit_mode) begin variable attack_mode, type := WEAPON_TYPE_UNARMED; if weapon and (hit_mode <= ATKTYPE_RWEP2) then begin - attack_mode := (get_proto_data(obj_pid(weapon), PROTO_IT_FLAGS)); - - if (hit_mode == ATKTYPE_LWEP2) or (hit_mode == ATKTYPE_RWEP2) then - attack_mode := (attack_mode bwand 0xF0) / 16; // shift 4 bits to the right - else - attack_mode := (attack_mode bwand 0x0F); - - if (attack_mode > ATKMODE_PRI_THROW) then - type := WEAPON_TYPE_GUNS; - else if (attack_mode == ATKMODE_PRI_THROW) then + attack_mode := weapon_attack_mode(obj_pid(weapon), hit_mode); + + if (attack_mode >= ATTACK_MODE_SINGLE) then + type := WEAPON_TYPE_RANGED; + else if (attack_mode == ATTACK_MODE_THROW) then type := WEAPON_TYPE_THROWN; - else if (attack_mode > ATKMODE_PRI_KICK) then + else if (attack_mode >= ATTACK_MODE_SWING) then type := WEAPON_TYPE_MELEE; + else if (attack_mode == ATTACK_MODE_NONE) then + type := WEAPON_TYPE_NONE; end return type; diff --git a/artifacts/scripting/headers/define_extra.h b/artifacts/scripting/headers/define_extra.h index b2fabb4c..e9914ea9 100644 --- a/artifacts/scripting/headers/define_extra.h +++ b/artifacts/scripting/headers/define_extra.h @@ -35,7 +35,7 @@ #define WEAPON_TYPE_UNARMED (1) #define WEAPON_TYPE_MELEE (2) #define WEAPON_TYPE_THROWN (3) -#define WEAPON_TYPE_GUNS (4) +#define WEAPON_TYPE_RANGED (4) /* Item Flags (FlagsExt in proto) */ #define HEALING_ITEM 0x04000000 // Healing Item (item will be used by NPCs for healing in combat) [sfall 4.3.1/3.8.31] @@ -502,6 +502,15 @@ #define C_ATTACK_KNOCKBACK_VALUE5 (0xB0) #define C_ATTACK_KNOCKBACK_VALUE6 (0xB4) +#define CRITICAL_VALUE_MULT (0) // This is divided by 2, so a value of 3 does 1.5x damage, and 8 does 4x damage. +#define CRITICAL_VALUE_EFFECTS (1) // This is a flag bit field (DAM_*) controlling what effects the critical causes. +#define CRITICAL_VALUE_STAT_CHECK (2) // This makes a check against a (SPECIAL) stat. Values of 2 (endurance), 5 (agility), and 6 (luck) are used, but other stats will probably work as well. A value of -1 indicates that no check is to be made. +#define CRITICAL_VALUE_STAT_MOD (3) // Affects the outcome of the stat check, if one is made. Positive values make it easier to pass the check, and negative ones make it harder. +#define CRITICAL_VALUE_FAIL_EFFECT (4) // Another bit field, using the same values as EFFECTS. If the stat check is failed, these are applied in addition to the earlier ones. +#define CRITICAL_VALUE_MSG (5) // The message to show when this critical occurs, taken from combat.msg . +#define CRITICAL_VALUE_FAIL_MSG (6) // Shown instead of Message if the stat check is failed. + + /* Playback mode defines for the soundplay function */ #define soundraw (0x80000000) // sfall flag #define Stereo8bit (soundstereo) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index dfeb7aeb..29b68cd3 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -273,6 +273,7 @@ #define weapon_attack_mode1(pid) (get_proto_data(pid, PROTO_FLAG_EXT) bwand 0x0000000F) #define weapon_attack_mode2(pid) ((get_proto_data(pid, PROTO_FLAG_EXT) bwand 0x000000F0) / 0x10) +#define weapon_attack_mode(pid, attackType) (weapon_attack_mode1(pid) if attackType == ATKTYPE_LWEP1 or attackType == ATKTYPE_RWEP1 else weapon_attack_mode2(pid)) /* SFALL_FUNCX MACROS */