diff --git a/config/fxdata/lua/aliases.lua b/config/fxdata/lua/aliases.lua index fa3335ca77..2fe946b77a 100644 --- a/config/fxdata/lua/aliases.lua +++ b/config/fxdata/lua/aliases.lua @@ -26,5 +26,6 @@ ---@alias actionpoint integer ---@alias location playersingle|actionpoint|"LAST_EVENT"|"COMBAT"|Pos3d ---@alias thing_class "Object"|"Shot"|"EffectElem"|"DeadCreature"|"Creature"|"Effect"|"EffectGen"|"Trap"|"Door"|"AmbientSnd"|"CaveIn" ----@alias effect_or_effelem_type integer|string|effect_type|effect_element_type -- I allow string here because there's to many entries for, the language server to handle +---@alias effect_or_effelem_type integer|string|effect_type|effect_element_type -- I allow string here because there's to many entries for, the language server to handle +---@alias damage_source_kind "UNKNOWN"|"LAVA"|"POWER_SLAP"|"POWER"|"DOT_SPELL"|"CREATURE"|"TRAP"|"OBJECT"|"DOOR" diff --git a/config/fxdata/lua/triggers/Builtins.lua b/config/fxdata/lua/triggers/Builtins.lua index 8d4be985ba..55d4c92bf6 100644 --- a/config/fxdata/lua/triggers/Builtins.lua +++ b/config/fxdata/lua/triggers/Builtins.lua @@ -70,15 +70,19 @@ function OnDungeonDestroyed(player) ProcessEvent("DungeonDestroyed",eventData) end ---- Called when a thing taked damage ----@param thing Thing ----@param damage integer ----@param dealing_player Player -function OnApplyDamage(thing, damage, dealing_player) +--- Called when a thing takes damage +---@param thing Thing The thing receiving damage +---@param damage integer Amount of damage dealt +---@param dealing_player Player Player responsible for the damage +---@param source_thing Thing|nil The thing that caused the damage (e.g., attacking creature, trap) +---@param source_kind damage_source_kind String identifying the damage source (e.g., "LAVA", "CREATURE", "TRAP") +function OnApplyDamage(thing, damage, dealing_player, source_thing, source_kind) local eventData = {} eventData.thing = thing eventData.damage = damage eventData.dealing_player = dealing_player + eventData.source_thing = source_thing + eventData.source_kind = source_kind ProcessEvent("ApplyDamage",eventData) end diff --git a/config/fxdata/lua/triggers/Events.lua b/config/fxdata/lua/triggers/Events.lua index 1fce4b2a6b..809d51d8f2 100644 --- a/config/fxdata/lua/triggers/Events.lua +++ b/config/fxdata/lua/triggers/Events.lua @@ -117,14 +117,22 @@ end ---Triggers when a thing takes damage ---@param action function|string the function to call when the event happens ---@param thing? Thing the unit that triggers the event +---@param source_thing? Thing the thing that cause the trigger +---@param source_kind? damage_source_kind kind of damage ---@return table -function RegisterThingDamageEvent(action, thing) - local trigData = {thing = thing} +function RegisterThingDamageEvent(action, thing, source_thing, source_kind) + local trigData = {thing = thing,source_thing = source_thing, source_kind = source_kind} local trigger = CreateTrigger("ApplyDamage",action,trigData) if thing then TriggerAddCondition(trigger, function(eventData,triggerData) return eventData.thing == triggerData.thing end) end + if source_thing then + TriggerAddCondition(trigger, function(eventData,triggerData) return eventData.source_thing == triggerData.source_thing end) + end + if source_kind then + TriggerAddCondition(trigger, function(eventData,triggerData) return eventData.source_kind == triggerData.source_kind end) + end return trigger end diff --git a/src/console_cmd.c b/src/console_cmd.c index bfd1541d24..d4ad74cbef 100644 --- a/src/console_cmd.c +++ b/src/console_cmd.c @@ -65,6 +65,7 @@ #include "thing_objects.h" #include "thing_navigate.h" #include "thing_physics.h" +#include "thing_creature.h" #include "version.h" #include "frontmenu_ingame_map.h" #include @@ -1839,7 +1840,7 @@ TbBool cmd_freeze_creature(PlayerNumber plyr_idx, char * args) } thing_play_sample(thing, 50, NORMAL_PITCH, 0, 3, 0, 4, FULL_LOUDNESS); // Not sure how to handle this yet, for now simply hardcode the intended spell kind with a number. - apply_spell_effect_to_thing(thing, 3, 8, plyr_idx); // 3 was 'SplK_Freeze' in the enum. + apply_spell_effect_to_thing(thing, 3, 8, plyr_idx, INVALID_THING, DSK_Power); // 3 was 'SplK_Freeze' in the enum. return true; } @@ -1857,7 +1858,7 @@ TbBool cmd_slow_creature(PlayerNumber plyr_idx, char * args) } thing_play_sample(thing, 50, NORMAL_PITCH, 0, 3, 0, 4, FULL_LOUDNESS); // Not sure how to handle this yet, for now simply hardcode the intended spell kind with a number. - apply_spell_effect_to_thing(thing, 12, 8, plyr_idx); // 12 was 'SplK_Slow' in the enum. + apply_spell_effect_to_thing(thing, 12, 8, plyr_idx, INVALID_THING, DSK_Power); // 12 was 'SplK_Slow' in the enum. return true; } diff --git a/src/creature_control.h b/src/creature_control.h index decd2475c7..7ff8c1cd24 100644 --- a/src/creature_control.h +++ b/src/creature_control.h @@ -133,6 +133,7 @@ struct CastedSpellData { GameTurnDelta duration; CrtrExpLevel caster_level; PlayerNumber caster_owner; + ThingIndex caster_thing_idx; }; struct CreatureControl { diff --git a/src/creature_states_pray.c b/src/creature_states_pray.c index 154d01a059..5951628669 100644 --- a/src/creature_states_pray.c +++ b/src/creature_states_pray.c @@ -377,7 +377,7 @@ void apply_spell_effect_to_players_creatures(PlayerNumber plyr_idx, ThingModel c // Thing list loop body if (creature_matches_model(thing,crmodel)) { - apply_spell_effect_to_thing(thing, spl_idx, overchrg, plyr_idx); + apply_spell_effect_to_thing(thing, spl_idx, overchrg, plyr_idx, INVALID_THING, DSK_None); } // Thing list loop body ends k++; diff --git a/src/lua_api.c b/src/lua_api.c index 1b20af88c6..3615b89169 100644 --- a/src/lua_api.c +++ b/src/lua_api.c @@ -25,6 +25,7 @@ #include "thing_effects.h" #include "magic_powers.h" +#include "lua_api.h" #include "lua_base.h" #include "lua_params.h" #include "lua_api_lens.h" diff --git a/src/lua_api_things.c b/src/lua_api_things.c index eb9b22fab1..aef6fbdfd3 100644 --- a/src/lua_api_things.c +++ b/src/lua_api_things.c @@ -213,7 +213,7 @@ static int thing_set_field(lua_State *L) { } else if (strcmp(key, "health") == 0) { thing->health = luaL_checkinteger(L, 3); - } else if (strcmp(key, "pos") == 0) + } else if (strcmp(key, "pos") == 0) { struct Coord3d pos; luaL_checkCoord3d(L, 3, &pos); @@ -569,7 +569,7 @@ static const struct luaL_Reg thing_methods[] = { {"stun", lua_stun_creature}, {"delete", lua_delete_thing}, {"isValid", lua_is_valid}, - + {"transfer" ,lua_Transfer_creature }, {"level_up" ,lua_Level_up_creature }, {"teleport" ,lua_Teleport_creature }, @@ -614,4 +614,5 @@ void Thing_register(lua_State *L) { // Pop the metatable lua_pop(L, 1); -} \ No newline at end of file +} + diff --git a/src/lua_triggers.c b/src/lua_triggers.c index 45f4a09fe5..5873a52762 100644 --- a/src/lua_triggers.c +++ b/src/lua_triggers.c @@ -184,7 +184,7 @@ void lua_on_creature_rebirth(struct Thing* crtng) } -void lua_on_apply_damage_to_thing(struct Thing *thing, HitPoints dmg, PlayerNumber dealing_plyr_idx) +void lua_on_apply_damage_to_thing(struct Thing *thing, HitPoints dmg, PlayerNumber dealing_plyr_idx, struct Thing *tngsrc, DamageSourceKind source_kind) { SYNCDBG(6,"Starting"); lua_getglobal(Lvl_script, "OnApplyDamage"); @@ -193,8 +193,10 @@ void lua_on_apply_damage_to_thing(struct Thing *thing, HitPoints dmg, PlayerNumb lua_pushThing(Lvl_script, thing); lua_pushinteger(Lvl_script, dmg); lua_pushPlayer(Lvl_script, dealing_plyr_idx); + lua_pushThing(Lvl_script, tngsrc); + lua_pushstring(Lvl_script, damage_source_kind_name(source_kind)); - CheckLua(Lvl_script, lua_pcall(Lvl_script, 3, 0, 0),"OnApplyDamage"); + CheckLua(Lvl_script, lua_pcall(Lvl_script, 5, 0, 0),"OnApplyDamage"); } else { diff --git a/src/lua_triggers.h b/src/lua_triggers.h index a7cbcb2905..6e30dbf9c5 100644 --- a/src/lua_triggers.h +++ b/src/lua_triggers.h @@ -19,6 +19,7 @@ #include "globals.h" #include "bflib_basics.h" +#include "thing_stats.h" #ifdef __cplusplus extern "C" { @@ -36,7 +37,7 @@ void lua_on_dungeon_destroyed(PlayerNumber plyr_idx); void lua_on_creature_death(struct Thing *crtng); void lua_on_creature_rebirth(struct Thing* crtng); void lua_on_trap_placed(struct Thing *traptng); -void lua_on_apply_damage_to_thing(struct Thing *thing, HitPoints dmg, PlayerNumber dealing_plyr_idx); +void lua_on_apply_damage_to_thing(struct Thing *thing, HitPoints dmg, PlayerNumber dealing_plyr_idx, struct Thing *tngsrc, DamageSourceKind source_kind); void lua_on_level_up(struct Thing *thing); void lua_on_slab_kind_change(MapSlabCoord slb_x, MapSlabCoord slb_y, SlabKind old_slab); void lua_on_slab_owner_change(MapSlabCoord slb_x, MapSlabCoord slb_y, PlayerNumber old_owner); diff --git a/src/magic_powers.c b/src/magic_powers.c index 67007d26b1..fb1c9ee0cc 100644 --- a/src/magic_powers.c +++ b/src/magic_powers.c @@ -613,7 +613,7 @@ void slap_creature(struct PlayerInfo *player, struct Thing *thing) if (crconf->slaps_to_kill > 0) { HitPoints slap_damage = calculate_correct_creature_max_health(thing) / crconf->slaps_to_kill; - apply_damage_to_thing_and_display_health(thing, slap_damage, player->id_number); + apply_damage_to_thing_and_display_health(thing, slap_damage, player->id_number, INVALID_THING, DSK_PowerSlap); } powerst = get_power_model_stats(PwrK_SLAP); i = cctrl->slap_turns; @@ -1386,7 +1386,7 @@ static TbResult magic_use_power_apply_spell(PowerKind power_kind, PlayerNumber p create_used_effect_or_element(&effpos, powerst->effect_id, thing->owner, 0); } thing_play_sample(thing, powerst->select_sound_idx, NORMAL_PITCH, 0, 3, 0, 2, FULL_LOUDNESS); - apply_spell_effect_to_thing(thing, powerst->spell_idx, power_level, plyr_idx); + apply_spell_effect_to_thing(thing, powerst->spell_idx, power_level, plyr_idx, INVALID_THING, DSK_None); // Special cases. if (flag_is_set(spconf->spell_flags, CSAfF_Disease)) { // Set disease_caster_plyridx if spell_idx has 'CSAfF_Disease'. diff --git a/src/main.cpp b/src/main.cpp index 7632d3c4ef..89f9525f30 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2494,7 +2494,7 @@ TngUpdateRet damage_creatures_with_physical_force(struct Thing *thing, ModTngFil } if (thing_is_creature(thing)) { - apply_damage_to_thing_and_display_health(thing, param->secondary_number, param->primary_number); + apply_damage_to_thing_and_display_health(thing, param->secondary_number, param->primary_number, INVALID_THING, DSK_Power); if ((thing->health >= 0) && !creature_is_leaving_and_cannot_be_stopped(thing)) { if (((thing->alloc_flags & TAlF_IsControlled) == 0) && !creature_is_kept_in_custody(thing)) @@ -2514,7 +2514,7 @@ TngUpdateRet damage_creatures_with_physical_force(struct Thing *thing, ModTngFil } else if (thing_is_destructible_trap(thing) > 0) { - apply_damage_to_thing(thing, param->secondary_number, param->primary_number); + apply_damage_to_thing(thing, param->secondary_number, param->primary_number, INVALID_THING, DSK_Power); return TUFRet_Modified; } return TUFRet_Unchanged; @@ -3529,7 +3529,7 @@ void gameplay_loop_timestep() exit_keeper = 1; } } - + frametime_end_measurement(Frametime_Sleep); } diff --git a/src/power_process.c b/src/power_process.c index 534aa63135..9b70103c30 100644 --- a/src/power_process.c +++ b/src/power_process.c @@ -32,6 +32,7 @@ #include "thing_objects.h" #include "thing_physics.h" #include "thing_effects.h" +#include "thing_creature.h" #include "thing_navigate.h" #include "creature_instances.h" #include "creature_states.h" @@ -214,7 +215,7 @@ void process_disease(struct Thing *creatng) && !creature_is_immune_to_spell_effect(thing, CSAfF_Disease) && (cctrl->disease_caster_plyridx != game.neutral_player_num)) { // Apply the spell kind stored in 'active_disease_spell'. - apply_spell_effect_to_thing(thing, cctrl->active_disease_spell, cctrl->exp_level, creatng->owner); + apply_spell_effect_to_thing(thing, cctrl->active_disease_spell, cctrl->exp_level, creatng->owner, creatng, DSK_Power); tngcctrl->disease_caster_plyridx = cctrl->disease_caster_plyridx; } // Per thing code ends. @@ -230,7 +231,7 @@ void process_disease(struct Thing *creatng) } if (((game.play_gameturn - cctrl->disease_start_turn) % game.conf.rules[creatng->owner].magic.disease_lose_health_time) == 0) { - apply_damage_to_thing_and_display_health(creatng, game.conf.rules[creatng->owner].magic.disease_lose_percentage_health * cctrl->max_health / 100, cctrl->disease_caster_plyridx); + apply_damage_to_thing_and_display_health(creatng, game.conf.rules[creatng->owner].magic.disease_lose_percentage_health * cctrl->max_health / 100, cctrl->disease_caster_plyridx, INVALID_THING, DSK_Power); } } @@ -303,7 +304,7 @@ void update_god_lightning_ball(struct Thing *thing) if (!thing_exists(target)) break; shotst = get_shot_model_stats(thing->model); - apply_damage_to_thing_and_display_health(target, shotst->damage, thing->owner); + apply_damage_to_thing_and_display_health(target, shotst->damage, thing->owner, thing, DSK_Power); if (target->health < 0) { struct CreatureControl* cctrl = creature_control_get_from_thing(target); diff --git a/src/thing_creature.c b/src/thing_creature.c index 59eb6d1cbc..d09009bfb9 100644 --- a/src/thing_creature.c +++ b/src/thing_creature.c @@ -855,7 +855,7 @@ long get_spell_slot(const struct Thing *thing, SpellKind spkind) return -1; } -TbBool fill_spell_slot(struct Thing *thing, SpellKind spell_idx, GameTurnDelta spell_power, CrtrExpLevel spell_level, PlayerNumber plyr_idx, int slot_idx) +TbBool fill_spell_slot(struct Thing *thing, SpellKind spell_idx, GameTurnDelta spell_power, CrtrExpLevel spell_level, PlayerNumber plyr_idx, int slot_idx, struct Thing *castertng) { if ((slot_idx < 0) || (slot_idx >= CREATURE_MAX_SPELLS_CASTED_AT)) return false; @@ -867,6 +867,10 @@ TbBool fill_spell_slot(struct Thing *thing, SpellKind spell_idx, GameTurnDelta s cspell->duration = spell_power; cspell->caster_level = spell_level; cspell->caster_owner = plyr_idx; + if (thing_exists(castertng)) + cspell->caster_thing_idx = castertng->index; + else + cspell->caster_thing_idx = 0; return true; } @@ -882,6 +886,7 @@ TbBool free_spell_slot(struct Thing *thing, int slot_idx) cspell->duration = 0; cspell->caster_level = 0; cspell->caster_owner = 0; + cspell->caster_thing_idx = 0; return true; } @@ -1413,7 +1418,7 @@ void update_aura_effect_to_thing(struct Thing *thing, SpellKind spell_idx) } } -void first_apply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel spell_level, PlayerNumber plyr_idx) +void first_apply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel spell_level, PlayerNumber plyr_idx, struct Thing *castertng, DamageSourceKind source_kind) { if (spell_level > SPELL_MAX_LEVEL) { @@ -1427,14 +1432,14 @@ void first_apply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, if (set_thing_spell_flags(thing, spell_idx, duration, spell_level) || spell_is_continuous(spell_idx, duration)) { - fill_spell_slot(thing, spell_idx, duration, spell_level, plyr_idx, i); + fill_spell_slot(thing, spell_idx, duration, spell_level, plyr_idx, i, castertng); update_aura_effect_to_thing(thing, spell_idx); } } return; } -void reapply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel spell_level, PlayerNumber plyr_idx, int slot_idx) +void reapply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel spell_level, PlayerNumber plyr_idx, int slot_idx, struct Thing *castertng, DamageSourceKind source_kind) { if (spell_level > SPELL_MAX_LEVEL) { @@ -1450,12 +1455,16 @@ void reapply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, Crt cspell->duration = duration; cspell->caster_level = spell_level; cspell->caster_owner = plyr_idx; + if (thing_exists(castertng)) + cspell->caster_thing_idx = castertng->index; + else + cspell->caster_thing_idx = 0; update_aura_effect_to_thing(thing, spell_idx); } return; } -void apply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel spell_level, PlayerNumber plyr_idx) +void apply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel spell_level, PlayerNumber plyr_idx, struct Thing *castertng, DamageSourceKind source_kind) { struct CreatureControl *cctrl = creature_control_get_from_thing(thing); if (creature_control_invalid(cctrl)) @@ -1497,7 +1506,7 @@ void apply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrE // Check for damage/heal one-time effect. if ((spconf->damage != 0) && (spconf->damage_frequency == 0)) { - process_thing_spell_damage_or_heal_effects(thing, spell_idx, spell_level, plyr_idx); + process_thing_spell_damage_or_heal_effects(thing, spell_idx, spell_level, plyr_idx, castertng, source_kind); if (spconf->spell_flags == 0 && !spell_is_continuous(spell_idx, duration)) { @@ -1523,11 +1532,11 @@ void apply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrE { if (cctrl->casted_spells[i].spkind == spell_idx) { - reapply_spell_effect_to_thing(thing, spell_idx, spell_level, plyr_idx, i); + reapply_spell_effect_to_thing(thing, spell_idx, spell_level, plyr_idx, i, castertng, source_kind); return; // Exit the function, spell is already active. } } - first_apply_spell_effect_to_thing(thing, spell_idx, spell_level, plyr_idx); + first_apply_spell_effect_to_thing(thing, spell_idx, spell_level, plyr_idx, castertng, source_kind); } void terminate_thing_spell_effect(struct Thing *thing, SpellKind spell_idx) @@ -1815,7 +1824,7 @@ void process_thing_spell_teleport_effects(struct Thing *thing, struct CastedSpel } } -void process_thing_spell_damage_or_heal_effects(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel caster_level, PlayerNumber caster_owner) +void process_thing_spell_damage_or_heal_effects(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel caster_level, PlayerNumber caster_owner, struct Thing *caster_tng, DamageSourceKind source_kind) { struct CreatureControl *cctrl = creature_control_get_from_thing(thing); struct SpellConfig *spconf = get_spell_config(spell_idx); @@ -1854,7 +1863,7 @@ void process_thing_spell_damage_or_heal_effects(struct Thing *thing, SpellKind s // Apply damage. if (damage >= 0) { - apply_damage_to_thing_and_display_health(thing, damage, caster_owner); + apply_damage_to_thing_and_display_health(thing, damage, caster_owner, caster_tng, source_kind); } else // Or heal if damage is negative. { @@ -1893,7 +1902,10 @@ void process_thing_spell_effects(struct Thing *thing) { if (cspell->duration % spconf->damage_frequency == 0) { - process_thing_spell_damage_or_heal_effects(thing, cspell->spkind, cspell->caster_level, cspell->caster_owner); + struct Thing* caster_tng = thing_get(cspell->caster_thing_idx); + if (!thing_exists(caster_tng)) + caster_tng = INVALID_THING; + process_thing_spell_damage_or_heal_effects(thing, cspell->spkind, cspell->caster_level, cspell->caster_owner, caster_tng, DSK_DOTSpell); } } // Process spell with teleport flag. @@ -2226,7 +2238,7 @@ void creature_cast_spell(struct Thing *castng, SpellKind spl_idx, CrtrExpLevel s { thing_play_sample(castng, spconf->caster_affect_sound + SOUND_RANDOM(spconf->caster_sounds_count), NORMAL_PITCH, 0, 3, 0, 4, FULL_LOUDNESS); } - apply_spell_effect_to_thing(castng, spl_idx, cctrl->exp_level, castng->owner); + apply_spell_effect_to_thing(castng, spl_idx, cctrl->exp_level, castng->owner, castng, DSK_None); } else if (spconf->shot_model > 0) { @@ -4263,7 +4275,7 @@ void draw_creature_view(struct Thing *thing) unsigned char* scrmem = lens_get_render_target(); unsigned int render_width = lens_get_render_target_width(); unsigned int render_height = lens_get_render_target_height(); - + // Store previous graphics settings unsigned char* wscr_cp = lbDisplay.WScreen; TbGraphicsWindow grwnd; @@ -4293,7 +4305,7 @@ void draw_creature_view(struct Thing *thing) // Pass full srcbuf so displacement map lookups work correctly // Calculate 2D viewport offset for destination buffer long dst_offset = view_y * lbDisplay.GraphicsScreenWidth + view_x; - draw_lens_effect(lbDisplay.WScreen + dst_offset, lbDisplay.GraphicsScreenWidth, + draw_lens_effect(lbDisplay.WScreen + dst_offset, lbDisplay.GraphicsScreenWidth, scrmem, render_width, view_width, view_height, view_x, game.applied_lens_type); } @@ -6056,12 +6068,12 @@ void process_creature_leave_footsteps(struct Thing *thing) * @param dmg * @param inflicting_plyr_idx */ -HitPoints apply_damage_to_thing_and_display_health(struct Thing *thing, HitPoints dmg, PlayerNumber inflicting_plyr_idx) +HitPoints apply_damage_to_thing_and_display_health(struct Thing *thing, HitPoints dmg, PlayerNumber inflicting_plyr_idx, struct Thing *scrtng, DamageSourceKind source_kind) { HitPoints cdamage; if (dmg > 0) { - cdamage = apply_damage_to_thing(thing, dmg, inflicting_plyr_idx); + cdamage = apply_damage_to_thing(thing, dmg, inflicting_plyr_idx, scrtng, source_kind); } else { cdamage = 0; } @@ -6111,7 +6123,7 @@ void process_landscape_affecting_creature(struct Thing *thing) if (cube_is_lava(i)) { struct CreatureModelConfig* crconf = creature_stats_get_from_thing(thing); - apply_damage_to_thing_and_display_health(thing, crconf->hurt_by_lava, -1); + apply_damage_to_thing_and_display_health(thing, crconf->hurt_by_lava, -1, INVALID_THING, DSK_Lava); thing->movement_flags |= TMvF_IsOnLava; } else if (cube_is_water(i)) @@ -7709,7 +7721,7 @@ TbResult script_use_spell_on_creature(PlayerNumber plyr_idx, struct Thing *thing { thing_play_sample(thing, spconf->caster_affect_sound + SOUND_RANDOM(spconf->caster_sounds_count), NORMAL_PITCH, 0, 3, 0, 4, FULL_LOUDNESS); } - apply_spell_effect_to_thing(thing, spkind, spell_level, plyr_idx); + apply_spell_effect_to_thing(thing, spkind, spell_level, plyr_idx, INVALID_THING, DSK_Power); if (flag_is_set(spconf->spell_flags, CSAfF_Disease)) { struct CreatureControl *cctrl; diff --git a/src/thing_creature.h b/src/thing_creature.h index d2514d7710..4bdeaeaed2 100644 --- a/src/thing_creature.h +++ b/src/thing_creature.h @@ -25,6 +25,7 @@ #include "bflib_filelst.h" #include "bflib_sprite.h" #include "thing_list.h" +#include "thing_stats.h" #include "map_locations.h" #include "packets.h" @@ -125,7 +126,7 @@ TbBool thing_can_be_eaten(struct Thing *thing); void food_eaten_by_creature(struct Thing *foodtng, struct Thing *creatng); void anger_apply_anger_to_creature_f(struct Thing *thing, long anger, AnnoyMotive reason, long a3, const char *func_name); #define anger_apply_anger_to_creature(thing, anger, reason, a3) anger_apply_anger_to_creature_f(thing, anger, reason, a3, __func__) -HitPoints apply_damage_to_thing_and_display_health(struct Thing *thing, HitPoints dmg, PlayerNumber inflicting_plyr_idx); +HitPoints apply_damage_to_thing_and_display_health(struct Thing *thing, HitPoints dmg, PlayerNumber inflicting_plyr_idx, struct Thing* scrtng, DamageSourceKind source_kind); void process_creature_standing_on_corpses_at(struct Thing *thing, struct Coord3d *pos); long creature_instance_has_reset(const struct Thing *thing, long a2); void set_creature_instance(struct Thing *thing, CrInstance inst_idx, long targtng_idx, const struct Coord3d *pos); @@ -159,11 +160,11 @@ void clean_spell_effect_f(struct Thing *thing, unsigned long spell_flags, const TbResult script_use_spell_on_creature(PlayerNumber plyr_idx, struct Thing *thing, SpellKind spkind, CrtrExpLevel spell_level); TbResult script_use_spell_on_creature_with_criteria(PlayerNumber plyr_idx, ThingModel crmodel, short criteria, SpellKind spkind, CrtrExpLevel spell_level); -void apply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel spell_level, PlayerNumber plyr_idx); -void first_apply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel spell_level, PlayerNumber plyr_idx); -void reapply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel spell_level, PlayerNumber plyr_idx, int slot_idx); +void apply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel spell_level, PlayerNumber plyr_idx, struct Thing *castertng, DamageSourceKind source_kind); +void first_apply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel spell_level, PlayerNumber plyr_idx, struct Thing *castertng, DamageSourceKind source_kind); +void reapply_spell_effect_to_thing(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel spell_level, PlayerNumber plyr_idx, int slot_idx, struct Thing *castertng, DamageSourceKind source_kind); void terminate_thing_spell_effect(struct Thing *thing, SpellKind spell_idx); -void process_thing_spell_damage_or_heal_effects(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel caster_level, PlayerNumber caster_owner); +void process_thing_spell_damage_or_heal_effects(struct Thing *thing, SpellKind spell_idx, CrtrExpLevel caster_level, PlayerNumber caster_owner, struct Thing *caster_tng, DamageSourceKind source_kind); void process_thing_spell_effects(struct Thing *thing); void process_thing_spell_effects_while_blocked(struct Thing *thing); void delete_armour_effects_attached_to_creature(struct Thing *thing); diff --git a/src/thing_effects.c b/src/thing_effects.c index 50b9e33cd6..cedc59ce92 100644 --- a/src/thing_effects.c +++ b/src/thing_effects.c @@ -1012,7 +1012,7 @@ TbBool explosion_affecting_thing(struct Thing *tngsrc, struct Thing *tngdst, con { HitPoints damage = get_radially_decaying_value(max_damage, max_dist / 4, 3 * max_dist / 4, distance) + 1; SYNCDBG(7,"Causing %d damage to %s at distance %d",(int)damage,thing_model_name(tngdst),(int)distance); - apply_damage_to_thing_and_display_health(tngdst, damage, owner); + apply_damage_to_thing_and_display_health(tngdst, damage, owner, tngsrc, DSK_None); if (flag_is_set(shotst->model_flags,ShMF_LifeDrain)) { give_shooter_drained_health(origtng, damage / 2); @@ -1038,7 +1038,7 @@ TbBool explosion_affecting_thing(struct Thing *tngsrc, struct Thing *tngdst, con { spell_level = scctrl->exp_level; } - apply_spell_effect_to_thing(tngdst, shotst->cast_spell_kind, spell_level, owner); + apply_spell_effect_to_thing(tngdst, shotst->cast_spell_kind, spell_level, owner, origtng, DSK_None); struct SpellConfig *spconf = get_spell_config(shotst->cast_spell_kind); if (flag_is_set(spconf->spell_flags, CSAfF_Disease)) { @@ -1066,7 +1066,7 @@ TbBool explosion_affecting_thing(struct Thing *tngsrc, struct Thing *tngdst, con { HitPoints damage = get_radially_decaying_value(max_damage, max_dist / 4, 3 * max_dist / 4, distance) + 1; SYNCDBG(7,"Causing %d damage to %s at distance %d",(int)damage,thing_model_name(tngdst),(int)distance); - apply_damage_to_thing(tngdst, damage, -1); + apply_damage_to_thing(tngdst, damage, -1, tngsrc, DSK_None); affected = true; event_create_event_or_update_nearby_existing_event(tngdst->mappos.x.val, tngdst->mappos.y.val,EvKind_HeartAttacked, tngdst->owner, 0); if (is_my_player_number(tngdst->owner)) @@ -1136,7 +1136,7 @@ TbBool explosion_affecting_door(struct Thing *tngsrc, struct Thing *tngdst, cons } } SYNCDBG(7,"Causing %d damage to %s at distance %d",(int)damage,thing_model_name(tngdst),(int)distance); - apply_damage_to_thing(tngdst, damage, -1); + apply_damage_to_thing(tngdst, damage, -1, tngsrc, DSK_None); affected = true; } } @@ -1446,7 +1446,7 @@ TbBool poison_cloud_affecting_thing(struct Thing *tngsrc, struct Thing *tngdst, HitPoints damage; damage = get_radially_decaying_value(max_damage, max_dist / 4, 3 * max_dist / 4, distance) + 1; SYNCDBG(7, "Causing %d damage to %s at distance %d", (int)damage, thing_model_name(tngdst), (int)distance); - apply_damage_to_thing_and_display_health(tngdst, damage, tngsrc->owner); + apply_damage_to_thing_and_display_health(tngdst, damage, tngsrc->owner, tngsrc, DSK_None); } break; case AAffT_GasDamageEffect: @@ -1455,14 +1455,14 @@ TbBool poison_cloud_affecting_thing(struct Thing *tngsrc, struct Thing *tngdst, HitPoints damage; damage = get_radially_decaying_value(max_damage, 3 * max_dist / 4, max_dist / 4, distance) + 1; SYNCDBG(7, "Causing %d damage to %s at distance %d", (int)damage, thing_model_name(tngdst), (int)distance); - apply_damage_to_thing_and_display_health(tngdst, damage, tngsrc->owner); + apply_damage_to_thing_and_display_health(tngdst, damage, tngsrc->owner, tngsrc, DSK_None); } spconf = get_spell_config(spell_idx); if ((!creature_under_spell_effect(tngdst, spconf->spell_flags)) && (!creature_is_immune_to_spell_effect(tngdst, spconf->spell_flags))) { struct CreatureControl *srcctrl; srcctrl = creature_control_get_from_thing(tngsrc); - apply_spell_effect_to_thing(tngdst, spell_idx, srcctrl->exp_level, tngsrc->owner); + apply_spell_effect_to_thing(tngdst, spell_idx, srcctrl->exp_level, tngsrc->owner, tngsrc, DSK_None); } break; case AAffT_GasEffect: @@ -1471,7 +1471,7 @@ TbBool poison_cloud_affecting_thing(struct Thing *tngsrc, struct Thing *tngdst, { struct CreatureControl *srcctrl; srcctrl = creature_control_get_from_thing(tngsrc); - apply_spell_effect_to_thing(tngdst, spell_idx, srcctrl->exp_level, tngsrc->owner); + apply_spell_effect_to_thing(tngdst, spell_idx, srcctrl->exp_level, tngsrc->owner, tngsrc, DSK_None); } break; default: diff --git a/src/thing_list.c b/src/thing_list.c index 4e03ed8481..861d99cd79 100644 --- a/src/thing_list.c +++ b/src/thing_list.c @@ -2077,7 +2077,7 @@ TbBool electricity_affecting_thing(struct Thing *tngsrc, struct Thing *tngdst, c HitPoints damage = get_radially_decaying_value(max_damage, max_dist / 2, max_dist / 2, distance); if (damage != 0) { - apply_damage_to_thing_and_display_health(tngdst, damage, owner); + apply_damage_to_thing_and_display_health(tngdst, damage, owner, tngsrc, DSK_Power); affected = true; } } diff --git a/src/thing_shots.c b/src/thing_shots.c index 822b44e654..375fcab4e6 100644 --- a/src/thing_shots.c +++ b/src/thing_shots.c @@ -56,6 +56,27 @@ extern "C" { #endif /******************************************************************************/ /******************************************************************************/ +/** Check if an effect model is a gas cloud (Gas1/Gas2/Gas3). */ +static TbBool effect_model_is_gas(EffectOrEffElModel eff_kind) +{ + return (eff_kind == TngEff_Gas1) || (eff_kind == TngEff_Gas2) || (eff_kind == TngEff_Gas3); +} + +/** + * Returns the parent index to assign to an effect spawned by a shot hit. + * Gas effects need to trace back to the original shooter (creature/trap) + * so that damage from lingering gas clouds is attributed to the correct source. + * Non-gas effects keep the shot itself as parent. + */ +static ThingIndex get_shot_hit_effect_parent(const struct Thing *shotng, EffectOrEffElModel eff_kind) +{ + if (effect_model_is_gas(eff_kind) && (shotng->parent_idx > 0) && (shotng->parent_idx != shotng->index)) + { + return shotng->parent_idx; + } + return shotng->index; +} + TbBool thing_is_shot(const struct Thing *thing) { if (thing_is_invalid(thing)) @@ -429,6 +450,7 @@ TbBool shot_hit_wall_at(struct Thing *shotng, struct Coord3d *pos) TbBool destroy_shot = 0; struct ShotConfigStats* shotst = get_shot_model_stats(shotng->model); long blocked_flags = get_thing_blocked_flags_at(shotng, pos); + struct Thing* damage_source = get_parent_thing(shotng); TbBool digging = (shotst->model_flags & ShMF_Digging); HitPoints old_health = 0; EffectOrEffElModel eff_kind; @@ -538,7 +560,7 @@ TbBool shot_hit_wall_at(struct Thing *shotng, struct Coord3d *pos) if (!shotst->hit_door.withstand) destroy_shot = 1; i = calculate_shot_real_damage_to_door(doortng, shotng); - apply_damage_to_thing(doortng, i, -1); + apply_damage_to_thing(doortng, i, -1, damage_source, DSK_None); reveal_secret_door_to_player(doortng,shotng->owner); } else if (cube_is_water(cube_id)) @@ -599,7 +621,7 @@ TbBool shot_hit_wall_at(struct Thing *shotng, struct Coord3d *pos) if (!shotst->hit_door.withstand) destroy_shot = 1; i = calculate_shot_real_damage_to_door(doortng, shotng); - apply_damage_to_thing(doortng, i, -1); + apply_damage_to_thing(doortng, i, -1, damage_source, DSK_None); reveal_secret_door_to_player(doortng,shotng->owner); } else { @@ -626,8 +648,33 @@ TbBool shot_hit_wall_at(struct Thing *shotng, struct Coord3d *pos) } if (!thing_is_invalid(efftng)) { efftng->shot_effect.hit_type = shotst->area_hit_type; - efftng->shot_effect.parent_class_id = TCls_Shot; - efftng->shot_effect.parent_model = shotng->model; + // Gas effects persist after the shot is gone and deal damage over time. + // Set their parent to the original shooter (creature/trap) so that + // damage from the gas cloud can be traced back to the correct source. + if (effect_model_is_gas(efftng->model)) + { + ThingIndex parent_idx = get_shot_hit_effect_parent(shotng, efftng->model); + struct Thing* parent_tng = (parent_idx > 0) ? thing_get(parent_idx) : INVALID_THING; + if (thing_exists(parent_tng)) + { + efftng->shot_effect.parent_class_id = parent_tng->class_id; + efftng->shot_effect.parent_model = parent_tng->model; + efftng->parent_idx = parent_tng->index; + } + else + { + // Shooter no longer exists — fall back to shot as parent. + efftng->shot_effect.parent_class_id = TCls_Shot; + efftng->shot_effect.parent_model = shotng->model; + efftng->parent_idx = parent_idx; + } + } + else + { + // Non-gas effects are short-lived; the shot itself is sufficient as parent. + efftng->shot_effect.parent_class_id = TCls_Shot; + efftng->shot_effect.parent_model = shotng->model; + } } if ( destroy_shot ) { @@ -662,6 +709,7 @@ long shot_hit_door_at(struct Thing *shotng, struct Coord3d *pos) TbBool shot_explodes = false; struct ShotConfigStats* shotst = get_shot_model_stats(shotng->model); struct Thing* efftng = INVALID_THING; + struct Thing* damage_source = get_parent_thing(shotng); long blocked_flags = get_thing_blocked_flags_at(shotng, pos); if (blocked_flags != 0) { @@ -672,7 +720,7 @@ long shot_hit_door_at(struct Thing *shotng, struct Coord3d *pos) // If the shot hit is supposed to create effect thing if (shotst->hit_door.effect_model != 0) { - efftng = create_used_effect_or_element(&shotng->mappos, shotst->hit_door.effect_model, shotng->owner, shotng->index); + efftng = create_used_effect_or_element(&shotng->mappos, shotst->hit_door.effect_model, shotng->owner, get_shot_hit_effect_parent(shotng, shotst->hit_door.effect_model)); } // If the shot hit is supposed to create sound int n = shotst->hit_door.sndsample_idx; @@ -692,7 +740,7 @@ long shot_hit_door_at(struct Thing *shotng, struct Coord3d *pos) } // Apply damage to the door i = calculate_shot_real_damage_to_door(doortng, shotng); - apply_damage_to_thing(doortng, i, -1); + apply_damage_to_thing(doortng, i, -1, damage_source, DSK_None); reveal_secret_door_to_player(doortng,shotng->owner); } } @@ -791,6 +839,7 @@ static TbBool shot_hit_trap_at(struct Thing* shotng, struct Thing* target, struc if (shotng->parent_idx != shotng->index) { shootertng = thing_get(shotng->parent_idx); } + struct Thing* damage_source = shootertng; int i = shotst->hit_generic.sndsample_idx; if (i > 0) { thing_play_sample(target, i, NORMAL_PITCH, 0, 3, 0, 3, FULL_LOUDNESS); @@ -802,7 +851,7 @@ static TbBool shot_hit_trap_at(struct Thing* shotng, struct Thing* target, struc if ((thing_is_destructible_trap(target) > 0) || ((thing_is_destructible_trap(target) > -1) && (shotst->model_flags & ShMF_Disarming))) { - damage_done = apply_damage_to_thing(target, shotng->shot.damage, -1); + damage_done = apply_damage_to_thing(target, shotng->shot.damage, -1, damage_source, DSK_None); // Drain allows caster to regain half of damage if ((shotst->model_flags & ShMF_LifeDrain) && thing_is_creature(shootertng)) @@ -860,7 +909,7 @@ static TbBool shot_hit_object_at(struct Thing *shotng, struct Thing *target, str { if (shotst->hit_heart.effect_model != 0) { - create_used_effect_or_element(&shotng->mappos, shotst->hit_heart.effect_model, shotng->owner, shotng->index); + create_used_effect_or_element(&shotng->mappos, shotst->hit_heart.effect_model, shotng->owner, get_shot_hit_effect_parent(shotng, shotst->hit_heart.effect_model)); } if (shotst->hit_heart.sndsample_idx > 0) { @@ -887,7 +936,7 @@ static TbBool shot_hit_object_at(struct Thing *shotng, struct Thing *target, str { if (object_can_be_damaged(target)) // do not damage objects that cannot be destroyed { - damage_done = apply_damage_to_thing(target, shotng->shot.damage, -1); + damage_done = apply_damage_to_thing(target, shotng->shot.damage, -1, shootertng, DSK_None); // Drain allows caster to regain half of damage if ((shotst->model_flags & ShMF_LifeDrain) && thing_is_creature(shootertng)) @@ -941,18 +990,18 @@ void create_relevant_effect_for_shot_hitting_thing(struct Thing *shotng, struct { thing_play_sample(target, shotst->hit_creature.sndsample_idx, NORMAL_PITCH, 0, 3, 0, 2, FULL_LOUDNESS); if (shotst->hit_creature.effect_model != 0) { - create_used_effect_or_element(&shotng->mappos, shotst->hit_creature.effect_model, shotng->owner, shotng->index); + create_used_effect_or_element(&shotng->mappos, shotst->hit_creature.effect_model, shotng->owner, get_shot_hit_effect_parent(shotng, shotst->hit_creature.effect_model)); } if (creature_under_spell_effect(target, CSAfF_Freeze)) { if (shotst->effect_frozen != 0) { - create_used_effect_or_element(&shotng->mappos, shotst->effect_frozen, shotng->owner, shotng->index); + create_used_effect_or_element(&shotng->mappos, shotst->effect_frozen, shotng->owner, get_shot_hit_effect_parent(shotng, shotst->effect_frozen)); } } else if (creature_model_bleeds(target->model)) { if (shotst->effect_bleeding != 0) { - create_used_effect_or_element(&shotng->mappos, shotst->effect_bleeding, shotng->owner, shotng->index); + create_used_effect_or_element(&shotng->mappos, shotst->effect_bleeding, shotng->owner, get_shot_hit_effect_parent(shotng, shotst->effect_bleeding)); } } } @@ -961,7 +1010,7 @@ void create_relevant_effect_for_shot_hitting_thing(struct Thing *shotng, struct // TODO for a later PR: introduces trap/object hit, for now it uses the on hit creature sound and effect. thing_play_sample(target, shotst->hit_creature.sndsample_idx, NORMAL_PITCH, 0, 3, 0, 2, FULL_LOUDNESS); if (shotst->hit_creature.effect_model != 0) { - create_used_effect_or_element(&shotng->mappos, shotst->hit_creature.effect_model, shotng->owner, shotng->index); + create_used_effect_or_element(&shotng->mappos, shotst->hit_creature.effect_model, shotng->owner, get_shot_hit_effect_parent(shotng, shotst->hit_creature.effect_model)); } } } @@ -1068,9 +1117,9 @@ long melee_shot_hit_creature_at(struct Thing *shotng, struct Thing *trgtng, stru } create_relevant_effect_for_shot_hitting_thing(shotng, trgtng); if (!thing_is_invalid(shooter)) { - damage = apply_damage_to_thing_and_display_health(trgtng, damage, shooter->owner); + damage = apply_damage_to_thing_and_display_health(trgtng, damage, shooter->owner, shooter, DSK_None); } else { - damage = apply_damage_to_thing_and_display_health(trgtng, damage, -1); + damage = apply_damage_to_thing_and_display_health(trgtng, damage, -1, shooter, DSK_None); } if (shotst->model_flags & ShMF_LifeDrain) { @@ -1084,7 +1133,7 @@ long melee_shot_hit_creature_at(struct Thing *shotng, struct Thing *trgtng, stru { spell_level = scctrl->exp_level; } - apply_spell_effect_to_thing(trgtng, shotst->cast_spell_kind, spell_level, shotng->owner); + apply_spell_effect_to_thing(trgtng, shotst->cast_spell_kind, spell_level, shotng->owner, shooter, DSK_None); struct SpellConfig *spconf = get_spell_config(shotst->cast_spell_kind); if (flag_is_set(spconf->spell_flags, CSAfF_Disease)) { @@ -1252,9 +1301,9 @@ long shot_hit_creature_at(struct Thing *shotng, struct Thing *trgtng, struct Coo { HitPoints damage_done; if (thing_exists(shooter)) { - damage_done = apply_damage_to_thing_and_display_health(trgtng, shotng->shot.damage, shooter->owner); + damage_done = apply_damage_to_thing_and_display_health(trgtng, shotng->shot.damage, shooter->owner, shooter, DSK_None); } else { - damage_done = apply_damage_to_thing_and_display_health(trgtng, shotng->shot.damage, -1); + damage_done = apply_damage_to_thing_and_display_health(trgtng, shotng->shot.damage, -1, shooter, DSK_None); } if (shotst->model_flags & ShMF_LifeDrain) { @@ -1276,7 +1325,7 @@ long shot_hit_creature_at(struct Thing *shotng, struct Thing *trgtng, struct Coo { spell_level = scctrl->exp_level; } - apply_spell_effect_to_thing(trgtng, shotst->cast_spell_kind, spell_level, shotng->owner); + apply_spell_effect_to_thing(trgtng, shotst->cast_spell_kind, spell_level, shotng->owner, shooter, DSK_None); struct SpellConfig *spconf = get_spell_config(shotst->cast_spell_kind); if (flag_is_set(spconf->spell_flags, CSAfF_Disease)) { @@ -1740,7 +1789,7 @@ TngUpdateRet update_shot(struct Thing *thing) { shotst = get_shot_model_stats(ShM_GodLightBall); draw_lightning(&thing->mappos,&target->mappos, shotst->effect_spacing, shotst->effect_id); - apply_damage_to_thing_and_display_health(target, shotst->damage, thing->owner); + apply_damage_to_thing_and_display_health(target, shotst->damage, thing->owner, thing, DSK_None); } } break; diff --git a/src/thing_stats.c b/src/thing_stats.c index fc2c2c467a..642d2e93b9 100644 --- a/src/thing_stats.c +++ b/src/thing_stats.c @@ -1067,6 +1067,68 @@ HitPoints calculate_shot_real_damage_to_door(const struct Thing *doortng, const return dmg; } +static struct Thing* resolve_damage_source_thing(struct Thing* scrtng) +{ + if ((scrtng == NULL) || thing_is_invalid(scrtng)) + return INVALID_THING; + + struct Thing* source_tng = scrtng; + switch (source_tng->class_id) + { + case TCls_Shot: + case TCls_Effect: + case TCls_EffectElem: + { + struct Thing* parent_tng = get_parent_thing(source_tng); + if (thing_exists(parent_tng)) + source_tng = parent_tng; + } + break; + default: + break; + } + return source_tng; +} + +static DamageSourceKind check_dmg_src_kind(struct Thing* scrtng, DamageSourceKind source_kind) +{ + if (source_kind != DSK_None) + return source_kind; + + if (!thing_exists(scrtng)) + return DSK_None; + + switch (scrtng->class_id) + { + case TCls_Creature: + return DSK_Creature; + case TCls_Trap: + return DSK_Trap; + case TCls_Object: + return DSK_Object; + case TCls_Door: + return DSK_Door; + default: + return DSK_None; + } +} + +const char *damage_source_kind_name(DamageSourceKind kind) +{ + switch (kind) + { + case DSK_Lava: return "LAVA"; + case DSK_PowerSlap: return "POWER_SLAP"; + case DSK_Power: return "POWER"; + case DSK_DOTSpell: return "DOT_SPELL"; + case DSK_Creature: return "CREATURE"; + case DSK_Trap: return "TRAP"; + case DSK_Object: return "OBJECT"; + case DSK_Door: return "DOOR"; + default: return "UNKNOWN"; + } +} + /** * Applies given damage points to a thing. * In case of targeting creature, uses its defense values to compute the actual damage. @@ -1076,7 +1138,7 @@ HitPoints calculate_shot_real_damage_to_door(const struct Thing *doortng, const * @param inflicting_plyr_idx * @return Amount of damage really inflicted. */ -HitPoints apply_damage_to_thing(struct Thing *thing, HitPoints dmg, PlayerNumber dealing_plyr_idx) +HitPoints apply_damage_to_thing(struct Thing *thing, HitPoints dmg, PlayerNumber dealing_plyr_idx, struct Thing* scrtng, DamageSourceKind source_kind) { // We're here to damage, not to heal. SYNCDBG(19, "Dealing %d damage to %s by player %d", (int)dmg, thing_model_name(thing), (int)dealing_plyr_idx); @@ -1085,7 +1147,10 @@ HitPoints apply_damage_to_thing(struct Thing *thing, HitPoints dmg, PlayerNumber // If it's already dead, then don't interfere. if (thing->health < 0) return 0; - lua_on_apply_damage_to_thing(thing, dmg, dealing_plyr_idx); + struct Thing* src_tng = resolve_damage_source_thing(scrtng); + DamageSourceKind damagesource = check_dmg_src_kind(src_tng, source_kind); + + lua_on_apply_damage_to_thing(thing, dmg, dealing_plyr_idx, src_tng, damagesource); HitPoints cdamage; switch (thing->class_id) @@ -1102,6 +1167,8 @@ HitPoints apply_damage_to_thing(struct Thing *thing, HitPoints dmg, PlayerNumber } break; case TCls_Trap: + cdamage = apply_damage_to_object(thing, dmg); + break; case TCls_Object: cdamage = apply_damage_to_object(thing, dmg); break; diff --git a/src/thing_stats.h b/src/thing_stats.h index 772479ab09..dd8471ef1c 100644 --- a/src/thing_stats.h +++ b/src/thing_stats.h @@ -57,6 +57,19 @@ enum CreatureLiveStatistics { CrLStat_Score, }; +typedef enum DamageSourceKind { + DSK_None = 0, + DSK_Lava, + DSK_PowerSlap, + DSK_Power, + DSK_DOTSpell, + DSK_Creature, + DSK_Trap, + DSK_Object, + DSK_Door, +} DamageSourceKind; + +const char *damage_source_kind_name(DamageSourceKind kind); /******************************************************************************/ #pragma pack(1) @@ -130,7 +143,7 @@ TbBool update_relative_creature_health(struct Thing *creatng); TbBool set_creature_health_to_max_with_heal_effect(struct Thing *thing); TbBool apply_health_to_thing(struct Thing *thing, HitPoints amount); void apply_health_to_thing_and_display_health(struct Thing *thing, HitPoints amount); -HitPoints apply_damage_to_thing(struct Thing *thing, HitPoints dmg, PlayerNumber dealing_plyr_idx); +HitPoints apply_damage_to_thing(struct Thing *thing, HitPoints dmg, PlayerNumber dealing_plyr_idx, struct Thing* scrtng, DamageSourceKind source_kind); HitPoints get_thing_max_health(const struct Thing *thing); /******************************************************************************/ #ifdef __cplusplus