From 8679462d1566a894acbcb61b0f738e674448d12f Mon Sep 17 00:00:00 2001 From: Evan Date: Mon, 7 Oct 2024 00:25:14 +0800 Subject: [PATCH 01/12] Fix some bugs of spell * Fix Speed is not cast when attacking object/door. * Fix Wind is not working. * Fix Ranged Rebound/Armour being casted when not in combat. Type: Bug Fix --- config/fxdata/creature.cfg | 47 ++++++++++------- src/config_creature.c | 4 ++ src/config_creature.h | 4 ++ src/creature_instances.c | 35 ++++++++++--- src/creature_instances.h | 4 +- src/creature_states_combt.c | 101 +++++++++++++++++++++--------------- src/thing_creature.c | 34 +++++++----- 7 files changed, 144 insertions(+), 85 deletions(-) diff --git a/config/fxdata/creature.cfg b/config/fxdata/creature.cfg index 9ac6b0b63c..8e3e1a5cf9 100644 --- a/config/fxdata/creature.cfg +++ b/config/fxdata/creature.cfg @@ -79,6 +79,13 @@ PrimaryTarget = 0 ; DISARMING allows instance to be used against traps ; DISPLAY_SWIPE Shows the swipe in possession loaded from the creatures PossessSwipeIndex ; NEEDS_TARGET Cannot be used in possession without a target to cast it on +; OFFENSIVE indicates that a buff will improve the offsenive ability, such as enhancing speed. For debuff, it +; indicates it will weaken the offsenive ability. +; DEFENSIVE indicates that a buff will improve the defensive ability, such as enhancing armour. For debugg, it +; indicates it will weaken the defensive ability. +; DAILY_LIFE_BUFF indicates that a buff can improve work throughput, or keep creature alive, or improve something +; when the creature is not in combat. +; ALLOWED_IN_PRISON indicates that this instance can be used when the caster is imprisoned. Properties = ; Function used as the instance action, and its parameters Function = none 0 0 @@ -214,7 +221,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF +Properties = RANGED_DEBUFF OFFENSIVE Function = creature_cast_spell SPELL_FREEZE 0 [instance8] @@ -230,7 +237,7 @@ ForceVisibility = 10 TooltipTextID = 228 SymbolSprites = 408 Graphics = ATTACK -Properties = SELF_BUFF +Properties = SELF_BUFF DEFENSIVE PrimaryTarget = 3 Function = creature_cast_spell SPELL_ARMOUR 0 @@ -266,7 +273,7 @@ ForceVisibility = 10 TooltipTextID = 230 SymbolSprites = 412 Graphics = ATTACK -Properties = SELF_BUFF +Properties = SELF_BUFF DEFENSIVE PrimaryTarget = 3 Function = creature_cast_spell SPELL_REBOUND 0 @@ -283,7 +290,7 @@ ForceVisibility = 1 TooltipTextID = 248 SymbolSprites = 414 Graphics = ATTACK -Properties = SELF_BUFF +Properties = SELF_BUFF DEFENSIVE DAILY_LIFE_BUFF ALLOWED_IN_PRISON PrimaryTarget = 3 Function = creature_cast_spell SPELL_HEAL 0 @@ -320,7 +327,7 @@ TooltipTextID = 239 SymbolSprites = 418 Graphics = ATTACK PrimaryTarget = 3 -Properties = SELF_BUFF +Properties = SELF_BUFF DEFENSIVE OFFENSIVE Function = creature_cast_spell SPELL_INVISIBILITY 0 [instance14] @@ -354,7 +361,7 @@ TooltipTextID = 236 SymbolSprites = 422 Graphics = ATTACK PrimaryTarget = 3 -Properties = SELF_BUFF +Properties = SELF_BUFF OFFENSIVE DAILY_LIFE_BUFF Function = creature_cast_spell SPELL_SPEED 0 [instance16] @@ -373,7 +380,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF +Properties = RANGED_DEBUFF OFFENSIVE Function = creature_cast_spell SPELL_SLOW 0 [instance17] @@ -411,7 +418,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF +Properties = RANGED_DEBUFF OFFENSIVE Function = creature_cast_spell SPELL_FEAR 0 [instance19] @@ -487,7 +494,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = MAX PrimaryTarget = 3 -Properties = +Properties = SELF_BUFF DEFENSIVE Function = creature_cast_spell SPELL_WIND 0 [instance23] @@ -503,7 +510,7 @@ ForceVisibility = 100 TooltipTextID = 242 SymbolSprites = 434 Graphics = ATTACK -Properties = SELF_BUFF +Properties = SELF_BUFF OFFENSIVE PrimaryTarget = 3 Function = creature_cast_spell SPELL_LIGHT 0 @@ -520,7 +527,7 @@ ForceVisibility = 1 TooltipTextID = 243 SymbolSprites = 436 Graphics = ATTACK -Properties = SELF_BUFF +Properties = SELF_BUFF OFFENSIVE DEFENSIVE PrimaryTarget = 3 Function = creature_cast_spell SPELL_FLIGHT 0 @@ -537,7 +544,7 @@ ForceVisibility = 0 TooltipTextID = 232 SymbolSprites = 438 Graphics = ATTACK -Properties = SELF_BUFF +Properties = SELF_BUFF OFFENSIVE DAILY_LIFE_BUFF ALLOWED_IN_PRISON PrimaryTarget = 3 Function = creature_cast_spell SPELL_SIGHT 0 @@ -811,7 +818,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF +Properties = RANGED_DEBUFF DEFENSIVE Function = creature_cast_spell SPELL_DISEASE 0 [instance42] @@ -830,7 +837,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = 1280 PrimaryTarget = 3 -Properties = RANGED_DEBUFF +Properties = RANGED_DEBUFF OFFENSIVE Function = creature_cast_spell SPELL_CHICKEN 0 [instance43] @@ -930,7 +937,7 @@ ForceVisibility = 30 TooltipTextID = 201 SymbolSprites = 434 Graphics = SCREAM -Properties = SELF_BUFF +Properties = SELF_BUFF OFFENSIVE DAILY_LIFE_BUFF PrimaryTarget = 8 Function = creature_cast_spell SPELL_SUMMON_FAMILIAR 0 @@ -948,7 +955,7 @@ ForceVisibility = 30 TooltipTextID = 201 SymbolSprites = 434 Graphics = SCREAM -Properties = SELF_BUFF +Properties = SELF_BUFF OFFENSIVE PrimaryTarget = 8 Function = creature_cast_spell SPELL_SUMMON_CREATURE 0 @@ -968,7 +975,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET DEFENSIVE DAILY_LIFE_BUFF ALLOWED_IN_PRISON Function = creature_cast_spell SPELL_HEAL 0 ValidateFunc = validate_source_ranged_heal validate_target_ranged_heal SearchTargetsFunc = search_target_ranged_heal @@ -989,7 +996,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET OFFENSIVE DAILY_LIFE_BUFF Function = creature_cast_spell SPELL_SPEED 0 ValidateFunc = validate_source_generic validate_target_generic SearchTargetsFunc = search_target_generic @@ -1010,7 +1017,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET DEFENSIVE Function = creature_cast_spell SPELL_ARMOUR 0 ValidateFunc = validate_source_generic validate_target_generic SearchTargetsFunc = search_target_generic @@ -1031,7 +1038,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET DEFENSIVE Function = creature_cast_spell SPELL_REBOUND 0 ValidateFunc = validate_source_generic validate_target_generic SearchTargetsFunc = search_target_generic diff --git a/src/config_creature.c b/src/config_creature.c index f12f537b0a..babaa15b57 100644 --- a/src/config_creature.c +++ b/src/config_creature.c @@ -112,6 +112,10 @@ const struct NamedCommand creaturetype_instance_properties[] = { {"DISPLAY_SWIPE", InstPF_UsesSwipe}, {"RANGED_BUFF", InstPF_RangedBuff}, {"NEEDS_TARGET", InstPF_NeedsTarget}, + {"OFFENSIVE", InstPF_Offensive}, + {"DEFENSIVE", InstPF_Defensive}, + {"DAILY_LIFE_BUFF", InstPF_DailyLifeBuff}, + {"ALLOWED_IN_PRISON", InstPF_AllowedInPrison}, {NULL, 0}, }; diff --git a/src/config_creature.h b/src/config_creature.h index 12aee03436..fbe9e19956 100644 --- a/src/config_creature.h +++ b/src/config_creature.h @@ -150,6 +150,10 @@ enum InstancePropertiesFlags { InstPF_UsesSwipe = 0x0200, InstPF_RangedBuff = 0x0400, InstPF_NeedsTarget = 0x0800, + InstPF_Offensive = 0x1000, + InstPF_Defensive = 0x2000, + InstPF_DailyLifeBuff = 0x4000, + InstPF_AllowedInPrison = 0x8000, }; enum CreatureDeathKind { diff --git a/src/creature_instances.c b/src/creature_instances.c index 1c0865fba4..6bd78b515f 100644 --- a/src/creature_instances.c +++ b/src/creature_instances.c @@ -553,28 +553,25 @@ long instf_creature_cast_spell(struct Thing *creatng, long *param) } -long process_creature_self_spell_casting(struct Thing* creatng) +TbBool process_creature_self_spell_casting(struct Thing* creatng) { TRACE_THING(creatng); struct CreatureControl* cctrl = creature_control_get_from_thing(creatng); if (((creatng->alloc_flags & TAlF_IsControlled) != 0) || (cctrl->conscious_back_turns != 0) || ((cctrl->stateblock_flags & CCSpl_Freeze) != 0)) { - return 0; + return false; } if (cctrl->instance_id != CrInst_NULL) { - return 0; - } - if (cctrl->combat_flags != 0) { - return 0; + return false; } long inst_idx = get_self_spell_casting(creatng); if (inst_idx <= 0) { - return 0; + return false; } set_creature_instance(creatng, inst_idx, creatng->index, 0); - return 1; + return true; } /** @@ -1273,6 +1270,28 @@ TbBool validate_target_generic(struct Thing *source, struct Thing *target, CrIns return false; } + struct CreatureControl* cctrl = creature_control_get_from_thing(target); + if (creature_control_invalid(cctrl)) + { + ERRORLOG("Invalid creature control"); + return false; + } + // Check if this creature needs this buff by examining its state. + if (flag_is_set(cctrl->combat_flags, CmbtF_ObjctFight) || flag_is_set(cctrl->combat_flags, CmbtF_DoorFight)) + { + if(!flag_is_set(inst_inf->instance_property_flags, InstPF_Offensive)) + { + return false; // Offensive buff is useful when attacking door/dungeon heart. + } + } + else if(cctrl->combat_flags == 0) + { + if(!flag_is_set(inst_inf->instance_property_flags, InstPF_DailyLifeBuff)) + { + return false; // Daiy life buff is useful when not in combat. + } + } + return true; } diff --git a/src/creature_instances.h b/src/creature_instances.h index 0a24324a71..a7cc9c8bc6 100644 --- a/src/creature_instances.h +++ b/src/creature_instances.h @@ -102,7 +102,7 @@ struct InstanceInfo { long reset_time; long fp_reset_time; unsigned char graphics_idx; - short instance_property_flags; + unsigned long instance_property_flags; short force_visibility; unsigned char primary_target; unsigned char func_idx; @@ -132,7 +132,7 @@ extern Creature_Target_Search_Func creature_instances_search_targets_func_list[] #define creature_instance_info_get(inst_idx) creature_instance_info_get_f(inst_idx,__func__) struct InstanceInfo *creature_instance_info_get_f(CrInstance inst_idx,const char *func_name); void process_creature_instance(struct Thing *thing); -long process_creature_self_spell_casting(struct Thing* thing); +TbBool process_creature_self_spell_casting(struct Thing* thing); CrInstance process_creature_ranged_buff_spell_casting(struct Thing* thing); TbBool creature_instance_info_invalid(const struct InstanceInfo *inst_inf); diff --git a/src/creature_states_combt.c b/src/creature_states_combt.c index 67c3e49129..7862778c2c 100644 --- a/src/creature_states_combt.c +++ b/src/creature_states_combt.c @@ -1862,70 +1862,87 @@ CrInstance get_best_self_preservation_instance_to_use(const struct Thing *thing) CrInstance get_self_spell_casting(const struct Thing *thing) { struct CreatureControl* cctrl = creature_control_get_from_thing(thing); - struct InstanceInfo* inst_inf; - if (creature_would_benefit_from_healing(thing)) - { - INSTANCE_RET_IF_AVAIL(thing, CrInst_HEAL); - } + TbBool is_fighting = cctrl->combat_flags != 0; + TbBool is_in_custody = creature_is_kept_in_custody(thing); + SYNCDBG(11, "Processing %s(%d), combat flag: %d, spell flag: %d, in custody? %d", + thing_model_name(thing), thing->index, cctrl->combat_flags, cctrl->spell_flags, is_in_custody); - if (thing_is_creature_special_digger(thing) && creature_is_doing_digger_activity(thing)) + for (int i = 0; i < game.conf.crtr_conf.instances_count; i++) { - // casting wind when under influence of gas - if ((cctrl->spell_flags & CSAfF_PoisonCloud) != 0) + struct InstanceInfo* inst_inf = creature_instance_info_get(i); + // Basic checks. + if (!flag_is_set(inst_inf->instance_property_flags, InstPF_SelfBuff) || + !creature_instance_is_available(thing, i) || + !creature_instance_has_reset(thing, i)) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_WIND); + continue; // Cannot use. } - - for (short i = 0; i < game.conf.crtr_conf.instances_count; i++) + SpellKind spell_idx = inst_inf->func_params[0]; + if(inst_inf->func_params[0] != SplK_None && creature_affected_by_spell(thing, spell_idx)) { - if (i == CrInst_HEAL) - continue; - - inst_inf = creature_instance_info_get(i); - if ((inst_inf->instance_property_flags & InstPF_SelfBuff)) - { - if (inst_inf->func_params[0] != SplK_None && - !creature_affected_by_spell(thing, inst_inf->func_params[0])) - { - INSTANCE_RET_IF_AVAIL(thing, i); - } - } + continue; // Already being affected. } - } - else - { - if (!creature_affected_by_spell(thing, SplK_Sight)) + if (!flag_is_set(inst_inf->instance_property_flags, InstPF_AllowedInPrison) && is_in_custody) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_SIGHT); + continue; // Cannot use in prison. } - - if (!creature_is_kept_in_custody(thing)) + // Our ideal goal is to avoid any hardcode for specific instance, + // but we still need to deal with some special instances. + switch(i) { - // casting wind when under influence of gas + case CrInst_WIND: if ((cctrl->spell_flags & CSAfF_PoisonCloud) != 0) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_WIND); + SYNCDBG(11, "Use Wind when under influence of posion cloud."); + return CrInst_WIND; // Cast Wind when under influence of gas. + } + break; + case CrInst_FLY: + if (is_fighting || terrain_toxic_for_creature_at_position(thing, + coord_subtile(thing->mappos.x.val), coord_subtile(thing->mappos.y.val))) + { + return CrInst_FLY; } - long state_type = get_creature_state_type(thing); - if (!creature_affected_by_spell(thing, SplK_Speed) && (state_type != CrStTyp_Idle)) + break; + case CrInst_HEAL: + case CrInst_RANGED_HEAL: + if (creature_would_benefit_from_healing(thing)) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_SPEED); + return CrInst_HEAL; } - if (!creature_affected_by_spell(thing, SplK_Fly) && ((state_type != CrStTyp_Idle) || terrain_toxic_for_creature_at_position(thing, coord_subtile(thing->mappos.x.val), coord_subtile(thing->mappos.y.val)))) + break; + default: + if (!is_fighting && flag_is_set(inst_inf->instance_property_flags, InstPF_DailyLifeBuff)) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_FLY); + SYNCDBG(11, "Use %s as daily life buff.", creature_instance_code_name(i)); + return i; } - //TODO CREATURE_AI allow using invisibility when creature is being attacked or escaping - if (!creature_affected_by_spell(thing, SplK_Invisibility) && (state_type != CrStTyp_Idle)) + else if (flag_is_set(cctrl->combat_flags, CmbtF_ObjctFight) || + flag_is_set(cctrl->combat_flags, CmbtF_DoorFight)) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_INVISIBILITY); + // Creature is fighting a object (such as dungeon heart) or a door. + if(flag_is_set(inst_inf->instance_property_flags, InstPF_Offensive)) + { + // @todo What about Armour? Armour is defensive but it can protect creature from the AOE + // of the Keeper lightning spell. + SYNCDBG(11, "Use %s for object/door fighting case.", creature_instance_code_name(i)); + return i; // Offensive buff is useful when attacking door/dungeon heart. + } } - if (state_type != CrStTyp_Idle) + else if (is_fighting) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_FAMILIAR); + // Other fighting case. + if(flag_is_set(inst_inf->instance_property_flags, InstPF_Offensive) || + flag_is_set(inst_inf->instance_property_flags, InstPF_Defensive)) + { + SYNCDBG(11, "Use %s for general fighting case.", creature_instance_code_name(i)); + return i; // Offensive and Defensive are both useful. + } } + break; } } + return CrInst_NULL; } diff --git a/src/thing_creature.c b/src/thing_creature.c index 500cc3d3d3..0ded5d5eba 100644 --- a/src/thing_creature.c +++ b/src/thing_creature.c @@ -1802,25 +1802,33 @@ void creature_cast_spell(struct Thing *castng, SpellKind spl_idx, long shot_lvl, cctrl->teleport_x = trg_x; cctrl->teleport_y = trg_y; } - // Check if the spell can be fired as a shot. It is definitely not if casted on itself. - if ((spconf->shot_model > 0) && (cctrl->targtng_idx != castng->index)) + + if (spconf->caster_affected && castng->index == cctrl->targtng_idx) { + if (spconf->caster_affect_sound > 0) + thing_play_sample(castng, spconf->caster_affect_sound, NORMAL_PITCH, 0, 3, 0, 4, FULL_LOUDNESS); + apply_spell_effect_to_thing(castng, spl_idx, cctrl->explevel); + } + else if (spconf->shot_model > 0) + { + // Note that Wind has shot model and its CastAtThing is 0, besides, the target index is itself. if ((castng->alloc_flags & TAlF_IsControlled) != 0) i = THit_CrtrsNObjcts; else i = THit_CrtrsOnlyNotOwn; - SYNCDBG(8,"The %s(%d) fire shot(%s) without a target with shot level %d, hit type: 0x%X", - thing_model_name(castng), castng->index, shot_code_name(spconf->shot_model), shot_lvl, i); - thing_fire_shot(castng, INVALID_THING, spconf->shot_model, shot_lvl, i); - } - // Check if the spell can be self-casted - else if (spconf->caster_affected) - { - if (spconf->caster_affect_sound > 0) - thing_play_sample(castng, spconf->caster_affect_sound, NORMAL_PITCH, 0, 3, 0, 4, FULL_LOUDNESS); - apply_spell_effect_to_thing(castng, spl_idx, cctrl->explevel); + const struct InstanceInfo* inst_inf = creature_instance_info_get(cctrl->instance_id); + if (flag_is_set(inst_inf->instance_property_flags, InstPF_RangedBuff)) + { + ERRORLOG("The %s(%d) tried to fire Ranged Buff's shot(%s) without a target!", + thing_model_name(castng), castng->index, shot_code_name(spconf->shot_model)); + } + else + { + thing_fire_shot(castng, INVALID_THING, spconf->shot_model, shot_lvl, i); + } } + if (spconf->crtr_summon_model > 0) { thing_summon_temporary_creature(castng, spconf->crtr_summon_model, spconf->crtr_summon_level, spconf->crtr_summon_amount, spconf->duration, spl_idx); @@ -5977,7 +5985,7 @@ TngUpdateRet update_creature(struct Thing *thing) return TUFRet_Deleted; } - if (process_creature_self_spell_casting(thing) == 0) + if (!process_creature_self_spell_casting(thing)) { // If this creature didn't cast anything to itself, try to help others. process_creature_ranged_buff_spell_casting(thing); From e18afb03bddcc97d4be8087a42f9eee4a099d7bb Mon Sep 17 00:00:00 2001 From: Evan Date: Mon, 7 Oct 2024 02:02:40 +0800 Subject: [PATCH 02/12] Revert "Fix some bugs of spell" This reverts commit 8679462d1566a894acbcb61b0f738e674448d12f. --- config/fxdata/creature.cfg | 47 +++++++---------- src/config_creature.c | 4 -- src/config_creature.h | 4 -- src/creature_instances.c | 35 +++---------- src/creature_instances.h | 4 +- src/creature_states_combt.c | 101 +++++++++++++++--------------------- src/thing_creature.c | 34 +++++------- 7 files changed, 85 insertions(+), 144 deletions(-) diff --git a/config/fxdata/creature.cfg b/config/fxdata/creature.cfg index 8e3e1a5cf9..9ac6b0b63c 100644 --- a/config/fxdata/creature.cfg +++ b/config/fxdata/creature.cfg @@ -79,13 +79,6 @@ PrimaryTarget = 0 ; DISARMING allows instance to be used against traps ; DISPLAY_SWIPE Shows the swipe in possession loaded from the creatures PossessSwipeIndex ; NEEDS_TARGET Cannot be used in possession without a target to cast it on -; OFFENSIVE indicates that a buff will improve the offsenive ability, such as enhancing speed. For debuff, it -; indicates it will weaken the offsenive ability. -; DEFENSIVE indicates that a buff will improve the defensive ability, such as enhancing armour. For debugg, it -; indicates it will weaken the defensive ability. -; DAILY_LIFE_BUFF indicates that a buff can improve work throughput, or keep creature alive, or improve something -; when the creature is not in combat. -; ALLOWED_IN_PRISON indicates that this instance can be used when the caster is imprisoned. Properties = ; Function used as the instance action, and its parameters Function = none 0 0 @@ -221,7 +214,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF OFFENSIVE +Properties = RANGED_DEBUFF Function = creature_cast_spell SPELL_FREEZE 0 [instance8] @@ -237,7 +230,7 @@ ForceVisibility = 10 TooltipTextID = 228 SymbolSprites = 408 Graphics = ATTACK -Properties = SELF_BUFF DEFENSIVE +Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_ARMOUR 0 @@ -273,7 +266,7 @@ ForceVisibility = 10 TooltipTextID = 230 SymbolSprites = 412 Graphics = ATTACK -Properties = SELF_BUFF DEFENSIVE +Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_REBOUND 0 @@ -290,7 +283,7 @@ ForceVisibility = 1 TooltipTextID = 248 SymbolSprites = 414 Graphics = ATTACK -Properties = SELF_BUFF DEFENSIVE DAILY_LIFE_BUFF ALLOWED_IN_PRISON +Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_HEAL 0 @@ -327,7 +320,7 @@ TooltipTextID = 239 SymbolSprites = 418 Graphics = ATTACK PrimaryTarget = 3 -Properties = SELF_BUFF DEFENSIVE OFFENSIVE +Properties = SELF_BUFF Function = creature_cast_spell SPELL_INVISIBILITY 0 [instance14] @@ -361,7 +354,7 @@ TooltipTextID = 236 SymbolSprites = 422 Graphics = ATTACK PrimaryTarget = 3 -Properties = SELF_BUFF OFFENSIVE DAILY_LIFE_BUFF +Properties = SELF_BUFF Function = creature_cast_spell SPELL_SPEED 0 [instance16] @@ -380,7 +373,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF OFFENSIVE +Properties = RANGED_DEBUFF Function = creature_cast_spell SPELL_SLOW 0 [instance17] @@ -418,7 +411,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF OFFENSIVE +Properties = RANGED_DEBUFF Function = creature_cast_spell SPELL_FEAR 0 [instance19] @@ -494,7 +487,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = MAX PrimaryTarget = 3 -Properties = SELF_BUFF DEFENSIVE +Properties = Function = creature_cast_spell SPELL_WIND 0 [instance23] @@ -510,7 +503,7 @@ ForceVisibility = 100 TooltipTextID = 242 SymbolSprites = 434 Graphics = ATTACK -Properties = SELF_BUFF OFFENSIVE +Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_LIGHT 0 @@ -527,7 +520,7 @@ ForceVisibility = 1 TooltipTextID = 243 SymbolSprites = 436 Graphics = ATTACK -Properties = SELF_BUFF OFFENSIVE DEFENSIVE +Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_FLIGHT 0 @@ -544,7 +537,7 @@ ForceVisibility = 0 TooltipTextID = 232 SymbolSprites = 438 Graphics = ATTACK -Properties = SELF_BUFF OFFENSIVE DAILY_LIFE_BUFF ALLOWED_IN_PRISON +Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_SIGHT 0 @@ -818,7 +811,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF DEFENSIVE +Properties = RANGED_DEBUFF Function = creature_cast_spell SPELL_DISEASE 0 [instance42] @@ -837,7 +830,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = 1280 PrimaryTarget = 3 -Properties = RANGED_DEBUFF OFFENSIVE +Properties = RANGED_DEBUFF Function = creature_cast_spell SPELL_CHICKEN 0 [instance43] @@ -937,7 +930,7 @@ ForceVisibility = 30 TooltipTextID = 201 SymbolSprites = 434 Graphics = SCREAM -Properties = SELF_BUFF OFFENSIVE DAILY_LIFE_BUFF +Properties = SELF_BUFF PrimaryTarget = 8 Function = creature_cast_spell SPELL_SUMMON_FAMILIAR 0 @@ -955,7 +948,7 @@ ForceVisibility = 30 TooltipTextID = 201 SymbolSprites = 434 Graphics = SCREAM -Properties = SELF_BUFF OFFENSIVE +Properties = SELF_BUFF PrimaryTarget = 8 Function = creature_cast_spell SPELL_SUMMON_CREATURE 0 @@ -975,7 +968,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET DEFENSIVE DAILY_LIFE_BUFF ALLOWED_IN_PRISON +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET Function = creature_cast_spell SPELL_HEAL 0 ValidateFunc = validate_source_ranged_heal validate_target_ranged_heal SearchTargetsFunc = search_target_ranged_heal @@ -996,7 +989,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET OFFENSIVE DAILY_LIFE_BUFF +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET Function = creature_cast_spell SPELL_SPEED 0 ValidateFunc = validate_source_generic validate_target_generic SearchTargetsFunc = search_target_generic @@ -1017,7 +1010,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET DEFENSIVE +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET Function = creature_cast_spell SPELL_ARMOUR 0 ValidateFunc = validate_source_generic validate_target_generic SearchTargetsFunc = search_target_generic @@ -1038,7 +1031,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET DEFENSIVE +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET Function = creature_cast_spell SPELL_REBOUND 0 ValidateFunc = validate_source_generic validate_target_generic SearchTargetsFunc = search_target_generic diff --git a/src/config_creature.c b/src/config_creature.c index babaa15b57..f12f537b0a 100644 --- a/src/config_creature.c +++ b/src/config_creature.c @@ -112,10 +112,6 @@ const struct NamedCommand creaturetype_instance_properties[] = { {"DISPLAY_SWIPE", InstPF_UsesSwipe}, {"RANGED_BUFF", InstPF_RangedBuff}, {"NEEDS_TARGET", InstPF_NeedsTarget}, - {"OFFENSIVE", InstPF_Offensive}, - {"DEFENSIVE", InstPF_Defensive}, - {"DAILY_LIFE_BUFF", InstPF_DailyLifeBuff}, - {"ALLOWED_IN_PRISON", InstPF_AllowedInPrison}, {NULL, 0}, }; diff --git a/src/config_creature.h b/src/config_creature.h index fbe9e19956..12aee03436 100644 --- a/src/config_creature.h +++ b/src/config_creature.h @@ -150,10 +150,6 @@ enum InstancePropertiesFlags { InstPF_UsesSwipe = 0x0200, InstPF_RangedBuff = 0x0400, InstPF_NeedsTarget = 0x0800, - InstPF_Offensive = 0x1000, - InstPF_Defensive = 0x2000, - InstPF_DailyLifeBuff = 0x4000, - InstPF_AllowedInPrison = 0x8000, }; enum CreatureDeathKind { diff --git a/src/creature_instances.c b/src/creature_instances.c index 6bd78b515f..1c0865fba4 100644 --- a/src/creature_instances.c +++ b/src/creature_instances.c @@ -553,25 +553,28 @@ long instf_creature_cast_spell(struct Thing *creatng, long *param) } -TbBool process_creature_self_spell_casting(struct Thing* creatng) +long process_creature_self_spell_casting(struct Thing* creatng) { TRACE_THING(creatng); struct CreatureControl* cctrl = creature_control_get_from_thing(creatng); if (((creatng->alloc_flags & TAlF_IsControlled) != 0) || (cctrl->conscious_back_turns != 0) || ((cctrl->stateblock_flags & CCSpl_Freeze) != 0)) { - return false; + return 0; } if (cctrl->instance_id != CrInst_NULL) { - return false; + return 0; + } + if (cctrl->combat_flags != 0) { + return 0; } long inst_idx = get_self_spell_casting(creatng); if (inst_idx <= 0) { - return false; + return 0; } set_creature_instance(creatng, inst_idx, creatng->index, 0); - return true; + return 1; } /** @@ -1270,28 +1273,6 @@ TbBool validate_target_generic(struct Thing *source, struct Thing *target, CrIns return false; } - struct CreatureControl* cctrl = creature_control_get_from_thing(target); - if (creature_control_invalid(cctrl)) - { - ERRORLOG("Invalid creature control"); - return false; - } - // Check if this creature needs this buff by examining its state. - if (flag_is_set(cctrl->combat_flags, CmbtF_ObjctFight) || flag_is_set(cctrl->combat_flags, CmbtF_DoorFight)) - { - if(!flag_is_set(inst_inf->instance_property_flags, InstPF_Offensive)) - { - return false; // Offensive buff is useful when attacking door/dungeon heart. - } - } - else if(cctrl->combat_flags == 0) - { - if(!flag_is_set(inst_inf->instance_property_flags, InstPF_DailyLifeBuff)) - { - return false; // Daiy life buff is useful when not in combat. - } - } - return true; } diff --git a/src/creature_instances.h b/src/creature_instances.h index a7cc9c8bc6..0a24324a71 100644 --- a/src/creature_instances.h +++ b/src/creature_instances.h @@ -102,7 +102,7 @@ struct InstanceInfo { long reset_time; long fp_reset_time; unsigned char graphics_idx; - unsigned long instance_property_flags; + short instance_property_flags; short force_visibility; unsigned char primary_target; unsigned char func_idx; @@ -132,7 +132,7 @@ extern Creature_Target_Search_Func creature_instances_search_targets_func_list[] #define creature_instance_info_get(inst_idx) creature_instance_info_get_f(inst_idx,__func__) struct InstanceInfo *creature_instance_info_get_f(CrInstance inst_idx,const char *func_name); void process_creature_instance(struct Thing *thing); -TbBool process_creature_self_spell_casting(struct Thing* thing); +long process_creature_self_spell_casting(struct Thing* thing); CrInstance process_creature_ranged_buff_spell_casting(struct Thing* thing); TbBool creature_instance_info_invalid(const struct InstanceInfo *inst_inf); diff --git a/src/creature_states_combt.c b/src/creature_states_combt.c index 7862778c2c..67c3e49129 100644 --- a/src/creature_states_combt.c +++ b/src/creature_states_combt.c @@ -1862,87 +1862,70 @@ CrInstance get_best_self_preservation_instance_to_use(const struct Thing *thing) CrInstance get_self_spell_casting(const struct Thing *thing) { struct CreatureControl* cctrl = creature_control_get_from_thing(thing); - TbBool is_fighting = cctrl->combat_flags != 0; - TbBool is_in_custody = creature_is_kept_in_custody(thing); - SYNCDBG(11, "Processing %s(%d), combat flag: %d, spell flag: %d, in custody? %d", - thing_model_name(thing), thing->index, cctrl->combat_flags, cctrl->spell_flags, is_in_custody); + struct InstanceInfo* inst_inf; + if (creature_would_benefit_from_healing(thing)) + { + INSTANCE_RET_IF_AVAIL(thing, CrInst_HEAL); + } - for (int i = 0; i < game.conf.crtr_conf.instances_count; i++) + if (thing_is_creature_special_digger(thing) && creature_is_doing_digger_activity(thing)) { - struct InstanceInfo* inst_inf = creature_instance_info_get(i); - // Basic checks. - if (!flag_is_set(inst_inf->instance_property_flags, InstPF_SelfBuff) || - !creature_instance_is_available(thing, i) || - !creature_instance_has_reset(thing, i)) + // casting wind when under influence of gas + if ((cctrl->spell_flags & CSAfF_PoisonCloud) != 0) { - continue; // Cannot use. + INSTANCE_RET_IF_AVAIL(thing, CrInst_WIND); } - SpellKind spell_idx = inst_inf->func_params[0]; - if(inst_inf->func_params[0] != SplK_None && creature_affected_by_spell(thing, spell_idx)) + + for (short i = 0; i < game.conf.crtr_conf.instances_count; i++) { - continue; // Already being affected. + if (i == CrInst_HEAL) + continue; + + inst_inf = creature_instance_info_get(i); + if ((inst_inf->instance_property_flags & InstPF_SelfBuff)) + { + if (inst_inf->func_params[0] != SplK_None && + !creature_affected_by_spell(thing, inst_inf->func_params[0])) + { + INSTANCE_RET_IF_AVAIL(thing, i); + } + } } - if (!flag_is_set(inst_inf->instance_property_flags, InstPF_AllowedInPrison) && is_in_custody) + } + else + { + if (!creature_affected_by_spell(thing, SplK_Sight)) { - continue; // Cannot use in prison. + INSTANCE_RET_IF_AVAIL(thing, CrInst_SIGHT); } - // Our ideal goal is to avoid any hardcode for specific instance, - // but we still need to deal with some special instances. - switch(i) + + if (!creature_is_kept_in_custody(thing)) { - case CrInst_WIND: + // casting wind when under influence of gas if ((cctrl->spell_flags & CSAfF_PoisonCloud) != 0) { - SYNCDBG(11, "Use Wind when under influence of posion cloud."); - return CrInst_WIND; // Cast Wind when under influence of gas. - } - break; - case CrInst_FLY: - if (is_fighting || terrain_toxic_for_creature_at_position(thing, - coord_subtile(thing->mappos.x.val), coord_subtile(thing->mappos.y.val))) - { - return CrInst_FLY; + INSTANCE_RET_IF_AVAIL(thing, CrInst_WIND); } - break; - case CrInst_HEAL: - case CrInst_RANGED_HEAL: - if (creature_would_benefit_from_healing(thing)) + long state_type = get_creature_state_type(thing); + if (!creature_affected_by_spell(thing, SplK_Speed) && (state_type != CrStTyp_Idle)) { - return CrInst_HEAL; + INSTANCE_RET_IF_AVAIL(thing, CrInst_SPEED); } - break; - default: - if (!is_fighting && flag_is_set(inst_inf->instance_property_flags, InstPF_DailyLifeBuff)) + if (!creature_affected_by_spell(thing, SplK_Fly) && ((state_type != CrStTyp_Idle) || terrain_toxic_for_creature_at_position(thing, coord_subtile(thing->mappos.x.val), coord_subtile(thing->mappos.y.val)))) { - SYNCDBG(11, "Use %s as daily life buff.", creature_instance_code_name(i)); - return i; + INSTANCE_RET_IF_AVAIL(thing, CrInst_FLY); } - else if (flag_is_set(cctrl->combat_flags, CmbtF_ObjctFight) || - flag_is_set(cctrl->combat_flags, CmbtF_DoorFight)) + //TODO CREATURE_AI allow using invisibility when creature is being attacked or escaping + if (!creature_affected_by_spell(thing, SplK_Invisibility) && (state_type != CrStTyp_Idle)) { - // Creature is fighting a object (such as dungeon heart) or a door. - if(flag_is_set(inst_inf->instance_property_flags, InstPF_Offensive)) - { - // @todo What about Armour? Armour is defensive but it can protect creature from the AOE - // of the Keeper lightning spell. - SYNCDBG(11, "Use %s for object/door fighting case.", creature_instance_code_name(i)); - return i; // Offensive buff is useful when attacking door/dungeon heart. - } + INSTANCE_RET_IF_AVAIL(thing, CrInst_INVISIBILITY); } - else if (is_fighting) + if (state_type != CrStTyp_Idle) { - // Other fighting case. - if(flag_is_set(inst_inf->instance_property_flags, InstPF_Offensive) || - flag_is_set(inst_inf->instance_property_flags, InstPF_Defensive)) - { - SYNCDBG(11, "Use %s for general fighting case.", creature_instance_code_name(i)); - return i; // Offensive and Defensive are both useful. - } + INSTANCE_RET_IF_AVAIL(thing, CrInst_FAMILIAR); } - break; } } - return CrInst_NULL; } diff --git a/src/thing_creature.c b/src/thing_creature.c index 0ded5d5eba..500cc3d3d3 100644 --- a/src/thing_creature.c +++ b/src/thing_creature.c @@ -1802,33 +1802,25 @@ void creature_cast_spell(struct Thing *castng, SpellKind spl_idx, long shot_lvl, cctrl->teleport_x = trg_x; cctrl->teleport_y = trg_y; } - - if (spconf->caster_affected && castng->index == cctrl->targtng_idx) + // Check if the spell can be fired as a shot. It is definitely not if casted on itself. + if ((spconf->shot_model > 0) && (cctrl->targtng_idx != castng->index)) { - if (spconf->caster_affect_sound > 0) - thing_play_sample(castng, spconf->caster_affect_sound, NORMAL_PITCH, 0, 3, 0, 4, FULL_LOUDNESS); - apply_spell_effect_to_thing(castng, spl_idx, cctrl->explevel); - } - else if (spconf->shot_model > 0) - { - // Note that Wind has shot model and its CastAtThing is 0, besides, the target index is itself. if ((castng->alloc_flags & TAlF_IsControlled) != 0) i = THit_CrtrsNObjcts; else i = THit_CrtrsOnlyNotOwn; - const struct InstanceInfo* inst_inf = creature_instance_info_get(cctrl->instance_id); - if (flag_is_set(inst_inf->instance_property_flags, InstPF_RangedBuff)) - { - ERRORLOG("The %s(%d) tried to fire Ranged Buff's shot(%s) without a target!", - thing_model_name(castng), castng->index, shot_code_name(spconf->shot_model)); - } - else - { - thing_fire_shot(castng, INVALID_THING, spconf->shot_model, shot_lvl, i); - } + SYNCDBG(8,"The %s(%d) fire shot(%s) without a target with shot level %d, hit type: 0x%X", + thing_model_name(castng), castng->index, shot_code_name(spconf->shot_model), shot_lvl, i); + thing_fire_shot(castng, INVALID_THING, spconf->shot_model, shot_lvl, i); + } + // Check if the spell can be self-casted + else if (spconf->caster_affected) + { + if (spconf->caster_affect_sound > 0) + thing_play_sample(castng, spconf->caster_affect_sound, NORMAL_PITCH, 0, 3, 0, 4, FULL_LOUDNESS); + apply_spell_effect_to_thing(castng, spl_idx, cctrl->explevel); } - if (spconf->crtr_summon_model > 0) { thing_summon_temporary_creature(castng, spconf->crtr_summon_model, spconf->crtr_summon_level, spconf->crtr_summon_amount, spconf->duration, spl_idx); @@ -5985,7 +5977,7 @@ TngUpdateRet update_creature(struct Thing *thing) return TUFRet_Deleted; } - if (!process_creature_self_spell_casting(thing)) + if (process_creature_self_spell_casting(thing) == 0) { // If this creature didn't cast anything to itself, try to help others. process_creature_ranged_buff_spell_casting(thing); From 0094b60d90a005bcded795f3413516a51e0d96b0 Mon Sep 17 00:00:00 2001 From: Evan Date: Mon, 7 Oct 2024 00:25:14 +0800 Subject: [PATCH 03/12] Fix some bugs of spell This adds more properties for creature instance. * Fix Speed is not cast when attacking object/door. * Fix Ranged Rebound/Armour being cast when not in combat. Type: Bug Fix --- config/fxdata/creature.cfg | 47 ++++++++++------- src/config_creature.c | 4 ++ src/config_creature.h | 4 ++ src/creature_instances.c | 35 ++++++++++--- src/creature_instances.h | 4 +- src/creature_states_combt.c | 101 +++++++++++++++++++++--------------- 6 files changed, 123 insertions(+), 72 deletions(-) diff --git a/config/fxdata/creature.cfg b/config/fxdata/creature.cfg index 9ac6b0b63c..8e3e1a5cf9 100644 --- a/config/fxdata/creature.cfg +++ b/config/fxdata/creature.cfg @@ -79,6 +79,13 @@ PrimaryTarget = 0 ; DISARMING allows instance to be used against traps ; DISPLAY_SWIPE Shows the swipe in possession loaded from the creatures PossessSwipeIndex ; NEEDS_TARGET Cannot be used in possession without a target to cast it on +; OFFENSIVE indicates that a buff will improve the offsenive ability, such as enhancing speed. For debuff, it +; indicates it will weaken the offsenive ability. +; DEFENSIVE indicates that a buff will improve the defensive ability, such as enhancing armour. For debugg, it +; indicates it will weaken the defensive ability. +; DAILY_LIFE_BUFF indicates that a buff can improve work throughput, or keep creature alive, or improve something +; when the creature is not in combat. +; ALLOWED_IN_PRISON indicates that this instance can be used when the caster is imprisoned. Properties = ; Function used as the instance action, and its parameters Function = none 0 0 @@ -214,7 +221,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF +Properties = RANGED_DEBUFF OFFENSIVE Function = creature_cast_spell SPELL_FREEZE 0 [instance8] @@ -230,7 +237,7 @@ ForceVisibility = 10 TooltipTextID = 228 SymbolSprites = 408 Graphics = ATTACK -Properties = SELF_BUFF +Properties = SELF_BUFF DEFENSIVE PrimaryTarget = 3 Function = creature_cast_spell SPELL_ARMOUR 0 @@ -266,7 +273,7 @@ ForceVisibility = 10 TooltipTextID = 230 SymbolSprites = 412 Graphics = ATTACK -Properties = SELF_BUFF +Properties = SELF_BUFF DEFENSIVE PrimaryTarget = 3 Function = creature_cast_spell SPELL_REBOUND 0 @@ -283,7 +290,7 @@ ForceVisibility = 1 TooltipTextID = 248 SymbolSprites = 414 Graphics = ATTACK -Properties = SELF_BUFF +Properties = SELF_BUFF DEFENSIVE DAILY_LIFE_BUFF ALLOWED_IN_PRISON PrimaryTarget = 3 Function = creature_cast_spell SPELL_HEAL 0 @@ -320,7 +327,7 @@ TooltipTextID = 239 SymbolSprites = 418 Graphics = ATTACK PrimaryTarget = 3 -Properties = SELF_BUFF +Properties = SELF_BUFF DEFENSIVE OFFENSIVE Function = creature_cast_spell SPELL_INVISIBILITY 0 [instance14] @@ -354,7 +361,7 @@ TooltipTextID = 236 SymbolSprites = 422 Graphics = ATTACK PrimaryTarget = 3 -Properties = SELF_BUFF +Properties = SELF_BUFF OFFENSIVE DAILY_LIFE_BUFF Function = creature_cast_spell SPELL_SPEED 0 [instance16] @@ -373,7 +380,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF +Properties = RANGED_DEBUFF OFFENSIVE Function = creature_cast_spell SPELL_SLOW 0 [instance17] @@ -411,7 +418,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF +Properties = RANGED_DEBUFF OFFENSIVE Function = creature_cast_spell SPELL_FEAR 0 [instance19] @@ -487,7 +494,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = MAX PrimaryTarget = 3 -Properties = +Properties = SELF_BUFF DEFENSIVE Function = creature_cast_spell SPELL_WIND 0 [instance23] @@ -503,7 +510,7 @@ ForceVisibility = 100 TooltipTextID = 242 SymbolSprites = 434 Graphics = ATTACK -Properties = SELF_BUFF +Properties = SELF_BUFF OFFENSIVE PrimaryTarget = 3 Function = creature_cast_spell SPELL_LIGHT 0 @@ -520,7 +527,7 @@ ForceVisibility = 1 TooltipTextID = 243 SymbolSprites = 436 Graphics = ATTACK -Properties = SELF_BUFF +Properties = SELF_BUFF OFFENSIVE DEFENSIVE PrimaryTarget = 3 Function = creature_cast_spell SPELL_FLIGHT 0 @@ -537,7 +544,7 @@ ForceVisibility = 0 TooltipTextID = 232 SymbolSprites = 438 Graphics = ATTACK -Properties = SELF_BUFF +Properties = SELF_BUFF OFFENSIVE DAILY_LIFE_BUFF ALLOWED_IN_PRISON PrimaryTarget = 3 Function = creature_cast_spell SPELL_SIGHT 0 @@ -811,7 +818,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF +Properties = RANGED_DEBUFF DEFENSIVE Function = creature_cast_spell SPELL_DISEASE 0 [instance42] @@ -830,7 +837,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = 1280 PrimaryTarget = 3 -Properties = RANGED_DEBUFF +Properties = RANGED_DEBUFF OFFENSIVE Function = creature_cast_spell SPELL_CHICKEN 0 [instance43] @@ -930,7 +937,7 @@ ForceVisibility = 30 TooltipTextID = 201 SymbolSprites = 434 Graphics = SCREAM -Properties = SELF_BUFF +Properties = SELF_BUFF OFFENSIVE DAILY_LIFE_BUFF PrimaryTarget = 8 Function = creature_cast_spell SPELL_SUMMON_FAMILIAR 0 @@ -948,7 +955,7 @@ ForceVisibility = 30 TooltipTextID = 201 SymbolSprites = 434 Graphics = SCREAM -Properties = SELF_BUFF +Properties = SELF_BUFF OFFENSIVE PrimaryTarget = 8 Function = creature_cast_spell SPELL_SUMMON_CREATURE 0 @@ -968,7 +975,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET DEFENSIVE DAILY_LIFE_BUFF ALLOWED_IN_PRISON Function = creature_cast_spell SPELL_HEAL 0 ValidateFunc = validate_source_ranged_heal validate_target_ranged_heal SearchTargetsFunc = search_target_ranged_heal @@ -989,7 +996,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET OFFENSIVE DAILY_LIFE_BUFF Function = creature_cast_spell SPELL_SPEED 0 ValidateFunc = validate_source_generic validate_target_generic SearchTargetsFunc = search_target_generic @@ -1010,7 +1017,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET DEFENSIVE Function = creature_cast_spell SPELL_ARMOUR 0 ValidateFunc = validate_source_generic validate_target_generic SearchTargetsFunc = search_target_generic @@ -1031,7 +1038,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET DEFENSIVE Function = creature_cast_spell SPELL_REBOUND 0 ValidateFunc = validate_source_generic validate_target_generic SearchTargetsFunc = search_target_generic diff --git a/src/config_creature.c b/src/config_creature.c index f12f537b0a..babaa15b57 100644 --- a/src/config_creature.c +++ b/src/config_creature.c @@ -112,6 +112,10 @@ const struct NamedCommand creaturetype_instance_properties[] = { {"DISPLAY_SWIPE", InstPF_UsesSwipe}, {"RANGED_BUFF", InstPF_RangedBuff}, {"NEEDS_TARGET", InstPF_NeedsTarget}, + {"OFFENSIVE", InstPF_Offensive}, + {"DEFENSIVE", InstPF_Defensive}, + {"DAILY_LIFE_BUFF", InstPF_DailyLifeBuff}, + {"ALLOWED_IN_PRISON", InstPF_AllowedInPrison}, {NULL, 0}, }; diff --git a/src/config_creature.h b/src/config_creature.h index 12aee03436..fbe9e19956 100644 --- a/src/config_creature.h +++ b/src/config_creature.h @@ -150,6 +150,10 @@ enum InstancePropertiesFlags { InstPF_UsesSwipe = 0x0200, InstPF_RangedBuff = 0x0400, InstPF_NeedsTarget = 0x0800, + InstPF_Offensive = 0x1000, + InstPF_Defensive = 0x2000, + InstPF_DailyLifeBuff = 0x4000, + InstPF_AllowedInPrison = 0x8000, }; enum CreatureDeathKind { diff --git a/src/creature_instances.c b/src/creature_instances.c index 1c0865fba4..6bd78b515f 100644 --- a/src/creature_instances.c +++ b/src/creature_instances.c @@ -553,28 +553,25 @@ long instf_creature_cast_spell(struct Thing *creatng, long *param) } -long process_creature_self_spell_casting(struct Thing* creatng) +TbBool process_creature_self_spell_casting(struct Thing* creatng) { TRACE_THING(creatng); struct CreatureControl* cctrl = creature_control_get_from_thing(creatng); if (((creatng->alloc_flags & TAlF_IsControlled) != 0) || (cctrl->conscious_back_turns != 0) || ((cctrl->stateblock_flags & CCSpl_Freeze) != 0)) { - return 0; + return false; } if (cctrl->instance_id != CrInst_NULL) { - return 0; - } - if (cctrl->combat_flags != 0) { - return 0; + return false; } long inst_idx = get_self_spell_casting(creatng); if (inst_idx <= 0) { - return 0; + return false; } set_creature_instance(creatng, inst_idx, creatng->index, 0); - return 1; + return true; } /** @@ -1273,6 +1270,28 @@ TbBool validate_target_generic(struct Thing *source, struct Thing *target, CrIns return false; } + struct CreatureControl* cctrl = creature_control_get_from_thing(target); + if (creature_control_invalid(cctrl)) + { + ERRORLOG("Invalid creature control"); + return false; + } + // Check if this creature needs this buff by examining its state. + if (flag_is_set(cctrl->combat_flags, CmbtF_ObjctFight) || flag_is_set(cctrl->combat_flags, CmbtF_DoorFight)) + { + if(!flag_is_set(inst_inf->instance_property_flags, InstPF_Offensive)) + { + return false; // Offensive buff is useful when attacking door/dungeon heart. + } + } + else if(cctrl->combat_flags == 0) + { + if(!flag_is_set(inst_inf->instance_property_flags, InstPF_DailyLifeBuff)) + { + return false; // Daiy life buff is useful when not in combat. + } + } + return true; } diff --git a/src/creature_instances.h b/src/creature_instances.h index 0a24324a71..a7cc9c8bc6 100644 --- a/src/creature_instances.h +++ b/src/creature_instances.h @@ -102,7 +102,7 @@ struct InstanceInfo { long reset_time; long fp_reset_time; unsigned char graphics_idx; - short instance_property_flags; + unsigned long instance_property_flags; short force_visibility; unsigned char primary_target; unsigned char func_idx; @@ -132,7 +132,7 @@ extern Creature_Target_Search_Func creature_instances_search_targets_func_list[] #define creature_instance_info_get(inst_idx) creature_instance_info_get_f(inst_idx,__func__) struct InstanceInfo *creature_instance_info_get_f(CrInstance inst_idx,const char *func_name); void process_creature_instance(struct Thing *thing); -long process_creature_self_spell_casting(struct Thing* thing); +TbBool process_creature_self_spell_casting(struct Thing* thing); CrInstance process_creature_ranged_buff_spell_casting(struct Thing* thing); TbBool creature_instance_info_invalid(const struct InstanceInfo *inst_inf); diff --git a/src/creature_states_combt.c b/src/creature_states_combt.c index 67c3e49129..7862778c2c 100644 --- a/src/creature_states_combt.c +++ b/src/creature_states_combt.c @@ -1862,70 +1862,87 @@ CrInstance get_best_self_preservation_instance_to_use(const struct Thing *thing) CrInstance get_self_spell_casting(const struct Thing *thing) { struct CreatureControl* cctrl = creature_control_get_from_thing(thing); - struct InstanceInfo* inst_inf; - if (creature_would_benefit_from_healing(thing)) - { - INSTANCE_RET_IF_AVAIL(thing, CrInst_HEAL); - } + TbBool is_fighting = cctrl->combat_flags != 0; + TbBool is_in_custody = creature_is_kept_in_custody(thing); + SYNCDBG(11, "Processing %s(%d), combat flag: %d, spell flag: %d, in custody? %d", + thing_model_name(thing), thing->index, cctrl->combat_flags, cctrl->spell_flags, is_in_custody); - if (thing_is_creature_special_digger(thing) && creature_is_doing_digger_activity(thing)) + for (int i = 0; i < game.conf.crtr_conf.instances_count; i++) { - // casting wind when under influence of gas - if ((cctrl->spell_flags & CSAfF_PoisonCloud) != 0) + struct InstanceInfo* inst_inf = creature_instance_info_get(i); + // Basic checks. + if (!flag_is_set(inst_inf->instance_property_flags, InstPF_SelfBuff) || + !creature_instance_is_available(thing, i) || + !creature_instance_has_reset(thing, i)) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_WIND); + continue; // Cannot use. } - - for (short i = 0; i < game.conf.crtr_conf.instances_count; i++) + SpellKind spell_idx = inst_inf->func_params[0]; + if(inst_inf->func_params[0] != SplK_None && creature_affected_by_spell(thing, spell_idx)) { - if (i == CrInst_HEAL) - continue; - - inst_inf = creature_instance_info_get(i); - if ((inst_inf->instance_property_flags & InstPF_SelfBuff)) - { - if (inst_inf->func_params[0] != SplK_None && - !creature_affected_by_spell(thing, inst_inf->func_params[0])) - { - INSTANCE_RET_IF_AVAIL(thing, i); - } - } + continue; // Already being affected. } - } - else - { - if (!creature_affected_by_spell(thing, SplK_Sight)) + if (!flag_is_set(inst_inf->instance_property_flags, InstPF_AllowedInPrison) && is_in_custody) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_SIGHT); + continue; // Cannot use in prison. } - - if (!creature_is_kept_in_custody(thing)) + // Our ideal goal is to avoid any hardcode for specific instance, + // but we still need to deal with some special instances. + switch(i) { - // casting wind when under influence of gas + case CrInst_WIND: if ((cctrl->spell_flags & CSAfF_PoisonCloud) != 0) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_WIND); + SYNCDBG(11, "Use Wind when under influence of posion cloud."); + return CrInst_WIND; // Cast Wind when under influence of gas. + } + break; + case CrInst_FLY: + if (is_fighting || terrain_toxic_for_creature_at_position(thing, + coord_subtile(thing->mappos.x.val), coord_subtile(thing->mappos.y.val))) + { + return CrInst_FLY; } - long state_type = get_creature_state_type(thing); - if (!creature_affected_by_spell(thing, SplK_Speed) && (state_type != CrStTyp_Idle)) + break; + case CrInst_HEAL: + case CrInst_RANGED_HEAL: + if (creature_would_benefit_from_healing(thing)) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_SPEED); + return CrInst_HEAL; } - if (!creature_affected_by_spell(thing, SplK_Fly) && ((state_type != CrStTyp_Idle) || terrain_toxic_for_creature_at_position(thing, coord_subtile(thing->mappos.x.val), coord_subtile(thing->mappos.y.val)))) + break; + default: + if (!is_fighting && flag_is_set(inst_inf->instance_property_flags, InstPF_DailyLifeBuff)) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_FLY); + SYNCDBG(11, "Use %s as daily life buff.", creature_instance_code_name(i)); + return i; } - //TODO CREATURE_AI allow using invisibility when creature is being attacked or escaping - if (!creature_affected_by_spell(thing, SplK_Invisibility) && (state_type != CrStTyp_Idle)) + else if (flag_is_set(cctrl->combat_flags, CmbtF_ObjctFight) || + flag_is_set(cctrl->combat_flags, CmbtF_DoorFight)) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_INVISIBILITY); + // Creature is fighting a object (such as dungeon heart) or a door. + if(flag_is_set(inst_inf->instance_property_flags, InstPF_Offensive)) + { + // @todo What about Armour? Armour is defensive but it can protect creature from the AOE + // of the Keeper lightning spell. + SYNCDBG(11, "Use %s for object/door fighting case.", creature_instance_code_name(i)); + return i; // Offensive buff is useful when attacking door/dungeon heart. + } } - if (state_type != CrStTyp_Idle) + else if (is_fighting) { - INSTANCE_RET_IF_AVAIL(thing, CrInst_FAMILIAR); + // Other fighting case. + if(flag_is_set(inst_inf->instance_property_flags, InstPF_Offensive) || + flag_is_set(inst_inf->instance_property_flags, InstPF_Defensive)) + { + SYNCDBG(11, "Use %s for general fighting case.", creature_instance_code_name(i)); + return i; // Offensive and Defensive are both useful. + } } + break; } } + return CrInst_NULL; } From d6c2cfd214fe06e7de943f58cb81abd7a0628f88 Mon Sep 17 00:00:00 2001 From: Evan Date: Mon, 7 Oct 2024 02:10:27 +0800 Subject: [PATCH 04/12] Reflect to the change of process_creature_self_spell_casting() --- src/thing_creature.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/thing_creature.c b/src/thing_creature.c index 500cc3d3d3..fa1eb471d2 100644 --- a/src/thing_creature.c +++ b/src/thing_creature.c @@ -5977,7 +5977,7 @@ TngUpdateRet update_creature(struct Thing *thing) return TUFRet_Deleted; } - if (process_creature_self_spell_casting(thing) == 0) + if (!process_creature_self_spell_casting(thing)) { // If this creature didn't cast anything to itself, try to help others. process_creature_ranged_buff_spell_casting(thing); From 314284fc5d083d8cb73182314b27b92b012e79f0 Mon Sep 17 00:00:00 2001 From: Evan Date: Tue, 8 Oct 2024 23:57:30 +0800 Subject: [PATCH 05/12] Improve validate function for buffs Type: New Feature --- config/fxdata/creature.cfg | 99 ++++++---- src/bflib_basics.h | 1 + src/config.c | 1 + src/config_creature.c | 65 +++++-- src/config_creature.h | 4 - src/creature_control.h | 4 +- src/creature_instances.c | 363 ++++++++++++++++++++++++++++++------ src/creature_instances.h | 33 ++-- src/creature_states_combt.c | 95 +++------- src/thing_creature.c | 26 +-- 10 files changed, 475 insertions(+), 216 deletions(-) diff --git a/config/fxdata/creature.cfg b/config/fxdata/creature.cfg index 8e3e1a5cf9..6a77edb308 100644 --- a/config/fxdata/creature.cfg +++ b/config/fxdata/creature.cfg @@ -79,20 +79,15 @@ PrimaryTarget = 0 ; DISARMING allows instance to be used against traps ; DISPLAY_SWIPE Shows the swipe in possession loaded from the creatures PossessSwipeIndex ; NEEDS_TARGET Cannot be used in possession without a target to cast it on -; OFFENSIVE indicates that a buff will improve the offsenive ability, such as enhancing speed. For debuff, it -; indicates it will weaken the offsenive ability. -; DEFENSIVE indicates that a buff will improve the defensive ability, such as enhancing armour. For debugg, it -; indicates it will weaken the defensive ability. -; DAILY_LIFE_BUFF indicates that a buff can improve work throughput, or keep creature alive, or improve something -; when the creature is not in combat. -; ALLOWED_IN_PRISON indicates that this instance can be used when the caster is imprisoned. Properties = ; Function used as the instance action, and its parameters Function = none 0 0 -; Functions used validate the source and the target of the spell. -ValidateFunc = validate_source_generic validate_target_generic -; Functions used search targets of the spell. -SearchTargetsFunc = search_target_generic +; Functions used validate the source of the spell. Could have two parameters. +ValidateSourceFunc = validate_source_generic 0 0 +; Functions used validate the the target of the spell. Could have two parameters. +ValidateTargetFunc = validate_target_generic 0 0 +; Functions used search targets of the spell. Could have two parameters. +SearchTargetsFunc = search_target_generic 0 0 [instance1] Name = SWING_WEAPON_SWORD @@ -221,7 +216,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF OFFENSIVE +Properties = RANGED_DEBUFF Function = creature_cast_spell SPELL_FREEZE 0 [instance8] @@ -237,9 +232,11 @@ ForceVisibility = 10 TooltipTextID = 228 SymbolSprites = 408 Graphics = ATTACK -Properties = SELF_BUFF DEFENSIVE +Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_ARMOUR 0 +ValidateSourceFunc = validate_source_generic 0 0 +ValidateTargetFunc = validate_target_benefits_from_defensive 0 0 [instance9] Name = LIGHTNING @@ -273,9 +270,11 @@ ForceVisibility = 10 TooltipTextID = 230 SymbolSprites = 412 Graphics = ATTACK -Properties = SELF_BUFF DEFENSIVE +Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_REBOUND 0 +ValidateSourceFunc = validate_source_generic 0 0 +ValidateTargetFunc = validate_target_benefits_from_defensive_missile 0 0 [instance11] Name = HEAL @@ -290,9 +289,11 @@ ForceVisibility = 1 TooltipTextID = 248 SymbolSprites = 414 Graphics = ATTACK -Properties = SELF_BUFF DEFENSIVE DAILY_LIFE_BUFF ALLOWED_IN_PRISON +Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_HEAL 0 +ValidateSourceFunc = validate_source_even_in_prison 0 0 +ValidateTargetFunc = validate_target_benefits_from_healing 0 0 [instance12] Name = POISON_CLOUD @@ -327,8 +328,10 @@ TooltipTextID = 239 SymbolSprites = 418 Graphics = ATTACK PrimaryTarget = 3 -Properties = SELF_BUFF DEFENSIVE OFFENSIVE +Properties = SELF_BUFF Function = creature_cast_spell SPELL_INVISIBILITY 0 +ValidateSourceFunc = validate_source_generic 0 0 +ValidateTargetFunc = validate_target_benefits_from_defensive 0 0 [instance14] Name = TELEPORT @@ -361,8 +364,10 @@ TooltipTextID = 236 SymbolSprites = 422 Graphics = ATTACK PrimaryTarget = 3 -Properties = SELF_BUFF OFFENSIVE DAILY_LIFE_BUFF +Properties = SELF_BUFF Function = creature_cast_spell SPELL_SPEED 0 +ValidateSourceFunc = validate_source_generic 0 0 +ValidateTargetFunc = validate_target_generic 0 0 [instance16] Name = SLOW @@ -380,7 +385,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF OFFENSIVE +Properties = RANGED_DEBUFF Function = creature_cast_spell SPELL_SLOW 0 [instance17] @@ -418,7 +423,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF OFFENSIVE +Properties = RANGED_DEBUFF Function = creature_cast_spell SPELL_FEAR 0 [instance19] @@ -494,8 +499,12 @@ Graphics = ATTACK RangeMin = MIN RangeMax = MAX PrimaryTarget = 3 -Properties = SELF_BUFF DEFENSIVE +Properties = SELF_BUFF Function = creature_cast_spell SPELL_WIND 0 +ValidateSourceFunc = validate_source_generic 0 0 +; Wind can disperse gas and push away melee enemies. +; It has more than one merit. It deserve one dedicated function +ValidateTargetFunc = validate_target_benefits_from_wind 0 0 [instance23] Name = LIGHT @@ -510,7 +519,7 @@ ForceVisibility = 100 TooltipTextID = 242 SymbolSprites = 434 Graphics = ATTACK -Properties = SELF_BUFF OFFENSIVE +Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_LIGHT 0 @@ -527,9 +536,11 @@ ForceVisibility = 1 TooltipTextID = 243 SymbolSprites = 436 Graphics = ATTACK -Properties = SELF_BUFF OFFENSIVE DEFENSIVE +Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_FLIGHT 0 +ValidateSourceFunc = validate_source_generic 0 0 +ValidateTargetFunc = validate_target_benefits_from_higher_altitude 0 0 [instance25] Name = SIGHT @@ -544,9 +555,11 @@ ForceVisibility = 0 TooltipTextID = 232 SymbolSprites = 438 Graphics = ATTACK -Properties = SELF_BUFF OFFENSIVE DAILY_LIFE_BUFF ALLOWED_IN_PRISON +Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_SIGHT 0 +ValidateSourceFunc = validate_source_even_in_prison 0 0 +ValidateTargetFunc = validate_target_generic 0 0 [instance26] Name = GRENADE @@ -818,7 +831,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = MAX PrimaryTarget = 3 -Properties = RANGED_DEBUFF DEFENSIVE +Properties = RANGED_DEBUFF Function = creature_cast_spell SPELL_DISEASE 0 [instance42] @@ -837,7 +850,7 @@ Graphics = ATTACK RangeMin = 156 RangeMax = 1280 PrimaryTarget = 3 -Properties = RANGED_DEBUFF OFFENSIVE +Properties = RANGED_DEBUFF Function = creature_cast_spell SPELL_CHICKEN 0 [instance43] @@ -937,9 +950,11 @@ ForceVisibility = 30 TooltipTextID = 201 SymbolSprites = 434 Graphics = SCREAM -Properties = SELF_BUFF OFFENSIVE DAILY_LIFE_BUFF +Properties = SELF_BUFF PrimaryTarget = 8 Function = creature_cast_spell SPELL_SUMMON_FAMILIAR 0 +ValidateSourceFunc = validate_source_generic 0 0 +ValidateTargetFunc = validate_target_generic 0 0 ; Creature is summoned only in battle [instance49] @@ -955,9 +970,11 @@ ForceVisibility = 30 TooltipTextID = 201 SymbolSprites = 434 Graphics = SCREAM -Properties = SELF_BUFF OFFENSIVE +Properties = SELF_BUFF PrimaryTarget = 8 Function = creature_cast_spell SPELL_SUMMON_CREATURE 0 +ValidateSourceFunc = validate_source_generic 0 0 +ValidateTargetFunc = validate_target_benefits_from_offensive 0 0 [instance50] Name = RANGED_HEAL @@ -975,10 +992,11 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET DEFENSIVE DAILY_LIFE_BUFF ALLOWED_IN_PRISON +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET Function = creature_cast_spell SPELL_HEAL 0 -ValidateFunc = validate_source_ranged_heal validate_target_ranged_heal -SearchTargetsFunc = search_target_ranged_heal +ValidateSourceFunc = validate_source_even_in_prison 0 0 +ValidateTargetFunc = validate_target_benefits_from_healing 0 0 +SearchTargetsFunc = search_target_ranged_heal 0 0 [instance51] Name = RANGED_SPEED @@ -996,10 +1014,11 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET OFFENSIVE DAILY_LIFE_BUFF +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET Function = creature_cast_spell SPELL_SPEED 0 -ValidateFunc = validate_source_generic validate_target_generic -SearchTargetsFunc = search_target_generic +ValidateSourceFunc = validate_source_generic 0 0 +ValidateTargetFunc = validate_target_generic 0 0 +SearchTargetsFunc = search_target_generic 0 0 [instance52] Name = RANGED_ARMOUR @@ -1017,10 +1036,11 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET DEFENSIVE +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET Function = creature_cast_spell SPELL_ARMOUR 0 -ValidateFunc = validate_source_generic validate_target_generic -SearchTargetsFunc = search_target_generic +ValidateSourceFunc = validate_source_generic 0 0 +ValidateTargetFunc = validate_target_benefits_from_defensive 0 0 +SearchTargetsFunc = search_target_generic 0 0 [instance53] Name = RANGED_REBOUND @@ -1038,10 +1058,11 @@ Graphics = ATTACK RangeMin = MIN RangeMax = 5120 PrimaryTarget = 6 -Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET DEFENSIVE +Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET Function = creature_cast_spell SPELL_REBOUND 0 -ValidateFunc = validate_source_generic validate_target_generic -SearchTargetsFunc = search_target_generic +ValidateSourceFunc = validate_source_generic 0 0 +ValidateTargetFunc = validate_target_benefits_from_defensive_missile 0 0 +SearchTargetsFunc = search_target_generic 0 0 [job0] ; Empty job, indicates no job assigned diff --git a/src/bflib_basics.h b/src/bflib_basics.h index 0c6f5c882b..6c3f780f73 100644 --- a/src/bflib_basics.h +++ b/src/bflib_basics.h @@ -64,6 +64,7 @@ enum TbErrorCode { /******************************************************************************/ #pragma pack(1) +// These types should be deprecated because we have stdint.h now. typedef unsigned long ulong; typedef unsigned int uint; typedef unsigned short ushort; diff --git a/src/config.c b/src/config.c index 5dfb8cb972..fdb5ef89a8 100644 --- a/src/config.c +++ b/src/config.c @@ -377,6 +377,7 @@ short find_conf_block(const char *buf,long *pos,long buflen,const char *blocknam /** * Recognizes config command and returns its number, or negative status code. + * The string comparison is done by case-insensitive. * @param buf * @param pos * @param buflen diff --git a/src/config_creature.c b/src/config_creature.c index babaa15b57..0db298bb84 100644 --- a/src/config_creature.c +++ b/src/config_creature.c @@ -76,6 +76,8 @@ const struct NamedCommand creaturetype_experience_commands[] = { {NULL, 0}, }; +// It is pointless to use all uppercase letters here. +// The comparison is case-insensitive and we use Camel-Case in the cfg files. const struct NamedCommand creaturetype_instance_commands[] = { {"NAME", 1}, {"TIME", 2}, @@ -94,8 +96,9 @@ const struct NamedCommand creaturetype_instance_commands[] = { {"PROPERTIES", 15}, {"FPINSTANTCAST", 16}, {"PRIMARYTARGET", 17}, - {"VALIDATEFUNC", 18}, - {"SEARCHTARGETSFUNC", 19}, + {"ValidateSourceFunc", 18}, + {"ValidateTargetFunc", 19}, + {"SearchTargetsFunc", 20}, {NULL, 0}, }; @@ -112,10 +115,6 @@ const struct NamedCommand creaturetype_instance_properties[] = { {"DISPLAY_SWIPE", InstPF_UsesSwipe}, {"RANGED_BUFF", InstPF_RangedBuff}, {"NEEDS_TARGET", InstPF_NeedsTarget}, - {"OFFENSIVE", InstPF_Offensive}, - {"DEFENSIVE", InstPF_Defensive}, - {"DAILY_LIFE_BUFF", InstPF_DailyLifeBuff}, - {"ALLOWED_IN_PRISON", InstPF_AllowedInPrison}, {NULL, 0}, }; @@ -841,9 +840,9 @@ TbBool parse_creaturetype_instance_blocks(char *buf, long len, const char *confi game.conf.magic_conf.instance_info[i].tooltip_stridx = 0; game.conf.magic_conf.instance_info[i].range_min = -1; game.conf.magic_conf.instance_info[i].range_max = -1; - game.conf.magic_conf.instance_info[i].validate_func_idx[0] = 1; - game.conf.magic_conf.instance_info[i].validate_func_idx[1] = 2; - game.conf.magic_conf.instance_info[i].search_func_idx = 1; + game.conf.magic_conf.instance_info[i].validate_source_func = 1; + game.conf.magic_conf.instance_info[i].validate_target_func = 3; + game.conf.magic_conf.instance_info[i].search_func = 1; } } } @@ -1156,26 +1155,64 @@ TbBool parse_creaturetype_instance_blocks(char *buf, long len, const char *confi COMMAND_TEXT(cmd_num), block_buf, config_textname); } break; - case 18: // VALIDATE_FUNC + case 18: // ValidateSourceFunc k = recognize_conf_parameter(buf, &pos, len, creature_instances_validate_func_type); if (k > 0) { - inst_inf->validate_func_idx[0] = k; + inst_inf->validate_source_func = k; n++; } + if (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0) + { + k = atoi(word_buf); + inst_inf->validate_source_func_params[0] = k; + n++; + if (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0) + { + k = atoi(word_buf); + inst_inf->validate_source_func_params[1] = k; + n++; + } + } + break; + case 19: // ValidateTargetFunc k = recognize_conf_parameter(buf, &pos, len, creature_instances_validate_func_type); if (k > 0) { - inst_inf->validate_func_idx[1] = k; + inst_inf->validate_target_func = k; + n++; + } + if (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0) + { + k = atoi(word_buf); + inst_inf->validate_target_func_params[0] = k; n++; + if (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0) + { + k = atoi(word_buf); + inst_inf->validate_target_func_params[1] = k; + n++; + } } break; - case 19: // SEARCH_TARGETS_FUNC + case 20: // SearchTargetsFunc k = recognize_conf_parameter(buf, &pos, len, creature_instances_search_targets_func_type); if (k > 0) { - inst_inf->search_func_idx = k; + inst_inf->search_func = k; + n++; + } + if (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0) + { + k = atoi(word_buf); + inst_inf->search_func_params[0] = k; n++; + if (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0) + { + k = atoi(word_buf); + inst_inf->search_func_params[1] = k; + n++; + } } break; case 0: // comment diff --git a/src/config_creature.h b/src/config_creature.h index fbe9e19956..12aee03436 100644 --- a/src/config_creature.h +++ b/src/config_creature.h @@ -150,10 +150,6 @@ enum InstancePropertiesFlags { InstPF_UsesSwipe = 0x0200, InstPF_RangedBuff = 0x0400, InstPF_NeedsTarget = 0x0800, - InstPF_Offensive = 0x1000, - InstPF_Defensive = 0x2000, - InstPF_DailyLifeBuff = 0x4000, - InstPF_AllowedInPrison = 0x8000, }; enum CreatureDeathKind { diff --git a/src/creature_control.h b/src/creature_control.h index 8f9bdf8bf1..b5124fec11 100644 --- a/src/creature_control.h +++ b/src/creature_control.h @@ -146,8 +146,8 @@ struct CreatureControl { unsigned char party_objective; unsigned long wait_to_turn; short distance_to_destination; - short opponents_melee[COMBAT_MELEE_OPPONENTS_LIMIT]; - short opponents_ranged[COMBAT_RANGED_OPPONENTS_LIMIT]; + ThingIndex opponents_melee[COMBAT_MELEE_OPPONENTS_LIMIT]; + ThingIndex opponents_ranged[COMBAT_RANGED_OPPONENTS_LIMIT]; unsigned char opponents_melee_count; unsigned char opponents_ranged_count; unsigned short players_prev_creature_idx; diff --git a/src/creature_instances.c b/src/creature_instances.c index 6bd78b515f..b22f8ffe31 100644 --- a/src/creature_instances.c +++ b/src/creature_instances.c @@ -114,19 +114,29 @@ Creature_Instf_Func creature_instances_func_list[] = { }; const struct NamedCommand creature_instances_validate_func_type[] = { - {"validate_source_generic", 1}, - {"validate_target_generic", 2}, - {"validate_source_ranged_heal", 3}, - {"validate_target_ranged_heal", 4}, - {NULL, 0}, + {"validate_source_generic", 1}, + {"validate_source_even_in_prison", 2}, + {"validate_target_generic", 3}, + {"validate_target_benefits_from_defensive_missile", 4}, + {"validate_target_benefits_from_defensive", 5}, + {"validate_target_benefits_from_healing", 6}, + {"validate_target_benefits_from_higher_altitude", 7}, + {"validate_target_benefits_from_offensive", 8}, + {"validate_target_benefits_from_wind", 9}, + {NULL, 0}, }; Creature_Validate_Func creature_instances_validate_func_list[] = { NULL, validate_source_generic, + validate_source_even_in_prison, validate_target_generic, - validate_source_ranged_heal, - validate_target_ranged_heal, + validate_target_benefits_from_defensive_missile, + validate_target_benefits_from_defensive, + validate_target_benefits_from_healing, + validate_target_benefits_from_higher_altitude, + validate_target_benefits_from_offensive, + validate_target_benefits_from_wind, NULL, }; @@ -594,13 +604,14 @@ CrInstance process_creature_ranged_buff_spell_casting(struct Thing* creatng) { continue; } - if((inst_inf->validate_func_idx[0] == 0) || (inst_inf->validate_func_idx[1] == 0) || - (inst_inf->search_func_idx == 0)) + if((inst_inf->validate_source_func == 0) || (inst_inf->validate_target_func == 0) || + (inst_inf->search_func == 0)) { ERRORLOG("The instance %d has no validate function or search function.", i); continue; } - if(!creature_instances_validate_func_list[inst_inf->validate_func_idx[0]](creatng, NULL, i)) + if(!creature_instances_validate_func_list[inst_inf->validate_source_func] + (creatng, NULL, i, inst_inf->validate_source_func_params[0], inst_inf->validate_source_func_params[1])) { // The input creature is not a legal source. continue; @@ -608,8 +619,9 @@ CrInstance process_creature_ranged_buff_spell_casting(struct Thing* creatng) ThingIndex *targets = NULL; unsigned short found_count = 0; - if(creature_instances_search_targets_func_list[inst_inf->search_func_idx](creatng, i, &targets, &found_count) && - targets && (found_count > 0)) + TbBool ok = creature_instances_search_targets_func_list[inst_inf->search_func](creatng, i, &targets, + &found_count, inst_inf->search_func_params[0], inst_inf->search_func_params[1]); + if(ok && targets && (found_count > 0)) { struct Thing* target = thing_get(targets[0]); SYNCDBG(8, "Set instance %s(%d) on %s(%d) for %s(%d).", @@ -1157,9 +1169,18 @@ void delay_heal_sleep(struct Thing *creatng) * @param source The source creature * @param target The target creature * @param inst_idx The spell instance index + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. * @return TbBool True if the creature can, false if otherwise. */ -TbBool validate_source_basic(struct Thing *source, struct Thing *target, CrInstance inst_idx) +TbBool validate_source_basic + ( + struct Thing *source, + struct Thing *target, + CrInstance inst_idx, + int32_t param1, + int32_t param2 + ) { if ((source->alloc_flags & TAlF_IsControlled) != 0) { @@ -1174,6 +1195,16 @@ TbBool validate_source_basic(struct Thing *source, struct Thing *target, CrInsta return false; } + if (!creature_instance_is_available(source, inst_idx) || + !creature_instance_has_reset(source, inst_idx) || + creature_is_fleeing_combat(source) || creature_affected_by_spell(source, SplK_Chicken) || + creature_is_being_unconscious(source) || creature_is_dying(source) || + thing_is_picked_up(source) || creature_is_being_dropped(source) || + creature_is_being_sacrificed(source) || creature_is_being_summoned(source)) + { + return false; + } + return true; } @@ -1183,22 +1214,25 @@ TbBool validate_source_basic(struct Thing *source, struct Thing *target, CrInsta * @param source The source creature * @param target The target creature * @param inst_idx The spell instance index + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. * @return TbBool True if the creature can, false if otherwise. */ -TbBool validate_source_generic(struct Thing *source, struct Thing *target, CrInstance inst_idx) +TbBool validate_source_generic + ( + struct Thing *source, + struct Thing *target, + CrInstance inst_idx, + int32_t param1, + int32_t param2 + ) { - if (!validate_source_basic(source, target, inst_idx)) + if (!validate_source_basic(source, target, inst_idx, param1, param2)) { return false; } - if (!creature_instance_is_available(source, inst_idx) || - !creature_instance_has_reset(source, inst_idx) || - creature_is_fleeing_combat(source) || creature_affected_by_spell(source, SplK_Chicken) || - creature_is_being_unconscious(source) || creature_is_dying(source) || - thing_is_picked_up(source) || creature_is_being_dropped(source) || - creature_is_being_sacrificed(source) || creature_is_being_summoned(source) || - creature_is_being_tortured(source) || creature_is_kept_in_prison(source)) + if (creature_is_being_tortured(source) || creature_is_kept_in_prison(source)) { return false; } @@ -1211,9 +1245,18 @@ TbBool validate_source_generic(struct Thing *source, struct Thing *target, CrIns * @param source The source creature * @param target The target creature * @param inst_idx The spell instance index + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. * @return TbBool True if the creature can be, false if otherwise. */ -TbBool validate_target_basic(struct Thing *source, struct Thing *target, CrInstance inst_idx) +TbBool validate_target_basic + ( + struct Thing *source, + struct Thing *target, + CrInstance inst_idx, + int32_t param1, + int32_t param2 + ) { if (creature_is_dying(target) || thing_is_picked_up(target) || creature_is_being_dropped(target) || creature_is_being_sacrificed(target) || creature_is_being_summoned(target)) @@ -1232,7 +1275,8 @@ TbBool validate_target_basic(struct Thing *source, struct Thing *target, CrInsta target->continue_state == CrSt_CreatureLeaves || target->active_state == CrSt_CreatureLeavingDungeon || target->active_state == CrSt_CreatureScavengedDisappear || - // Candidate shouldn't be fight with the caster. + target->active_state == CrSt_LeavesBecauseOwnerLost || + // Target shouldn't be fighting with the caster. creature_has_creature_in_combat(source, target) || creature_has_creature_in_combat(target, source)) { @@ -1247,13 +1291,22 @@ TbBool validate_target_basic(struct Thing *source, struct Thing *target, CrInsta * @param source The source creature * @param target The target creature * @param inst_idx The spell instance index + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. * @return TbBool True if the creature can be, false if otherwise. */ -TbBool validate_target_generic(struct Thing *source, struct Thing *target, CrInstance inst_idx) +TbBool validate_target_generic + ( + struct Thing *source, + struct Thing *target, + CrInstance inst_idx, + int32_t param1, + int32_t param2 + ) { // We don't check the spatial conditions, such as distacne, angle, and sight here, because // they should be checked in the search function. - if (!validate_target_basic(source, target, inst_idx) || creature_is_being_unconscious(target) || + if (!validate_target_basic(source, target, inst_idx, param1, param2) || creature_is_being_unconscious(target) || creature_is_being_tortured(target) || creature_is_kept_in_prison(target)) { return false; @@ -1270,78 +1323,244 @@ TbBool validate_target_generic(struct Thing *source, struct Thing *target, CrIns return false; } + return true; +} + +/** + * @brief Check if the given creature can cast spell in prison or torture room. + * + * @param source The source creature + * @param target The target creature + * @param inst_idx The spell instance index + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. + * @return TbBool True if the creature can, false if otherwise. + */ +TbBool validate_source_even_in_prison + ( + struct Thing *source, + struct Thing *target, + CrInstance inst_idx, + int32_t param1, + int32_t param2 + ) +{ + if (!validate_source_basic(source, target, inst_idx, param1, param2)) + { + return false; + } + + return true; +} + +/** + * @brief Check if the target creature can benefits from buff that provides missile protection. + * + * @param source The source creature + * @param target The target creature + * @param inst_idx The spell instance index + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. + * @return TbBool True if the creature can, false if otherwise. + */ +TbBool validate_target_benefits_from_defensive_missile + ( + struct Thing *source, + struct Thing *target, + CrInstance inst_idx, + int32_t param1, + int32_t param2 + ) +{ + if (!validate_target_generic(source, target, inst_idx, param1, param2)) + { + return false; + } struct CreatureControl* cctrl = creature_control_get_from_thing(target); if (creature_control_invalid(cctrl)) { ERRORLOG("Invalid creature control"); return false; } - // Check if this creature needs this buff by examining its state. - if (flag_is_set(cctrl->combat_flags, CmbtF_ObjctFight) || flag_is_set(cctrl->combat_flags, CmbtF_DoorFight)) + if (!has_ranged_combat_attackers(target)) { - if(!flag_is_set(inst_inf->instance_property_flags, InstPF_Offensive)) - { - return false; // Offensive buff is useful when attacking door/dungeon heart. - } + return false; } - else if(cctrl->combat_flags == 0) + return true; +} + +/** + * @brief Check if the target creature can benefits from general defensive buffs. + * + * @param source The source creature + * @param target The target creature + * @param inst_idx The spell instance index + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. + * @return TbBool True if the creature can, false if otherwise. + */ +TbBool validate_target_benefits_from_defensive + ( + struct Thing *source, + struct Thing *target, + CrInstance inst_idx, + int32_t param1, + int32_t param2 + ) +{ + if (!validate_target_generic(source, target, inst_idx, param1, param2)) { - if(!flag_is_set(inst_inf->instance_property_flags, InstPF_DailyLifeBuff)) - { - return false; // Daiy life buff is useful when not in combat. - } + return false; + } + struct CreatureControl* cctrl = creature_control_get_from_thing(target); + if (creature_control_invalid(cctrl)) + { + ERRORLOG("Invalid creature control"); + return false; + } + // As long as the target is fighting, return true, no matter what thing the target is attacking. + // Even if the target is attacking a door or dungeon heart, it still needs defensive buffs because + // the hostile keepers can use keeper offensive spells. + if (cctrl->combat_flags == 0) + { + return false; // Not in any combat. } - return true; } /** - * @brief Check if the given creature can cast Ranged Heal spell. + * @brief Check if the target creature can benefits from higher altitude. * * @param source The source creature * @param target The target creature * @param inst_idx The spell instance index + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. * @return TbBool True if the creature can, false if otherwise. */ -TbBool validate_source_ranged_heal(struct Thing *source, struct Thing *target, CrInstance inst_idx) +TbBool validate_target_benefits_from_higher_altitude + ( + struct Thing *source, + struct Thing *target, + CrInstance inst_idx, + int32_t param1, + int32_t param2 + ) { - if (!validate_source_basic(source, target, inst_idx)) + if (!validate_target_generic(source, target, inst_idx, param1, param2)) { return false; } + // Water or lava. Flying on water is beneficial because the target can go on a Guard Post. + if (subtile_is_liquid(target->mappos.x.stl.num, target->mappos.y.stl.num)) + { + return true; + } + return false; +} - // Omit creature_is_kept_in_prison() and creature_is_being_tortured() because we want the caster to heal itself. - if (!creature_instance_is_available(source, inst_idx) || - !creature_instance_has_reset(source, inst_idx) || - creature_is_fleeing_combat(source) || creature_affected_by_spell(source, SplK_Chicken) || - creature_is_being_unconscious(source) || creature_is_dying(source) || - thing_is_picked_up(source) || creature_is_being_dropped(source) || - creature_is_being_sacrificed(source) || creature_is_being_summoned(source)) +/** + * @brief Check if the target creature can benefits from general offensive buffs. + * + * @param source The source creature + * @param target The target creature + * @param inst_idx The spell instance index + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. + * @return TbBool True if the creature can, false if otherwise. + */ +TbBool validate_target_benefits_from_offensive + ( + struct Thing *source, + struct Thing *target, + CrInstance inst_idx, + int32_t param1, + int32_t param2 + ) +{ + if (!validate_target_generic(source, target, inst_idx, param1, param2)) + { + return false; + } + struct CreatureControl* cctrl = creature_control_get_from_thing(target); + if (creature_control_invalid(cctrl)) { + ERRORLOG("Invalid creature control"); return false; } + if (cctrl->combat_flags != 0) + { + return true; // In any combat. + } + return false; +} - // Creature who is leaving doesn't care about any allies. - if (source->continue_state == CrSt_CreatureLeaves || - source->active_state == CrSt_CreatureLeavingDungeon || - source->active_state == CrSt_CreatureScavengedDisappear ) +/** + * @brief Check if the target creature can benefits from using Wind. + * Wind can disperse gas and push away enemies. + * It has more than one merit. It deserve one dedicated function. + * + * @param source The source creature + * @param target The target creature + * @param inst_idx The spell instance index + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. + * @return TbBool True if the creature can, false if otherwise. + */ +TbBool validate_target_benefits_from_wind + ( + struct Thing *source, + struct Thing *target, + CrInstance inst_idx, + int32_t param1, + int32_t param2 + ) +{ + if (!validate_target_generic(source, target, inst_idx, param1, param2)) { return false; } - return true; + struct CreatureControl* cctrl = creature_control_get_from_thing(target); + if (creature_control_invalid(cctrl)) + { + ERRORLOG("Invalid creature control"); + return false; + } + + if ((cctrl->spell_flags & CSAfF_PoisonCloud) != 0) + { + return true; + } + struct CreatureStats* stats = creature_stats_get_from_thing(target); + if (stats->attack_preference == AttckT_Ranged && cctrl->opponents_melee_count >= 2) + { + // Surrounded by 2+ enemies. This could be definitely smarter but not now. + return true; + } + + return false; } /** - * @brief Check if the given creature can be the target of Ranged Heal. + * @brief Check if the given creature can be the target of healing spells. * * @param source The source creature * @param target The target creature * @param inst_idx The spell instance index + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. * @return TbBool True if the creature can, false if otherwise. */ -TbBool validate_target_ranged_heal(struct Thing *source, struct Thing *target, CrInstance inst_idx) +TbBool validate_target_benefits_from_healing + ( + struct Thing *source, + struct Thing *target, + CrInstance inst_idx, + int32_t param1, + int32_t param2 + ) { - if (!validate_target_basic(source, target, CrInst_RANGED_HEAL) || creature_is_being_unconscious(target) || + if (!validate_target_basic(source, target, inst_idx, param1, param2) || creature_is_being_unconscious(target) || !creature_would_benefit_from_healing(target)) { return false; @@ -1372,9 +1591,19 @@ TbBool validate_target_ranged_heal(struct Thing *source, struct Thing *target, C * @param inst_idx The spell instance index * @param targets The list of the found creatures. Caller must free this. * @param found_count The number of the found creatures. + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. * @return TbBool True if there is no error, false if otherwise. */ -TbBool search_target_generic(struct Thing *source, CrInstance inst_idx, ThingIndex **targets, unsigned short *found_count) +TbBool search_target_generic + ( + struct Thing *source, + CrInstance inst_idx, + ThingIndex **targets, + uint16_t *found_count, + int32_t param1, + int32_t param2 + ) { if (!targets || !found_count) { @@ -1405,9 +1634,11 @@ TbBool search_target_generic(struct Thing *source, CrInstance inst_idx, ThingInd creature_idx = cctrl->players_next_creature_idx; const struct InstanceInfo* inst_inf = creature_instance_info_get(inst_idx); - if (inst_inf->validate_func_idx[1] > 0) + if (inst_inf->validate_target_func > 0) { - if(!creature_instances_validate_func_list[inst_inf->validate_func_idx[1]](source, candidate, inst_idx)) + if(!creature_instances_validate_func_list[inst_inf->validate_target_func] + (source, candidate, inst_idx, inst_inf->validate_target_func_params[0], + inst_inf->validate_target_func_params[1])) { // This candidate is out. continue; @@ -1449,9 +1680,19 @@ TbBool search_target_generic(struct Thing *source, CrInstance inst_idx, ThingInd * @param inst_idx The spell instance index * @param targets The list of the found creatures. Caller must free this. * @param found_count The number of the found creatures. + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. * @return TbBool True if there is no error, false if otherwise */ -TbBool search_target_ranged_heal(struct Thing *source, CrInstance inst_idx, ThingIndex **targets, unsigned short *found_count) +TbBool search_target_ranged_heal + ( + struct Thing *source, + CrInstance inst_idx, + ThingIndex **targets, + uint16_t *found_count, + int32_t param1, + int32_t param2 + ) { if (!targets || !found_count) { @@ -1459,7 +1700,7 @@ TbBool search_target_ranged_heal(struct Thing *source, CrInstance inst_idx, Thin return false; } - if (!search_target_generic(source, inst_idx, targets, found_count)) + if (!search_target_generic(source, inst_idx, targets, found_count, param1, param2)) { return false; } diff --git a/src/creature_instances.h b/src/creature_instances.h index a7cc9c8bc6..0b51ea6d2c 100644 --- a/src/creature_instances.h +++ b/src/creature_instances.h @@ -90,8 +90,8 @@ enum CreatureInstances { struct Thing; typedef long (*Creature_Instf_Func)(struct Thing *, long *); -typedef TbBool (*Creature_Validate_Func)(struct Thing *, struct Thing *, CrInstance); -typedef TbBool (*Creature_Target_Search_Func)(struct Thing *, CrInstance, ThingIndex **, unsigned short *); +typedef TbBool (*Creature_Validate_Func)(struct Thing *, struct Thing *, CrInstance, int32_t, int32_t); +typedef TbBool (*Creature_Target_Search_Func)(struct Thing *, CrInstance, ThingIndex **, uint16_t *, int32_t, int32_t); struct InstanceInfo { TbBool instant; @@ -111,10 +111,14 @@ struct InstanceInfo { long range_max; long symbol_spridx; short tooltip_stridx; - // [0] for source, [1] for target. Refer to creature_instances_validate_func_list - unsigned char validate_func_idx[2]; + // Refer to creature_instances_validate_func_list + uint8_t validate_source_func; + int32_t validate_source_func_params[2]; + uint8_t validate_target_func; + int32_t validate_target_func_params[2]; // Refer to creature_instances_search_targets_func_list - unsigned char search_func_idx; + uint8_t search_func; + int32_t search_func_params[2]; }; /******************************************************************************/ @@ -155,13 +159,20 @@ TbBool instance_draws_possession_swipe(CrInstance inum); void delay_teleport(struct Thing *creatng); void delay_heal_sleep(struct Thing *creatng); /******************************************************************************/ -TbBool validate_source_generic(struct Thing *source, struct Thing *target, CrInstance inst_idx); -TbBool validate_target_generic(struct Thing *source, struct Thing *target, CrInstance inst_idx); -TbBool validate_source_ranged_heal(struct Thing *source, struct Thing *target, CrInstance inst_idx); -TbBool validate_target_ranged_heal(struct Thing *source, struct Thing *target, CrInstance inst_idx); +TbBool validate_source_generic(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); +TbBool validate_source_even_in_prison(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); + +TbBool validate_target_generic(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); +TbBool validate_target_benefits_from_defensive_missile(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); +TbBool validate_target_benefits_from_defensive(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); +TbBool validate_target_benefits_from_higher_altitude(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); +TbBool validate_target_benefits_from_offensive(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); +TbBool validate_target_benefits_from_wind(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); +TbBool validate_target_benefits_from_healing(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); + +TbBool search_target_generic(struct Thing *source, CrInstance inst_idx, ThingIndex **targets, uint16_t *found_count, int32_t param1, int32_t param2); +TbBool search_target_ranged_heal(struct Thing *source, CrInstance inst_idx, ThingIndex **targets, uint16_t *found_count, int32_t param1, int32_t param2); -TbBool search_target_generic(struct Thing *source, CrInstance inst_idx, ThingIndex **targets, unsigned short *found_count); -TbBool search_target_ranged_heal(struct Thing *source, CrInstance inst_idx, ThingIndex **targets, unsigned short *found_count); /******************************************************************************/ #ifdef __cplusplus } diff --git a/src/creature_states_combt.c b/src/creature_states_combt.c index 7862778c2c..a326baed9a 100644 --- a/src/creature_states_combt.c +++ b/src/creature_states_combt.c @@ -1861,86 +1861,43 @@ CrInstance get_best_self_preservation_instance_to_use(const struct Thing *thing) CrInstance get_self_spell_casting(const struct Thing *thing) { - struct CreatureControl* cctrl = creature_control_get_from_thing(thing); - TbBool is_fighting = cctrl->combat_flags != 0; - TbBool is_in_custody = creature_is_kept_in_custody(thing); - SYNCDBG(11, "Processing %s(%d), combat flag: %d, spell flag: %d, in custody? %d", - thing_model_name(thing), thing->index, cctrl->combat_flags, cctrl->spell_flags, is_in_custody); - + TbBool ok = false; for (int i = 0; i < game.conf.crtr_conf.instances_count; i++) { struct InstanceInfo* inst_inf = creature_instance_info_get(i); - // Basic checks. - if (!flag_is_set(inst_inf->instance_property_flags, InstPF_SelfBuff) || - !creature_instance_is_available(thing, i) || - !creature_instance_has_reset(thing, i)) - { - continue; // Cannot use. - } - SpellKind spell_idx = inst_inf->func_params[0]; - if(inst_inf->func_params[0] != SplK_None && creature_affected_by_spell(thing, spell_idx)) - { - continue; // Already being affected. - } - if (!flag_is_set(inst_inf->instance_property_flags, InstPF_AllowedInPrison) && is_in_custody) - { - continue; // Cannot use in prison. - } - // Our ideal goal is to avoid any hardcode for specific instance, - // but we still need to deal with some special instances. - switch(i) + if (inst_inf->validate_source_func != 0) { - case CrInst_WIND: - if ((cctrl->spell_flags & CSAfF_PoisonCloud) != 0) + ok = creature_instances_validate_func_list[inst_inf->validate_source_func]((struct Thing *)thing, + (struct Thing *)thing, i, inst_inf->validate_source_func_params[0], + inst_inf->validate_source_func_params[1]); + if(!ok) { - SYNCDBG(11, "Use Wind when under influence of posion cloud."); - return CrInst_WIND; // Cast Wind when under influence of gas. + continue; } - break; - case CrInst_FLY: - if (is_fighting || terrain_toxic_for_creature_at_position(thing, - coord_subtile(thing->mappos.x.val), coord_subtile(thing->mappos.y.val))) - { - return CrInst_FLY; - } - break; - case CrInst_HEAL: - case CrInst_RANGED_HEAL: - if (creature_would_benefit_from_healing(thing)) - { - return CrInst_HEAL; - } - break; - default: - if (!is_fighting && flag_is_set(inst_inf->instance_property_flags, InstPF_DailyLifeBuff)) - { - SYNCDBG(11, "Use %s as daily life buff.", creature_instance_code_name(i)); - return i; - } - else if (flag_is_set(cctrl->combat_flags, CmbtF_ObjctFight) || - flag_is_set(cctrl->combat_flags, CmbtF_DoorFight)) + } + + if (inst_inf->validate_target_func != 0) + { + ok = creature_instances_validate_func_list[inst_inf->validate_target_func]((struct Thing *)thing, + (struct Thing *)thing, i, inst_inf->validate_target_func_params[0], + inst_inf->validate_target_func_params[1]); + if(!ok) { - // Creature is fighting a object (such as dungeon heart) or a door. - if(flag_is_set(inst_inf->instance_property_flags, InstPF_Offensive)) - { - // @todo What about Armour? Armour is defensive but it can protect creature from the AOE - // of the Keeper lightning spell. - SYNCDBG(11, "Use %s for object/door fighting case.", creature_instance_code_name(i)); - return i; // Offensive buff is useful when attacking door/dungeon heart. - } + continue; } - else if (is_fighting) + } + + if (!ok) + { + // If we reach here, it means that this instance has no validate function for source and target, such + // as TOKING. Just check if it has SELF_BUFF flag, this should cover IMP's case. + if (!flag_is_set(inst_inf->instance_property_flags, InstPF_SelfBuff)) { - // Other fighting case. - if(flag_is_set(inst_inf->instance_property_flags, InstPF_Offensive) || - flag_is_set(inst_inf->instance_property_flags, InstPF_Defensive)) - { - SYNCDBG(11, "Use %s for general fighting case.", creature_instance_code_name(i)); - return i; // Offensive and Defensive are both useful. - } + continue; } - break; } + + return i; } return CrInst_NULL; diff --git a/src/thing_creature.c b/src/thing_creature.c index 5d7a8b33ba..742a6634ca 100644 --- a/src/thing_creature.c +++ b/src/thing_creature.c @@ -3564,24 +3564,18 @@ ThingIndex process_player_use_instance(struct Thing *thing, CrInstance inst_id, { ThingIndex target_idx = get_human_controlled_creature_target(thing, inst_id, packet); struct InstanceInfo *inst_inf = creature_instance_info_get(inst_id); - if (flag_is_set(inst_inf->instance_property_flags,InstPF_RangedBuff)) + TbBool ok = false; + struct Thing *target = thing_get(target_idx); + if (flag_is_set(inst_inf->instance_property_flags, InstPF_RangedBuff) && inst_inf->validate_target_func != 0) { - TbBool ok = false; - // Ranged buffs need further validation. - struct Thing *target = thing_get(target_idx); - if (inst_id == CrInst_RANGED_HEAL) - { - ok = validate_target_ranged_heal(thing, target, CrInst_RANGED_HEAL); - } - else - { - ok = validate_target_generic(thing, target, inst_id); - } - if (!ok) - { - target = 0; - } + ok = creature_instances_validate_func_list[inst_inf->validate_target_func](thing, target, inst_id, + inst_inf->validate_target_func_params[0], inst_inf->validate_target_func_params[1]); } + if (!ok) + { + target = 0; + } + if (flag_is_set(inst_inf->instance_property_flags, InstPF_NeedsTarget)) { if (target_idx == 0) From 3f338b90f225ce336a0ed0eef04ae1dfc4f6b447 Mon Sep 17 00:00:00 2001 From: Evan Date: Wed, 9 Oct 2024 01:15:19 +0800 Subject: [PATCH 06/12] Remove unnecessary changes and add initializations. --- config/fxdata/creature.cfg | 2 +- src/config_creature.c | 4 ++++ src/creature_instances.h | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/config/fxdata/creature.cfg b/config/fxdata/creature.cfg index 6a77edb308..5ce8f9beb8 100644 --- a/config/fxdata/creature.cfg +++ b/config/fxdata/creature.cfg @@ -499,7 +499,7 @@ Graphics = ATTACK RangeMin = MIN RangeMax = MAX PrimaryTarget = 3 -Properties = SELF_BUFF +Properties = Function = creature_cast_spell SPELL_WIND 0 ValidateSourceFunc = validate_source_generic 0 0 ; Wind can disperse gas and push away melee enemies. diff --git a/src/config_creature.c b/src/config_creature.c index 0db298bb84..165ac7096d 100644 --- a/src/config_creature.c +++ b/src/config_creature.c @@ -841,7 +841,11 @@ TbBool parse_creaturetype_instance_blocks(char *buf, long len, const char *confi game.conf.magic_conf.instance_info[i].range_min = -1; game.conf.magic_conf.instance_info[i].range_max = -1; game.conf.magic_conf.instance_info[i].validate_source_func = 1; + game.conf.magic_conf.instance_info[i].validate_source_func_params[0] = 0; + game.conf.magic_conf.instance_info[i].validate_source_func_params[1] = 0; game.conf.magic_conf.instance_info[i].validate_target_func = 3; + game.conf.magic_conf.instance_info[i].validate_target_func_params[0] = 0; + game.conf.magic_conf.instance_info[i].validate_target_func_params[1] = 0; game.conf.magic_conf.instance_info[i].search_func = 1; } } diff --git a/src/creature_instances.h b/src/creature_instances.h index 0b51ea6d2c..864765ff08 100644 --- a/src/creature_instances.h +++ b/src/creature_instances.h @@ -102,7 +102,7 @@ struct InstanceInfo { long reset_time; long fp_reset_time; unsigned char graphics_idx; - unsigned long instance_property_flags; + short instance_property_flags; short force_visibility; unsigned char primary_target; unsigned char func_idx; From bbe838403a17fa2a3c2dc6094ac5c1f88ac8925c Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 10 Oct 2024 01:01:14 +0800 Subject: [PATCH 07/12] Complete the previous commit-3f338b90f225ce336a0ed0eef04ae1dfc4f6b447 --- config/fxdata/creature.cfg | 2 +- src/config_creature.c | 6 +-- src/creature_instances.c | 79 +++++++++++++++++++++++-------------- src/creature_instances.h | 3 ++ src/creature_states_combt.c | 75 ++++++----------------------------- 5 files changed, 69 insertions(+), 96 deletions(-) diff --git a/config/fxdata/creature.cfg b/config/fxdata/creature.cfg index 5ce8f9beb8..53b36d913e 100644 --- a/config/fxdata/creature.cfg +++ b/config/fxdata/creature.cfg @@ -559,7 +559,7 @@ Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_SIGHT 0 ValidateSourceFunc = validate_source_even_in_prison 0 0 -ValidateTargetFunc = validate_target_generic 0 0 +ValidateTargetFunc = validate_target_even_in_prison 0 0 [instance26] Name = GRENADE diff --git a/src/config_creature.c b/src/config_creature.c index 165ac7096d..5867402f5b 100644 --- a/src/config_creature.c +++ b/src/config_creature.c @@ -840,13 +840,13 @@ TbBool parse_creaturetype_instance_blocks(char *buf, long len, const char *confi game.conf.magic_conf.instance_info[i].tooltip_stridx = 0; game.conf.magic_conf.instance_info[i].range_min = -1; game.conf.magic_conf.instance_info[i].range_max = -1; - game.conf.magic_conf.instance_info[i].validate_source_func = 1; + game.conf.magic_conf.instance_info[i].validate_source_func = 0; game.conf.magic_conf.instance_info[i].validate_source_func_params[0] = 0; game.conf.magic_conf.instance_info[i].validate_source_func_params[1] = 0; - game.conf.magic_conf.instance_info[i].validate_target_func = 3; + game.conf.magic_conf.instance_info[i].validate_target_func = 0; game.conf.magic_conf.instance_info[i].validate_target_func_params[0] = 0; game.conf.magic_conf.instance_info[i].validate_target_func_params[1] = 0; - game.conf.magic_conf.instance_info[i].search_func = 1; + game.conf.magic_conf.instance_info[i].search_func = 0; } } } diff --git a/src/creature_instances.c b/src/creature_instances.c index b22f8ffe31..cf4051c3ff 100644 --- a/src/creature_instances.c +++ b/src/creature_instances.c @@ -117,12 +117,13 @@ const struct NamedCommand creature_instances_validate_func_type[] = { {"validate_source_generic", 1}, {"validate_source_even_in_prison", 2}, {"validate_target_generic", 3}, - {"validate_target_benefits_from_defensive_missile", 4}, - {"validate_target_benefits_from_defensive", 5}, - {"validate_target_benefits_from_healing", 6}, - {"validate_target_benefits_from_higher_altitude", 7}, - {"validate_target_benefits_from_offensive", 8}, - {"validate_target_benefits_from_wind", 9}, + {"validate_target_even_in_prison", 4}, + {"validate_target_benefits_from_defensive_missile", 5}, + {"validate_target_benefits_from_defensive", 6}, + {"validate_target_benefits_from_healing", 7}, + {"validate_target_benefits_from_higher_altitude", 8}, + {"validate_target_benefits_from_offensive", 9}, + {"validate_target_benefits_from_wind", 10}, {NULL, 0}, }; @@ -131,6 +132,7 @@ Creature_Validate_Func creature_instances_validate_func_list[] = { validate_source_generic, validate_source_even_in_prison, validate_target_generic, + validate_target_even_in_prison, validate_target_benefits_from_defensive_missile, validate_target_benefits_from_defensive, validate_target_benefits_from_healing, @@ -562,24 +564,15 @@ long instf_creature_cast_spell(struct Thing *creatng, long *param) return 0; } - TbBool process_creature_self_spell_casting(struct Thing* creatng) { TRACE_THING(creatng); - struct CreatureControl* cctrl = creature_control_get_from_thing(creatng); - if (((creatng->alloc_flags & TAlF_IsControlled) != 0) - || (cctrl->conscious_back_turns != 0) - || ((cctrl->stateblock_flags & CCSpl_Freeze) != 0)) { - return false; - } - if (cctrl->instance_id != CrInst_NULL) { - return false; - } - - long inst_idx = get_self_spell_casting(creatng); - if (inst_idx <= 0) { + CrInstance inst_idx = get_self_spell_casting(creatng); + if (inst_idx == CrInst_NULL) { return false; } + SYNCDBG(9, "%s(%d) use %s(%d) on itself.", thing_model_name(creatng), creatng->index, + creature_instance_code_name(inst_idx), inst_idx); set_creature_instance(creatng, inst_idx, creatng->index, 0); return true; } @@ -624,9 +617,9 @@ CrInstance process_creature_ranged_buff_spell_casting(struct Thing* creatng) if(ok && targets && (found_count > 0)) { struct Thing* target = thing_get(targets[0]); - SYNCDBG(8, "Set instance %s(%d) on %s(%d) for %s(%d).", - creature_instance_code_name(i), i, thing_model_name(creatng), creatng->index, - thing_model_name(target), target->index); + SYNCDBG(8, "Set instance %s(%d) on %s(%d)(%d) for %s(%d)(%d).", + creature_instance_code_name(i), i, thing_model_name(creatng), creatng->index, creatng->owner, + thing_model_name(target), target->index, target->owner); struct CreatureControl* cctrl = creature_control_get_from_thing(creatng); // Enter the temporary state. @@ -1197,6 +1190,7 @@ TbBool validate_source_basic if (!creature_instance_is_available(source, inst_idx) || !creature_instance_has_reset(source, inst_idx) || + ((cctrl->stateblock_flags & CCSpl_Freeze) != 0) || creature_is_fleeing_combat(source) || creature_affected_by_spell(source, SplK_Chicken) || creature_is_being_unconscious(source) || creature_is_dying(source) || thing_is_picked_up(source) || creature_is_being_dropped(source) || @@ -1268,6 +1262,8 @@ TbBool validate_target_basic if ((inst_inf->instance_property_flags & InstPF_SelfBuff) == 0 && source->index == target->index) { // If this spell doesn't have SELF_BUFF flag, exclude itself. + WARNDBG(8, "%s(%d) try to cast %s(%d) on itself but this instance has no SELF_BUFF flag", + thing_model_name(target), target->index, creature_instance_code_name(inst_idx), inst_idx); return false; } @@ -1303,11 +1299,38 @@ TbBool validate_target_generic int32_t param1, int32_t param2 ) +{ + if (!validate_target_even_in_prison(source, target, inst_idx, param1, param2) || + creature_is_being_tortured(target) || creature_is_kept_in_prison(target)) + { + return false; + } + + return true; +} + +/** + * @brief Check if the given creature can be the target of the specified spell when the creature + * is in prison/torture room. + * @param source The source creature + * @param target The target creature + * @param inst_idx The spell instance index + * @param param1 Optional 1st parameter. + * @param param2 Optional 2nd parameter. + * @return TbBool True if the creature can be, false if otherwise. + */ +TbBool validate_target_even_in_prison + ( + struct Thing *source, + struct Thing *target, + CrInstance inst_idx, + int32_t param1, + int32_t param2 + ) { // We don't check the spatial conditions, such as distacne, angle, and sight here, because // they should be checked in the search function. - if (!validate_target_basic(source, target, inst_idx, param1, param2) || creature_is_being_unconscious(target) || - creature_is_being_tortured(target) || creature_is_kept_in_prison(target)) + if (!validate_target_basic(source, target, inst_idx, param1, param2) || creature_is_being_unconscious(target)) { return false; } @@ -1386,6 +1409,7 @@ TbBool validate_target_benefits_from_defensive_missile { return false; } + return true; } @@ -1516,17 +1540,14 @@ TbBool validate_target_benefits_from_wind int32_t param2 ) { - if (!validate_target_generic(source, target, inst_idx, param1, param2)) - { - return false; - } + // Note that we don't need to call validate_target_generic or validate_target_basic because + // Wind isn't SELF_BUFF. It doesn't require a target, the target parameter is just the source. struct CreatureControl* cctrl = creature_control_get_from_thing(target); if (creature_control_invalid(cctrl)) { ERRORLOG("Invalid creature control"); return false; } - if ((cctrl->spell_flags & CSAfF_PoisonCloud) != 0) { return true; diff --git a/src/creature_instances.h b/src/creature_instances.h index 864765ff08..7177f399eb 100644 --- a/src/creature_instances.h +++ b/src/creature_instances.h @@ -159,10 +159,13 @@ TbBool instance_draws_possession_swipe(CrInstance inum); void delay_teleport(struct Thing *creatng); void delay_heal_sleep(struct Thing *creatng); /******************************************************************************/ +TbBool validate_source_basic(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); TbBool validate_source_generic(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); TbBool validate_source_even_in_prison(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); +TbBool validate_target_basic(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); TbBool validate_target_generic(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); +TbBool validate_target_even_in_prison(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); TbBool validate_target_benefits_from_defensive_missile(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); TbBool validate_target_benefits_from_defensive(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); TbBool validate_target_benefits_from_higher_altitude(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); diff --git a/src/creature_states_combt.c b/src/creature_states_combt.c index a326baed9a..6dc8f8a95b 100644 --- a/src/creature_states_combt.c +++ b/src/creature_states_combt.c @@ -1802,63 +1802,10 @@ TbBool creature_would_benefit_from_healing(const struct Thing* thing) } /** - * @brief Get the best self buff instance. - * As long as the instance has SELF_BUFF flag and the creature is not being already affected, - * the instance will be considered valid. The returned instance might has RANGED_BUFF flag, so - * be careful about how you set the instance. You must not set an index of enemy to the target - * parameter. - * @param thing The creature to use self buff. - * @return CrInstance The valid self buff instance. + * @brief Get suitable spell for the caster itself. + * @param thing The creature to use spell. + * @return CrInstance The index of the instance. */ -CrInstance get_best_self_preservation_instance_to_use(const struct Thing *thing) -{ - struct InstanceInfo* inst_inf; - struct CreatureControl* cctrl = creature_control_get_from_thing(thing); - if ((cctrl->spell_flags & CSAfF_PoisonCloud) != 0) - { - INSTANCE_RET_IF_AVAIL(thing, CrInst_WIND); - } - if (!creature_affected_by_spell(thing, SplK_Invisibility)) - { - INSTANCE_RET_IF_AVAIL(thing, CrInst_INVISIBILITY); - } - if (creature_requires_healing(thing)) - { - INSTANCE_RET_IF_AVAIL(thing, CrInst_HEAL); - } - if (!creature_affected_by_spell(thing, SplK_Armour)) - { - INSTANCE_RET_IF_AVAIL(thing, CrInst_ARMOUR); - } - if (!creature_affected_by_spell(thing, SplK_Speed)) - { - INSTANCE_RET_IF_AVAIL(thing, CrInst_SPEED); - } - if (!creature_affected_by_spell(thing, SplK_Rebound)) - { - INSTANCE_RET_IF_AVAIL(thing, CrInst_REBOUND); - } - if (!creature_affected_by_spell(thing, SplK_Fly)) - { - INSTANCE_RET_IF_AVAIL(thing, CrInst_FLY); - } - INSTANCE_RET_IF_AVAIL(thing, CrInst_SUMMON); - INSTANCE_RET_IF_AVAIL(thing, CrInst_FAMILIAR); - for (int i = CrInst_LISTEND; i < game.conf.crtr_conf.instances_count; i++) - { - inst_inf = creature_instance_info_get(i); - if ((inst_inf->instance_property_flags & InstPF_SelfBuff)) - { - if (inst_inf->func_params[0] != SplK_None && - !creature_affected_by_spell(thing, inst_inf->func_params[0])) - { - INSTANCE_RET_IF_AVAIL(thing, i); - } - } - } - return CrInst_NULL; -} - CrInstance get_self_spell_casting(const struct Thing *thing) { TbBool ok = false; @@ -1890,8 +1837,10 @@ CrInstance get_self_spell_casting(const struct Thing *thing) if (!ok) { // If we reach here, it means that this instance has no validate function for source and target, such - // as TOKING. Just check if it has SELF_BUFF flag, this should cover IMP's case. - if (!flag_is_set(inst_inf->instance_property_flags, InstPF_SelfBuff)) + // as TOKING. Just check some basic conditions and check if the instance has SELF_BUFF flag, + // this should cover IMP's case. + if (!flag_is_set(inst_inf->instance_property_flags, InstPF_SelfBuff) || + !validate_source_basic((struct Thing *)thing, (struct Thing *)thing, i, 0, 0) ) { continue; } @@ -2295,7 +2244,7 @@ long melee_combat_move(struct Thing *thing, struct Thing *enmtng, long enmdist, if (thing_in_field_of_view(thing, enmtng)) { // Firstly, check if any self buff is available. - inst_id = get_best_self_preservation_instance_to_use(thing); + inst_id = get_self_spell_casting(thing); if (inst_id > CrInst_NULL) { set_creature_instance(thing, inst_id, thing->index, 0); @@ -2321,7 +2270,7 @@ long melee_combat_move(struct Thing *thing, struct Thing *enmtng, long enmdist, // If cannot move to enemy, and not waiting for ranged weapon cooldown, then retreat from him if (!creature_has_ranged_weapon(thing)) { - inst_id = get_best_self_preservation_instance_to_use(thing); + inst_id = get_self_spell_casting(thing); if (inst_id > CrInst_NULL) { set_creature_instance(thing, inst_id, thing->index, 0); @@ -2689,7 +2638,7 @@ long waiting_combat_move(struct Thing *figtng, struct Thing *enmtng, long enmdis return 0; } // If the creature has self buff, use it now. - CrInstance inst_id = get_best_self_preservation_instance_to_use(figtng); + CrInstance inst_id = get_self_spell_casting(figtng); if (inst_id > CrInst_NULL) { set_creature_instance(figtng, inst_id, figtng->index, 0); @@ -2779,7 +2728,7 @@ void creature_in_ranged_combat(struct Thing *creatng) return; } // If the creature has self buff, prefer it to weapon. - CrInstance buff_inst = get_best_self_preservation_instance_to_use(creatng); + CrInstance buff_inst = get_self_spell_casting(creatng); CrInstance weapon = CrInst_NULL; long dist = get_combat_distance(creatng, enmtng); if (buff_inst > CrInst_NULL) @@ -3078,7 +3027,7 @@ TbBool creature_look_for_combat(struct Thing *creatng) if ( (cctrl->opponents_melee_count == 0) && (cctrl->opponents_ranged_count == 0) ) { return false; } - CrInstance inst_id = get_best_self_preservation_instance_to_use(creatng); + CrInstance inst_id = get_self_spell_casting(creatng); if (inst_id > CrInst_NULL) { set_creature_instance(creatng, inst_id, creatng->index, 0); From ede5bff01fee70d85486db24f265e9302d528a12 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 10 Oct 2024 13:58:11 +0800 Subject: [PATCH 08/12] Add explanations for ValidateSourceFunc, ValidateTargetFunc, and SearchTargetsFunc in creature.cfg --- config/fxdata/creature.cfg | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/config/fxdata/creature.cfg b/config/fxdata/creature.cfg index 53b36d913e..ae10871a35 100644 --- a/config/fxdata/creature.cfg +++ b/config/fxdata/creature.cfg @@ -82,11 +82,30 @@ PrimaryTarget = 0 Properties = ; Function used as the instance action, and its parameters Function = none 0 0 -; Functions used validate the source of the spell. Could have two parameters. +; Function to validate the source of the spell. Could have two parameters. +; It checks whether the caster (source creature) is able and legitimate to cast the spell. It +; mainly check the state of the caster. For example, checking whether it's unconscious/picked up. +; An unconscious/picked creature is unable to cast spells. Checking whether it's in prison. +; validate_source_generic() forbid casting in prison but validate_source_even_in_prison() doesn't +; forbid. +; This must be set for RANGED_BUFF. ValidateSourceFunc = validate_source_generic 0 0 -; Functions used validate the the target of the spell. Could have two parameters. +; Function to validate the target of the spell. Could have two parameters. +; It checks whether the target creature is able and legitimate to receive the spell. It mainly +; checks if the target can benefit from the spell. For example, a creature having 100% HP doesn't +; need healing. A creature having Speed effect already doesn't the Speed spell. The Speed spell +; should be cast on another creature who doesn't have the Speed effect. +; It also checks the target's state. For example, an unconscious creature is not a legitimate +; target of Speed or Armour spell. +; This must be set for RANGED_BUFF. ValidateTargetFunc = validate_target_generic 0 0 -; Functions used search targets of the spell. Could have two parameters. +; Function to search targets of the ranged spell. Could have two parameters. +; It works with ValidateTargetFunc together to collect one or more targets and run dedicated +; logic to process them. For example, search_target_ranged_heal() will search nearby area +; to collect validated creatures (by validate_target_benefits_from_healing()) and select the creature +; who has lowest percentage of HP as the final receiver of the Heal spell. +; This must be set for RANGED_BUFF. +; When this is set, ValidateTargetFunc must be set, too. SearchTargetsFunc = search_target_generic 0 0 [instance1] From 69b167a66e8d8a9ab4d1b47d1d1c184b0afed5c8 Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 11 Oct 2024 20:56:52 +0800 Subject: [PATCH 09/12] Fix typos in the comments in creature.cfg --- config/fxdata/creature.cfg | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/config/fxdata/creature.cfg b/config/fxdata/creature.cfg index ae10871a35..2d9ff8bfd5 100644 --- a/config/fxdata/creature.cfg +++ b/config/fxdata/creature.cfg @@ -84,17 +84,16 @@ Properties = Function = none 0 0 ; Function to validate the source of the spell. Could have two parameters. ; It checks whether the caster (source creature) is able and legitimate to cast the spell. It -; mainly check the state of the caster. For example, checking whether it's unconscious/picked up. +; mainly checks the state of the caster. For example, checking whether it's unconscious/picked up. ; An unconscious/picked creature is unable to cast spells. Checking whether it's in prison. -; validate_source_generic() forbid casting in prison but validate_source_even_in_prison() doesn't -; forbid. +; validate_source_generic() forbids casting in prison but validate_source_even_in_prison() doesn't. ; This must be set for RANGED_BUFF. ValidateSourceFunc = validate_source_generic 0 0 ; Function to validate the target of the spell. Could have two parameters. ; It checks whether the target creature is able and legitimate to receive the spell. It mainly ; checks if the target can benefit from the spell. For example, a creature having 100% HP doesn't -; need healing. A creature having Speed effect already doesn't the Speed spell. The Speed spell -; should be cast on another creature who doesn't have the Speed effect. +; need healing. A creature having Speed effect already doesn't need the Speed spell. The Speed +; spell should be cast on another creature who doesn't have the Speed effect. ; It also checks the target's state. For example, an unconscious creature is not a legitimate ; target of Speed or Armour spell. ; This must be set for RANGED_BUFF. From 1e0a59ce8d8d6e75b5ebcf4b168c00da3926a30f Mon Sep 17 00:00:00 2001 From: Evan Date: Fri, 11 Oct 2024 21:13:28 +0800 Subject: [PATCH 10/12] Rename function validate_target_benefits_from_defensive_missile Change it to validate_target_benefits_from_missile_defense. --- config/fxdata/creature.cfg | 4 ++-- src/creature_instances.c | 6 +++--- src/creature_instances.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/fxdata/creature.cfg b/config/fxdata/creature.cfg index 2d9ff8bfd5..ec4bc48737 100644 --- a/config/fxdata/creature.cfg +++ b/config/fxdata/creature.cfg @@ -292,7 +292,7 @@ Properties = SELF_BUFF PrimaryTarget = 3 Function = creature_cast_spell SPELL_REBOUND 0 ValidateSourceFunc = validate_source_generic 0 0 -ValidateTargetFunc = validate_target_benefits_from_defensive_missile 0 0 +ValidateTargetFunc = validate_target_benefits_from_missile_defense 0 0 [instance11] Name = HEAL @@ -1079,7 +1079,7 @@ PrimaryTarget = 6 Properties = RANGED_BUFF SELF_BUFF NEEDS_TARGET Function = creature_cast_spell SPELL_REBOUND 0 ValidateSourceFunc = validate_source_generic 0 0 -ValidateTargetFunc = validate_target_benefits_from_defensive_missile 0 0 +ValidateTargetFunc = validate_target_benefits_from_missile_defense 0 0 SearchTargetsFunc = search_target_generic 0 0 [job0] diff --git a/src/creature_instances.c b/src/creature_instances.c index cf4051c3ff..4ddb7e79e1 100644 --- a/src/creature_instances.c +++ b/src/creature_instances.c @@ -118,7 +118,7 @@ const struct NamedCommand creature_instances_validate_func_type[] = { {"validate_source_even_in_prison", 2}, {"validate_target_generic", 3}, {"validate_target_even_in_prison", 4}, - {"validate_target_benefits_from_defensive_missile", 5}, + {"validate_target_benefits_from_missile_defense", 5}, {"validate_target_benefits_from_defensive", 6}, {"validate_target_benefits_from_healing", 7}, {"validate_target_benefits_from_higher_altitude", 8}, @@ -133,7 +133,7 @@ Creature_Validate_Func creature_instances_validate_func_list[] = { validate_source_even_in_prison, validate_target_generic, validate_target_even_in_prison, - validate_target_benefits_from_defensive_missile, + validate_target_benefits_from_missile_defense, validate_target_benefits_from_defensive, validate_target_benefits_from_healing, validate_target_benefits_from_higher_altitude, @@ -1386,7 +1386,7 @@ TbBool validate_source_even_in_prison * @param param2 Optional 2nd parameter. * @return TbBool True if the creature can, false if otherwise. */ -TbBool validate_target_benefits_from_defensive_missile +TbBool validate_target_benefits_from_missile_defense ( struct Thing *source, struct Thing *target, diff --git a/src/creature_instances.h b/src/creature_instances.h index 7177f399eb..7f1d32f2f7 100644 --- a/src/creature_instances.h +++ b/src/creature_instances.h @@ -166,7 +166,7 @@ TbBool validate_source_even_in_prison(struct Thing *source, struct Thing *target TbBool validate_target_basic(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); TbBool validate_target_generic(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); TbBool validate_target_even_in_prison(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); -TbBool validate_target_benefits_from_defensive_missile(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); +TbBool validate_target_benefits_from_missile_defense(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); TbBool validate_target_benefits_from_defensive(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); TbBool validate_target_benefits_from_higher_altitude(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); TbBool validate_target_benefits_from_offensive(struct Thing *source, struct Thing *target, CrInstance inst_idx, int32_t param1, int32_t param2); From 3f14eadcc55d3a84ea2b22605eeed6084e4ce109 Mon Sep 17 00:00:00 2001 From: Loobinex Date: Fri, 11 Oct 2024 15:59:51 +0200 Subject: [PATCH 11/12] Corrected shitty merging --- src/config_creature.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/config_creature.c b/src/config_creature.c index ac51af3fb4..c6cf2717f6 100644 --- a/src/config_creature.c +++ b/src/config_creature.c @@ -833,9 +833,12 @@ TbBool parse_creaturetype_instance_blocks(char *buf, long len, const char *confi inst_inf->tooltip_stridx = 0; inst_inf->range_min = -1; inst_inf->range_max = -1; - inst_inf->validate_func_idx[0] = 1; - inst_inf->validate_func_idx[1] = 2; - inst_inf->search_func_idx = 1; + inst_inf->validate_source_func = 0; + inst_inf->validate_source_func_params[0] = 0; + inst_inf->validate_source_func_params[1] = 0; + inst_inf->validate_target_func = 0; + inst_inf->validate_target_func_params[0] = 0; + inst_inf->validate_target_func_params[1] = 0; } } instance_desc[INSTANCE_TYPES_MAX - 1].name = NULL; // must be null for get_id From 7626c0690dc6f422f410050bd1656440f68ddf50 Mon Sep 17 00:00:00 2001 From: Loobinex Date: Fri, 11 Oct 2024 16:09:07 +0200 Subject: [PATCH 12/12] Made creaturetype_instance_commands CamelCase --- src/config_creature.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/config_creature.c b/src/config_creature.c index c6cf2717f6..994556dd96 100644 --- a/src/config_creature.c +++ b/src/config_creature.c @@ -76,26 +76,24 @@ const struct NamedCommand creaturetype_experience_commands[] = { {NULL, 0}, }; -// It is pointless to use all uppercase letters here. -// The comparison is case-insensitive and we use Camel-Case in the cfg files. const struct NamedCommand creaturetype_instance_commands[] = { - {"NAME", 1}, - {"TIME", 2}, - {"ACTIONTIME", 3}, - {"RESETTIME", 4}, - {"FPTIME", 5}, - {"FPACTIONTIME", 6}, - {"FPRESETTIME", 7}, - {"FORCEVISIBILITY", 8}, - {"TOOLTIPTEXTID", 9}, - {"SYMBOLSPRITES", 10}, - {"GRAPHICS", 11}, - {"FUNCTION", 12}, - {"RANGEMIN", 13}, - {"RANGEMAX", 14}, - {"PROPERTIES", 15}, - {"FPINSTANTCAST", 16}, - {"PRIMARYTARGET", 17}, + {"Name", 1}, + {"Time", 2}, + {"ActionTime", 3}, + {"ResetTime", 4}, + {"FPTime", 5}, + {"FPActiontime", 6}, + {"FPResettime", 7}, + {"ForceVisibility", 8}, + {"TooltipTextID", 9}, + {"SymbolSprites", 10}, + {"Graphics", 11}, + {"Function", 12}, + {"RangeMin", 13}, + {"RangeMax", 14}, + {"Properties", 15}, + {"FpinstantCast", 16}, + {"PrimaryTarget", 17}, {"ValidateSourceFunc", 18}, {"ValidateTargetFunc", 19}, {"SearchTargetsFunc", 20},