From a0db91a0c90827cda8461d7085f25323df4e3d4b Mon Sep 17 00:00:00 2001 From: GreaseMonkey Date: Thu, 8 Feb 2024 20:32:15 +1300 Subject: [PATCH 1/8] Preliminary 4-player counter-op mode Here's some things that will be needed off the top of my head: - Handling the half-screen-width case for the pause menus - Changing the player select menu to select the not-counter-op player instead, then extending that to 4 players - Thorough testing of each level for any weird counter-op-specific features --- port/src/pdmain.c | 6 ++++++ src/game/bondwalk.c | 8 ++++++++ src/game/chraction.c | 30 ++++++++++++++++++++++++++++-- src/game/mainmenu.c | 13 +++++++++++++ src/game/mplayer/mplayer.c | 11 ++++++++++- src/game/player.c | 32 +++++++++++++++++++++++++++++--- src/game/playerreset.c | 17 +++++++++++++++-- src/game/prop.c | 7 ++++++- src/game/propobj.c | 9 ++++++++- src/include/constants.h | 3 ++- 10 files changed, 125 insertions(+), 11 deletions(-) diff --git a/port/src/pdmain.c b/port/src/pdmain.c index b08870abd..f361310c8 100644 --- a/port/src/pdmain.c +++ b/port/src/pdmain.c @@ -465,7 +465,13 @@ void mainLoop(void) } if (g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0) { +#ifdef PLATFORM_N64 g_MpSetup.chrslots = 0x03; +#else + if (getNumPlayers() <= 2) { + g_MpSetup.chrslots = 0x03; + } +#endif mpReset(); } else if (g_Vars.perfectbuddynum) { mpReset(); diff --git a/src/game/bondwalk.c b/src/game/bondwalk.c index 8bd907e2e..cdfa78e7e 100644 --- a/src/game/bondwalk.c +++ b/src/game/bondwalk.c @@ -475,7 +475,11 @@ bool bwalkCalculateNewPositionWithPush(struct coord *delta, f32 rotateamount, bo } } else if (chr->chrflags & CHRCFLAG_PUSHABLE) { if (g_Vars.antiplayernum < 0 +#ifdef PLATFORM_N64 || g_Vars.currentplayer != g_Vars.anti +#else + || g_Vars.currentplayer == g_Vars.bond +#endif || (chr->hidden & CHRHFLAG_ANTINONINTERACTABLE) == 0) { canpush = true; } @@ -809,7 +813,11 @@ void bwalkUpdateVertical(void) // Maybe reset counter-op's radius - not sure why // Maybe it gets set to 0 when they die? if (g_Vars.antiplayernum >= 0 +#ifdef PLATFORM_N64 && g_Vars.currentplayer == g_Vars.anti +#else + && g_Vars.currentplayer != g_Vars.bond +#endif && g_Vars.currentplayer->bond2.radius != 30 && cdTestVolume(&g_Vars.currentplayer->prop->pos, 30, g_Vars.currentplayer->prop->rooms, CDTYPE_ALL, CHECKVERTICAL_YES, ymax - g_Vars.currentplayer->prop->pos.y, ymin - g_Vars.currentplayer->prop->pos.y)) { g_Vars.currentplayer->prop->chr->radius = 30; diff --git a/src/game/chraction.c b/src/game/chraction.c index 4bf5ccb64..bf35338b5 100644 --- a/src/game/chraction.c +++ b/src/game/chraction.c @@ -4351,7 +4351,14 @@ void chrDamage(struct chrdata *chr, f32 damage, struct coord *vector, struct gse // Don't damage if attacker was anti and chr is non-interactable by anti if (g_Vars.antiplayernum >= 0 && aprop + +#ifdef PLATFORM_N64 && aprop == g_Vars.anti->prop +#else + && aprop->type == PROPTYPE_PLAYER + && aprop != g_Vars.bond->prop +#endif + && (chr->hidden & CHRHFLAG_ANTINONINTERACTABLE)) { return; } @@ -4490,7 +4497,15 @@ void chrDamage(struct chrdata *chr, f32 damage, struct coord *vector, struct gse } // Anti shooting other enemies is lethal - if (aprop && aprop == g_Vars.anti->prop && vprop != g_Vars.bond->prop) { + if (aprop +#ifdef PLATFORM_N64 + && aprop == g_Vars.anti->prop +#else + && aprop->type == PROPTYPE_PLAYER + && aprop != g_Vars.bond->prop +#endif + + && vprop != g_Vars.bond->prop) { damage *= 100; } } else { @@ -8321,7 +8336,14 @@ void chrAlertOthersOfInjury(struct chrdata *chr, bool dying) s32 numinrange = 0; s32 numchrs = chrsGetNumSlots(); - if (g_Vars.antiplayernum >= 0 && chr->prop == g_Vars.anti->prop) { + if (g_Vars.antiplayernum >= 0 +#ifdef PLATFORM_N64 + && chr->prop == g_Vars.anti->prop +#else + && chr->prop->type == PROPTYPE_PLAYER + && chr->prop != g_Vars.bond->prop +#endif + ) { return; } @@ -13556,7 +13578,11 @@ void chraTickBg(void) if (targetprop && (targetprop->type == PROPTYPE_CHR || targetprop->type == PROPTYPE_PLAYER)) { if ((targetprop->type == PROPTYPE_PLAYER +#ifdef PLATFORM_N64 && !(g_Vars.antiplayernum >= 0 && g_Vars.anti && g_Vars.anti->prop == targetprop) +#else + && !(g_Vars.antiplayernum >= 0 && g_Vars.bond && g_Vars.bond->prop != targetprop) +#endif && chrCompareTeams(chr, targetprop->chr, COMPARE_ENEMIES)) || CHRRACE(targetprop->chr) == RACE_EYESPY) { s32 time60; diff --git a/src/game/mainmenu.c b/src/game/mainmenu.c index 1e84f2daf..0b65599b5 100644 --- a/src/game/mainmenu.c +++ b/src/game/mainmenu.c @@ -757,7 +757,20 @@ MenuItemHandlerResult menuhandlerAcceptMission(s32 operation, struct menuitem *i } g_Vars.coopplayernum = -1; +#ifdef PLATFORM_N64 setNumPlayers(2); +#else + if ((joyGetConnectedControllers() & 0xF) == 0xF) { + setNumPlayers(4); + g_MpSetup.chrslots = 0xF; + } else if ((joyGetConnectedControllers() & 0x7) == 0x7) { + setNumPlayers(3); + g_MpSetup.chrslots = 0x7; + } else { + setNumPlayers(2); + g_MpSetup.chrslots = 0x3; + } +#endif } else { // Solo g_Vars.bondplayernum = 0; diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index 7f27e0165..6b27e3ad3 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -256,7 +256,12 @@ void mpReset(void) g_Vars.lvmpbotlevel = true; } - if (g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0) { +#ifdef PLATFORM_N64 + if (g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0) +#else + if ((g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0) && getNumPlayers() <= 2) +#endif + { struct mpplayerconfig tmp; tmp = g_PlayerConfigsArray[MAX_PLAYERS]; @@ -1333,7 +1338,11 @@ Gfx *mpRenderModalText(Gfx *gdl) && g_Vars.currentplayer->redbloodfinished && g_Vars.currentplayer->deathanimfinished && !(g_Vars.coopplayernum >= 0 && ((g_Vars.bond->isdead && g_Vars.coop->isdead) || !g_Vars.currentplayer->coopcanrestart || g_InCutscene)) +#ifdef PLATFORM_N64 && !(g_Vars.antiplayernum >= 0 && ((g_Vars.currentplayer != g_Vars.anti || g_InCutscene))) +#else + && !(g_Vars.antiplayernum >= 0 && ((g_Vars.currentplayer == g_Vars.bond || g_InCutscene))) +#endif && g_NumReasonsToEndMpMatch == 0) { // Render "Press START" text gdl = text0f153628(gdl); diff --git a/src/game/player.c b/src/game/player.c index 6c54d5bde..3366d0f69 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -606,7 +606,13 @@ void playerStartNewLife(void) if (cmd); if (cmd); - if (g_Vars.antiplayernum < 0 || g_Vars.currentplayer != g_Vars.anti) { + if (g_Vars.antiplayernum < 0 +#ifdef PLATFORM_N64 + || g_Vars.currentplayer != g_Vars.anti +#else + || g_Vars.currentplayer == g_Vars.bond +#endif + ) { while (cmd[0] != INTROCMD_END) { switch (cmd[0]) { case INTROCMD_SPAWN: @@ -980,7 +986,13 @@ void playerSpawn(void) } if (g_Vars.mplayerisrunning) { - if (g_Vars.antiplayernum >= 0 && g_Vars.currentplayer == g_Vars.anti) { + if (g_Vars.antiplayernum >= 0 +#ifdef PLATFORM_N64 + && g_Vars.currentplayer == g_Vars.anti +#else + && g_Vars.currentplayer != g_Vars.bond +#endif + ) { numsqdists = 0; force = false; @@ -1182,7 +1194,11 @@ void playerChooseBodyAndHead(s32 *bodynum, s32 *headnum, s32 *arg2) bool solo; if (g_Vars.antiplayernum >= 0 +#ifdef PLATFORM_N64 && g_Vars.currentplayer == g_Vars.anti +#else + && g_Vars.currentplayer != g_Vars.bond +#endif && g_Vars.antiheadnum >= 0 && g_Vars.antibodynum >= 0) { *headnum = g_Vars.antiheadnum; @@ -1514,7 +1530,11 @@ void playerTickChrBody(void) #if VERSION >= VERSION_NTSC_1_0 if (g_Vars.antiplayernum >= 0 +#ifdef PLATFORM_N64 && g_Vars.currentplayer == g_Vars.anti +#else + && g_Vars.currentplayer != g_Vars.bond +#endif && g_Vars.currentplayer->vv_eyeheight > 159) { g_Vars.currentplayer->vv_eyeheight = 159; } @@ -4713,7 +4733,13 @@ Gfx *playerRenderHud(Gfx *gdl) chr->chrflags |= CHRCFLAG_HIDDEN; } - if (g_Vars.antiplayernum >= 0 && g_Vars.currentplayer == g_Vars.anti) { + if (g_Vars.antiplayernum >= 0 +#ifdef PLATFORM_N64 + && g_Vars.currentplayer == g_Vars.anti +#else + && g_Vars.currentplayer != g_Vars.bond +#endif + ) { // Anti #ifndef PLATFORM_N64 if (g_NetMode == NETMODE_SERVER && g_Vars.currentplayer->isremote) { diff --git a/src/game/playerreset.c b/src/game/playerreset.c index 6e941dd9c..1091ea2a6 100644 --- a/src/game/playerreset.c +++ b/src/game/playerreset.c @@ -186,7 +186,14 @@ void playerReset(void) cmd = (struct cmd32 *)((uintptr_t)cmd + 8); break; case INTROCMD_WEAPON: - if (cmd->param3 == 0 && g_Vars.currentplayer != g_Vars.anti) { + if (cmd->param3 == 0 +#ifdef PLATFORM_N64 + && g_Vars.currentplayer != g_Vars.anti +#else + && (g_Vars.antiplayernum < 0 || g_Vars.currentplayer == g_Vars.bond) +#endif + ) { + modelmgrLoadProjectileModeldefs(cmd->param1); if (cmd->param2 >= 0) { @@ -213,7 +220,13 @@ void playerReset(void) cmd = (struct cmd32 *)((uintptr_t)cmd + 16); break; case INTROCMD_AMMO: - if (cmd->param3 == 0 && g_Vars.currentplayer != g_Vars.anti) { + if (cmd->param3 == 0 +#ifdef PLATFORM_N64 + && g_Vars.currentplayer != g_Vars.anti +#else + && (g_Vars.antiplayernum < 0 || g_Vars.currentplayer == g_Vars.bond) +#endif + ) { bgunSetAmmoQuantity(cmd->param1, cmd->param2); } cmd = (struct cmd32 *)((uintptr_t)cmd + 16); diff --git a/src/game/prop.c b/src/game/prop.c index e9d544e06..7db390774 100644 --- a/src/game/prop.c +++ b/src/game/prop.c @@ -2358,7 +2358,12 @@ void propsTestForPickup(void) if (g_Vars.currentplayer->bondmovemode != MOVEMODE_CUTSCENE && !g_PlayerInvincible - && g_Vars.currentplayer != g_Vars.anti) { +#ifdef PLATFORM_N64 + && g_Vars.currentplayer != g_Vars.anti +#else + && (g_Vars.antiplayernum < 0 || g_Vars.currentplayer == g_Vars.bond) +#endif + ) { roomsCopy(g_Vars.currentplayer->prop->rooms, allrooms); for (i = 0; g_Vars.currentplayer->prop->rooms[i] != -1; i++) { diff --git a/src/game/propobj.c b/src/game/propobj.c index 598eae641..aa3ed5893 100644 --- a/src/game/propobj.c +++ b/src/game/propobj.c @@ -15942,7 +15942,14 @@ void objHit(struct shotdata *shotdata, struct hit *hit) } } - if (g_Vars.antiplayernum < 0 || g_Vars.currentplayer != g_Vars.anti || (obj->flags2 & OBJFLAG2_IMMUNETOANTI) == 0) { + if (g_Vars.antiplayernum < 0 +#ifdef PLATFORM_N64 + || g_Vars.currentplayer != g_Vars.anti +#else + || g_Vars.currentplayer == g_Vars.bond +#endif + || (obj->flags2 & OBJFLAG2_IMMUNETOANTI) == 0) { + if (hit->hitthing.texturenum != 10000) { f32 damage = gsetGetDamage(&shotdata->gset); diff --git a/src/include/constants.h b/src/include/constants.h index 163812868..571f09be8 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -20,8 +20,9 @@ #define MAX_CHRWAYPOINTS 6 #define MAX_EXPLOSIONS 6 #define MAX_EYESPYDARTS 8 +#define MAX_COOPCHRS MAX_PLAYERS #define MAX_MPCHRS (MAX_PLAYERS + MAX_BOTS) -#define MAX_MPPLAYERCONFIGS (MAX_PLAYERS + 2) +#define MAX_MPPLAYERCONFIGS (MAX_PLAYERS + MAX_COOPCHRS) #define MAX_OBJECTIVES 10 #define MAX_LOCAL_PLAYERS 4 #define MAX_PLAYERS 8 From 634eecb35d1cb311dfda9b8ca8d2fc889f763c38 Mon Sep 17 00:00:00 2001 From: GreaseMonkey Date: Thu, 8 Feb 2024 22:00:58 +1300 Subject: [PATCH 2/8] Use vertical-split versions of menus in 3+-player main missions These work fine in the 2x2 split arrangement. --- src/game/endscreen.c | 36 ++++++++++++++++++++++++++++++------ src/game/mplayer/ingame.c | 6 +++++- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/game/endscreen.c b/src/game/endscreen.c index add6d4949..8f0691d66 100644 --- a/src/game/endscreen.c +++ b/src/game/endscreen.c @@ -1700,14 +1700,22 @@ void endscreenPushCoop(void) #endif { // Failed or aborted - if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) { + if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL +#ifndef PLATFORM_N64 + || PLAYERCOUNT() >= 3 +#endif + ) { menuPushRootDialog(&g_2PMissionEndscreenFailedVMenuDialog, MENUROOT_MPENDSCREEN); } else { menuPushRootDialog(&g_2PMissionEndscreenFailedHMenuDialog, MENUROOT_MPENDSCREEN); } } else { // Completed - if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) { + if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL +#ifndef PLATFORM_N64 + || PLAYERCOUNT() >= 3 +#endif + ) { menuPushRootDialog(&g_2PMissionEndscreenCompletedVMenuDialog, MENUROOT_MPENDSCREEN); } else { menuPushRootDialog(&g_2PMissionEndscreenCompletedHMenuDialog, MENUROOT_MPENDSCREEN); @@ -1793,14 +1801,22 @@ void endscreenPushAnti(void) #endif { // Bond - failed or aborted - if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) { + if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL +#ifndef PLATFORM_N64 + || PLAYERCOUNT() >= 3 +#endif + ) { menuPushRootDialog(&g_2PMissionEndscreenFailedVMenuDialog, MENUROOT_MPENDSCREEN); } else { menuPushRootDialog(&g_2PMissionEndscreenFailedHMenuDialog, MENUROOT_MPENDSCREEN); } } else { // Bond - completed - if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) { + if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL +#ifndef PLATFORM_N64 + || PLAYERCOUNT() >= 3 +#endif + ) { menuPushRootDialog(&g_2PMissionEndscreenCompletedVMenuDialog, MENUROOT_MPENDSCREEN); } else { menuPushRootDialog(&g_2PMissionEndscreenCompletedHMenuDialog, MENUROOT_MPENDSCREEN); @@ -1816,14 +1832,22 @@ void endscreenPushAnti(void) #endif { // Anti - completed - if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) { + if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL +#ifndef PLATFORM_N64 + || PLAYERCOUNT() >= 3 +#endif + ) { menuPushRootDialog(&g_2PMissionEndscreenCompletedVMenuDialog, MENUROOT_MPENDSCREEN); } else { menuPushRootDialog(&g_2PMissionEndscreenCompletedHMenuDialog, MENUROOT_MPENDSCREEN); } } else { // Anti - failed or aborted - if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) { + if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL +#ifndef PLATFORM_N64 + || PLAYERCOUNT() >= 3 +#endif + ) { menuPushRootDialog(&g_2PMissionEndscreenFailedVMenuDialog, MENUROOT_MPENDSCREEN); } else { menuPushRootDialog(&g_2PMissionEndscreenFailedHMenuDialog, MENUROOT_MPENDSCREEN); diff --git a/src/game/mplayer/ingame.c b/src/game/mplayer/ingame.c index 390860a46..5d157e753 100644 --- a/src/game/mplayer/ingame.c +++ b/src/game/mplayer/ingame.c @@ -800,7 +800,11 @@ void mpPushPauseDialog(void) menuPushRootDialog(&g_MpPausePlayerRankingMenuDialog, MENUROOT_MPPAUSE); } } else { - if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) { + if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL +#ifndef PLATFORM_N64 + || PLAYERCOUNT() >= 3 +#endif + ) { menuPushRootDialog(&g_2PMissionPauseVMenuDialog, MENUROOT_MPPAUSE); } else { menuPushRootDialog(&g_2PMissionPauseHMenuDialog, MENUROOT_MPPAUSE); From 44a67e60e8320867aa586e50fc18f1444e760d60 Mon Sep 17 00:00:00 2001 From: GreaseMonkey Date: Fri, 9 Feb 2024 09:10:33 +1300 Subject: [PATCH 3/8] net: Use `LOCALPLAYERCOUNT()` for UI size determination --- src/game/endscreen.c | 12 ++++++------ src/game/mplayer/ingame.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/game/endscreen.c b/src/game/endscreen.c index 8f0691d66..d9aa5ae84 100644 --- a/src/game/endscreen.c +++ b/src/game/endscreen.c @@ -1702,7 +1702,7 @@ void endscreenPushCoop(void) // Failed or aborted if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL #ifndef PLATFORM_N64 - || PLAYERCOUNT() >= 3 + || LOCALPLAYERCOUNT() >= 3 #endif ) { menuPushRootDialog(&g_2PMissionEndscreenFailedVMenuDialog, MENUROOT_MPENDSCREEN); @@ -1713,7 +1713,7 @@ void endscreenPushCoop(void) // Completed if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL #ifndef PLATFORM_N64 - || PLAYERCOUNT() >= 3 + || LOCALPLAYERCOUNT() >= 3 #endif ) { menuPushRootDialog(&g_2PMissionEndscreenCompletedVMenuDialog, MENUROOT_MPENDSCREEN); @@ -1803,7 +1803,7 @@ void endscreenPushAnti(void) // Bond - failed or aborted if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL #ifndef PLATFORM_N64 - || PLAYERCOUNT() >= 3 + || LOCALPLAYERCOUNT() >= 3 #endif ) { menuPushRootDialog(&g_2PMissionEndscreenFailedVMenuDialog, MENUROOT_MPENDSCREEN); @@ -1814,7 +1814,7 @@ void endscreenPushAnti(void) // Bond - completed if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL #ifndef PLATFORM_N64 - || PLAYERCOUNT() >= 3 + || LOCALPLAYERCOUNT() >= 3 #endif ) { menuPushRootDialog(&g_2PMissionEndscreenCompletedVMenuDialog, MENUROOT_MPENDSCREEN); @@ -1834,7 +1834,7 @@ void endscreenPushAnti(void) // Anti - completed if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL #ifndef PLATFORM_N64 - || PLAYERCOUNT() >= 3 + || LOCALPLAYERCOUNT() >= 3 #endif ) { menuPushRootDialog(&g_2PMissionEndscreenCompletedVMenuDialog, MENUROOT_MPENDSCREEN); @@ -1845,7 +1845,7 @@ void endscreenPushAnti(void) // Anti - failed or aborted if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL #ifndef PLATFORM_N64 - || PLAYERCOUNT() >= 3 + || LOCALPLAYERCOUNT() >= 3 #endif ) { menuPushRootDialog(&g_2PMissionEndscreenFailedVMenuDialog, MENUROOT_MPENDSCREEN); diff --git a/src/game/mplayer/ingame.c b/src/game/mplayer/ingame.c index 5d157e753..4286739f0 100644 --- a/src/game/mplayer/ingame.c +++ b/src/game/mplayer/ingame.c @@ -802,7 +802,7 @@ void mpPushPauseDialog(void) } else { if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL #ifndef PLATFORM_N64 - || PLAYERCOUNT() >= 3 + || LOCALPLAYERCOUNT() >= 3 #endif ) { menuPushRootDialog(&g_2PMissionPauseVMenuDialog, MENUROOT_MPPAUSE); From 92d3a9f564b399d63874a9d89838b65d621d6418 Mon Sep 17 00:00:00 2001 From: GreaseMonkey Date: Fri, 9 Feb 2024 13:44:04 +1300 Subject: [PATCH 4/8] Add macros for counter-op player determination; Handle some missing checks --- src/game/bondwalk.c | 12 ++---------- src/game/chr.c | 2 +- src/game/chraction.c | 32 ++++---------------------------- src/game/explosions.c | 18 +++++++++++++++--- src/game/mplayer/mplayer.c | 6 +----- src/game/player.c | 36 +++++------------------------------- src/game/playerreset.c | 16 ++-------------- src/game/prop.c | 6 +----- src/game/propobj.c | 8 ++------ src/include/constants.h | 12 ++++++++++++ 10 files changed, 45 insertions(+), 103 deletions(-) diff --git a/src/game/bondwalk.c b/src/game/bondwalk.c index cdfa78e7e..1d0853c06 100644 --- a/src/game/bondwalk.c +++ b/src/game/bondwalk.c @@ -475,11 +475,7 @@ bool bwalkCalculateNewPositionWithPush(struct coord *delta, f32 rotateamount, bo } } else if (chr->chrflags & CHRCFLAG_PUSHABLE) { if (g_Vars.antiplayernum < 0 -#ifdef PLATFORM_N64 - || g_Vars.currentplayer != g_Vars.anti -#else - || g_Vars.currentplayer == g_Vars.bond -#endif + || PLAYER_IS_NOT_ANTI(g_Vars.currentplayer) || (chr->hidden & CHRHFLAG_ANTINONINTERACTABLE) == 0) { canpush = true; } @@ -813,11 +809,7 @@ void bwalkUpdateVertical(void) // Maybe reset counter-op's radius - not sure why // Maybe it gets set to 0 when they die? if (g_Vars.antiplayernum >= 0 -#ifdef PLATFORM_N64 - && g_Vars.currentplayer == g_Vars.anti -#else - && g_Vars.currentplayer != g_Vars.bond -#endif + && PLAYER_IS_ANTI(g_Vars.currentplayer) && g_Vars.currentplayer->bond2.radius != 30 && cdTestVolume(&g_Vars.currentplayer->prop->pos, 30, g_Vars.currentplayer->prop->rooms, CDTYPE_ALL, CHECKVERTICAL_YES, ymax - g_Vars.currentplayer->prop->pos.y, ymin - g_Vars.currentplayer->prop->pos.y)) { g_Vars.currentplayer->prop->chr->radius = 30; diff --git a/src/game/chr.c b/src/game/chr.c index 08817916a..8d6e20f9e 100644 --- a/src/game/chr.c +++ b/src/game/chr.c @@ -4662,7 +4662,7 @@ void chrHit(struct shotdata *shotdata, struct hit *hit) hit->model, hit->hitthing.unk28 / 2, sp90); if (g_Vars.antiplayernum >= 0 - && g_Vars.currentplayer == g_Vars.anti + && PLAYER_IS_ANTI(g_Vars.currentplayer) && (chr->hidden & CHRHFLAG_ANTINONINTERACTABLE)) { return; } diff --git a/src/game/chraction.c b/src/game/chraction.c index bf35338b5..2b5bc41cb 100644 --- a/src/game/chraction.c +++ b/src/game/chraction.c @@ -4351,14 +4351,7 @@ void chrDamage(struct chrdata *chr, f32 damage, struct coord *vector, struct gse // Don't damage if attacker was anti and chr is non-interactable by anti if (g_Vars.antiplayernum >= 0 && aprop - -#ifdef PLATFORM_N64 - && aprop == g_Vars.anti->prop -#else - && aprop->type == PROPTYPE_PLAYER - && aprop != g_Vars.bond->prop -#endif - + && PROP_IS_FOR_ANTI_PLAYER(aprop) && (chr->hidden & CHRHFLAG_ANTINONINTERACTABLE)) { return; } @@ -4498,13 +4491,7 @@ void chrDamage(struct chrdata *chr, f32 damage, struct coord *vector, struct gse // Anti shooting other enemies is lethal if (aprop -#ifdef PLATFORM_N64 - && aprop == g_Vars.anti->prop -#else - && aprop->type == PROPTYPE_PLAYER - && aprop != g_Vars.bond->prop -#endif - + && PROP_IS_FOR_ANTI_PLAYER(aprop) && vprop != g_Vars.bond->prop) { damage *= 100; } @@ -8336,14 +8323,7 @@ void chrAlertOthersOfInjury(struct chrdata *chr, bool dying) s32 numinrange = 0; s32 numchrs = chrsGetNumSlots(); - if (g_Vars.antiplayernum >= 0 -#ifdef PLATFORM_N64 - && chr->prop == g_Vars.anti->prop -#else - && chr->prop->type == PROPTYPE_PLAYER - && chr->prop != g_Vars.bond->prop -#endif - ) { + if (g_Vars.antiplayernum >= 0 && PROP_IS_FOR_ANTI_PLAYER(chr->prop)) { return; } @@ -13578,11 +13558,7 @@ void chraTickBg(void) if (targetprop && (targetprop->type == PROPTYPE_CHR || targetprop->type == PROPTYPE_PLAYER)) { if ((targetprop->type == PROPTYPE_PLAYER -#ifdef PLATFORM_N64 - && !(g_Vars.antiplayernum >= 0 && g_Vars.anti && g_Vars.anti->prop == targetprop) -#else - && !(g_Vars.antiplayernum >= 0 && g_Vars.bond && g_Vars.bond->prop != targetprop) -#endif + && !(g_Vars.antiplayernum >= 0 && g_Vars.anti && PROP_IS_FOR_ANTI_PLAYER(targetprop)) && chrCompareTeams(chr, targetprop->chr, COMPARE_ENEMIES)) || CHRRACE(targetprop->chr) == RACE_EYESPY) { s32 time60; diff --git a/src/game/explosions.c b/src/game/explosions.c index c7516f216..72d1877b5 100644 --- a/src/game/explosions.c +++ b/src/game/explosions.c @@ -847,7 +847,7 @@ void explosionInflictDamage(struct prop *expprop) minfrac = (minfrac * 0.7f + 0.3f) * type->damage; if (g_Vars.antiplayernum >= 0 - && g_Vars.antiplayernum == exp->owner + && EXPLOSION_OWNER_IS_ANTI_PLAYER(exp->owner) && (obj->flags2 & OBJFLAG2_IMMUNETOANTI)) { // anti cannot damage this obj } else if (isfirstframe) { @@ -997,8 +997,16 @@ void explosionInflictDamage(struct prop *expprop) ownerprop = g_Vars.bond->prop; } else if (g_Vars.coopplayernum >= 0 && exp->owner == g_Vars.coopplayernum) { ownerprop = g_Vars.coop->prop; - } else if (g_Vars.antiplayernum >= 0 && exp->owner == g_Vars.antiplayernum) { + } else if (g_Vars.antiplayernum >= 0 && EXPLOSION_OWNER_IS_ANTI_PLAYER(exp->owner)) { +#if PLATFORM_N64 ownerprop = g_Vars.anti->prop; +#else + struct chrdata *ownerchr = mpGetChrFromPlayerIndex(exp->owner); + + if (ownerchr) { + ownerprop = ownerchr->prop; + } +#endif } chrDamageByExplosion(chr, minfrac, &spa0, ownerprop, &expprop->pos); @@ -1212,8 +1220,12 @@ u32 explosionTick(struct prop *prop) if (g_Vars.normmplayerisrunning) { chr = mpGetChrFromPlayerIndex(exp->owner); - } else if (g_Vars.antiplayernum >= 0 && exp->owner == g_Vars.antiplayernum) { + } else if (g_Vars.antiplayernum >= 0 && EXPLOSION_OWNER_IS_ANTI_PLAYER(exp->owner)) { +#if PLATFORM_N64 chr = g_Vars.anti->prop->chr; +#else + chr = mpGetChrFromPlayerIndex(exp->owner); +#endif } else if (g_Vars.coopplayernum >= 0 && exp->owner == g_Vars.coopplayernum) { chr = g_Vars.coop->prop->chr; } else { diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index 6b27e3ad3..5cfb64141 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -1338,11 +1338,7 @@ Gfx *mpRenderModalText(Gfx *gdl) && g_Vars.currentplayer->redbloodfinished && g_Vars.currentplayer->deathanimfinished && !(g_Vars.coopplayernum >= 0 && ((g_Vars.bond->isdead && g_Vars.coop->isdead) || !g_Vars.currentplayer->coopcanrestart || g_InCutscene)) -#ifdef PLATFORM_N64 - && !(g_Vars.antiplayernum >= 0 && ((g_Vars.currentplayer != g_Vars.anti || g_InCutscene))) -#else - && !(g_Vars.antiplayernum >= 0 && ((g_Vars.currentplayer == g_Vars.bond || g_InCutscene))) -#endif + && !(g_Vars.antiplayernum >= 0 && ((PLAYER_IS_NOT_ANTI(g_Vars.currentplayer) || g_InCutscene))) && g_NumReasonsToEndMpMatch == 0) { // Render "Press START" text gdl = text0f153628(gdl); diff --git a/src/game/player.c b/src/game/player.c index 3366d0f69..87346e8b9 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -606,13 +606,7 @@ void playerStartNewLife(void) if (cmd); if (cmd); - if (g_Vars.antiplayernum < 0 -#ifdef PLATFORM_N64 - || g_Vars.currentplayer != g_Vars.anti -#else - || g_Vars.currentplayer == g_Vars.bond -#endif - ) { + if (g_Vars.antiplayernum < 0 || PLAYER_IS_NOT_ANTI(g_Vars.currentplayer)) { while (cmd[0] != INTROCMD_END) { switch (cmd[0]) { case INTROCMD_SPAWN: @@ -986,13 +980,7 @@ void playerSpawn(void) } if (g_Vars.mplayerisrunning) { - if (g_Vars.antiplayernum >= 0 -#ifdef PLATFORM_N64 - && g_Vars.currentplayer == g_Vars.anti -#else - && g_Vars.currentplayer != g_Vars.bond -#endif - ) { + if (g_Vars.antiplayernum >= 0 && PLAYER_IS_ANTI(g_Vars.currentplayer)) { numsqdists = 0; force = false; @@ -1194,11 +1182,7 @@ void playerChooseBodyAndHead(s32 *bodynum, s32 *headnum, s32 *arg2) bool solo; if (g_Vars.antiplayernum >= 0 -#ifdef PLATFORM_N64 - && g_Vars.currentplayer == g_Vars.anti -#else - && g_Vars.currentplayer != g_Vars.bond -#endif + && PLAYER_IS_ANTI(g_Vars.currentplayer) && g_Vars.antiheadnum >= 0 && g_Vars.antibodynum >= 0) { *headnum = g_Vars.antiheadnum; @@ -1530,11 +1514,7 @@ void playerTickChrBody(void) #if VERSION >= VERSION_NTSC_1_0 if (g_Vars.antiplayernum >= 0 -#ifdef PLATFORM_N64 - && g_Vars.currentplayer == g_Vars.anti -#else - && g_Vars.currentplayer != g_Vars.bond -#endif + && PLAYER_IS_ANTI(g_Vars.currentplayer) && g_Vars.currentplayer->vv_eyeheight > 159) { g_Vars.currentplayer->vv_eyeheight = 159; } @@ -4733,13 +4713,7 @@ Gfx *playerRenderHud(Gfx *gdl) chr->chrflags |= CHRCFLAG_HIDDEN; } - if (g_Vars.antiplayernum >= 0 -#ifdef PLATFORM_N64 - && g_Vars.currentplayer == g_Vars.anti -#else - && g_Vars.currentplayer != g_Vars.bond -#endif - ) { + if (g_Vars.antiplayernum >= 0 && PLAYER_IS_ANTI(g_Vars.currentplayer)) { // Anti #ifndef PLATFORM_N64 if (g_NetMode == NETMODE_SERVER && g_Vars.currentplayer->isremote) { diff --git a/src/game/playerreset.c b/src/game/playerreset.c index 1091ea2a6..6e3e79aa2 100644 --- a/src/game/playerreset.c +++ b/src/game/playerreset.c @@ -186,13 +186,7 @@ void playerReset(void) cmd = (struct cmd32 *)((uintptr_t)cmd + 8); break; case INTROCMD_WEAPON: - if (cmd->param3 == 0 -#ifdef PLATFORM_N64 - && g_Vars.currentplayer != g_Vars.anti -#else - && (g_Vars.antiplayernum < 0 || g_Vars.currentplayer == g_Vars.bond) -#endif - ) { + if (cmd->param3 == 0 && PLAYER_IS_NOT_ANTI(g_Vars.currentplayer)) { modelmgrLoadProjectileModeldefs(cmd->param1); @@ -220,13 +214,7 @@ void playerReset(void) cmd = (struct cmd32 *)((uintptr_t)cmd + 16); break; case INTROCMD_AMMO: - if (cmd->param3 == 0 -#ifdef PLATFORM_N64 - && g_Vars.currentplayer != g_Vars.anti -#else - && (g_Vars.antiplayernum < 0 || g_Vars.currentplayer == g_Vars.bond) -#endif - ) { + if (cmd->param3 == 0 && PLAYER_IS_NOT_ANTI(g_Vars.currentplayer)) { bgunSetAmmoQuantity(cmd->param1, cmd->param2); } cmd = (struct cmd32 *)((uintptr_t)cmd + 16); diff --git a/src/game/prop.c b/src/game/prop.c index 7db390774..ff1e73e7a 100644 --- a/src/game/prop.c +++ b/src/game/prop.c @@ -2358,11 +2358,7 @@ void propsTestForPickup(void) if (g_Vars.currentplayer->bondmovemode != MOVEMODE_CUTSCENE && !g_PlayerInvincible -#ifdef PLATFORM_N64 - && g_Vars.currentplayer != g_Vars.anti -#else - && (g_Vars.antiplayernum < 0 || g_Vars.currentplayer == g_Vars.bond) -#endif + && PLAYER_IS_NOT_ANTI(g_Vars.currentplayer) ) { roomsCopy(g_Vars.currentplayer->prop->rooms, allrooms); diff --git a/src/game/propobj.c b/src/game/propobj.c index aa3ed5893..dbc2540c4 100644 --- a/src/game/propobj.c +++ b/src/game/propobj.c @@ -9148,7 +9148,7 @@ void autogunTickShoot(struct prop *autogunprop) // (ie. autogun is a Defense autogun or a thrown laptop) if (g_Vars.normmplayerisrunning || (targetprop && (targetprop->type == PROPTYPE_CHR)) - || (g_Vars.antiplayernum >= 0 && targetprop && targetprop == g_Vars.anti->prop)) { + || (g_Vars.antiplayernum >= 0 && targetprop && PROP_IS_FOR_ANTI_PLAYER(targetprop))) { if (cdExamLos08(&gunpos, gunrooms, &hitpos, CDTYPE_ALL, GEOFLAG_BLOCK_SHOOT) == CDRESULT_COLLISION) { #if VERSION >= VERSION_PAL_FINAL cdGetPos(&hitpos, 11480, "prop/propobj.c"); @@ -15943,11 +15943,7 @@ void objHit(struct shotdata *shotdata, struct hit *hit) } if (g_Vars.antiplayernum < 0 -#ifdef PLATFORM_N64 - || g_Vars.currentplayer != g_Vars.anti -#else - || g_Vars.currentplayer == g_Vars.bond -#endif + || PLAYER_IS_NOT_ANTI(g_Vars.currentplayer) || (obj->flags2 & OBJFLAG2_IMMUNETOANTI) == 0) { if (hit->hitthing.texturenum != 10000) { diff --git a/src/include/constants.h b/src/include/constants.h index 571f09be8..3440de7c6 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -82,6 +82,18 @@ #define PLAYERCOUNT() 1 #endif +#if MAX_LOCAL_PLAYERS > 2 +#define PLAYER_IS_ANTI(plr) (g_Vars.antiplayernum >= 0 && (plr) != g_Vars.bond) +#define PLAYER_IS_NOT_ANTI(plr) (g_Vars.antiplayernum < 0 || (plr) == g_Vars.bond) +#define PROP_IS_FOR_ANTI_PLAYER(plrprop) (g_Vars.antiplayernum >= 0 && g_Vars.anti && (plrprop)->type == PROPTYPE_PLAYER && (plrprop) != g_Vars.bond->prop) +#define EXPLOSION_OWNER_IS_ANTI_PLAYER(expowner) (g_Vars.antiplayernum >= 0 && (expowner) != g_Vars.bondplayernum && mpGetChrFromPlayerIndex((expowner)) != NULL) +#else +#define PLAYER_IS_ANTI(plr) ((plr) == g_Vars.anti) +#define PLAYER_IS_NOT_ANTI(plr) ((plr) != g_Vars.anti) +#define PROP_IS_FOR_ANTI_PLAYER(plrprop) ((plrprop) == g_Vars.anti->prop) +#define EXPLOSION_OWNER_IS_ANTI_PLAYER(expowner) ((expowner) == g_Vars.antiplayernum) +#endif + #define VALIDWEAPON() (g_Vars.currentplayer->gunctrl.weaponnum >= WEAPON_UNARMED && g_Vars.currentplayer->gunctrl.weaponnum <= WEAPON_COMBATBOOST) #define FUNCISSEC() (VALIDWEAPON() && (g_PlayerConfigsArray[g_Vars.currentplayerstats->mpindex].gunfuncs[(g_Vars.currentplayer->gunctrl.weaponnum - 1) >> 3] & (1 << ((g_Vars.currentplayer->gunctrl.weaponnum - 1) & 7)))) From 94e244a2360a9f259f3a73c70b39f8b84f2be89d Mon Sep 17 00:00:00 2001 From: GreaseMonkey Date: Fri, 9 Feb 2024 16:15:09 +1300 Subject: [PATCH 5/8] Report correct ending success/failure when any counter-op player aborts --- src/game/endscreen.c | 33 +++++++++++++++++++++++++++------ src/include/constants.h | 8 +++++++- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/game/endscreen.c b/src/game/endscreen.c index d9aa5ae84..6c18cf072 100644 --- a/src/game/endscreen.c +++ b/src/game/endscreen.c @@ -292,6 +292,27 @@ char *endscreenMenuTextAccuracy(struct menuitem *item) return g_StringPointer; } +#if MAX_COOPCHRS > 2 +static bool enscreenAnyAntiAborted(void) { + size_t num; + + if (g_Vars.antiplayernum < 0) { + return false; + } + + for (num = 0; num < PLAYERCOUNT(); num++) { + if (PLAYERNUM_IS_ANTI(num) && g_Vars.players[num] && g_Vars.players[num]->aborted) { + return true; + } + } + + return false; +} +#define ANTI_ABORTED() (enscreenAnyAntiAborted()) +#else +#define ANTI_ABORTED() (g_Vars.anti->aborted) +#endif + char *endscreenMenuTextMissionStatus(struct menuitem *item) { if (g_CheatsActiveBank0 || g_CheatsActiveBank1) { @@ -312,7 +333,7 @@ char *endscreenMenuTextMissionStatus(struct menuitem *item) return langGet(L_OPTIONS_295); // "Aborted" } - if (g_Vars.anti->aborted) { + if (ANTI_ABORTED()) { return langGet(L_OPTIONS_295); // "Aborted" } @@ -320,7 +341,7 @@ char *endscreenMenuTextMissionStatus(struct menuitem *item) return langGet(L_OPTIONS_293); // "Failed" } } else { - if (g_Vars.anti->aborted) { + if (ANTI_ABORTED()) { return langGet(L_OPTIONS_295); // "Aborted" } @@ -1795,9 +1816,9 @@ void endscreenPushAnti(void) if (g_Vars.currentplayer == g_Vars.bond) { #if VERSION >= VERSION_NTSC_1_0 && defined(DEBUG) - if (!g_Vars.anti->aborted && (g_Vars.bond->isdead || g_Vars.bond->aborted || !objectiveIsAllComplete()) && !debugIsSetCompleteEnabled()) + if (!ANTI_ABORTED() && (g_Vars.bond->isdead || g_Vars.bond->aborted || !objectiveIsAllComplete()) && !debugIsSetCompleteEnabled()) #else - if (!g_Vars.anti->aborted && (g_Vars.bond->isdead || g_Vars.bond->aborted || !objectiveIsAllComplete())) + if (!ANTI_ABORTED() && (g_Vars.bond->isdead || g_Vars.bond->aborted || !objectiveIsAllComplete())) #endif { // Bond - failed or aborted @@ -1826,9 +1847,9 @@ void endscreenPushAnti(void) filemgrSaveOrLoad(&g_GameFileGuid, FILEOP_SAVE_GAME_000, 0); } else { #if VERSION >= VERSION_NTSC_1_0 && defined(DEBUG) - if (!g_Vars.anti->aborted && (g_Vars.bond->isdead || g_Vars.bond->aborted || !objectiveIsAllComplete()) && !debugIsSetCompleteEnabled()) + if (!ANTI_ABORTED() && (g_Vars.bond->isdead || g_Vars.bond->aborted || !objectiveIsAllComplete()) && !debugIsSetCompleteEnabled()) #else - if (!g_Vars.anti->aborted && (g_Vars.bond->isdead || g_Vars.bond->aborted || !objectiveIsAllComplete())) + if (!ANTI_ABORTED() && (g_Vars.bond->isdead || g_Vars.bond->aborted || !objectiveIsAllComplete())) #endif { // Anti - completed diff --git a/src/include/constants.h b/src/include/constants.h index 3440de7c6..69880383d 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -20,7 +20,11 @@ #define MAX_CHRWAYPOINTS 6 #define MAX_EXPLOSIONS 6 #define MAX_EYESPYDARTS 8 +#ifdef PLATFORM_N64 +#define MAX_COOPCHRS 2 +#else #define MAX_COOPCHRS MAX_PLAYERS +#endif #define MAX_MPCHRS (MAX_PLAYERS + MAX_BOTS) #define MAX_MPPLAYERCONFIGS (MAX_PLAYERS + MAX_COOPCHRS) #define MAX_OBJECTIVES 10 @@ -82,14 +86,16 @@ #define PLAYERCOUNT() 1 #endif -#if MAX_LOCAL_PLAYERS > 2 +#if MAX_COOPCHRS > 2 #define PLAYER_IS_ANTI(plr) (g_Vars.antiplayernum >= 0 && (plr) != g_Vars.bond) #define PLAYER_IS_NOT_ANTI(plr) (g_Vars.antiplayernum < 0 || (plr) == g_Vars.bond) +#define PLAYERNUM_IS_ANTI(plrnum) (g_Vars.antiplayernum >= 0 && (plrnum) != g_Vars.bondplayernum) #define PROP_IS_FOR_ANTI_PLAYER(plrprop) (g_Vars.antiplayernum >= 0 && g_Vars.anti && (plrprop)->type == PROPTYPE_PLAYER && (plrprop) != g_Vars.bond->prop) #define EXPLOSION_OWNER_IS_ANTI_PLAYER(expowner) (g_Vars.antiplayernum >= 0 && (expowner) != g_Vars.bondplayernum && mpGetChrFromPlayerIndex((expowner)) != NULL) #else #define PLAYER_IS_ANTI(plr) ((plr) == g_Vars.anti) #define PLAYER_IS_NOT_ANTI(plr) ((plr) != g_Vars.anti) +//define PLAYERNUM_IS_ANTI(plrnum) ((plrnum) == g_Vars.antiplayernum) #define PROP_IS_FOR_ANTI_PLAYER(plrprop) ((plrprop) == g_Vars.anti->prop) #define EXPLOSION_OWNER_IS_ANTI_PLAYER(expowner) ((expowner) == g_Vars.antiplayernum) #endif From f4530633d6364bb2953a94303bee8c09dfab5b8c Mon Sep 17 00:00:00 2001 From: GreaseMonkey Date: Sat, 10 Feb 2024 11:20:01 +1300 Subject: [PATCH 6/8] Replace "Counter-Operative" player selection with "Main Player" + extend to 4 entries --- src/game/mainmenu.c | 94 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/src/game/mainmenu.c b/src/game/mainmenu.c index 0b65599b5..bef20db6d 100644 --- a/src/game/mainmenu.c +++ b/src/game/mainmenu.c @@ -748,6 +748,7 @@ MenuItemHandlerResult menuhandlerAcceptMission(s32 operation, struct menuitem *i setNumPlayers(1); } } else if (g_MissionConfig.isanti) { +#ifdef PLATFORM_N64 if (g_Vars.pendingantiplayernum == 1) { g_Vars.bondplayernum = 0; g_Vars.antiplayernum = 1; @@ -757,9 +758,13 @@ MenuItemHandlerResult menuhandlerAcceptMission(s32 operation, struct menuitem *i } g_Vars.coopplayernum = -1; -#ifdef PLATFORM_N64 + setNumPlayers(2); #else + g_Vars.bondplayernum = g_Vars.pendingantiplayernum ^ 1; + g_Vars.antiplayernum = (g_Vars.bondplayernum == 0 ? 1 : 0); + g_Vars.coopplayernum = -1; + if ((joyGetConnectedControllers() & 0xF) == 0xF) { setNumPlayers(4); g_MpSetup.chrslots = 0xF; @@ -1558,6 +1563,8 @@ MenuItemHandlerResult menuhandlerAntiRadar(s32 operation, struct menuitem *item, return 0; } +#ifdef PLATFORM_N64 + MenuItemHandlerResult menuhandlerAntiPlayer(s32 operation, struct menuitem *item, union handlerdata *data) { const u16 labels[] = {L_OPTIONS_271, L_OPTIONS_272}; @@ -1624,6 +1631,91 @@ struct menuitem g_AntiOptionsMenuItems[] = { { MENUITEMTYPE_END }, }; +#else + +MenuItemHandlerResult menuhandlerAntiMainPlayer(s32 operation, struct menuitem *item, union handlerdata *data) +{ + static const char *labels[] = { + "Player 1", + "Player 2", + "Player 3", + "Player 4", + }; + + // For consistency with the main save format, + // the main player is (pendingantiplayernum XOR 1) + + switch (operation) { + case MENUOP_GETOPTIONCOUNT: + if ((joyGetConnectedControllers() & 0xF) == 0xF) { + data->dropdown.value = 4; + } else if ((joyGetConnectedControllers() & 0x7) == 0x7) { + data->dropdown.value = 3; + } else { + data->dropdown.value = 2; + } + break; + case MENUOP_GETOPTIONTEXT: + return (s32) (labels[data->dropdown.value]); + case MENUOP_SET: + g_Vars.pendingantiplayernum = data->dropdown.value ^ 1; + g_Vars.modifiedfiles |= MODFILE_GAME; + break; + case MENUOP_GETSELECTEDINDEX: + data->dropdown.value = g_Vars.pendingantiplayernum ^ 1; + break; + } + + return 0; +} + +struct menuitem g_AntiOptionsMenuItems[] = { + { + MENUITEMTYPE_CHECKBOX, + 0, + 0, + L_OPTIONS_267, // "Radar On" + 0, + menuhandlerAntiRadar, + }, + { + MENUITEMTYPE_DROPDOWN, + 0, + MENUITEMFLAG_LITERAL_TEXT, + (uintptr_t)"Main Player", + 0, + menuhandlerAntiMainPlayer, + }, + { + MENUITEMTYPE_SEPARATOR, + 0, + 0, + 0, + 0, + NULL, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + 0, + L_OPTIONS_269, // "Continue" + 0, + menuhandlerBuddyOptionsContinue, + }, + { + MENUITEMTYPE_SELECTABLE, + 0, + MENUITEMFLAG_SELECTABLE_CLOSESDIALOG, + L_OPTIONS_270, // "Cancel" + 0, + NULL, + }, + { MENUITEMTYPE_END }, +}; + +#endif + + struct menudialogdef g_AntiOptionsMenuDialog = { MENUDIALOGTYPE_DEFAULT, L_OPTIONS_266, // "Counter-Operative Options" From 63a9a2690882d6b1ea208f94480e3db0007c380d Mon Sep 17 00:00:00 2001 From: GreaseMonkey Date: Sat, 10 Feb 2024 14:17:22 +1300 Subject: [PATCH 7/8] `set_chr_team(CHR_ANTI, *)` now sets all counter-op player teams The easiest-to-test effect of this is that counter-op players will no longer attempt to auto-aim against one another (p2 vs any other). Maps where this was necessary: Villa, Chicago, Rescue, Air Base, Air Force One (Other maps stick to the default of `TEAM_ENEMY`, and so this doesn't really affect it.) --- src/game/chraicommands.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/game/chraicommands.c b/src/game/chraicommands.c index 9a5517348..85f89f83c 100644 --- a/src/game/chraicommands.c +++ b/src/game/chraicommands.c @@ -52,6 +52,10 @@ #include "data.h" #include "types.h" +#ifndef PLATFORM_N64 +#include "game/mplayer/mplayer.h" +#endif + /** * @cmd 0000 */ @@ -6035,11 +6039,35 @@ bool aiSetChrPresetToChrNearPad(void) bool aiChrSetTeam(void) { u8 *cmd = g_Vars.ailist + g_Vars.aioffset; +#ifdef PLATFORM_N64 struct chrdata *chr = chrFindById(g_Vars.chrdata, cmd[2]); if (chr) { chr->team = cmd[3]; } +#else + u32 playernum; + if (cmd[2] == CHR_ANTI && g_Vars.antiplayernum >= 0) { + // There can be multiple counter-op players, so set this for all. + for (playernum = 0; playernum < PLAYERCOUNT(); playernum++) { + struct player *player = g_Vars.players[playernum]; + + if (player && PLAYER_IS_ANTI(player)) { + struct chrdata *chr = player->prop->chr; + + if (chr) { + chr->team = cmd[3]; + } + } + } + } else { + struct chrdata *chr = chrFindById(g_Vars.chrdata, cmd[2]); + + if (chr) { + chr->team = cmd[3]; + } + } +#endif g_Vars.aioffset += 4; From adcf56ed343276cad385ad5e59425923fbba576b Mon Sep 17 00:00:00 2001 From: GreaseMonkey Date: Sun, 11 Feb 2024 13:25:35 +1300 Subject: [PATCH 8/8] Handle cases for Counter-Op where certain local players can be missing; Don't support 2.x controls for Counter-Op I'm not sure if this happens in practice in the port itself, but with a bit of temporary hackery one can force certain players to be absent. This handles that case cleanly. --- port/src/pdmain.c | 4 ++- src/game/mainmenu.c | 50 +++++++++++++++++++++++--------------- src/game/mplayer/mplayer.c | 5 +++- 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/port/src/pdmain.c b/port/src/pdmain.c index f361310c8..fbab2b0fb 100644 --- a/port/src/pdmain.c +++ b/port/src/pdmain.c @@ -468,7 +468,9 @@ void mainLoop(void) #ifdef PLATFORM_N64 g_MpSetup.chrslots = 0x03; #else - if (getNumPlayers() <= 2) { + if (g_Vars.antiplayernum < 0) { + // Counter-Operative now uses a different approach which allows more than 2 players. + // Co-Operative, on the other hand, is currently limited to 2 players. g_MpSetup.chrslots = 0x03; } #endif diff --git a/src/game/mainmenu.c b/src/game/mainmenu.c index bef20db6d..be898224d 100644 --- a/src/game/mainmenu.c +++ b/src/game/mainmenu.c @@ -761,20 +761,23 @@ MenuItemHandlerResult menuhandlerAcceptMission(s32 operation, struct menuitem *i setNumPlayers(2); #else + u32 contmask = joyGetConnectedControllers(); + u32 plrcount = 0; + u32 tmpmask; + + // Count all players + tmpmask = contmask; + while (tmpmask) { + plrcount += (tmpmask & 0x1); + tmpmask >>= 1; + } + g_Vars.bondplayernum = g_Vars.pendingantiplayernum ^ 1; g_Vars.antiplayernum = (g_Vars.bondplayernum == 0 ? 1 : 0); g_Vars.coopplayernum = -1; - if ((joyGetConnectedControllers() & 0xF) == 0xF) { - setNumPlayers(4); - g_MpSetup.chrslots = 0xF; - } else if ((joyGetConnectedControllers() & 0x7) == 0x7) { - setNumPlayers(3); - g_MpSetup.chrslots = 0x7; - } else { - setNumPlayers(2); - g_MpSetup.chrslots = 0x3; - } + setNumPlayers(plrcount); + g_MpSetup.chrslots = contmask; #endif } else { // Solo @@ -1645,18 +1648,27 @@ MenuItemHandlerResult menuhandlerAntiMainPlayer(s32 operation, struct menuitem * // For consistency with the main save format, // the main player is (pendingantiplayernum XOR 1) + // g_Vars.(bond|anti|coop)playernum is based on the n'th player slot, not the n'th controller; + // that is, it's not based on g_MpSetup.chrslots. + // Because of this, it's easier just to change the label. + + u32 tmpmask = joyGetConnectedControllers(); + u32 plrcount = 0; + u32 labelidx = 0; + while (tmpmask) { + plrcount += (tmpmask & 0x1); + if (plrcount <= data->dropdown.value) { + labelidx += 1; + } + tmpmask >>= 1; + } + switch (operation) { case MENUOP_GETOPTIONCOUNT: - if ((joyGetConnectedControllers() & 0xF) == 0xF) { - data->dropdown.value = 4; - } else if ((joyGetConnectedControllers() & 0x7) == 0x7) { - data->dropdown.value = 3; - } else { - data->dropdown.value = 2; - } + data->dropdown.value = plrcount; break; case MENUOP_GETOPTIONTEXT: - return (s32) (labels[data->dropdown.value]); + return (s32) (labels[labelidx]); case MENUOP_SET: g_Vars.pendingantiplayernum = data->dropdown.value ^ 1; g_Vars.modifiedfiles |= MODFILE_GAME; @@ -4959,7 +4971,7 @@ MenuItemHandlerResult menuhandlerMainMenuCooperative(s32 operation, struct menui MenuItemHandlerResult menuhandlerMainMenuCounterOperative(s32 operation, struct menuitem *item, union handlerdata *data) { if (operation == MENUOP_CHECKDISABLED) { - if ((joyGetConnectedControllers() & 2) == 0) { + if ((joyGetConnectedControllers() & ~0x1) == 0) { return true; } } diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index 5cfb64141..9336747fb 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -259,7 +259,10 @@ void mpReset(void) #ifdef PLATFORM_N64 if (g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0) #else - if ((g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0) && getNumPlayers() <= 2) + // Counter-Operative now uses a different approach which allows more than 2 players. + // Co-Operative, on the other hand, is currently limited to 2 players. + // Due to the change in approach, it's better to ignore 2.x controls for Counter-Operative. + if (g_Vars.coopplayernum >= 0) #endif { struct mpplayerconfig tmp;