diff --git a/doc/040_cvarlist.md b/doc/040_cvarlist.md index 19295354..9f137ccd 100644 --- a/doc/040_cvarlist.md +++ b/doc/040_cvarlist.md @@ -259,6 +259,8 @@ it's `+set busywait 0` (setting the `busywait` cvar) and `-portable` * **g_itemsbobeffect**: Bob effect of items like in ReRelease. Defaults to `0`. +* **g_start_items**: List of start items on level. + * **g_swap_speed**: Sets the speed of the "changing weapon" animation. Default is `1`. If set to `2`, it will be double the speed, `3` is the triple... up until the max of `8`, since there are at least 2 diff --git a/src/common/netchan.c b/src/common/netchan.c index 773f8c0e..e20a8857 100644 --- a/src/common/netchan.c +++ b/src/common/netchan.c @@ -281,7 +281,7 @@ Netchan_Transmit(netchan_t *chan, int length, byte *data) } else { - Com_Printf("Netchan_Transmit: dumped unreliable\n"); + Com_Printf("%s: dumped unreliable\n", __func__); } /* send the datagram */ diff --git a/src/game/g_ctf.c b/src/game/g_ctf.c index 3d2b44ba..d6640844 100644 --- a/src/game/g_ctf.c +++ b/src/game/g_ctf.c @@ -4279,7 +4279,7 @@ CTFObserver(edict_t *ent) ent->client->ps.gunindex = 0; ent->client->resp.score = 0; memcpy(userinfo, ent->client->pers.userinfo, sizeof(userinfo)); - InitClientPersistant(ent->client); + InitClientPersistant(ent); ClientUserinfoChanged(ent, userinfo); gi.linkentity(ent); CTFOpenJoinMenu(ent); diff --git a/src/game/g_items.c b/src/game/g_items.c index 6a932376..e89d2a61 100644 --- a/src/game/g_items.c +++ b/src/game/g_items.c @@ -88,7 +88,7 @@ GetItemByIndex(int index) } gitem_t * -FindItemByClassname(char *classname) +FindItemByClassname(const char *classname) { int i; gitem_t *it; @@ -117,7 +117,7 @@ FindItemByClassname(char *classname) } gitem_t * -FindItem(char *pickup_name) +FindItem(const char *pickup_name) { int i; gitem_t *it; diff --git a/src/game/g_main.c b/src/game/g_main.c index bf220750..756d0490 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -104,6 +104,7 @@ cvar_t *g_quick_weap; cvar_t *g_swap_speed; cvar_t *g_language; cvar_t *g_itemsbobeffect; +cvar_t *g_start_items; cvar_t *g_game; static void G_RunFrame(void); diff --git a/src/game/g_spawn.c b/src/game/g_spawn.c index 8a1a7899..8a77338b 100644 --- a/src/game/g_spawn.c +++ b/src/game/g_spawn.c @@ -1319,6 +1319,15 @@ SP_worldspawn(edict_t *ent) } } + if (st.start_items && *st.start_items) + { + level.start_items = st.start_items; + } + else + { + level.start_items = NULL; + } + /* --------------- */ /* help icon for statusbar */ diff --git a/src/game/header/local.h b/src/game/header/local.h index 1a1b8399..0b53f105 100644 --- a/src/game/header/local.h +++ b/src/game/header/local.h @@ -384,6 +384,8 @@ typedef struct edict_t *disguise_violator; int disguise_violation_framenum; + + char *start_items; } level_locals_t; /* spawn_temp_t is only used to hold entity field values that @@ -406,6 +408,7 @@ typedef struct float pausetime; char *item; char *gravity; + char *start_items; float minyaw; float maxyaw; @@ -688,6 +691,7 @@ extern cvar_t *g_quick_weap; extern cvar_t *g_swap_speed; extern cvar_t *g_language; extern cvar_t *g_itemsbobeffect; +extern cvar_t *g_start_items; extern cvar_t *g_game; /* this is for the count of monsters */ @@ -761,8 +765,8 @@ void Cmd_Score_f(edict_t *ent); void PrecacheItem(gitem_t *it); void InitItems(void); void SetItemNames(void); -gitem_t *FindItem(char *pickup_name); -gitem_t *FindItemByClassname(char *classname); +gitem_t *FindItem(const char *pickup_name); +gitem_t *FindItemByClassname(const char *classname); #define ITEM_INDEX(x) ((x) - itemlist) @@ -958,7 +962,7 @@ edict_t *PlayerTrail_LastSpot(void); void respawn(edict_t *ent); void BeginIntermission(edict_t *targ); void PutClientInServer(edict_t *ent); -void InitClientPersistant(gclient_t *client); +void InitClientPersistant(edict_t *ent); void InitClientResp(gclient_t *client); void InitBodyQue(void); void ClientBeginServerFrame(edict_t *ent); diff --git a/src/game/player/client.c b/src/game/player/client.c index 463a7ad4..b2e5fa54 100644 --- a/src/game/player/client.c +++ b/src/game/player/client.c @@ -1244,16 +1244,85 @@ player_die(edict_t *self, edict_t *inflictor, edict_t *attacker, /* ======================================================================= */ +static void +Player_GiveStartItems(edict_t *ent, const char *ptr) +{ + if (!ptr || !*ptr) + { + return; + } + + while (*ptr) + { + char buffer[MAX_QPATH + 1] = {0}; + const char *buffer_end = NULL, *item_name = NULL; + char *curr_buf; + + buffer_end = strchr(ptr, ';'); + if (!buffer_end) + { + buffer_end = ptr + strlen(ptr); + } + strncpy(buffer, ptr, Q_min(MAX_QPATH, buffer_end - ptr)); + + curr_buf = buffer; + item_name = COM_Parse(&curr_buf); + if (item_name) + { + gitem_t *item; + + item = FindItemByClassname(item_name); + if (!item || !item->pickup) + { + gi.dprintf("%s: Invalid g_start_item entry: %s\n", __func__, item_name); + } + else + { + edict_t *dummy; + int count = 1; + + if (*curr_buf) + { + count = atoi(COM_Parse(&curr_buf)); + } + + if (count == 0) + { + ent->client->pers.inventory[ITEM_INDEX(item)] = 0; + continue; + } + + dummy = G_Spawn(); + dummy->item = item; + dummy->count = count; + dummy->spawnflags |= DROPPED_PLAYER_ITEM; + item->pickup(dummy, ent); + G_FreeEdict(dummy); + } + } + + /* skip end of section */ + ptr = buffer_end; + if (*ptr == ';') + { + ptr ++; + } + } +} + /* * This is only called when the game first * initializes in single player, but is called * after each death and level change in deathmatch */ void -InitClientPersistant(gclient_t *client) +InitClientPersistant(edict_t *ent) { + gclient_t *client; gitem_t *item; + client = ent->client; + if (!client) { return; @@ -1292,6 +1361,26 @@ InitClientPersistant(gclient_t *client) /* Default chasecam to off */ client->pers.chasetoggle = 0; + + /* start items */ + if (*g_start_items->string) + { + if ((deathmatch->value || coop->value) && !sv_cheats->value) + { + gi.cprintf(ent, PRINT_HIGH, + "You must run the server with '+set cheats 1' to enable 'g_start_items'.\n"); + return; + } + else + { + Player_GiveStartItems(ent, g_start_items->string); + } + } + + if (level.start_items && *level.start_items) + { + Player_GiveStartItems(ent, level.start_items); + } } void @@ -2142,7 +2231,7 @@ PutClientInServer(edict_t *ent) resp = client->resp; memcpy(userinfo, client->pers.userinfo, sizeof(userinfo)); - InitClientPersistant(client); + InitClientPersistant(ent); ClientUserinfoChanged(ent, userinfo); } else if (coop->value) @@ -2188,7 +2277,7 @@ PutClientInServer(edict_t *ent) if (client->pers.health <= 0) { - InitClientPersistant(client); + InitClientPersistant(ent); } client->resp = resp; @@ -2638,7 +2727,7 @@ ClientConnect(edict_t *ent, char *userinfo) if (!game.autosaved || !ent->client->pers.weapon) { - InitClientPersistant(ent->client); + InitClientPersistant(ent); } } diff --git a/src/game/savegame/savegame.c b/src/game/savegame/savegame.c index 669bdfa9..38ca0ea4 100644 --- a/src/game/savegame/savegame.c +++ b/src/game/savegame/savegame.c @@ -266,6 +266,7 @@ InitGame(void) g_language = gi.cvar("g_language", "english", CVAR_ARCHIVE); g_itemsbobeffect = gi.cvar("g_itemsbobeffect", "0", CVAR_ARCHIVE); g_game = gi.cvar("game", "", 0); + g_start_items = gi.cvar("g_start_items", "", 0); /* initilize localization */ LocalizationInit(); diff --git a/src/game/savegame/tables/fields.h b/src/game/savegame/tables/fields.h index 13b7686d..40256010 100644 --- a/src/game/savegame/tables/fields.h +++ b/src/game/savegame/tables/fields.h @@ -106,6 +106,7 @@ {"item", STOFS(item), F_LRAWSTRING, FFL_SPAWNTEMP}, {"item", FOFS(item), F_ITEM}, {"gravity", STOFS(gravity), F_LRAWSTRING, FFL_SPAWNTEMP}, +{"start_items", STOFS(start_items), F_LRAWSTRING, FFL_SPAWNTEMP}, {"sky", STOFS(sky), F_LRAWSTRING, FFL_SPAWNTEMP}, {"skyrotate", STOFS(skyrotate), F_FLOAT, FFL_SPAWNTEMP}, {"skyautorotate", STOFS(skyautorotate), F_INT, FFL_SPAWNTEMP},