From f2bbc445a07713f91b85c3341657dad765ff2a81 Mon Sep 17 00:00:00 2001 From: Lactozilla Date: Sat, 5 Aug 2023 20:14:44 -0300 Subject: [PATCH] Implement adding new teams --- src/d_netcmd.c | 2 +- src/deh_lua.c | 29 ++++ src/deh_soc.c | 54 ++++++- src/doomstat.h | 13 +- src/g_game.c | 81 ++++++++-- src/g_game.h | 2 + src/hu_stuff.c | 12 +- src/lua_baselib.c | 44 ++++- src/lua_infolib.c | 403 +++++++++++++++++++++++++++++++++++++++++++++- src/lua_libs.h | 2 + src/lua_script.c | 30 +++- src/m_menu.c | 15 +- src/p_inter.c | 34 ++-- src/p_saveg.c | 10 -- 14 files changed, 660 insertions(+), 71 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 723cc6725..4d6679559 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -4262,7 +4262,7 @@ void D_GameTypeChanged(INT32 lastgametype) // When swapping to a gametype that supports spectators, // make everyone a spectator initially. // Averted with GTR_NOSPECTATORSPAWN. - if (!splitscreen && (G_GametypeHasSpectators())) + if (!splitscreen && G_GametypeHasSpectators()) { INT32 i; for (i = 0; i < MAXPLAYERS; i++) diff --git a/src/deh_lua.c b/src/deh_lua.c index fea96ce5d..f43eacb43 100644 --- a/src/deh_lua.c +++ b/src/deh_lua.c @@ -131,6 +131,22 @@ static inline int lib_freeslot(lua_State *L) if (i == NUMCOLORFREESLOTS) CONS_Alert(CONS_WARNING, "Ran out of free skincolor slots!\n"); } + else if (fastcmp(type, "TEAM")) + { + UINT8 i; + for (i = 0; i < MAXTEAMS; i++) + if (!teamnames[i]) { + CONS_Printf("Team TEAM_%s allocated.\n",word); + teamnames[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); + strcpy(teamnames[i],word); + lua_pushinteger(L, i); + numteams++; + r++; + break; + } + if (i == MAXTEAMS) + CONS_Alert(CONS_WARNING, "Ran out of free team slots!\n"); + } else if (fastcmp(type, "SPR2")) { // Search if we already have an SPR2 by that name... @@ -560,6 +576,19 @@ static int ScanConstants(lua_State *L, boolean mathlib, const char *word) } return luaL_error(L, "skincolor '%s' could not be found.\n", word); } + else if (fastncmp("TEAM_",word,5)) { + p = word+5; + for (i = 0; i < MAXTEAMS; i++) + { + if (!teamnames[i]) + break; + if (fastcmp(p, teamnames[i])) { + CacheAndPushConstant(L, word, i); + return 1; + } + } + return luaL_error(L, "team '%s' could not be found.\n", word); + } else if (fastncmp("GRADE_",word,6)) { p = word+6; diff --git a/src/deh_soc.c b/src/deh_soc.c index 4ab146734..dee24b37c 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -473,6 +473,16 @@ void readfreeslots(MYFILE *f) break; } } + else if (fastcmp(type, "TEAM")) + { + for (i = 0; i < MAXTEAMS; i++) + if (!teamnames[i]) { + teamnames[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); + strcpy(teamnames[i],word); + numteams++; + break; + } + } else if (fastcmp(type, "SPR2")) { // Search if we already have an SPR2 by that name... @@ -1149,11 +1159,15 @@ void readgametype(MYFILE *f, char *gtname) int newgtinttype = 0; char gtdescription[441]; char gtconst[MAXLINELEN]; + UINT8 teamcount = 0; + UINT8 teamlist[MAXTEAMS]; // Empty strings. gtdescription[0] = '\0'; gtconst[0] = '\0'; + strcpy(gtdescription, "???"); + do { if (myfgets(s, MAXLINELEN, f)) @@ -1278,10 +1292,37 @@ void readgametype(MYFILE *f, char *gtname) newgttol = tol; } } - // The SOC probably provided gametype rules as words, - // instead of using the RULES keyword. - // Like for example "NOSPECTATORSPAWN = TRUE". - // This is completely valid, and looks better anyway. + // Teams + else if (fastcmp(word, "TEAMLIST")) + { + tmp = strtok(word2,","); + do { + if (teamcount == MAXTEAMS) + { + deh_warning("readgametype %s: too many teams\n", gtname); + break; + } + UINT8 team_id = TEAM_NONE; + for (i = 1; i < MAXTEAMS; i++) + { + if (!teamnames[i]) + break; + if (fasticmp(tmp, teamnames[i])) + { + team_id = i; + break; + } + } + if (team_id == TEAM_NONE) + deh_warning("readgametype %s: unknown team %s\n", gtname, tmp); + else + { + teamlist[teamcount++] = team_id; + } + } while((tmp = strtok(NULL,",")) != NULL); + } + // This SOC probably provided gametype rules as words, instead of using the RULES keyword. + // (For example, "NOSPECTATORSPAWN = TRUE") else { UINT32 wordgt = 0; @@ -1323,6 +1364,11 @@ void readgametype(MYFILE *f, char *gtname) gametypes[newgtidx].pointlimit = newgtpointlimit; gametypes[newgtidx].timelimit = newgttimelimit; + // Copy the teams + gametypes[newgtidx].teams.num = teamcount; + if (teamcount) + memcpy(gametypes[newgtidx].teams.list, teamlist, sizeof(teamlist[0]) * teamcount); + // Write the new gametype name. gametypes[newgtidx].name = Z_StrDup(gtname); diff --git a/src/doomstat.h b/src/doomstat.h index e7d53e6b5..ad2cd87bd 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -406,7 +406,7 @@ typedef struct { char *name; char *flag_name; - UINT8 flag; + UINT16 flag; UINT32 flag_mobj_type; UINT16 color; UINT16 weapon_color; @@ -417,6 +417,8 @@ typedef struct extern team_t teams[MAXTEAMS]; extern UINT8 numteams; +extern char *teamnames[MAXTEAMS]; + #define NUMGAMETYPEFREESLOTS 128 // Gametypes @@ -484,6 +486,12 @@ enum RANKINGS_RACE }; +typedef struct +{ + UINT8 num; + UINT8 list[MAXTEAMS]; +} teamlist_t; + typedef struct { char *name; @@ -494,8 +502,7 @@ typedef struct INT16 rankings_type; INT32 pointlimit; INT32 timelimit; - UINT8 numteams; - UINT8 teams[MAXTEAMS]; + teamlist_t teams; } gametype_t; extern gametype_t gametypes[NUMGAMETYPES]; diff --git a/src/g_game.c b/src/g_game.c index 45da6eeda..584d84972 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3465,8 +3465,10 @@ gametype_t gametypes[NUMGAMETYPES] = { // default settings for match: timelimit 10 mins, no pointlimit .timelimit = 10, .pointlimit = 0, - .numteams = 2, - .teams = { TEAM_RED, TEAM_BLUE } + .teams = { + .num = 2, + .list = { TEAM_RED, TEAM_BLUE } + } }, // GT_TAG { @@ -3499,8 +3501,10 @@ gametype_t gametypes[NUMGAMETYPES] = { // default settings for CTF: no timelimit, pointlimit 5 .timelimit = 0, .pointlimit = 5, - .numteams = 2, - .teams = { TEAM_RED, TEAM_BLUE } + .teams = { + .num = 2, + .list = { TEAM_RED, TEAM_BLUE } + } }, }; @@ -3525,6 +3529,8 @@ team_t teams[MAXTEAMS] = { } }; +char *teamnames[MAXTEAMS]; + static void G_InitTeams(void) { numteams = 3; @@ -3532,6 +3538,7 @@ static void G_InitTeams(void) teams[TEAM_NONE].name = Z_StrDup("None"); teams[TEAM_NONE].flag_name = Z_StrDup("Thingmabob"); + teamnames[TEAM_NONE] = Z_StrDup("NONE"); teams[TEAM_RED].name = Z_StrDup("Red"); teams[TEAM_RED].flag_name = Z_StrDup("Red Flag"); @@ -3539,6 +3546,7 @@ static void G_InitTeams(void) teams[TEAM_RED].icons[TEAM_ICON_FLAG] = Z_StrDup("RFLAGICO"); teams[TEAM_RED].icons[TEAM_ICON_GOT_FLAG] = Z_StrDup("GOTRFLAG"); teams[TEAM_RED].icons[TEAM_ICON_MISSING_FLAG] = Z_StrDup("NONICON2"); + teamnames[TEAM_RED] = Z_StrDup("RED"); teams[TEAM_BLUE].name = Z_StrDup("Blue"); teams[TEAM_BLUE].flag_name = Z_StrDup("Blue Flag"); @@ -3546,6 +3554,7 @@ static void G_InitTeams(void) teams[TEAM_BLUE].icons[TEAM_ICON_FLAG] = Z_StrDup("BFLAGICO"); teams[TEAM_BLUE].icons[TEAM_ICON_GOT_FLAG] = Z_StrDup("GOTBFLAG"); teams[TEAM_BLUE].icons[TEAM_ICON_MISSING_FLAG] = Z_StrDup("NONICON"); + teamnames[TEAM_BLUE] = Z_StrDup("BLUE"); G_UpdateTeamSelection(); } @@ -3572,11 +3581,20 @@ void G_UpdateTeamSelection(void) i++; } - for (UINT8 j = 1; j < teamsingame; j++, i++) + if (G_GametypeHasTeams()) { - UINT8 team = G_GetTeam(j); - dummyteam_cons_t[i].value = team; - dummyteam_cons_t[i].strvalue = teams[team].name; + for (UINT8 j = 1; j < teamsingame; j++, i++) + { + UINT8 team = G_GetTeam(j); + dummyteam_cons_t[i].value = team; + dummyteam_cons_t[i].strvalue = teams[team].name; + } + } + else + { + dummyteam_cons_t[i].value = 1; + dummyteam_cons_t[i].strvalue = "Playing"; + i++; } dummyteam_cons_t[i].value = 0; @@ -3584,6 +3602,7 @@ void G_UpdateTeamSelection(void) cv_dummyteam.defaultvalue = dummyteam_cons_t[0].strvalue; cv_dummyteam.value = 0; + cv_dummyteam.string = cv_dummyteam.defaultvalue; } // @@ -3595,9 +3614,9 @@ void G_SetGametype(INT16 gtype) gametyperules = gametypes[gametype].rules; if (G_GametypeHasTeams()) - teamsingame = gametypes[gametype].numteams + 1; + teamsingame = gametypes[gametype].teams.num + 1; else - teamsingame = 3; + teamsingame = 0; G_UpdateTeamSelection(); } @@ -3804,7 +3823,10 @@ boolean G_GametypeUsesCoopStarposts(void) // boolean G_GametypeHasTeams(void) { - return (gametyperules & GTR_TEAMS); + if (gametyperules & GTR_TEAMS) + return gametypes[gametype].teams.num > 0; + + return false; } // @@ -3885,10 +3907,10 @@ UINT32 G_TOLFlag(INT32 pgametype) UINT8 G_GetGametypeTeam(UINT8 gtype, UINT8 team) { - if (team == TEAM_NONE || team >= gametypes[gtype].numteams + 1) + if (team == TEAM_NONE || team >= gametypes[gtype].teams.num + 1) return TEAM_NONE; - return gametypes[gtype].teams[team - 1] % MAXTEAMS; + return gametypes[gtype].teams.list[team - 1] % MAXTEAMS; } UINT8 G_GetTeam(UINT8 team) @@ -3982,6 +4004,39 @@ boolean G_HasTeamIcon(UINT8 team, UINT8 icon_type) return true; } +void G_SetTeamIcon(UINT8 team, UINT8 icon_type, const char *icon) +{ + if (team >= numteams || icon_type >= TEAM_ICON_MAX) + return; + + Z_Free(teams[team].icons[icon_type]); + teams[team].icons[icon_type] = NULL; + if (icon) + teams[team].icons[icon_type] = Z_StrDup(icon); +} + +void G_FreeTeamData(UINT8 team) +{ + if (team >= numteams) + return; + + team_t *team_ptr = &teams[team]; + + if (team_ptr->name) + Z_Free(team_ptr->name); + if (team_ptr->flag_name) + Z_Free(team_ptr->flag_name); + + for (UINT8 i = 0; i < TEAM_ICON_MAX; i++) + { + Z_Free(team_ptr->icons[i]); + team_ptr->icons[i] = NULL; + } + + team_ptr->name = NULL; + team_ptr->flag_name = NULL; +} + /** Select a random map with the given typeoflevel flags. * If no map has those flags, this arbitrarily gives you map 1. * \param tolflags The typeoflevel flags to insist on. Other bits may diff --git a/src/g_game.h b/src/g_game.h index ee9d9f52d..9bde7a9ee 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -231,6 +231,8 @@ UINT16 G_GetTeamWeaponColor(UINT8 team); UINT16 G_GetTeamMissileColor(UINT8 team); const char *G_GetTeamIcon(UINT8 team, UINT8 icon_type); boolean G_HasTeamIcon(UINT8 team, UINT8 icon_type); +void G_SetTeamIcon(UINT8 team, UINT8 icon_type, const char *icon); +void G_FreeTeamData(UINT8 team); void G_Ticker(boolean run); boolean G_Responder(event_t *ev); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 7aa9930b1..7bd61a439 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2323,13 +2323,13 @@ static void HU_Draw32TeamTabRankings(playersort_t *tab, INT32 whiteplayer) greycheck = greycheckdef; supercheck = supercheckdef; - if (tab[i].team == TEAM_RED) //red + if (tab[i].team == G_GetTeam(1)) //red { redplayers++; x = 14 + (BASEVIDWIDTH/2); y = (redplayers * 9) + 20; } - else if (tab[i].team == TEAM_BLUE) //blue + else if (tab[i].team == G_GetTeam(2)) //blue { blueplayers++; x = 14; @@ -2409,7 +2409,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer) if (players[tab[i].num].spectator) continue; //ignore them. - if (tab[i].team == TEAM_RED) //red + if (tab[i].team == G_GetTeam(1)) //red { if (redplayers++ > 8) { @@ -2417,7 +2417,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer) break; // don't make more loops than we need to. } } - else if (tab[i].team == TEAM_BLUE) //blue + else if (tab[i].team == G_GetTeam(2)) //blue { if (blueplayers++ > 8) { @@ -2448,14 +2448,14 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer) if (players[tab[i].num].spectator) continue; //ignore them. - if (tab[i].team == TEAM_RED) //red + if (tab[i].team == G_GetTeam(1)) //red { if (redplayers++ > 8) continue; x = 32 + (BASEVIDWIDTH/2); y = (redplayers * 16) + 16; } - else if (tab[i].team == TEAM_BLUE) //blue + else if (tab[i].team == G_GetTeam(2)) //blue { if (blueplayers++ > 8) continue; diff --git a/src/lua_baselib.c b/src/lua_baselib.c index cac518084..c8e342975 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -156,6 +156,8 @@ static const struct { {META_SKINCOLOR, "skincolor_t"}, {META_COLORRAMP, "skincolor_t.ramp"}, {META_GAMETYPE, "gametype_t"}, + {META_TEAM, "team_t"}, + {META_TEAMLIST, "teamlist_t"}, {META_SPRITEINFO, "spriteinfo_t"}, {META_PIVOTLIST, "spriteframepivot_t[]"}, {META_FRAMEPIVOT, "spriteframepivot_t"}, @@ -3402,7 +3404,6 @@ static int lib_sResumeMusic(lua_State *L) // G_GAME //////////// -// Copypasted from lib_cvRegisterVar :] static int lib_gAddGametype(lua_State *L) { const char *k; @@ -3420,6 +3421,8 @@ static int lib_gAddGametype(lua_State *L) UINT8 newgtrightcolor = 0; INT16 newgtrankingstype = -1; int newgtinttype = 0; + UINT8 teamcount = 0; + UINT8 teamlist[MAXTEAMS]; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one. @@ -3445,7 +3448,6 @@ static int lib_gAddGametype(lua_State *L) else if (lua_isstring(L, 2)) k = lua_tostring(L, 2); - // Sorry, no gametype rules as key names. if (i == 1 || (k && fasticmp(k, "name"))) { if (!lua_isstring(L, 3)) TYPEERROR("name", LUA_TSTRING) @@ -3490,6 +3492,36 @@ static int lib_gAddGametype(lua_State *L) if (!lua_isnumber(L, 3)) TYPEERROR("headerrightcolor", LUA_TNUMBER) newgtrightcolor = (UINT8)lua_tointeger(L, 3); + } else if (i == 12 || (k && fasticmp(k, "teams"))) { + if (lua_istable(L, 3)) + { + lua_pushnil(L); + + while (lua_next(L, 3)) { + lua_Integer idx = luaL_checkinteger(L, -2) - 1; + if (idx >= 0 && idx < MAXTEAMS) + { + int team_index = luaL_checkinteger(L, -1); + if (team_index < 0 || team_index >= numteams) + luaL_error(L, "team index %d out of range (0 - %d)", team_index, numteams-1); + + teamlist[idx] = (UINT8)team_index; + + if ((lua_Integer)teamcount < idx + 1) + teamcount = (UINT8)idx + 1; + } + + lua_pop(L, 1); + } + } + else if (lua_isuserdata(L, 3)) + { + teamlist_t *tl = *((teamlist_t **)luaL_checkudata(L, 3, META_TEAMLIST)); + teamcount = tl->num; + memcpy(teamlist, tl->list, sizeof(teamlist[0]) * teamcount); + } + else + TYPEERROR("teams", LUA_TTABLE) // Key name specified } else if ((!i) && (k && fasticmp(k, "headercolor"))) { if (!lua_isnumber(L, 3)) @@ -3514,8 +3546,7 @@ static int lib_gAddGametype(lua_State *L) // Add the new gametype newgtidx = G_AddGametype(newgtrules); G_AddGametypeTOL(newgtidx, newgttol); - G_SetGametypeDescription(newgtidx, NULL, newgtleftcolor, newgtrightcolor); - strncpy(gametypedesc[newgtidx].notes, gtdescription, 441); + G_SetGametypeDescription(newgtidx, gtdescription, newgtleftcolor, newgtrightcolor); // Not covered by G_AddGametype alone. if (newgtrankingstype == -1) @@ -3525,6 +3556,11 @@ static int lib_gAddGametype(lua_State *L) gametypes[newgtidx].pointlimit = newgtpointlimit; gametypes[newgtidx].timelimit = newgttimelimit; + // Copy the teams + gametypes[newgtidx].teams.num = teamcount; + if (teamcount) + memcpy(gametypes[newgtidx].teams.list, teamlist, sizeof(teamlist[0]) * teamcount); + // Write the new gametype name. gametypes[newgtidx].name = gtname; diff --git a/src/lua_infolib.c b/src/lua_infolib.c index b070ef85d..6c6518980 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -1930,7 +1930,8 @@ enum gametype_e gametype_intermission_type, gametype_rankings_type, gametype_pointlimit, - gametype_timelimit + gametype_timelimit, + gametype_teams }; const char *const gametype_opt[] = { @@ -1941,6 +1942,7 @@ const char *const gametype_opt[] = { "rankings_type", "point_limit", "time_limit", + "teams", NULL, }; @@ -1977,6 +1979,11 @@ static int gametype_get(lua_State *L) case gametype_timelimit: lua_pushinteger(L, gt->timelimit); break; + case gametype_teams: + LUA_PushUserdata(L, >->teams, META_TEAMLIST); + break; + default: + return luaL_error(L, LUA_QL("gametype_t") " has no field named " LUA_QS, gametype_opt[field]); } return 1; } @@ -1986,6 +1993,8 @@ static int gametype_set(lua_State *L) gametype_t *gt = *((gametype_t **)luaL_checkudata(L, 1, META_GAMETYPE)); enum gametype_e field = Lua_optoption(L, 2, -1, gametype_fields_ref); + if (!lua_lumploading) + return luaL_error(L, "Do not alter gametype data from within a hook or coroutine!"); if (hud_running) return luaL_error(L, "Do not alter gametype data in HUD rendering code!"); if (hook_cmd_running) @@ -1994,6 +2003,8 @@ static int gametype_set(lua_State *L) I_Assert(gt != NULL); I_Assert(gt >= gametypes); + INT16 gametype_id = gt - gametypes; + switch (field) { case gametype_name: @@ -2019,6 +2030,52 @@ static int gametype_set(lua_State *L) case gametype_timelimit: gt->timelimit = luaL_checkinteger(L, 3); break; + case gametype_teams: + if (lua_istable(L, 3)) + { + gt->teams.num = 0; + memset(gt->teams.list, TEAM_NONE, sizeof(gt->teams.list)); + + lua_pushnil(L); + + while (lua_next(L, 3)) { + lua_Integer i = luaL_checkinteger(L, -2) - 1; + if (i >= 0 && i < MAXTEAMS) + { + int team_index = luaL_checkinteger(L, -1); + if (team_index < 0 || team_index >= numteams) + luaL_error(L, "team index %d out of range (0 - %d)", team_index, numteams-1); + + gt->teams.list[i] = (UINT8)team_index; + + if ((lua_Integer)gt->teams.num < i + 1) + gt->teams.num = (UINT8)i + 1; + } + + lua_pop(L, 1); + } + + if (gametype == gametype_id) + { + teamsingame = gt->teams.num; + G_UpdateTeamSelection(); + } + } + else + { + teamlist_t *teamlist = *((teamlist_t **)luaL_checkudata(L, 3, META_TEAMLIST)); + + memcpy(>->teams, teamlist, sizeof(teamlist_t)); + + if (gametype == gametype_id) + { + teamsingame = gt->teams.num; + G_UpdateTeamSelection(); + } + } + break; + default: + return luaL_error(L, LUA_QL("gametype_t") " has no field named " LUA_QS, gametype_opt[field]); } return 0; } @@ -2034,6 +2091,313 @@ static int gametype_num(lua_State *L) return 1; } +/////////// +// TEAMS // +/////////// + +enum team_e +{ + team_name, + team_flag_name, + team_flag, + team_flag_mobj_type, + team_color, + team_weapon_color, + team_missile_color, + team_icon, + team_icon_flag, + team_icon_got_flag, + team_icon_missing_flag +}; + +const char *const team_opt[] = { + "name", + "flag_name", + "flag", + "flag_mobj_type", + "color", + "weapon_color", + "missile_color", + "icon", + "flag_icon", + "captured_flag_icon", + "missing_flag_icon", + NULL, +}; + +static int team_fields_ref = LUA_NOREF; + +static int lib_getTeams(lua_State *L) +{ + INT16 i; + lua_remove(L, 1); + + i = luaL_checkinteger(L, 1); + if (i < 0 || i >= numteams) + return luaL_error(L, "teams[] index %d out of range (0 - %d)", i, numteams-1); + LUA_PushUserdata(L, &teams[i], META_GAMETYPE); + return 1; +} + +static int set_team_field(lua_State *L, team_t *team, enum team_e field) +{ + switch (field) + { + case team_name: + Z_Free(team->name); + team->name = Z_StrDup(luaL_checkstring(L, 3)); + G_UpdateTeamSelection(); + break; + case team_flag_name: + Z_Free(team->flag_name); + team->flag_name = Z_StrDup(luaL_checkstring(L, 3)); + break; + case team_flag: + team->flag = (UINT16)luaL_checkinteger(L, 3); + break; + case team_flag_mobj_type: + { + mobjtype_t type = luaL_checkinteger(L, 3); + if (type >= NUMMOBJTYPES) + { + luaL_error(L, "mobj type %d out of range (0 - %d)", type, NUMMOBJTYPES-1); + return 0; + } + team->flag_mobj_type = type; + break; + } + case team_color: + { + UINT16 newcolor = (UINT16)luaL_checkinteger(L, 3); + if (newcolor >= numskincolors) + { + luaL_error(L, "skincolor %d out of range (0 - %d).", newcolor, numskincolors-1); + return 0; + } + team->color = newcolor; + break; + } + case team_weapon_color: + { + UINT16 newcolor = (UINT16)luaL_checkinteger(L, 3); + if (newcolor >= numskincolors) + { + luaL_error(L, "skincolor %d out of range (0 - %d).", newcolor, numskincolors-1); + return 0; + } + team->weapon_color = newcolor; + break; + } + case team_missile_color: + { + UINT16 newcolor = (UINT16)luaL_checkinteger(L, 3); + if (newcolor >= numskincolors) + { + luaL_error(L, "skincolor %d out of range (0 - %d).", newcolor, numskincolors-1); + return 0; + } + team->missile_color = newcolor; + break; + } + case team_icon: + G_SetTeamIcon(team - teams, TEAM_ICON, luaL_checkstring(L, 3)); + ST_LoadTeamIcons(); + break; + case team_icon_flag: + G_SetTeamIcon(team - teams, TEAM_ICON_FLAG, luaL_checkstring(L, 3)); + ST_LoadTeamIcons(); + break; + case team_icon_got_flag: + G_SetTeamIcon(team - teams, TEAM_ICON_GOT_FLAG, luaL_checkstring(L, 3)); + ST_LoadTeamIcons(); + break; + case team_icon_missing_flag: + G_SetTeamIcon(team - teams, TEAM_ICON_MISSING_FLAG, luaL_checkstring(L, 3)); + ST_LoadTeamIcons(); + break; + default: + return -1; + } + return 1; +} + +static int lib_setTeams(lua_State *L) +{ + UINT32 teamnum; + team_t *team; + lua_remove(L, 1); + { + teamnum = luaL_checkinteger(L, 1); + if (teamnum >= numteams) + return luaL_error(L, "teams[] index %d out of range (0 - %d)", teamnum, numteams-1); + team = &teams[teamnum]; + } + luaL_checktype(L, 2, LUA_TTABLE); + lua_remove(L, 1); + lua_settop(L, 1); + + if (hud_running) + return luaL_error(L, "Do not alter team data in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter team data in CMD building code!"); + + G_FreeTeamData(teamnum); + + memset(team, 0, sizeof(team_t)); + + lua_pushnil(L); + while (lua_next(L, 1)) { + const char *str = luaL_checkstring(L, 2); + int field = -1; + + for (int i = 0; team_opt[i]; i++) { + if (fastcmp(str, team_opt[i])) + { + field = i; + break; + } + } + + if (field != -1) + set_team_field(L, team, field); + else + luaL_error(L, LUA_QL("team_t") " has no field named " LUA_QS, str); + + lua_pop(L, 1); + } + return 0; +} + +// #teams -> numteams +static int lib_teamslen(lua_State *L) +{ + lua_pushinteger(L, numteams); + return 1; +} + +static int team_get(lua_State *L) +{ + team_t *team = *((team_t **)luaL_checkudata(L, 1, META_GAMETYPE)); + enum team_e field = Lua_optoption(L, 2, team_name, team_fields_ref); + + I_Assert(team != NULL); + I_Assert(team >= teams); + + switch (field) + { + case team_name: + lua_pushstring(L, team->name); + break; + case team_flag_name: + lua_pushstring(L, team->flag_name); + break; + case team_flag: + lua_pushinteger(L, team->flag); + break; + case team_flag_mobj_type: + lua_pushinteger(L, team->flag_mobj_type); + break; + case team_color: + lua_pushinteger(L, team->color); + break; + case team_weapon_color: + lua_pushinteger(L, team->weapon_color); + break; + case team_missile_color: + lua_pushinteger(L, team->missile_color); + break; + case team_icon: + if (G_HasTeamIcon(team - teams, TEAM_ICON)) + lua_pushstring(L, G_GetTeamIcon(team - teams, TEAM_ICON)); + else + lua_pushnil(L); + break; + case team_icon_flag: + if (G_HasTeamIcon(team - teams, TEAM_ICON_FLAG)) + lua_pushstring(L, G_GetTeamIcon(team - teams, TEAM_ICON_FLAG)); + else + lua_pushnil(L); + break; + case team_icon_got_flag: + if (G_HasTeamIcon(team - teams, TEAM_ICON_GOT_FLAG)) + lua_pushstring(L, G_GetTeamIcon(team - teams, TEAM_ICON_GOT_FLAG)); + else + lua_pushnil(L); + break; + case team_icon_missing_flag: + if (G_HasTeamIcon(team - teams, TEAM_ICON_MISSING_FLAG)) + lua_pushstring(L, G_GetTeamIcon(team - teams, TEAM_ICON_MISSING_FLAG)); + else + lua_pushnil(L); + break; + default: + return luaL_error(L, LUA_QL("team_t") " has no field named " LUA_QS, lua_tostring(L, 2)); + } + return 1; +} + +static int team_set(lua_State *L) +{ + team_t *team = *((team_t **)luaL_checkudata(L, 1, META_GAMETYPE)); + enum team_e field = Lua_optoption(L, 2, -1, team_fields_ref); + + if (!lua_lumploading) + return luaL_error(L, "Do not alter team data from within a hook or coroutine!"); + if (hud_running) + return luaL_error(L, "Do not alter team data in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter team data in CMD building code!"); + + I_Assert(team != NULL); + I_Assert(team >= teams); + + if (set_team_field(L, team, field) < 0) + return luaL_error(L, LUA_QL("team_t") " has no field named " LUA_QS, lua_tostring(L, 2)); + + return 0; +} + +static int team_num(lua_State *L) +{ + team_t *team = *((team_t **)luaL_checkudata(L, 1, META_GAMETYPE)); + + I_Assert(team != NULL); + I_Assert(team >= teams); + + lua_pushinteger(L, team-teams); + return 1; +} + +static int teamlist_len(lua_State *L) +{ + teamlist_t *teamlist = *((teamlist_t **)luaL_checkudata(L, 1, META_TEAMLIST)); + lua_pushinteger(L, teamlist->num); + return 1; +} + +static int teamlist_get(lua_State *L) +{ + teamlist_t *teamlist = *((teamlist_t **)luaL_checkudata(L, 1, META_TEAMLIST)); + int i = luaL_checkinteger(L, 2); + if (i < 0 || i > teamlist->num) + return luaL_error(L, "list index %d out of range (1 - %d)", i, teamlist->num); + lua_pushinteger(L, teamlist->list[i - 1]); + return 1; +} + +static int teamlist_set(lua_State *L) +{ + teamlist_t *teamlist = *((teamlist_t **)luaL_checkudata(L, 1, META_TEAMLIST)); + int i = luaL_checkinteger(L, 2); + if (i < 0 || i > teamlist->num) + return luaL_error(L, "list index %d out of range (1 - %d)", i, teamlist->num); + int team = luaL_checkinteger(L, 3); + if (team < 0 || team >= numteams) + return luaL_error(L, "team index %d out of range (0 - %d)", i, numteams - 1); + teamlist->list[i - 1] = (UINT8)team; + return 0; +} + ////////////////////////////// // // Now push all these functions into the Lua state! @@ -2086,6 +2450,30 @@ int LUA_InfoLib(lua_State *L) gametype_fields_ref = Lua_CreateFieldTable(L, gametype_opt); + luaL_newmetatable(L, META_TEAM); + lua_pushcfunction(L, team_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, team_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, team_num); + lua_setfield(L, -2, "__len"); + lua_pop(L, 1); + + team_fields_ref = Lua_CreateFieldTable(L, team_opt); + + luaL_newmetatable(L, META_TEAMLIST); + lua_pushcfunction(L, teamlist_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, teamlist_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, teamlist_len); + lua_setfield(L, -2, "__len"); + lua_pop(L, 1); + luaL_newmetatable(L, META_SKINCOLOR); lua_pushcfunction(L, skincolor_get); lua_setfield(L, -2, "__index"); @@ -2262,6 +2650,19 @@ int LUA_InfoLib(lua_State *L) lua_setmetatable(L, -2); lua_setglobal(L, "gametypes"); + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getTeams); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_setTeams); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, lib_teamslen); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "teams"); + luaL_newmetatable(L, META_LUABANKS); lua_pushcfunction(L, lib_getluabanks); lua_setfield(L, -2, "__index"); diff --git a/src/lua_libs.h b/src/lua_libs.h index df1898597..2078fc7e9 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -29,6 +29,8 @@ extern boolean mousegrabbedbylua; #define META_COLORRAMP "SKINCOLOR_T*RAMP" #define META_SPRITEINFO "SPRITEINFO_T*" #define META_GAMETYPE "GAMETYPE_T*" +#define META_TEAM "TEAM_T*" +#define META_TEAMLIST "TEAMLIST_T*" #define META_PIVOTLIST "SPRITEFRAMEPIVOT_T[]" #define META_FRAMEPIVOT "SPRITEFRAMEPIVOT_T*" diff --git a/src/lua_script.c b/src/lua_script.c index 73b57e792..bbc1e649a 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -214,10 +214,10 @@ int LUA_PushGlobals(lua_State *L, const char *word) lua_pushboolean(L, paused); return 1; } else if (fastcmp(word,"bluescore")) { - lua_pushinteger(L, teamscores[TEAM_BLUE]); + lua_pushinteger(L, teamscores[G_GetTeam(2)]); return 1; } else if (fastcmp(word,"redscore")) { - lua_pushinteger(L, teamscores[TEAM_RED]); + lua_pushinteger(L, teamscores[G_GetTeam(1)]); return 1; } else if (fastcmp(word,"timelimit")) { lua_pushinteger(L, cv_timelimit.value); @@ -226,16 +226,16 @@ int LUA_PushGlobals(lua_State *L, const char *word) lua_pushinteger(L, cv_pointlimit.value); return 1; } else if (fastcmp(word, "redflag")) { - LUA_PushUserdata(L, flagmobjs[TEAM_RED], META_MOBJ); + LUA_PushUserdata(L, flagmobjs[G_GetTeam(1)], META_MOBJ); return 1; } else if (fastcmp(word, "blueflag")) { - LUA_PushUserdata(L, flagmobjs[TEAM_BLUE], META_MOBJ); + LUA_PushUserdata(L, flagmobjs[G_GetTeam(2)], META_MOBJ); return 1; } else if (fastcmp(word, "rflagpoint")) { - LUA_PushUserdata(L, flagpoints[TEAM_RED], META_MAPTHING); + LUA_PushUserdata(L, flagpoints[G_GetTeam(1)], META_MAPTHING); return 1; } else if (fastcmp(word, "bflagpoint")) { - LUA_PushUserdata(L, flagpoints[TEAM_BLUE], META_MAPTHING); + LUA_PushUserdata(L, flagpoints[G_GetTeam(2)], META_MAPTHING); return 1; // begin map vars } else if (fastcmp(word,"spstage_start")) { @@ -271,6 +271,12 @@ int LUA_PushGlobals(lua_State *L, const char *word) } else if (fastcmp(word,"tutorialmode")) { lua_pushboolean(L, tutorialmode); return 1; + } else if (fastcmp(word,"numteams")) { + lua_pushinteger(L, max(numteams - 1, 0)); + return 1; + } else if (fastcmp(word,"teamsingame")) { + lua_pushinteger(L, max(teamsingame - 1, 0)); + return 1; // end map vars // begin CTF colors } else if (fastcmp(word,"skincolor_redteam")) { @@ -991,6 +997,7 @@ enum ARCH_MOUSE, ARCH_SKIN, ARCH_GAMETYPE, + ARCH_TEAM, ARCH_TEND=0xFF, }; @@ -1021,6 +1028,7 @@ static const struct { {META_MOUSE, ARCH_MOUSE}, {META_SKIN, ARCH_SKIN}, {META_GAMETYPE, ARCH_GAMETYPE}, + {META_TEAM, ARCH_TEAM}, {NULL, ARCH_NULL} }; @@ -1356,6 +1364,13 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex) WRITEUINT8(save_p, gt - gametypes); break; } + case ARCH_TEAM: + { + team_t *team = *((team_t **)lua_touserdata(gL, myindex)); + WRITEUINT8(save_p, ARCH_TEAM); + WRITEUINT8(save_p, team - teams); + break; + } default: WRITEUINT8(save_p, ARCH_NULL); return 2; @@ -1608,6 +1623,9 @@ static UINT8 UnArchiveValue(int TABLESINDEX) case ARCH_GAMETYPE: LUA_PushUserdata(gL, &gametypes[READUINT8(save_p)], META_GAMETYPE); break; + case ARCH_TEAM: + LUA_PushUserdata(gL, &teams[READUINT8(save_p)], META_TEAM); + break; case ARCH_TEND: return 1; } diff --git a/src/m_menu.c b/src/m_menu.c index ae8179a2f..059ec161d 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -473,7 +473,7 @@ static CV_PossibleValue_t dummymares_cons_t[] = { {-1, "END"}, {0,"Overall"}, {1,"Mare 1"}, {2,"Mare 2"}, {3,"Mare 3"}, {4,"Mare 4"}, {5,"Mare 5"}, {6,"Mare 6"}, {7,"Mare 7"}, {8,"Mare 8"}, {0,NULL} }; -CV_PossibleValue_t dummyteam_cons_t[MAXTEAMS + 1]; +CV_PossibleValue_t dummyteam_cons_t[MAXTEAMS + 2]; consvar_t cv_dummyteam = CVAR_INIT ("dummyteam", "Spectator", CV_HIDEN, dummyteam_cons_t, NULL); @@ -6992,18 +6992,7 @@ static void M_ConfirmTeamChange(INT32 choice) M_ClearMenus(true); - switch (cv_dummyteam.value) - { - case 0: - COM_ImmedExecute("changeteam spectator"); - break; - case 1: - COM_ImmedExecute("changeteam red"); - break; - case 2: - COM_ImmedExecute("changeteam blue"); - break; - } + COM_ImmedExecute(va("changeteam %d", cv_dummyteam.value)); } static void M_Options(INT32 choice) diff --git a/src/p_inter.c b/src/p_inter.c index 798faaf8b..0aa829d9e 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2282,8 +2282,20 @@ void P_CheckTimeLimit(void) } else { - //In team match and CTF, determining a tie is much simpler. =P - if (teamscores[TEAM_RED] == teamscores[TEAM_BLUE]) + boolean is_tied = true; + UINT32 lastscore = teamscores[G_GetTeam(1)]; + + for (UINT8 j = 2; j < teamsingame; j++) + { + if (teamscores[G_GetTeam(j)] != lastscore) + { + is_tied = false; + break; + } + lastscore = teamscores[G_GetTeam(j)]; + } + + if (is_tied) return; } } @@ -2303,7 +2315,8 @@ void P_CheckTimeLimit(void) */ void P_CheckPointLimit(void) { - INT32 i; + if (!server) + return; if (!cv_pointlimit.value) return; @@ -2314,27 +2327,28 @@ void P_CheckPointLimit(void) if (!(gametyperules & GTR_POINTLIMIT)) return; - // pointlimit is nonzero, check if it's been reached by this player if (G_GametypeHasTeams()) { - // Just check both teams - if ((UINT32)cv_pointlimit.value <= teamscores[TEAM_RED] || (UINT32)cv_pointlimit.value <= teamscores[TEAM_BLUE]) + for (UINT8 i = 1; i < teamsingame; i++) { - if (server) + if (teamscores[G_GetTeam(i)] >= (UINT32)cv_pointlimit.value) + { SendNetXCmd(XD_EXITLEVEL, NULL, 0); + return; + } } } else { - for (i = 0; i < MAXPLAYERS; i++) + // pointlimit is nonzero, check if it's been reached by this player + for (INT32 i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator) continue; if ((UINT32)cv_pointlimit.value <= players[i].score) { - if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); return; } } diff --git a/src/p_saveg.c b/src/p_saveg.c index 1d11c55a4..231e9e616 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4340,12 +4340,7 @@ static void P_NetArchiveMisc(boolean resending) WRITEUINT8(save_p, teamsingame); for (i = 0; i < MAXTEAMS; i++) - { WRITEUINT32(save_p, teamscores[i]); - WRITEUINT16(save_p, teams[i].color); - WRITEUINT16(save_p, teams[i].weapon_color); - WRITEUINT16(save_p, teams[i].missile_color); - } WRITEINT32(save_p, modulothing); @@ -4440,12 +4435,7 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) teamsingame = READUINT8(save_p); for (i = 0; i < MAXTEAMS; i++) - { teamscores[i] = READUINT32(save_p); - teams[i].color = READUINT16(save_p); - teams[i].weapon_color = READUINT16(save_p); - teams[i].missile_color = READUINT16(save_p); - } modulothing = READINT32(save_p);