Updated gl_compute_damage example script to work as 1:1 reflection of vanilla damage formula

- In hope that this will make it easier for someone to write his own damage formula
This commit is contained in:
phobos2077
2023-05-14 22:50:33 +02:00
parent 493003af54
commit e6e941e14c
3 changed files with 91 additions and 35 deletions
@@ -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;
+10 -1
View File
@@ -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)
+1
View File
@@ -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 */