From e205b50c2dcb7efd0ca5a467eae22d9512e5ab03 Mon Sep 17 00:00:00 2001 From: walter253 <130906143+walt253@users.noreply.github.com> Date: Tue, 5 Nov 2024 00:17:53 +0100 Subject: [PATCH] TrapNewProperties Add new properties: InstantPlacement & RemoveOnceDepleted --- config/fxdata/trapdoor.cfg | 4 ++ src/config_trapdoor.c | 140 +++++++++++++++++++++++-------------- src/config_trapdoor.h | 1 + src/lvl_script_commands.c | 113 ++++++++++++++++-------------- src/player_instances.c | 29 +++++--- src/thing_traps.c | 10 +-- src/thing_traps.h | 1 + 7 files changed, 180 insertions(+), 118 deletions(-) diff --git a/config/fxdata/trapdoor.cfg b/config/fxdata/trapdoor.cfg index 5d90c625bd..3fc2bb4bd8 100644 --- a/config/fxdata/trapdoor.cfg +++ b/config/fxdata/trapdoor.cfg @@ -222,6 +222,10 @@ ThingSize = 0 0 HitType = 0 ; Will make it impossible to sell. Unsellable = 0 +; Place the trap immediately without needing imps to drag the crate to arm it. +InstantPlacement = 0 +; Destroy the trap once it runs out of Shots. +RemoveOnceDepleted = 0 LightRadius = 0 LightIntensity = 0 LightFlags = 0 diff --git a/src/config_trapdoor.c b/src/config_trapdoor.c index 6b08aa2778..715cee3e16 100644 --- a/src/config_trapdoor.c +++ b/src/config_trapdoor.c @@ -63,58 +63,60 @@ const struct NamedCommand trapdoor_door_commands[] = { }; const struct NamedCommand trapdoor_trap_commands[] = { - {"NAME", 1}, - {"MANUFACTURELEVEL", 2}, - {"MANUFACTUREREQUIRED", 3}, - {"SHOTS", 4}, - {"TIMEBETWEENSHOTS", 5}, - {"SELLINGVALUE", 6}, - {"NAMETEXTID", 7}, - {"TOOLTIPTEXTID", 8}, - {"CRATE", 9}, - {"SYMBOLSPRITES", 10}, - {"POINTERSPRITES", 11}, - {"PANELTABINDEX", 12}, - {"TRIGGERTYPE", 13}, - {"ACTIVATIONTYPE", 14}, - {"EFFECTTYPE", 15}, - {"ANIMATIONID", 16}, - {"MODEL", 16},//backward compatibility - {"MODELSIZE", 17}, - {"ANIMATIONSPEED", 18}, - {"UNANIMATED", 19}, - {"HIDDEN", 20}, - {"SLAPPABLE", 21}, - {"TRIGGERALARM", 22}, - {"HEALTH", 23}, - {"UNSHADED", 24}, - {"RANDOMSTARTFRAME", 25}, - {"THINGSIZE", 26}, - {"HITTYPE", 27}, - {"LIGHTRADIUS", 28}, - {"LIGHTINTENSITY", 29}, - {"LIGHTFLAGS", 30}, - {"TRANSPARENCYFLAGS", 31}, - {"SHOTVECTOR", 32}, - {"DESTRUCTIBLE", 33}, - {"UNSTABLE", 34}, - {"UNSELLABLE", 35}, - {"PLACEONBRIDGE", 36}, - {"SHOTORIGIN", 37}, - {"PLACESOUND", 38}, - {"TRIGGERSOUND", 39}, - {"RECHARGEANIMATIONID", 40}, - {"ATTACKANIMATIONID", 41}, - {"DESTROYEDEFFECT", 42}, - {"INITIALDELAY", 43}, - {"PLACEONSUBTILE", 44}, - {"FLAMEANIMATIONID", 45}, - {"FLAMEANIMATIONSPEED", 46}, - {"FLAMEANIMATIONSIZE", 47}, - {"FLAMEANIMATIONOFFSET", 48}, - {"FLAMETRANSPARENCYFLAGS", 49}, - {"DETECTINVISIBLE", 50}, - {NULL, 0}, + {"NAME", 1}, + {"MANUFACTURELEVEL", 2}, + {"MANUFACTUREREQUIRED", 3}, + {"SHOTS", 4}, + {"TIMEBETWEENSHOTS", 5}, + {"SELLINGVALUE", 6}, + {"NAMETEXTID", 7}, + {"TOOLTIPTEXTID", 8}, + {"CRATE", 9}, + {"SYMBOLSPRITES", 10}, + {"POINTERSPRITES", 11}, + {"PANELTABINDEX", 12}, + {"TRIGGERTYPE", 13}, + {"ACTIVATIONTYPE", 14}, + {"EFFECTTYPE", 15}, + {"ANIMATIONID", 16}, + {"MODEL", 16}, // Backward compatibility. + {"MODELSIZE", 17}, + {"ANIMATIONSPEED", 18}, + {"UNANIMATED", 19}, + {"HIDDEN", 20}, + {"SLAPPABLE", 21}, + {"TRIGGERALARM", 22}, + {"HEALTH", 23}, + {"UNSHADED", 24}, + {"RANDOMSTARTFRAME", 25}, + {"THINGSIZE", 26}, + {"HITTYPE", 27}, + {"LIGHTRADIUS", 28}, + {"LIGHTINTENSITY", 29}, + {"LIGHTFLAGS", 30}, + {"TRANSPARENCYFLAGS", 31}, + {"SHOTVECTOR", 32}, + {"DESTRUCTIBLE", 33}, + {"UNSTABLE", 34}, + {"UNSELLABLE", 35}, + {"PLACEONBRIDGE", 36}, + {"SHOTORIGIN", 37}, + {"PLACESOUND", 38}, + {"TRIGGERSOUND", 39}, + {"RECHARGEANIMATIONID", 40}, + {"ATTACKANIMATIONID", 41}, + {"DESTROYEDEFFECT", 42}, + {"INITIALDELAY", 43}, + {"PLACEONSUBTILE", 44}, + {"FLAMEANIMATIONID", 45}, + {"FLAMEANIMATIONSPEED", 46}, + {"FLAMEANIMATIONSIZE", 47}, + {"FLAMEANIMATIONOFFSET", 48}, + {"FLAMETRANSPARENCYFLAGS", 49}, + {"DETECTINVISIBLE", 50}, + {"INSTANTPLACEMENT", 51}, + {"REMOVEONCEDEPLETED", 52}, + {NULL, 0}, }; const struct NamedCommand door_properties_commands[] = { @@ -205,6 +207,7 @@ TbBool parse_trapdoor_trap_blocks(char *buf, long len, const char *config_textna trapst->notify = false; trapst->place_on_bridge = false; trapst->place_on_subtile = false; + trapst->instant_placement = false; // Default destroyed_effect is TngEffElm_Blast2. trapst->destroyed_effect = -39; @@ -235,6 +238,7 @@ TbBool parse_trapdoor_trap_blocks(char *buf, long len, const char *config_textna game.conf.trap_stats[i].shot_shift_z = 0; game.conf.trap_stats[i].initial_delay = 0; game.conf.trap_stats[i].detect_invisible = 1; // Set to 1 by default: backward compatibility for custom traps made before this implementation. + game.conf.trap_stats[i].remove_once_depleted = false; mconf = &game.conf.traps_config[i]; mconf->manufct_level = 0; mconf->manufct_required = 0; @@ -1134,6 +1138,38 @@ TbBool parse_trapdoor_trap_blocks(char *buf, long len, const char *config_textna COMMAND_TEXT(cmd_num), blocknamelen, blockname, config_textname); } break; + case 51: // INSTANTPLACEMENT + if (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0) + { + k = atoi(word_buf); + if (k >= 0) + { + trapst->instant_placement = k; + n++; + } + } + if (n < 1) + { + CONFWRNLOG("Incorrect value of \"%s\" parameter in [%.*s] block of %s file.", + COMMAND_TEXT(cmd_num), blocknamelen, blockname, config_textname); + } + break; + case 52: // REMOVEONCEDEPLETED + if (get_conf_parameter_single(buf, &pos, len, word_buf, sizeof(word_buf)) > 0) + { + k = atoi(word_buf); + if (k >= 0) + { + game.conf.trap_stats[i].remove_once_depleted = k; + n++; + } + } + if (n < 1) + { + CONFWRNLOG("Incorrect value of \"%s\" parameter in [%.*s] block of %s file.", + COMMAND_TEXT(cmd_num), blocknamelen, blockname, config_textname); + } + break; case ccr_comment: break; case ccr_endOfFile: diff --git a/src/config_trapdoor.h b/src/config_trapdoor.h index 8e1d0ddf7d..61f44aa5b0 100644 --- a/src/config_trapdoor.h +++ b/src/config_trapdoor.h @@ -90,6 +90,7 @@ struct TrapConfigStats { TbBool unsellable; TbBool place_on_bridge; TbBool place_on_subtile; + TbBool instant_placement; EffectOrEffElModel destroyed_effect; struct FlameProperties flame; }; diff --git a/src/lvl_script_commands.c b/src/lvl_script_commands.c index 462e44615f..028ff98855 100644 --- a/src/lvl_script_commands.c +++ b/src/lvl_script_commands.c @@ -217,57 +217,59 @@ const struct NamedCommand creature_select_criteria_desc[] = { }; const struct NamedCommand trap_config_desc[] = { - {"NameTextID", 1}, - {"TooltipTextID", 2}, - {"SymbolSprites", 3}, - {"PointerSprites", 4}, - {"PanelTabIndex", 5}, - {"Crate", 6}, - {"ManufactureLevel", 7}, - {"ManufactureRequired", 8}, - {"Shots", 9}, - {"TimeBetweenShots", 10}, - {"SellingValue", 11}, - {"AnimationID", 12}, - {"Model", 12}, //legacy name - {"ModelSize", 13}, - {"AnimationSpeed", 14}, - {"TriggerType", 15}, - {"ActivationType", 16}, - {"EffectType", 17}, - {"Hidden", 18}, - {"TriggerAlarm", 19}, - {"Slappable", 20}, - {"Unanimated", 21}, - {"Health", 22}, - {"Unshaded", 23}, - {"RandomStartFrame", 24}, - {"ThingSize", 25}, - {"HitType", 26}, - {"LightRadius", 27}, - {"LightIntensity", 28}, - {"LightFlags", 29}, - {"TransparencyFlags", 30}, - {"ShotVector", 31}, - {"Destructible", 32}, - {"Unstable", 33}, - {"Unsellable", 34}, - {"PlaceOnBridge", 35}, - {"ShotOrigin", 36}, - {"PlaceSound", 37}, - {"TriggerSound", 38}, - {"RechargeAnimationID", 39}, - {"AttackAnimationID", 40}, - {"DestroyedEffect", 41}, - {"InitialDelay", 42}, - {"PlaceOnSubtile", 43}, - {"FlameAnimationID", 44}, - {"FlameAnimationSpeed", 45}, - {"FlameAnimationSize", 46}, - {"FlameAnimationOffset", 47}, - {"FlameTransparencyFlags", 48}, - {"DetectInvisible", 49}, - {NULL, 0}, + {"NameTextID", 1}, + {"TooltipTextID", 2}, + {"SymbolSprites", 3}, + {"PointerSprites", 4}, + {"PanelTabIndex", 5}, + {"Crate", 6}, + {"ManufactureLevel", 7}, + {"ManufactureRequired", 8}, + {"Shots", 9}, + {"TimeBetweenShots", 10}, + {"SellingValue", 11}, + {"AnimationID", 12}, + {"Model", 12}, // Legacy name. + {"ModelSize", 13}, + {"AnimationSpeed", 14}, + {"TriggerType", 15}, + {"ActivationType", 16}, + {"EffectType", 17}, + {"Hidden", 18}, + {"TriggerAlarm", 19}, + {"Slappable", 20}, + {"Unanimated", 21}, + {"Health", 22}, + {"Unshaded", 23}, + {"RandomStartFrame", 24}, + {"ThingSize", 25}, + {"HitType", 26}, + {"LightRadius", 27}, + {"LightIntensity", 28}, + {"LightFlags", 29}, + {"TransparencyFlags", 30}, + {"ShotVector", 31}, + {"Destructible", 32}, + {"Unstable", 33}, + {"Unsellable", 34}, + {"PlaceOnBridge", 35}, + {"ShotOrigin", 36}, + {"PlaceSound", 37}, + {"TriggerSound", 38}, + {"RechargeAnimationID", 39}, + {"AttackAnimationID", 40}, + {"DestroyedEffect", 41}, + {"InitialDelay", 42}, + {"PlaceOnSubtile", 43}, + {"FlameAnimationID", 44}, + {"FlameAnimationSpeed", 45}, + {"FlameAnimationSize", 46}, + {"FlameAnimationOffset", 47}, + {"FlameTransparencyFlags", 48}, + {"DetectInvisible", 49}, + {"InstantPlacement", 50}, + {"RemoveOnceDepleted", 51}, + {NULL, 0}, }; const struct NamedCommand room_config_desc[] = { @@ -1627,6 +1629,7 @@ static void new_trap_type_check(const struct ScriptLine* scline) trapst->notify = false; trapst->place_on_bridge = false; trapst->place_on_subtile = false; + trapst->instant_placement = false; trapst->place_sound_idx = 117; trapst->trigger_sound_idx = 176; trapst->destroyed_effect = -39; @@ -1651,6 +1654,8 @@ static void new_trap_type_check(const struct ScriptLine* scline) game.conf.trap_stats[i].shotvector.x = 0; game.conf.trap_stats[i].shotvector.y = 0; game.conf.trap_stats[i].shotvector.z = 0; + game.conf.trap_stats[i].detect_invisible = 1; // Set to 1 by default: backward compatibility for custom traps made before this implementation. + game.conf.trap_stats[i].remove_once_depleted = false; trap_desc[i].name = trapst->code_name; trap_desc[i].num = i; struct ManfctrConfig* mconf = &game.conf.traps_config[i]; @@ -1919,6 +1924,12 @@ static void set_trap_configuration_process(struct ScriptContext *context) case 49: // DetectInvisible trapstat->detect_invisible = value; break; + case 50: // InstantPlacement + trapst->instant_placement = value; + break; + case 51: // RemoveOnceDepleted + trapstat->remove_once_depleted = value; + break; default: WARNMSG("Unsupported Trap configuration, variable %d.", context->value->shorts[1]); break; diff --git a/src/player_instances.c b/src/player_instances.c index 48b94feddd..be78be05d6 100644 --- a/src/player_instances.c +++ b/src/player_instances.c @@ -1226,8 +1226,9 @@ struct Room *player_build_room_at(MapSubtlCoord stl_x, MapSubtlCoord stl_y, Play TbBool player_place_trap_at(MapSubtlCoord stl_x, MapSubtlCoord stl_y, PlayerNumber plyr_idx, ThingModel tngmodel) { - if (!is_trap_placeable(plyr_idx, tngmodel)) { - WARNLOG("Player %d tried to build %s but has none to place",(int)plyr_idx,trap_code_name(tngmodel)); + if (!is_trap_placeable(plyr_idx, tngmodel)) + { + WARNLOG("Player %d tried to build %s but has none to place", (int)plyr_idx, trap_code_name(tngmodel)); return false; } struct TrapConfigStats* trap_cfg = get_trap_model_stats(tngmodel); @@ -1240,21 +1241,28 @@ TbBool player_place_trap_at(MapSubtlCoord stl_x, MapSubtlCoord stl_y, PlayerNumb { set_coords_to_slab_center(&pos, subtile_slab(stl_x), subtile_slab(stl_y)); } - delete_room_slabbed_objects(get_slab_number(subtile_slab(stl_x),subtile_slab(stl_y))); + delete_room_slabbed_objects(get_slab_number(subtile_slab(stl_x), subtile_slab(stl_y))); struct Thing* traptng = create_trap(&pos, tngmodel, plyr_idx); - if (thing_is_invalid(traptng)) { + if (thing_is_invalid(traptng)) + { return false; } traptng->mappos.z.val = get_thing_height_at(traptng, &traptng->mappos); traptng->trap.revealed = 0; struct Dungeon* dungeon = get_players_num_dungeon(plyr_idx); - remove_workshop_item_from_amount_placeable(plyr_idx, TCls_Trap, tngmodel); - if (placing_offmap_workshop_item(plyr_idx, TCls_Trap, tngmodel)) { + if (placing_offmap_workshop_item(plyr_idx, TCls_Trap, tngmodel)) + { remove_workshop_item_from_amount_stored(plyr_idx, TCls_Trap, tngmodel, WrkCrtF_NoStored); rearm_trap(traptng); dungeon->lvstats.traps_armed++; } + else if (trap_cfg->instant_placement) + { + remove_workshop_object_from_player(plyr_idx, trap_crate_object_model(tngmodel)); + rearm_trap(traptng); + dungeon->lvstats.traps_armed++; + } dungeon->camera_deviate_jump = 192; if (is_my_player_number(plyr_idx)) { @@ -1265,13 +1273,14 @@ TbBool player_place_trap_at(MapSubtlCoord stl_x, MapSubtlCoord stl_y, PlayerNumb TbBool player_place_door_at(MapSubtlCoord stl_x, MapSubtlCoord stl_y, PlayerNumber plyr_idx, ThingModel tngmodel) { - if (!is_door_placeable(plyr_idx, tngmodel)) { - WARNLOG("Player %d tried to build %s but has none to place",(int)plyr_idx,door_code_name(tngmodel)); + if (!is_door_placeable(plyr_idx, tngmodel)) + { + WARNLOG("Player %d tried to build %s but has none to place", (int)plyr_idx, door_code_name(tngmodel)); return 0; } unsigned char orient = find_door_angle(stl_x, stl_y, plyr_idx); struct Coord3d pos; - set_coords_to_slab_center(&pos,subtile_slab(stl_x),subtile_slab(stl_y)); + set_coords_to_slab_center(&pos, subtile_slab(stl_x), subtile_slab(stl_y)); create_door(&pos, tngmodel, orient, plyr_idx, 0); do_slab_efficiency_alteration(subtile_slab(stl_x), subtile_slab(stl_y)); struct Dungeon* dungeon = get_players_num_dungeon(plyr_idx); @@ -1286,7 +1295,7 @@ TbBool player_place_door_at(MapSubtlCoord stl_x, MapSubtlCoord stl_y, PlayerNumb remove_workshop_object_from_player(plyr_idx, door_crate_object_model(tngmodel)); break; default: - WARNLOG("Placeable door %s amount for player %d was incorrect; fixed",door_code_name(tngmodel),(int)dungeon->owner); + WARNLOG("Placeable door %s amount for player %d was incorrect; fixed", door_code_name(tngmodel), (int)dungeon->owner); dungeon->mnfct_info.door_amount_placeable[tngmodel] = 0; break; } diff --git a/src/thing_traps.c b/src/thing_traps.c index 023570d56a..9d3a2a1b31 100644 --- a/src/thing_traps.c +++ b/src/thing_traps.c @@ -739,11 +739,11 @@ void process_trap_charge(struct Thing* traptng) if (game.conf.trap_stats[traptng->model].attack_sprite_anim_idx != 0) { GameTurnDelta trigger_duration; - if (trapstat->activation_type == 2) //Effect stays on trap, so the attack animation remains visible for as long as the effect is alive + if (trapstat->activation_type == 2) // Effect stays on trap, so the attack animation remains visible for as long as the effect is alive. { trigger_duration = get_effect_model_stats(trapstat->created_itm_model)->start_health; } else - if (trapstat->activation_type == 3) //Shot stays on trap, so the attack animation remains visible for as long as the trap is alive + if (trapstat->activation_type == 3) // Shot stays on trap, so the attack animation remains visible for as long as the trap is alive. { trigger_duration = get_shot_model_stats(trapstat->created_itm_model)->health; } @@ -774,14 +774,14 @@ void process_trap_charge(struct Thing* traptng) set_flag(traptng->rendering_flags, TRF_Transpar_4); if (!is_neutral_thing(traptng) && !is_hero_thing(traptng)) { - if (placing_offmap_workshop_item(traptng->owner, TCls_Trap, traptng->model)) + if ((placing_offmap_workshop_item(traptng->owner, TCls_Trap, traptng->model)) || (trapstat->remove_once_depleted)) { - //When there's only offmap traps, destroy the disarmed one so the player can place a new one. + // When there's only offmap traps, destroy the disarmed one so the player can place a new one. delete_thing_structure(traptng, 0); } else { - //Trap is available to be rearmed, so earmark a workshop crate for it. + // Trap is available to be rearmed, so earmark a workshop crate for it. remove_workshop_item_from_amount_placeable(traptng->owner, traptng->class_id, traptng->model); } } diff --git a/src/thing_traps.h b/src/thing_traps.h index 2dc4782c01..90322022f0 100644 --- a/src/thing_traps.h +++ b/src/thing_traps.h @@ -95,6 +95,7 @@ struct TrapStats { unsigned short shot_shift_z; unsigned short initial_delay; // Trap is placed on reload phase, value in game turns. unsigned char detect_invisible; + TbBool remove_once_depleted; }; /******************************************************************************/