From 7a06fe2466049620a420a0b0fba7414311baec8e Mon Sep 17 00:00:00 2001 From: Aciz Date: Tue, 31 Dec 2024 15:23:11 +0200 Subject: [PATCH] Add support for cursorhints in set/delete script actions (#1584) This required special handling as cursorhints are written as strings by mappers, but are internally mapped into an enum value. --- src/game/bg_public.h | 27 ++++++++++++++ src/game/etj_entity_utilities.cpp | 11 ++++++ src/game/etj_entity_utilities.h | 4 +++ src/game/g_local.h | 3 +- src/game/g_mover.cpp | 60 ++++--------------------------- src/game/g_script_actions.cpp | 19 ++++++++++ src/game/g_spawn.cpp | 35 ++++++++++-------- 7 files changed, 91 insertions(+), 68 deletions(-) diff --git a/src/game/bg_public.h b/src/game/bg_public.h index 44212d174..51587ba83 100644 --- a/src/game/bg_public.h +++ b/src/game/bg_public.h @@ -1891,6 +1891,33 @@ typedef enum { HINT_NUM_HINTS } hintType_t; +static constexpr std::array hintStrings = { + "", // HINT_NONE + "HINT_NONE", // actually HINT_FORCENONE, but since this is being specified + // in the ent, the designer actually means HINT_FORCENONE + "HINT_PLAYER", "HINT_ACTIVATE", "HINT_DOOR", "HINT_DOOR_ROTATING", + "HINT_DOOR_LOCKED", "HINT_DOOR_ROTATING_LOCKED", "HINT_MG42", + "HINT_BREAKABLE", "HINT_BREAKABLE_BIG", "HINT_CHAIR", "HINT_ALARM", + "HINT_HEALTH", "HINT_TREASURE", "HINT_KNIFE", "HINT_LADDER", "HINT_BUTTON", + "HINT_WATER", "HINT_CAUTION", "HINT_DANGER", "HINT_SECRET", "HINT_QUESTION", + "HINT_EXCLAMATION", "HINT_CLIPBOARD", "HINT_WEAPON", "HINT_AMMO", + "HINT_ARMOR", "HINT_POWERUP", "HINT_HOLDABLE", "HINT_INVENTORY", + "HINT_SCENARIC", "HINT_EXIT", "HINT_NOEXIT", "HINT_PLYR_FRIEND", + "HINT_PLYR_NEUTRAL", "HINT_PLYR_ENEMY", "HINT_PLYR_UNKNOWN", + "HINT_BUILD", // DHM - Nerve + "HINT_DISARM", // DHM - Nerve + "HINT_REVIVE", // DHM - Nerve + "HINT_DYNAMITE", // DHM - Nerve + + "HINT_CONSTRUCTIBLE", "HINT_UNIFORM", "HINT_LANDMINE", "HINT_TANK", + "HINT_SATCHELCHARGE", + // START Mad Doc - TDF + "HINT_LOCKPICK", + // END Mad Doc - TDF + + "", // HINT_BAD_USER +}; + void BG_EvaluateTrajectory(const trajectory_t *tr, int atTime, vec3_t result, qboolean isAngle, int splinePath); void BG_EvaluateTrajectoryDelta(const trajectory_t *tr, int atTime, diff --git a/src/game/etj_entity_utilities.cpp b/src/game/etj_entity_utilities.cpp index e07fa7611..879214d86 100644 --- a/src/game/etj_entity_utilities.cpp +++ b/src/game/etj_entity_utilities.cpp @@ -23,6 +23,7 @@ */ #include "etj_entity_utilities.h" +#include "etj_string_utilities.h" namespace ETJump { bool EntityUtilities::isPlayer(gentity_t *ent) { @@ -124,4 +125,14 @@ bool EntityUtilities::entitiesFree(const int threshold) { return free > threshold; } + +void EntityUtilities::setCursorhintFromString(int &value, + const std::string &hint) { + for (int i = 0; i < HINT_NUM_HINTS; i++) { + if (StringUtil::iEqual(hint, hintStrings[i])) { + value = i; + } + } +} + } // namespace ETJump diff --git a/src/game/etj_entity_utilities.h b/src/game/etj_entity_utilities.h index b5cc66f05..166afc3fa 100644 --- a/src/game/etj_entity_utilities.h +++ b/src/game/etj_entity_utilities.h @@ -37,5 +37,9 @@ class EntityUtilities { // 'threshold' indicates the number of entities that must be free static bool entitiesFree(int threshold); + + // sets 'value' to corresponding cursorhint from 'hint' + // if 'hint' isn't found in hintStrings, no modification is performed + static void setCursorhintFromString(int &value, const std::string &hint); }; } // namespace ETJump diff --git a/src/game/g_local.h b/src/game/g_local.h index e9e27e711..3cc8920fc 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -256,7 +256,8 @@ typedef enum { F_ENTITY, // index on disk, pointer in memory F_ITEM, // index on disk, pointer in memory F_CLIENT, // index on disk, pointer in memory - F_IGNORE + F_IGNORE, + F_CURSORHINT, // maps cursorhint string to a correct int } fieldtype_t; typedef struct { diff --git a/src/game/g_mover.cpp b/src/game/g_mover.cpp index 2c461c293..d836686a2 100644 --- a/src/game/g_mover.cpp +++ b/src/game/g_mover.cpp @@ -10,33 +10,6 @@ #include "etj_utilities.h" #include "etj_entity_utilities.h" -static constexpr std::array hintStrings = { - "", // HINT_NONE - "HINT_NONE", // actually HINT_FORCENONE, but since this is being specified - // in the ent, the designer actually means HINT_FORCENONE - "HINT_PLAYER", "HINT_ACTIVATE", "HINT_DOOR", "HINT_DOOR_ROTATING", - "HINT_DOOR_LOCKED", "HINT_DOOR_ROTATING_LOCKED", "HINT_MG42", - "HINT_BREAKABLE", "HINT_BREAKABLE_BIG", "HINT_CHAIR", "HINT_ALARM", - "HINT_HEALTH", "HINT_TREASURE", "HINT_KNIFE", "HINT_LADDER", "HINT_BUTTON", - "HINT_WATER", "HINT_CAUTION", "HINT_DANGER", "HINT_SECRET", "HINT_QUESTION", - "HINT_EXCLAMATION", "HINT_CLIPBOARD", "HINT_WEAPON", "HINT_AMMO", - "HINT_ARMOR", "HINT_POWERUP", "HINT_HOLDABLE", "HINT_INVENTORY", - "HINT_SCENARIC", "HINT_EXIT", "HINT_NOEXIT", "HINT_PLYR_FRIEND", - "HINT_PLYR_NEUTRAL", "HINT_PLYR_ENEMY", "HINT_PLYR_UNKNOWN", - "HINT_BUILD", // DHM - Nerve - "HINT_DISARM", // DHM - Nerve - "HINT_REVIVE", // DHM - Nerve - "HINT_DYNAMITE", // DHM - Nerve - - "HINT_CONSTRUCTIBLE", "HINT_UNIFORM", "HINT_LANDMINE", "HINT_TANK", - "HINT_SATCHELCHARGE", - // START Mad Doc - TDF - "HINT_LOCKPICK", - // END Mad Doc - TDF - - "", // HINT_BAD_USER -}; - /* =============================================================================== @@ -2699,11 +2672,8 @@ void SP_func_button(gentity_t *ent) { ent->s.dmgFlags = HINT_BUTTON; if (G_SpawnString("cursorhint", "0", &cursorhint)) { - for (int i = 0; i < HINT_NUM_HINTS; i++) { - if (!Q_stricmp(cursorhint, hintStrings[i])) { - ent->s.dmgFlags = i; - } - } + ETJump::EntityUtilities::setCursorhintFromString(ent->s.dmgFlags, + cursorhint); } // first position @@ -4346,22 +4316,12 @@ void SP_func_explosive(gentity_t *ent) { } } - //----(SA) added - - ent->s.dmgFlags = 0; + ent->s.dmgFlags = HINT_NONE; if (G_SpawnString("cursorhint", "0", &cursorhint)) { - - for (i = 0; i < HINT_NUM_HINTS; i++) { - if (!Q_stricmp(cursorhint, hintStrings[i])) { - ent->s.dmgFlags = i; - } - } + ETJump::EntityUtilities::setCursorhintFromString(ent->s.dmgFlags, + cursorhint); } - //----(SA) end - - // (SA) shouldn't need this - // ent->s.density = ent->count; // pass the "mass" to the client ent->die = func_explosive_explode; } @@ -4506,16 +4466,10 @@ void SP_func_invisible_user(gentity_t *ent) { ent->use = use_invisible_user; - //----(SA) added if (G_SpawnString("cursorhint", "0", &cursorhint)) { - - for (i = 0; i < HINT_NUM_HINTS; i++) { - if (!Q_stricmp(cursorhint, hintStrings[i])) { - ent->s.dmgFlags = i; - } - } + ETJump::EntityUtilities::setCursorhintFromString(ent->s.dmgFlags, + cursorhint); } - //----(SA) end if (!(ent->spawnflags & static_cast(ETJump::FuncInvisSpawnflags::NoOffNoise))) { diff --git a/src/game/g_script_actions.cpp b/src/game/g_script_actions.cpp index de8939b3d..e0c1b54a5 100644 --- a/src/game/g_script_actions.cpp +++ b/src/game/g_script_actions.cpp @@ -6,6 +6,7 @@ // Tab Size: 4 (real tabs) //=========================================================================== +#include "etj_entity_utilities.h" #include "../game/g_local.h" #include "../game/q_shared.h" #include "etj_printer.h" @@ -4785,6 +4786,24 @@ qboolean G_ScriptAction_Delete(gentity_t *ent, char *params) { ETJump::stringFormat(R"(%s "%s")", key, value)); } + break; + case F_CURSORHINT: + if (args.size() != 1) { + invalidArgCount(1, args.size()); + break; + } + + // set to end cap initially, so we don't delete all entities + // with HINT_NONE if we don't find a match + valueInt = HINT_NUM_HINTS; + ETJump::EntityUtilities::setCursorhintFromString(valueInt, value); + + while ((found = G_FindInt(found, fields[i].ofs, valueInt)) != nullptr) { + pass[found->s.number].first++; + pass[found->s.number].second.emplace_back( + ETJump::stringFormat(R"(%s "%s")", key, value)); + } + break; default: G_Printf(S_COLOR_YELLOW "%s: invalid key '%s'\n", __func__, key); diff --git a/src/game/g_spawn.cpp b/src/game/g_spawn.cpp index 46f7a8902..0f6d13018 100644 --- a/src/game/g_spawn.cpp +++ b/src/game/g_spawn.cpp @@ -4,6 +4,8 @@ * desc: * */ +#include "etj_entity_utilities.h" + #include #include #include @@ -172,6 +174,8 @@ field_t fields[] = { {"targetShaderName", FOFS(targetShaderName), F_LSTRING}, {"targetShaderNewName", FOFS(targetShaderNewName), F_LSTRING}, + {"cursorhint", FOFS(s.dmgFlags), F_CURSORHINT}, + {nullptr}}; typedef struct { @@ -808,42 +812,45 @@ in a gentity =============== */ void G_ParseField(const char *key, const char *value, gentity_t *ent) { - field_t *f; - byte *b; float v; vec3_t vec; - for (f = fields; f->name; f++) { + for (const field_t *f = fields; f->name; f++) { if (!Q_stricmp(f->name, key)) { // found it - b = (byte *)ent; + const auto b = reinterpret_cast(ent); switch (f->type) { case F_LSTRING: - *(char **)(b + f->ofs) = G_NewString(value); + *reinterpret_cast(b + f->ofs) = G_NewString(value); break; case F_VECTOR: sscanf(value, "%f %f %f", &vec[0], &vec[1], &vec[2]); - ((float *)(b + f->ofs))[0] = vec[0]; - ((float *)(b + f->ofs))[1] = vec[1]; - ((float *)(b + f->ofs))[2] = vec[2]; + reinterpret_cast(b + f->ofs)[0] = vec[0]; + reinterpret_cast(b + f->ofs)[1] = vec[1]; + reinterpret_cast(b + f->ofs)[2] = vec[2]; break; case F_INT: - *(int *)(b + f->ofs) = Q_atoi(value); + *reinterpret_cast(b + f->ofs) = Q_atoi(value); break; case F_FLOAT: - *(float *)(b + f->ofs) = Q_atof(value); + *reinterpret_cast(b + f->ofs) = Q_atof(value); break; case F_ANGLEHACK: v = Q_atof(value); - ((float *)(b + f->ofs))[0] = 0; - ((float *)(b + f->ofs))[1] = v; - ((float *)(b + f->ofs))[2] = 0; + reinterpret_cast(b + f->ofs)[0] = 0; + reinterpret_cast(b + f->ofs)[1] = v; + reinterpret_cast(b + f->ofs)[2] = 0; + break; + case F_CURSORHINT: + ETJump::EntityUtilities::setCursorhintFromString( + *reinterpret_cast(b + f->ofs), value); break; - default: case F_IGNORE: + default: break; } + return; } }