diff --git a/config/fxdata/magic.cfg b/config/fxdata/magic.cfg index aa1751034f..12316ab7dd 100644 --- a/config/fxdata/magic.cfg +++ b/config/fxdata/magic.cfg @@ -326,6 +326,10 @@ DamageType = None ;6 - Affect all things. ;7 - Affect only dungeon hearts. ;8 - Affect only not own dungeon hearts. +;9 - Affect all creatures and all objects, also allow colliding with other shots +;11 - Affect only own creatures. +;12 - Affect own creatures, allied, and neutral creatures. +;13 - Affect hostile creatures. HitType = 0 ; For shots that can damage surrounding area - range, damage and blow. ; The damage and blow will be at its max only very near to the casting point at distance it will start decaying, until zero is reached at given range. @@ -1249,7 +1253,7 @@ AnimationTransparency = 0 Health = 64 Damage = 16 DamageType = -HitType = 4 +HitType = 13 Speed = 0 BaseExperienceGain = 256 DestroyOnHit = 0 diff --git a/src/config_magic.c b/src/config_magic.c index 9721ceb037..5db95f10a1 100644 --- a/src/config_magic.c +++ b/src/config_magic.c @@ -754,7 +754,7 @@ TbBool parse_magic_shot_blocks(char *buf, long len, const char *config_textname, shot_desc[i].num = i; } shotst->model_flags = 0; - shotst->area_hit_type = THit_CrtrsOnly; + shotst->area_hit_type = THit_Creatures; shotst->area_range = 0; shotst->area_damage = 0; shotst->area_blow = 0; diff --git a/src/creature_instances.c b/src/creature_instances.c index e250739237..7e7a585c40 100644 --- a/src/creature_instances.c +++ b/src/creature_instances.c @@ -486,54 +486,26 @@ void process_creature_instance(struct Thing *thing) long instf_creature_fire_shot(struct Thing *creatng, long *param) { - struct Thing *target; - int hittype; - TRACE_THING(creatng); + struct Thing *target = NULL; struct CreatureControl* cctrl = creature_control_get_from_thing(creatng); - if (cctrl->targtng_idx == 0) - { - if ((creatng->alloc_flags & TAlF_IsControlled) == 0) - hittype = THit_CrtrsOnlyNotOwn; - else - hittype = THit_CrtrsNObjcts; - } - else if ((creatng->alloc_flags & TAlF_IsControlled) != 0) + HitTargetFlags hit_targets_flags = 0; + TRACE_THING(creatng); + + target = thing_get(cctrl->targtng_idx); + if (target == NULL || target == INVALID_THING) { - target = thing_get(cctrl->targtng_idx); - TRACE_THING(target); - if (target->class_id == TCls_Object) - hittype = THit_CrtrsNObjcts; - else if (target->class_id == TCls_Trap) - hittype = THit_TrapsAll; - else - hittype = THit_CrtrsOnly; + SYNCDBG(8,"The %s(%d) fires %s",thing_model_name(creatng), creatng->index, shot_code_name(*param)); + hit_targets_flags = get_hit_targets_for_shot(creatng, NULL, *param); } else { - target = thing_get(cctrl->targtng_idx); - TRACE_THING(target); - if (target->class_id == TCls_Object) - hittype = THit_CrtrsNObjctsNotOwn; - else if (thing_is_destructible_trap(target) > 0) - hittype = THit_CrtrsNObjctsNotOwn; - else if (target->class_id == TCls_Trap) - hittype = THit_TrapsAll; - else if (target->owner == creatng->owner) - hittype = THit_CrtrsOnly; - else - hittype = THit_CrtrsOnlyNotOwn; - } - if (cctrl->targtng_idx > 0) - { - target = thing_get(cctrl->targtng_idx); - SYNCDBG(8,"The %s index %d fires %s at %s index %d",thing_model_name(creatng),(int)creatng->index,shot_code_name(*param),thing_model_name(target),(int)target->index); + SYNCDBG(8,"The %s(%d) fires %s at %s(%d)", thing_model_name(creatng), creatng->index, + shot_code_name(*param), thing_model_name(target), target->index); TRACE_THING(target); - } else - { - target = NULL; - SYNCDBG(8,"The %s index %d fires %s",thing_model_name(creatng),(int)creatng->index,shot_code_name(*param)); + hit_targets_flags = get_hit_targets_for_shot(creatng, target, *param); } - thing_fire_shot(creatng, target, *param, 1, hittype); + + thing_fire_shot(creatng, target, *param, 1, hit_targets_flags); // Start cooldown after shot is fired cctrl->instance_use_turn[cctrl->instance_id] = game.play_gameturn; return 0; @@ -550,12 +522,20 @@ long instf_creature_cast_spell(struct Thing *creatng, long *param) SYNCDBG(8,"The %s(%d) casts %s at %d", thing_model_name(creatng), (int)creatng->index, spell_code_name(spl_idx), cctrl->targtng_idx); - if (spconf->cast_at_thing && cctrl->targtng_idx != creatng->index) + // If the targtng_idx is jus the caster itself, we can call creature_cast_spell + // instead of creature_cast_spell_at_thing. + if (cctrl->targtng_idx != creatng->index) { - // If the targtng_idx is just the caster itself, we can call creature_cast_spell - // instead of creature_cast_spell_at_thing. - target = thing_get(cctrl->targtng_idx); - if (thing_is_invalid(target)) target = NULL; + if (!spconf->cast_at_thing) + { + WARNLOG("The spell %s is casted on a thing(%d) but its CastAtThing property is false", + spell_code_name(spl_idx), cctrl->targtng_idx); + } + else + { + target = thing_get(cctrl->targtng_idx); + if (thing_is_invalid(target)) target = NULL; + } } if (target != NULL) @@ -888,7 +868,7 @@ long instf_fart(struct Thing *creatng, long *param) TRACE_THING(creatng); struct Thing* efftng = create_effect(&creatng->mappos, TngEff_Gas3, creatng->owner); if (!thing_is_invalid(efftng)) - efftng->shot_effect.hit_type = THit_CrtrsOnlyNotOwn; + efftng->shot_effect.hit_type = THit_CreaturesNotOwn; // Consider using THit_CreaturesHostile. thing_play_sample(creatng,94+UNSYNC_RANDOM(6), NORMAL_PITCH, 0, 3, 0, 4, FULL_LOUDNESS); // Start cooldown after fart created struct CreatureControl* cctrl = creature_control_get_from_thing(creatng); diff --git a/src/game_loop.c b/src/game_loop.c index 037f557ac4..376e90d022 100644 --- a/src/game_loop.c +++ b/src/game_loop.c @@ -198,10 +198,10 @@ void process_dungeon_destroy(struct Thing* heartng) struct Thing* efftng; efftng = create_used_effect_or_element(central_pos, objst->effect.explosion1, plyr_idx); if (!thing_is_invalid(efftng)) - efftng->shot_effect.hit_type = THit_HeartOnlyNotOwn; + efftng->shot_effect.hit_type = THit_HeartNotOwn; efftng = create_used_effect_or_element(central_pos, objst->effect.explosion2, plyr_idx); if (!thing_is_invalid(efftng)) - efftng->shot_effect.hit_type = THit_HeartOnlyNotOwn; + efftng->shot_effect.hit_type = THit_HeartNotOwn; destroy_dungeon_heart_room(plyr_idx, heartng); delete_thing_structure(heartng, 0); } diff --git a/src/magic.c b/src/magic.c index 4940221b54..91a1345330 100644 --- a/src/magic.c +++ b/src/magic.c @@ -1473,7 +1473,7 @@ static TbResult magic_use_power_lightning(PowerKind power_kind, PlayerNumber ply if (!thing_is_invalid(shtng)) { shtng->mappos.z.val = get_thing_height_at(shtng, &shtng->mappos) + COORD_PER_STL/2; - shtng->shot.hit_type = THit_CrtrsOnly; + shtng->shot.hit_targets = hit_type_to_hit_targets(THit_Creatures); shtng->shot.spell_level = splevel; } pwrdynst = get_power_dynamic_stats(power_kind); diff --git a/src/thing_creature.c b/src/thing_creature.c index 992135efeb..e1ed131ee0 100644 --- a/src/thing_creature.c +++ b/src/thing_creature.c @@ -1551,33 +1551,16 @@ short creature_take_wage_from_gold_pile(struct Thing *creatng,struct Thing *gold */ void creature_cast_spell_at_thing(struct Thing *castng, struct Thing *targetng, SpellKind spl_idx, long shot_lvl) { - unsigned char hit_type; - if ((castng->alloc_flags & TAlF_IsControlled) != 0) - { - if ((targetng->class_id == TCls_Object) || (targetng->class_id == TCls_Trap)) - hit_type = THit_CrtrsNObjcts; - else - hit_type = THit_CrtrsOnly; - } else - { - if ((targetng->class_id == TCls_Object) || (targetng->class_id == TCls_Trap)) - hit_type = THit_CrtrsNObjctsNotOwn; - else - if (targetng->owner == castng->owner) - hit_type = THit_CrtrsOnly; - else - hit_type = THit_CrtrsOnlyNotOwn; - } + HitTargetFlags hit_targets = get_hit_targets_for_spell(castng, targetng, spl_idx); const struct SpellConfig* spconf = get_spell_config(spl_idx); if (spell_config_is_invalid(spconf)) { ERRORLOG("The %s owned by player %d tried to cast invalid spell %d",thing_model_name(castng),(int)castng->owner,(int)spl_idx); return; } - - SYNCDBG(12,"The %s(%u) fire shot(%s) at %s(%u) with shot level %ld, hit type: 0x%02X", thing_model_name(castng), castng->index, - shot_code_name(spconf->shot_model), thing_model_name(targetng), targetng->index, shot_lvl, hit_type); - thing_fire_shot(castng, targetng, spconf->shot_model, shot_lvl, hit_type); + SYNCDBG(12,"The %s(%u) fire shot(%s) at %s(%u) with shot level %ld, hit targets: 0x%02X", thing_model_name(castng), castng->index, + shot_code_name(spconf->shot_model), thing_model_name(targetng), targetng->index, shot_lvl, hit_targets); + thing_fire_shot(castng, targetng, spconf->shot_model, shot_lvl, hit_targets); } /** @@ -1767,12 +1750,11 @@ void remove_creature_from_summon_list(struct Dungeon* dungeon, ThingIndex famlrt * @param castng The caster creature. * @param spl_idx Spell index to be casted. * @param shot_lvl Spell level to be casted. - * @param trg_x - * @param trg_y + * @param trg_x Coordinate X of the target location + * @param trg_y Coordinate Y of the target location */ void creature_cast_spell(struct Thing *castng, SpellKind spl_idx, long shot_lvl, MapSubtlCoord trg_x, MapSubtlCoord trg_y) { - long i; const struct SpellConfig* spconf = get_spell_config(spl_idx); struct CreatureControl* cctrl = creature_control_get_from_thing(castng); if (creature_control_invalid(cctrl)) @@ -1785,7 +1767,19 @@ 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->shot_model > 0) && (cctrl->targtng_idx != castng->index)) + { + // If we reach here, it means that this spell cannot find a Thing as the target, + // either due to invalid target index or incorrect CastAtThing setting. + // Besides, this spell has a shot model. It means the caster want to fire a shot without + // locking on a target. + // In this case, it should use instf_creature_fire_shot, not instf_creature_cast_spell. + // This is a misuse. + WARNLOG("Consider using instf_creature_fire_shot for spell %s", spell_code_name(spl_idx)); + HitTargetFlags hit_targets_flags = get_hit_targets_for_shot(castng, NULL, spconf->shot_model); + thing_fire_shot(castng, INVALID_THING, spconf->shot_model, shot_lvl, hit_targets_flags); + } + // Check if the spell can be self-casted if (spconf->caster_affected) { if (spconf->caster_affect_sound > 0) @@ -3090,7 +3084,7 @@ static void shot_set_start_pos(const struct Thing *firing, const struct ShotConf } } -void thing_fire_shot(struct Thing *firing, struct Thing *target, ThingModel shot_model, char shot_lvl, unsigned char hit_type) +void thing_fire_shot(struct Thing *firing, struct Thing *target, ThingModel shot_model, char shot_lvl, HitTargetFlags hit_targets) { struct Coord3d pos2; struct Thing *tmptng; @@ -3199,10 +3193,7 @@ void thing_fire_shot(struct Thing *firing, struct Thing *target, ThingModel shot damage = calculate_shot_damage(firing, shot_model); } } - if ((shotst->model_flags & ShMF_Disarming) && thing_is_deployed_trap(target)) - { - hit_type = THit_TrapsAll; - } + struct Thing* shotng = NULL; long target_idx = 0; // Set target index for navigating shots @@ -3257,7 +3248,7 @@ void thing_fire_shot(struct Thing *firing, struct Thing *target, ThingModel shot if (thing_is_invalid(tmptng)) break; shotng = tmptng; - shotng->shot.hit_type = hit_type; + shotng->shot.hit_targets = hit_targets; shotng->move_angle_xy = (short)((angle_xy + CREATURE_RANDOM(firing, 2 * shotst->spread_xy + 1) - shotst->spread_xy) & LbFPMath_AngleMask); shotng->move_angle_z = (short)((angle_yz + CREATURE_RANDOM(firing, 2 * shotst->spread_z + 1) - shotst->spread_z) & LbFPMath_AngleMask); angles_to_vector(shotng->move_angle_xy, shotng->move_angle_z, speed, &cvect); @@ -3326,7 +3317,7 @@ void thing_fire_shot(struct Thing *firing, struct Thing *target, ThingModel shot WARNLOG("Shot of type %d carries %d damage",(int)shot_model,(int)damage); } #endif - shotng->shot.hit_type = hit_type; + shotng->shot.hit_targets = hit_targets; if (shotst->firing_sound > 0) { thing_play_sample(firing, shotst->firing_sound + UNSYNC_RANDOM(shotst->firing_sound_variants), diff --git a/src/thing_creature.h b/src/thing_creature.h index c31902f14e..539771e529 100644 --- a/src/thing_creature.h +++ b/src/thing_creature.h @@ -113,7 +113,7 @@ long creature_available_for_combat_this_turn(struct Thing *thing); TbBool set_creature_object_combat(struct Thing *crthing, struct Thing *obthing); TbBool set_creature_object_snipe(struct Thing* crthing, struct Thing* obthing); TbBool set_creature_door_combat(struct Thing *crthing, struct Thing *obthing); -void thing_fire_shot(struct Thing *firing,struct Thing *target, ThingModel shot_model, char shot_lvl, unsigned char hit_type); +void thing_fire_shot(struct Thing *firing,struct Thing *target, ThingModel shot_model, char shot_lvl, HitTargetFlags hit_targets); void creature_cast_spell_at_thing(struct Thing *caster, struct Thing *target, SpellKind spl_idx, long shot_lvl); void creature_cast_spell(struct Thing *caster, SpellKind spl_idx, long shot_lvl, MapSubtlCoord trg_x, MapSubtlCoord trg_y); diff --git a/src/thing_data.h b/src/thing_data.h index 704f0480ad..ebd0c1b14a 100644 --- a/src/thing_data.h +++ b/src/thing_data.h @@ -176,12 +176,12 @@ struct Thing { short unused3; long last_turn_drawn; unsigned char display_timer; - }roomflag2; // both roomflag and roomflag2 are used in same function on same object but have 2 bytes overlapping between room_idx and last_turn_drawn + } roomflag2; // both roomflag and roomflag2 are used in same function on same object but have 2 bytes overlapping between room_idx and last_turn_drawn //TCls_Shot struct { unsigned char dexterity; short damage; - unsigned char hit_type; + HitTargetFlags hit_targets; short target_idx; unsigned char spell_level; struct Coord3d originpos; diff --git a/src/thing_effects.c b/src/thing_effects.c index b78e0cdb5e..0463670ef3 100644 --- a/src/thing_effects.c +++ b/src/thing_effects.c @@ -1158,7 +1158,7 @@ long explosion_effect_affecting_map_block(struct Thing *efftng, struct Thing *tn } i = thing->next_on_mapblk; // Per thing processing block - if ((thing->class_id == TCls_Door) && (efftng->shot_effect.hit_type != THit_CrtrsOnlyNotOwn)) //TODO: Find pretty way to say that WoP traps should not destroy doors. And make it configurable through configs. + if ((thing->class_id == TCls_Door) && (efftng->shot_effect.hit_type != THit_CreaturesNotOwn)) //TODO: Find pretty way to say that WoP traps should not destroy doors. And make it configurable through configs. { if (explosion_affecting_door(tngsrc, thing, &efftng->mappos, max_dist, shotst->area_damage, shotst->area_blow, shotst->damage_type, owner)) { diff --git a/src/thing_effects.h b/src/thing_effects.h index 8008cb0081..859820d327 100644 --- a/src/thing_effects.h +++ b/src/thing_effects.h @@ -30,18 +30,24 @@ extern "C" { #endif /******************************************************************************/ + +// This provides a convenient way to specify hit types in the cfg files. +// They will be converted to a combination of HitTargetFlags for further process. enum ThingHitTypes { THit_None = 0, - THit_CrtrsNObjcts, // Affect all creatures and all objects - THit_CrtrsOnly, // Affect only creatures - THit_CrtrsNObjctsNotOwn, // Affect not own creatures and objects - THit_CrtrsOnlyNotOwn, // Affect not own creatures - THit_CrtrsNotArmourNotOwn, // Affect not own creatures which are not protected by Armour spell + THit_CreaturesAndObjects, // Affect all creatures and all objects + THit_Creatures, // Affect only creatures + THit_CreaturesAndObjectsNotOwn, // Affect not own creatures and objects + THit_CreaturesNotOwn, // Affect not own creatures + THit_CreaturesHostileNotArmour, // Affect hostile creatures which are not protected by Armour spell THit_All, // Affect all things - THit_HeartOnly, // Affect only dungeon hearts - THit_HeartOnlyNotOwn, // Affect only not own dungeon hearts - THit_CrtrsNObjctsNShot, // Affect all creatures and all objects, also allow colliding with other shots - THit_TrapsAll, // Affect all traps, not just the ones that are destructable + THit_Heart, // Affect only dungeon hearts + THit_HeartNotOwn, // Affect only not own dungeon hearts + THit_CreaturesAndObjectsCanCollide, // Affect all creatures and all objects, also allow colliding with other shots + THit_TrapsAll, // Affect all traps, not just the ones that are destructible + THit_CreaturesOwn, // Affect own creatures + THit_CreaturesFriendly, // Affect own creatures and allied/neutral creatures. + THit_CreaturesHostile, // Affect hostile creatures. THit_TypesCount, // Last item in enumeration, allows checking amount of valid types }; diff --git a/src/thing_list.c b/src/thing_list.c index 10823510e6..6881ad62ae 100644 --- a/src/thing_list.c +++ b/src/thing_list.c @@ -3382,34 +3382,40 @@ HitTargetFlags hit_type_to_hit_targets(long hit_type) switch (hit_type) { case THit_All: - case THit_CrtrsNObjctsNShot: + case THit_CreaturesAndObjectsCanCollide: return HitTF_EnemyCreatures|HitTF_AlliedCreatures|HitTF_OwnedCreatures|HitTF_ArmourAffctdCreatrs|HitTF_PreventDmgCreatrs| HitTF_EnemySoulContainer|HitTF_AlliedSoulContainer|HitTF_OwnedSoulContainer| HitTF_AnyWorkshopBoxes|HitTF_AnySpellbooks|HitTF_AnyDnSpecialBoxes| HitTF_EnemyShotsCollide|HitTF_AlliedShotsCollide|HitTF_OwnedShotsCollide| HitTF_EnemyDestructibleTraps|HitTF_AlliedDestructibleTraps|HitTF_OwnedDestructibleTraps| HitTF_AnyFoodObjects|HitTF_AnyGoldPiles; - case THit_CrtrsNObjcts: + case THit_CreaturesAndObjects: return HitTF_EnemyCreatures|HitTF_AlliedCreatures|HitTF_OwnedCreatures|HitTF_ArmourAffctdCreatrs|HitTF_PreventDmgCreatrs| HitTF_EnemySoulContainer|HitTF_AlliedSoulContainer|HitTF_OwnedSoulContainer| HitTF_AnyWorkshopBoxes|HitTF_AnySpellbooks|HitTF_AnyDnSpecialBoxes| HitTF_EnemyDestructibleTraps|HitTF_AlliedDestructibleTraps|HitTF_OwnedDestructibleTraps| HitTF_AnyFoodObjects|HitTF_AnyGoldPiles; - case THit_CrtrsOnly: - return HitTF_EnemyCreatures|HitTF_AlliedCreatures|HitTF_OwnedCreatures|HitTF_ArmourAffctdCreatrs; - case THit_CrtrsNObjctsNotOwn: + case THit_CreaturesAndObjectsNotOwn: return HitTF_EnemyCreatures|HitTF_AlliedCreatures|HitTF_ArmourAffctdCreatrs| HitTF_EnemySoulContainer|HitTF_AlliedSoulContainer| HitTF_AnyWorkshopBoxes|HitTF_AnySpellbooks|HitTF_AnyDnSpecialBoxes| HitTF_EnemyDestructibleTraps | HitTF_AlliedDestructibleTraps| HitTF_AnyFoodObjects|HitTF_AnyGoldPiles; - case THit_CrtrsOnlyNotOwn: + case THit_Creatures: + return HitTF_EnemyCreatures | HitTF_AlliedCreatures| HitTF_OwnedCreatures | HitTF_ArmourAffctdCreatrs; + case THit_CreaturesNotOwn: return HitTF_EnemyCreatures|HitTF_AlliedCreatures|HitTF_ArmourAffctdCreatrs; - case THit_CrtrsNotArmourNotOwn: - return HitTF_EnemyCreatures|HitTF_AlliedCreatures; - case THit_HeartOnly: + case THit_CreaturesOwn: + return HitTF_OwnedCreatures | HitTF_ArmourAffctdCreatrs; + case THit_CreaturesFriendly: + return HitTF_OwnedCreatures | HitTF_AlliedCreatures | HitTF_ArmourAffctdCreatrs; + case THit_CreaturesHostile: + return HitTF_EnemyCreatures | HitTF_ArmourAffctdCreatrs; + case THit_CreaturesHostileNotArmour: + return HitTF_EnemyCreatures; + case THit_Heart: return HitTF_EnemySoulContainer|HitTF_AlliedSoulContainer|HitTF_OwnedSoulContainer; - case THit_HeartOnlyNotOwn: + case THit_HeartNotOwn: return HitTF_EnemySoulContainer|HitTF_AlliedSoulContainer; case THit_TrapsAll: return HitTF_EnemyDestructibleTraps|HitTF_AlliedDestructibleTraps|HitTF_OwnedDestructibleTraps| @@ -3422,6 +3428,140 @@ HitTargetFlags hit_type_to_hit_targets(long hit_type) } } +/** + * @brief Get the hit targets for a spell. + * A spell may and may not use Shot object. + * The HitType for the main effct of a shot isn't specified in the magic.cfg. Only the HitType for area damage + * is specified there. This function is used to compute its reasonable HitTargetFlags of the main effect. + * @param caster + * @param target + * @param spell_idx + * @return HitTargetFlags + */ +HitTargetFlags get_hit_targets_for_spell(struct Thing *caster, struct Thing *target, SpellKind spell_idx) +{ + HitTargetFlags result = 0; + if ((caster->alloc_flags & TAlF_IsControlled) != 0) + { + // The caster is under player's control. Apply looser restrictions. + if ((target->class_id == TCls_Object) || (target->class_id == TCls_Trap)) + { + result = hit_type_to_hit_targets(THit_CreaturesAndObjects); + } + else + { + result = hit_type_to_hit_targets(THit_Creatures); + } + } + else + { + if ((target->class_id == TCls_Object) || (target->class_id == TCls_Trap)) + { + result = hit_type_to_hit_targets(THit_CreaturesAndObjectsNotOwn); + } + else + { + if (caster->owner == target->owner) + { + result = hit_type_to_hit_targets(THit_CreaturesOwn); + } + else if(players_are_mutual_allies(caster->owner, target->owner)) + { + // Allied target. + result = HitTF_AlliedCreatures|HitTF_ArmourAffctdCreatrs; + } + else + { + // Enemy. + result = HitTF_EnemyCreatures|HitTF_ArmourAffctdCreatrs; + } + } + } + + return result; +} + +/** + * @brief Get the hit targets for a shot + * The HitType for the main effct of a shot isn't specified in the magic.cfg. Only the HitType for area damage + * is specified there. This function is used to compute its reasonable HitTargetFlags of the main effect. + * @param caster + * @param target + * @param shot_model + * @return HitTargetFlags + */ +HitTargetFlags get_hit_targets_for_shot(struct Thing *caster, struct Thing *target, ThingModel shot_model) +{ + HitTargetFlags result = 0; + + struct ShotConfigStats* shotst = get_shot_model_stats(shot_model); + if ((shotst->model_flags & ShMF_Disarming) && thing_is_deployed_trap(target)) + { + result = hit_type_to_hit_targets(THit_TrapsAll); + return result; + } + + if (target == NULL) + { + // If no target is specified. + if ((caster->alloc_flags & TAlF_IsControlled) != 0) + { + // The caster is under player's control. Apply looser restrictions. + result = hit_type_to_hit_targets(THit_CreaturesAndObjects); + } + else + { + result = hit_type_to_hit_targets(THit_CreaturesNotOwn); + } + } + else if ((caster->alloc_flags & TAlF_IsControlled) != 0) + { + if (target->class_id == TCls_Object) + { + result = hit_type_to_hit_targets(THit_CreaturesAndObjects); + } + else if (target->class_id == TCls_Trap) + { + result = hit_type_to_hit_targets(THit_TrapsAll); + } + else + { + result = hit_type_to_hit_targets(THit_Creatures); + } + } + else + { + if (target->class_id == TCls_Object) + { + result = hit_type_to_hit_targets(THit_CreaturesAndObjectsNotOwn); + } + else if (thing_is_destructible_trap(target) > 0) + { + result = hit_type_to_hit_targets(THit_CreaturesAndObjectsNotOwn); + } + else if (target->class_id == TCls_Trap) + { + result = hit_type_to_hit_targets(THit_TrapsAll); + } + else if (caster->owner == target->owner) + { + result = hit_type_to_hit_targets(THit_CreaturesOwn); + } + else if(players_are_mutual_allies(caster->owner, target->owner)) + { + // Allied target. + result = HitTF_AlliedCreatures|HitTF_ArmourAffctdCreatrs; + } + else + { + // Enemy. + result = HitTF_EnemyCreatures|HitTF_ArmourAffctdCreatrs; + } + } + + return result; +} + /** * Returns whether a thing can be shot by given players shot. * @param thing The thing to be checked. diff --git a/src/thing_list.h b/src/thing_list.h index c825d6c158..5a9908b2f5 100644 --- a/src/thing_list.h +++ b/src/thing_list.h @@ -108,7 +108,7 @@ enum HitTargetFlagsList { HitTF_OwnedSoulContainer = 0x0000000000008000,//!< Allow targeting own soul container. HitTF_AnyWorkshopBoxes = 0x0000000000040000,//!< Allow targeting Workshop boxes owned by anyone. HitTF_AnySpellbooks = 0x0000000000080000,//!< Allow targeting spellbook objects owned by anyone. - HitTF_AnyDnSpecialBoxes = 0x0000000000100000,//!< Allow targeting Dnungeon Special boxes owned by anyone. + HitTF_AnyDnSpecialBoxes = 0x0000000000100000,//!< Allow targeting Dungeon Special boxes owned by anyone. HitTF_AnyGoldHoards = 0x0000000000200000,//!< Allow targeting Gold Hoards owned by anyone. HitTF_AnyFoodObjects = 0x0000000000400000,//!< Allow targeting Food Objects owned by anyone. HitTF_AnyGoldPiles = 0x0000000000800000,//!< Allow targeting gold laying on ground before storing in treasury, pots and piles. @@ -321,6 +321,8 @@ struct Thing *find_creature_lair_totem_at_subtile(MapSubtlCoord stl_x, MapSubtlC TbBool thing_is_shootable(const struct Thing *thing, PlayerNumber shot_owner, HitTargetFlags hit_targets); HitTargetFlags hit_type_to_hit_targets(long hit_type); +HitTargetFlags get_hit_targets_for_spell(struct Thing *caster, struct Thing *target, SpellKind spell_idx); +HitTargetFlags get_hit_targets_for_shot(struct Thing *caster, struct Thing *target, ThingModel shot_model); HitTargetFlags collide_filter_thing_is_of_type(const struct Thing *thing, const struct Thing *sectng, HitTargetFlags a3, long a4); TbBool imp_already_digging_at_excluding(struct Thing *excltng, MapSubtlCoord stl_x, MapSubtlCoord stl_y); diff --git a/src/thing_objects.c b/src/thing_objects.c index 586851231b..3c455a8ffa 100644 --- a/src/thing_objects.c +++ b/src/thing_objects.c @@ -1289,10 +1289,10 @@ static TngUpdateRet object_update_dungeon_heart(struct Thing *heartng) struct Thing* efftng; efftng = create_used_effect_or_element(&heartng->mappos, objst->effect.explosion1, heartng->owner); if (!thing_is_invalid(efftng)) - efftng->shot_effect.hit_type = THit_HeartOnlyNotOwn; + efftng->shot_effect.hit_type = THit_HeartNotOwn; efftng = create_used_effect_or_element(&heartng->mappos, objst->effect.explosion2, heartng->owner); if (!thing_is_invalid(efftng)) - efftng->shot_effect.hit_type = THit_HeartOnlyNotOwn; + efftng->shot_effect.hit_type = THit_HeartNotOwn; destroy_dungeon_heart_room(heartng->owner, heartng); if (!dungeon_invalid(dungeon) && heartng->index == dungeon->backup_heart_idx) { diff --git a/src/thing_shots.c b/src/thing_shots.c index aa7808c623..e4d4ce4b65 100644 --- a/src/thing_shots.c +++ b/src/thing_shots.c @@ -1288,7 +1288,7 @@ long shot_hit_creature_at(struct Thing *shotng, struct Thing *trgtng, struct Coo pos2.z.val += (killertng->clipbox_size_z >> 1); if (thing_is_destructible_trap(killertng)) { - shotng->shot.hit_type = THit_CrtrsNObjctsNotOwn; + shotng->shot.hit_targets = hit_type_to_hit_targets(THit_CreaturesAndObjectsNotOwn); } } else @@ -1326,7 +1326,7 @@ long shot_hit_creature_at(struct Thing *shotng, struct Thing *trgtng, struct Coo // TODO make this configurable somehow. struct Thing* efftng = create_effect(&trgtng->mappos, TngEff_WoPExplosion, trgtng->owner); if (!thing_is_invalid(efftng)) { - efftng->shot_effect.hit_type = THit_HeartOnlyNotOwn; + efftng->shot_effect.hit_type = THit_HeartNotOwn; } shotng->health = -1; return 1; @@ -1611,10 +1611,9 @@ struct Thing *get_thing_collided_with_at_satisfying_filter(struct Thing *shotng, */ TbBool shot_hit_something_while_moving(struct Thing *shotng, struct Coord3d *nxpos) { - SYNCDBG(18,"Starting for %s index %d, hit type %d",thing_model_name(shotng),(int)shotng->index, (int)shotng->shot.hit_type); + SYNCDBG(18,"Starting for %s index %d, hit targets %d",thing_model_name(shotng),(int)shotng->index, (int)shotng->shot.hit_targets); struct Thing* targetng = INVALID_THING; - HitTargetFlags hit_targets = hit_type_to_hit_targets(shotng->shot.hit_type); - targetng = get_thing_collided_with_at_satisfying_filter(shotng, nxpos, collide_filter_thing_is_shootable, hit_targets, 0); + targetng = get_thing_collided_with_at_satisfying_filter(shotng, nxpos, collide_filter_thing_is_shootable, shotng->shot.hit_targets, 0); if (thing_is_invalid(targetng)) { return false; } diff --git a/src/thing_traps.c b/src/thing_traps.c index 91badff38e..59996ba8d6 100644 --- a/src/thing_traps.c +++ b/src/thing_traps.c @@ -446,7 +446,7 @@ void activate_trap_shot_head_for_target90(struct Thing *traptng, struct Thing *c shotng->veloc_push_add.y.val += cvect.y; shotng->veloc_push_add.z.val += cvect.z; shotng->state_flags |= TF1_PushAdd; - shotng->shot.hit_type = trapst->hit_type; + shotng->shot.hit_targets = hit_type_to_hit_targets(trapstat->hit_type); if (shotst->firing_sound > 0) { thing_play_sample(traptng, shotst->firing_sound+UNSYNC_RANDOM(shotst->firing_sound_variants), NORMAL_PITCH, 0, 3, 0, 6, FULL_LOUDNESS); @@ -502,7 +502,7 @@ void activate_trap_shot_on_trap(struct Thing *traptng) shot_origin.z.val += trapst->shot_shift_z; struct Thing* shotng = create_shot(&shot_origin, trapst->created_itm_model, traptng->owner); if (!thing_is_invalid(shotng)) { - shotng->shot.hit_type = trapst->hit_type; + shotng->shot.hit_targets = hit_type_to_hit_targets(trapstat->hit_type); shotng->parent_idx = 0; shotng->veloc_push_add.x.val += trapst->shotvector.x; shotng->veloc_push_add.y.val += trapst->shotvector.y; @@ -616,7 +616,7 @@ void activate_trap(struct Thing *traptng, struct Thing *creatng) activate_trap_slab_change(traptng); break; case TrpAcT_CreatureShot: - thing_fire_shot(traptng, creatng, trapst->created_itm_model, 1, trapst->hit_type); + thing_fire_shot(traptng, creatng, trapstat->created_itm_model, 1, hit_type_to_hit_targets(trapstat->hit_type)); break; case TrpAcT_CreatureSpawn: activate_trap_spawn_creature(traptng, trapst->created_itm_model); @@ -888,7 +888,7 @@ TngUpdateRet update_trap(struct Thing *traptng) } if (traptng->trap.volley_repeat > 0) { - thing_fire_shot(traptng, thing_get(traptng->trap.firing_at), trapst->created_itm_model, 1, THit_CrtrsNObjcts); + thing_fire_shot(traptng, thing_get(traptng->trap.firing_at) , trapstat->created_itm_model, 1, hit_type_to_hit_targets(THit_CreaturesAndObjects)); return TUFRet_Modified; } } @@ -1094,7 +1094,7 @@ void external_activate_trap_shot_at_angle(struct Thing *thing, short angle, stru } if (!thing_is_invalid(trgtng)) { - thing_fire_shot(thing, trgtng, trapst->created_itm_model, 1, trapst->hit_type); + thing_fire_shot(thing, trgtng, trapstat->created_itm_model, 1, hit_type_to_hit_targets(trapstat->hit_type)); } else { @@ -1266,7 +1266,7 @@ void trap_fire_shot_without_target(struct Thing *firing, ThingModel shot_model, shotng->veloc_push_add.y.val += cvect.y; shotng->veloc_push_add.z.val += cvect.z; shotng->state_flags |= TF1_PushAdd; - shotng->shot.hit_type = trapst->hit_type; + shotng->shot.hit_targets = hit_type_to_hit_targets(trapstat->hit_type); break; } }