From 2a8a2f390a17ec9352f9d8656bad77f08411da89 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Mon, 8 Aug 2022 13:30:06 +0300 Subject: [PATCH 1/4] game: add spawn entity by coordinates --- doc/050_commands.md | 3 +++ src/client/cl_main.c | 1 + src/game/g_cmds.c | 55 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/doc/050_commands.md b/doc/050_commands.md index f322947c..fee62b8b 100644 --- a/doc/050_commands.md +++ b/doc/050_commands.md @@ -28,6 +28,9 @@ original clients (Vanilla Quake II) commands are still in place. * **teleport **: Teleports the player to the given coordinates. +* **spawnentity classname x y z **: + Spawn new entity of `classname` at `x y z` coordinates. + * **listmaps**: Lists available maps for the player to load. Maps from loaded pak files will be listed first followed by maps placed in the current game's maps folder. diff --git a/src/client/cl_main.c b/src/client/cl_main.c index 53e38561..b1700ce1 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -612,6 +612,7 @@ CL_InitLocal(void) Cmd_AddCommand("weapprev", NULL); Cmd_AddCommand("listentities", NULL); Cmd_AddCommand("teleport", NULL); + Cmd_AddCommand("spawnentity", NULL); Cmd_AddCommand("cycleweap", NULL); } diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index e91ff062..f5bc1f6d 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -249,7 +249,7 @@ Cmd_Give_f(edict_t *ent) if (gi.argc() == 3) { ent->health = (int)strtol(gi.argv(2), (char **)NULL, 10); - ent->health = ent->health < 1 ? 1 : ent->health; + ent->health = ent->health < 1 ? 1 : ent->health; } else { @@ -1276,6 +1276,55 @@ Cmd_Teleport_f(edict_t *ent) gi.linkentity(ent); } +edict_t* G_Spawn(void); +void ED_CallSpawn(edict_t* ent); + +static void +Cmd_SpawnEntity_f(edict_t *ent) +{ + if (!ent) + { + return; + } + + if ((deathmatch->value || coop->value) && !sv_cheats->value) + { + gi.cprintf(ent, PRINT_HIGH, + "You must run the server with '+set cheats 1' to enable this command.\n"); + return; + } + + if (gi.argc() < 5 || gi.argc() > 9) + { + gi.cprintf(ent, PRINT_HIGH, + "Usage: spawnentity classname x y z \n"); + return; + } + + ent = G_Spawn(); + + // set position + ent->s.origin[0] = atof(gi.argv(2)); + ent->s.origin[1] = atof(gi.argv(3)); + ent->s.origin[2] = atof(gi.argv(4)); + // angles + if (gi.argc() >= 8) + { + ent->s.angles[0] = atof(gi.argv(5)); + ent->s.angles[1] = atof(gi.argv(6)); + ent->s.angles[2] = atof(gi.argv(7)); + } + // flags + if (gi.argc() >= 9) + { + ent->spawnflags = atoi(gi.argv(8)); + } + + ent->classname = strdup(gi.argv(1)); + + ED_CallSpawn(ent); +} + void Cmd_ListEntities_f(edict_t *ent) { @@ -1818,6 +1867,10 @@ ClientCommand(edict_t *ent) { Cmd_Teleport_f(ent); } + else if (Q_stricmp(cmd, "spawnentity") == 0) + { + Cmd_SpawnEntity_f(ent); + } else if (Q_stricmp(cmd, "listentities") == 0) { Cmd_ListEntities_f(ent); From cf110b5ea0a47a0ca97c96eddb729cb3b222f2b0 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Sat, 13 Aug 2022 15:54:48 +0300 Subject: [PATCH 2/4] game: Add spawn on start point It could be used for spawn weapon or opponnent for coop at spawn point. --- doc/050_commands.md | 4 +++ src/client/cl_main.c | 1 + src/game/g_cmds.c | 66 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/doc/050_commands.md b/doc/050_commands.md index fee62b8b..e01e8e10 100644 --- a/doc/050_commands.md +++ b/doc/050_commands.md @@ -26,11 +26,15 @@ original clients (Vanilla Quake II) commands are still in place. whitespaces. The special class `all` lists the coordinates of all entities. +* **viewpos**: Show player position. + * **teleport **: Teleports the player to the given coordinates. * **spawnentity classname x y z **: Spawn new entity of `classname` at `x y z` coordinates. +* **spawnonstart classname**: Spawn new entity of `classname` at start point. + * **listmaps**: Lists available maps for the player to load. Maps from loaded pak files will be listed first followed by maps placed in the current game's maps folder. diff --git a/src/client/cl_main.c b/src/client/cl_main.c index b1700ce1..4f1565b6 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -613,6 +613,7 @@ CL_InitLocal(void) Cmd_AddCommand("listentities", NULL); Cmd_AddCommand("teleport", NULL); Cmd_AddCommand("spawnentity", NULL); + Cmd_AddCommand("spawnonstart", NULL); Cmd_AddCommand("cycleweap", NULL); } diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index f5bc1f6d..52544c63 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -1325,7 +1325,67 @@ Cmd_SpawnEntity_f(edict_t *ent) ED_CallSpawn(ent); } -void +static void +Cmd_SpawnOnStartByClass(const char *classname, const vec3_t origin) +{ + edict_t *opponent = G_Spawn(); + + // set position + opponent->s.origin[0] = origin[0]; + opponent->s.origin[1] = origin[1]; + opponent->s.origin[2] = origin[2]; + // and class + opponent->classname = strdup(classname); + + ED_CallSpawn(opponent); + + gi.dprintf("Spawned entity at %f %f %f\n", + origin[0], origin[1], origin[2]); +} + +static void +Cmd_SpawnOnStart_f(edict_t *ent) +{ + edict_t *cur = NULL; + + if (!ent) + { + return; + } + + if ((deathmatch->value || coop->value) && !sv_cheats->value) + { + gi.cprintf(ent, PRINT_HIGH, + "You must run the server with '+set cheats 1' to enable this command.\n"); + return; + } + + if (gi.argc() != 2) + { + gi.cprintf(ent, PRINT_HIGH, "Usage: spawnonstart classname\n"); + return; + } + + while ((cur = G_Find(cur, FOFS(classname), + "info_player_deathmatch")) != NULL) + { + Cmd_SpawnOnStartByClass(gi.argv(1), cur->s.origin); + } + + while ((cur = G_Find(cur, FOFS(classname), + "info_player_coop")) != NULL) + { + Cmd_SpawnOnStartByClass(gi.argv(1), cur->s.origin); + } + + while ((cur = G_Find(cur, FOFS(classname), + "info_player_start")) != NULL) + { + Cmd_SpawnOnStartByClass(gi.argv(1), cur->s.origin); + } +} + +static void Cmd_ListEntities_f(edict_t *ent) { if ((deathmatch->value || coop->value) && !sv_cheats->value) @@ -1871,6 +1931,10 @@ ClientCommand(edict_t *ent) { Cmd_SpawnEntity_f(ent); } + else if (Q_stricmp(cmd, "spawnonstart") == 0) + { + Cmd_SpawnOnStart_f(ent); + } else if (Q_stricmp(cmd, "listentities") == 0) { Cmd_ListEntities_f(ent); From 5cddd2110c70d70dde5624ea64a8554532f6c16f Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Sat, 13 Aug 2022 23:01:54 +0300 Subject: [PATCH 3/4] client: add multiplayer model preview animation --- doc/040_cvarlist.md | 8 ++++++++ src/client/menu/menu.c | 26 ++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/doc/040_cvarlist.md b/doc/040_cvarlist.md index 15b6314e..25d38cde 100644 --- a/doc/040_cvarlist.md +++ b/doc/040_cvarlist.md @@ -140,6 +140,14 @@ Set `0` by default. * **cl_showfps**: Shows the framecounter. Set to `2` for more and to `3` for even more informations. +* **cl_model_preview_start**: start frame value in multiplayer model preview. + As example 84 for `male` model for show salute animation. + Defaults to `-1` (don't show animation). + +* **cl_model_preview_end**: end frame value in multiplayer model preview. + As example 94 for `male` model for show salute animation. + Defaults to `-1` (don't show animation). + * **in_grab**: Defines how the mouse is grabbed by Yamagi Quake IIs window. If set to `0` the mouse is never grabbed and if set to `1` it's always grabbed. If set to `2` (the default) the mouse is grabbed diff --git a/src/client/menu/menu.c b/src/client/menu/menu.c index be358185..60ec4f4d 100644 --- a/src/client/menu/menu.c +++ b/src/client/menu/menu.c @@ -733,11 +733,11 @@ M_Menu_Main_f(void) InitMainMenu(); - // force first available item to have focus + // force first available item to have focus while (s_main.cursor >= 0 && s_main.cursor < s_main.nitems) { item = ( menucommon_s * )s_main.items[s_main.cursor]; - + if ((item->flags & (QMF_INACTIVE))) { s_main.cursor++; @@ -5656,6 +5656,27 @@ PlayerConfig_MenuInit(void) extern float CalcFov(float fov_x, float w, float h); +/* + * Model animation + */ +static void +PlayerConfig_AnimateModel(entity_t *entity, int curTime) +{ + cvar_t *cl_start_frame, *cl_end_frame; + int startFrame, endFrame; + + cl_start_frame = Cvar_Get("cl_model_preview_start", "-1", CVAR_ARCHIVE); + cl_end_frame = Cvar_Get("cl_model_preview_end", "-1", CVAR_ARCHIVE); + startFrame = cl_start_frame->value; + endFrame = cl_end_frame->value; + + if (startFrame >= 0 && endFrame > startFrame) + { + /* salute male 84..94 frame */ + entity->frame = (curTime / 100) % (endFrame - startFrame) + startFrame; + } +} + static void PlayerConfig_MenuDraw(void) { @@ -5699,6 +5720,7 @@ PlayerConfig_MenuDraw(void) entity.backlerp = 0.0; int curTime = Sys_Milliseconds(); + PlayerConfig_AnimateModel(&entity, curTime); // one full turn is 3s = 3000ms => 3000/360 deg per millisecond curTime = curTime % 3000; entity.angles[1] = (float)curTime/(3000.0f/360.0f); From 1dfeddc40f727313229b09c035c31fc549390834 Mon Sep 17 00:00:00 2001 From: Denis Pauk Date: Wed, 17 Aug 2022 23:33:37 +0300 Subject: [PATCH 4/4] game: fix memory leak with spawn entities cheat --- src/game/g_cmds.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index 52544c63..0b645ec0 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -1320,13 +1320,13 @@ Cmd_SpawnEntity_f(edict_t *ent) ent->spawnflags = atoi(gi.argv(8)); } - ent->classname = strdup(gi.argv(1)); + ent->classname = G_CopyString(gi.argv(1)); ED_CallSpawn(ent); } static void -Cmd_SpawnOnStartByClass(const char *classname, const vec3_t origin) +Cmd_SpawnOnStartByClass(char *classname, const vec3_t origin) { edict_t *opponent = G_Spawn(); @@ -1335,7 +1335,7 @@ Cmd_SpawnOnStartByClass(const char *classname, const vec3_t origin) opponent->s.origin[1] = origin[1]; opponent->s.origin[2] = origin[2]; // and class - opponent->classname = strdup(classname); + opponent->classname = G_CopyString(classname); ED_CallSpawn(opponent);