Skip to content

Commit

Permalink
Slappable traps can be configured to auto-aim at nearest enemy (#3211)
Browse files Browse the repository at this point in the history
by setting 'Slappable' to '2'
Fixes #3210

Co-authored-by: Loobinex <Loobinex@users.noreply.github.com>
  • Loading branch information
AdamPlenty and Loobinex committed May 12, 2024
1 parent 759e0a1 commit 2d2a892
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 36 deletions.
2 changes: 1 addition & 1 deletion config/fxdata/trapdoor.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ ActivationType = 0
EffectType = 0
; If the trap is visible before getting triggered.
Hidden = 0
; Can the trap be triggered by activating it. Shots only.
; Can the trap be triggered by activating it. Set to 1 to activate by slap, set to 2 to make it target nearest enemy in range on slap.
Slappable = 0
; Can the trap be destroyed by shooting it. If 1 it can by any attack, 0 only by units with 'Disarm ability', -1 by nobody.
Destructible = 0
Expand Down
8 changes: 4 additions & 4 deletions src/config_trapdoor.c
Original file line number Diff line number Diff line change
Expand Up @@ -289,8 +289,8 @@ TbBool parse_trapdoor_trap_blocks(char *buf, long len, const char *config_textna
trapst->unstable = 0;
trapst->unsellable = false;
trapst->notify = false;
trapst->placeonbridge = false;
trapst->placeonsubtile = false;
trapst->place_on_bridge = false;
trapst->place_on_subtile = false;
// Default destroyed_effect is TngEffElm_Blast2.
trapst->destroyed_effect = -39;

Expand Down Expand Up @@ -960,7 +960,7 @@ TbBool parse_trapdoor_trap_blocks(char *buf, long len, const char *config_textna
k = atoi(word_buf);
if (k >= 0)
{
trapst->placeonbridge = k;
trapst->place_on_bridge = k;
n++;
}
}
Expand Down Expand Up @@ -1111,7 +1111,7 @@ TbBool parse_trapdoor_trap_blocks(char *buf, long len, const char *config_textna
k = atoi(word_buf);
if (k >= 0)
{
trapst->placeonsubtile = k;
trapst->place_on_subtile = k;
n++;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/config_trapdoor.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ struct TrapConfigStats {
short unstable;
TbBool notify;
TbBool unsellable;
TbBool placeonbridge;
TbBool placeonsubtile;
TbBool place_on_bridge;
TbBool place_on_subtile;
EffectOrEffElModel destroyed_effect;
};

Expand Down
6 changes: 3 additions & 3 deletions src/lvl_script_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -1593,8 +1593,8 @@ static void new_trap_type_check(const struct ScriptLine* scline)
trapst->unstable = 0;
trapst->unsellable = false;
trapst->notify = false;
trapst->placeonbridge = false;
trapst->placeonsubtile = false;
trapst->place_on_bridge = false;
trapst->place_on_subtile = false;
trapst->place_sound_idx = 117;
trapst->trigger_sound_idx = 176;
trapst->destroyed_effect = -39;
Expand Down Expand Up @@ -1836,7 +1836,7 @@ static void set_trap_configuration_process(struct ScriptContext *context)
trapst->unsellable = value;
break;
case 35: // PlaceOnBridge
trapst->placeonbridge = value;
trapst->place_on_bridge = value;
break;
case 36: // ShotOrigin
trapstat->shot_shift_x = value;
Expand Down
2 changes: 1 addition & 1 deletion src/magic.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ TbBool can_cast_power_on_thing(PlayerNumber plyr_idx, const struct Thing *thing,
if (thing->owner == plyr_idx) {
struct TrapConfigStats *trapst;
trapst = &game.conf.trapdoor_conf.trap_cfgstats[thing->model];
if ((trapst->slappable == 1) && trap_is_active(thing)) {
if ((trapst->slappable > 0) && trap_is_active(thing)) {
return true;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/packets_input.c
Original file line number Diff line number Diff line change
Expand Up @@ -631,7 +631,7 @@ TbBool process_dungeon_control_packet_dungeon_place_trap(long plyr_idx)
return false;
}
struct TrapConfigStats *trapst = get_trap_model_stats(player->chosen_trap_kind);
player->full_slab_cursor = (trapst->placeonsubtile == false);
player->full_slab_cursor = (trapst->place_on_subtile == false);
long i = tag_cursor_blocks_place_trap(player->id_number, stl_x, stl_y, player->full_slab_cursor, player->chosen_trap_kind);
if ((pckt->control_flags & PCtr_LBtnClick) == 0)
{
Expand Down
18 changes: 14 additions & 4 deletions src/player_instances.c
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,20 @@ long pinstfe_hand_whip(struct PlayerInfo *player, long *n)
break;
case TCls_Trap:
trapst = &game.conf.trapdoor_conf.trap_cfgstats[thing->model];
if ((trapst->slappable == 1) && trap_is_active(thing))
if ((trapst->slappable > 0) && trap_is_active(thing))
{
external_activate_trap_shot_at_angle(thing, player->acamera->orient_a, thing_get(player->hand_thing_idx));
struct TrapStats* trapstat = &game.conf.trap_stats[thing->model];
struct Thing* trgtng = INVALID_THING;
shotst = get_shot_model_stats(trapstat->created_itm_model);
if (trapst->slappable == 1)
{
external_activate_trap_shot_at_angle(thing, player->acamera->orient_a, trgtng);
} else
if (trapst->slappable == 2)
{
trgtng = get_nearest_enemy_creature_in_sight_and_range_of_trap(thing);
external_activate_trap_shot_at_angle(thing, player->acamera->orient_a, trgtng);
}
}
break;
case TCls_Object:
Expand Down Expand Up @@ -1195,8 +1206,7 @@ TbBool player_place_trap_at(MapSubtlCoord stl_x, MapSubtlCoord stl_y, PlayerNumb
}
struct TrapConfigStats* trap_cfg = get_trap_model_stats(tngmodel);
struct Coord3d pos;
struct PlayerInfo* player = get_player(plyr_idx);
if (trap_cfg->placeonsubtile)
if (trap_cfg->place_on_subtile)
{
set_coords_to_subtile_center(&pos, stl_x, stl_y, 1);
}
Expand Down
127 changes: 108 additions & 19 deletions src/thing_traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ TbBool trap_is_slappable(const struct Thing *thing, PlayerNumber plyr_idx)
if (thing->owner == plyr_idx)
{
trapst = &game.conf.trapdoor_conf.trap_cfgstats[thing->model];
return (trapst->slappable == 1) && trap_is_active(thing);
return (trapst->slappable > 0) && trap_is_active(thing);
}
return false;
}
Expand Down Expand Up @@ -1012,7 +1012,7 @@ unsigned long remove_traps_around_subtile(MapSubtlCoord stl_x, MapSubtlCoord stl
return total;
}

void external_activate_trap_shot_at_angle(struct Thing *thing, long a2, struct Thing *hand)
void external_activate_trap_shot_at_angle(struct Thing *thing, short angle, struct Thing *trgtng)
{
struct TrapStats* trapstat = &game.conf.trap_stats[thing->model];
if (trapstat->created_itm_model <= 0) {
Expand All @@ -1022,7 +1022,7 @@ void external_activate_trap_shot_at_angle(struct Thing *thing, long a2, struct T
if ((trapstat->activation_type != TrpAcT_CreatureShot)
&& (trapstat->activation_type != TrpAcT_HeadforTarget90))
{
activate_trap(thing, hand);
activate_trap(thing, trgtng);
process_trap_charge(thing);
if (thing->trap.num_shots != INFINITE_CHARGES)
{
Expand All @@ -1035,20 +1035,14 @@ void external_activate_trap_shot_at_angle(struct Thing *thing, long a2, struct T
}
return;
}
struct Thing* shotng = create_shot(&thing->mappos, trapstat->created_itm_model, thing->owner);
if (thing_is_invalid(shotng)) {
return;
if (!thing_is_invalid(trgtng))
{
thing_fire_shot(thing, trgtng, trapstat->created_itm_model, 1, trapstat->hit_type);
}
else
{
trap_fire_shot_without_target(thing, trapstat->created_itm_model, 1, angle);
}
struct ShotConfigStats* shotst = get_shot_model_stats(shotng->model);
shotng->move_angle_xy = a2;
shotng->move_angle_z = 0;
struct ComponentVector cvect;
angles_to_vector(shotng->move_angle_xy, 0, shotst->speed, &cvect);
shotng->veloc_push_add.x.val += cvect.x;
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 = trapstat->hit_type;
const struct ManfctrConfig* mconf = &game.conf.traps_config[thing->model];
thing->trap.rearm_turn = game.play_gameturn + mconf->shots_delay;
if (thing->trap.num_shots != INFINITE_CHARGES)
Expand All @@ -1065,7 +1059,7 @@ void external_activate_trap_shot_at_angle(struct Thing *thing, long a2, struct T
TbBool trap_on_bridge(ThingModel trpkind)
{
struct TrapConfigStats* trapst = &game.conf.trapdoor_conf.trap_cfgstats[trpkind];
return trapst->placeonbridge;
return trapst->place_on_bridge;
}

TbBool can_place_trap_on(PlayerNumber plyr_idx, MapSubtlCoord stl_x, MapSubtlCoord stl_y, ThingModel trpkind)
Expand All @@ -1074,7 +1068,6 @@ TbBool can_place_trap_on(PlayerNumber plyr_idx, MapSubtlCoord stl_x, MapSubtlCoo
MapSlabCoord slb_y = subtile_slab(stl_y);
struct SlabMap* slb = get_slabmap_block(slb_x, slb_y);
struct SlabAttr* slbattr = get_slab_attrs(slb);
struct PlayerInfo* player = get_player(plyr_idx);
TbBool HasTrap = true;
TbBool HasDoor = true;
struct TrapConfigStats* trap_cfg = get_trap_model_stats(trpkind);
Expand All @@ -1090,7 +1083,7 @@ TbBool can_place_trap_on(PlayerNumber plyr_idx, MapSubtlCoord stl_x, MapSubtlCoo
}
if ((slabmap_owner(slb) == plyr_idx) && (((slb->kind == SlbT_BRIDGE) && (trap_on_bridge(trpkind))) || (slb->kind == SlbT_CLAIMED) || (slab_is_door(slb_x, slb_y))))
{
if (trap_cfg->placeonsubtile == false)
if (trap_cfg->place_on_subtile == false)
{
HasTrap = slab_has_trap_on(slb_x, slb_y);
HasDoor = slab_is_door(slb_x, slb_y);
Expand Down Expand Up @@ -1126,6 +1119,102 @@ TbBool can_place_trap_on(PlayerNumber plyr_idx, MapSubtlCoord stl_x, MapSubtlCoo
return false;
}

void trap_fire_shot_without_target(struct Thing *firing, ThingModel shot_model, char shot_lvl, short angle_xy)
{
struct Thing* shotng;
struct ComponentVector cvect;
struct ShotConfigStats* shotst = get_shot_model_stats(shot_model);
struct TrapStats* trapstat = &game.conf.trap_stats[firing->model];
switch (shotst->fire_logic)
{
case ShFL_Beam:
{
struct Coord3d pos2;
long damage;
// Prepare source position
struct Coord3d pos1;
pos1.x.val = firing->mappos.x.val;
pos1.y.val = firing->mappos.y.val;
pos1.z.val = firing->mappos.z.val;
if (shotst->fire_logic == ShFL_Volley)
{
if (!firing->trap.volley_fire)
{
firing->trap.volley_fire = true;
firing->trap.volley_repeat = shotst->effect_amount - 1; // N x shots + (N - 1) x pauses and one shot is this one
firing->trap.volley_delay = shotst->effect_spacing;
firing->trap.firing_at = 0;
}
else
{
firing->trap.volley_delay = shotst->effect_spacing;
if (firing->trap.volley_repeat == 0)
return;
firing->trap.volley_repeat--;
}
}
firing->move_angle_xy = angle_xy; //visually rotates the trap
pos1.x.val += distance_with_angle_to_coord_x(trapstat->shot_shift_x, firing->move_angle_xy + LbFPMath_PI / 2);
pos1.y.val += distance_with_angle_to_coord_y(trapstat->shot_shift_x, firing->move_angle_xy + LbFPMath_PI / 2);
pos1.x.val += distance_with_angle_to_coord_x(trapstat->shot_shift_y, firing->move_angle_xy);
pos1.y.val += distance_with_angle_to_coord_y(trapstat->shot_shift_y, firing->move_angle_xy);
pos1.z.val += trapstat->shot_shift_z;
// Compute launch angles
pos2.x.val = 0;
pos2.y.val = 0;
pos2.z.val = 0;
if (((shotst->model_flags & ShMF_StrengthBased) != 0) && ((shotst->model_flags & ShMF_ReboundImmune) != 0))
{
pos1.z.val = pos2.z.val;
}
// Compute shot damage
if (shotst->fixed_damage == 0)
{
if ((shotst->model_flags & ShMF_StrengthBased) != 0)
{
damage = calculate_melee_damage(firing);
}
else
{
damage = calculate_shot_damage(firing, shot_model);
}
}
else
{
damage = shotst->damage;
}
if (get_2d_distance(&firing->mappos, &pos2) > shotst->max_range)
{
project_point_to_wall_on_angle(&pos1, &pos2, firing->move_angle_xy, firing->move_angle_z, COORD_PER_STL, shotst->max_range/COORD_PER_STL);
}
shotng = create_shot(&pos2, shot_model, firing->owner);
if (thing_is_invalid(shotng))
return;
draw_lightning(&pos1, &pos2, shotst->effect_spacing, shotst->effect_id);
shotng->health = shotst->health;
shotng->shot.damage = damage;
shotng->parent_idx = firing->index;
break;
}
default:
shotng = create_shot(&firing->mappos, shot_model, firing->owner);
if (thing_is_invalid(shotng)) {
return;
}
firing->move_angle_xy = angle_xy; //visually rotates the trap
shotst = get_shot_model_stats(shotng->model);
shotng->move_angle_xy = angle_xy;
shotng->move_angle_z = 0;
angles_to_vector(shotng->move_angle_xy, 0, shotst->speed, &cvect);
shotng->veloc_push_add.x.val += cvect.x;
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 = trapstat->hit_type;
break;
}
}

/******************************************************************************/
#ifdef __cplusplus
}
Expand Down
3 changes: 2 additions & 1 deletion src/thing_traps.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ unsigned long remove_trap(struct Thing *traptng, long *sell_value);
unsigned long remove_trap_on_subtile(MapSubtlCoord stl_x, MapSubtlCoord stl_y, long *sell_value);
unsigned long remove_traps_around_subtile(MapSubtlCoord stl_x, MapSubtlCoord stl_y, long *sell_value);

void external_activate_trap_shot_at_angle(struct Thing *thing, long a2, struct Thing *hand);
void external_activate_trap_shot_at_angle(struct Thing *thing, short angle, struct Thing *trgtng);
void trap_fire_shot_without_target(struct Thing *firing, ThingModel shot_model, char shot_lvl, short angle_xy);

/******************************************************************************/
#ifdef __cplusplus
Expand Down

0 comments on commit 2d2a892

Please sign in to comment.