diff --git a/port/src/pdmain.c b/port/src/pdmain.c index b08870abd..fbab2b0fb 100644 --- a/port/src/pdmain.c +++ b/port/src/pdmain.c @@ -465,7 +465,15 @@ void mainLoop(void) } if (g_Vars.coopplayernum >= 0 || g_Vars.antiplayernum >= 0) { +#ifdef PLATFORM_N64 g_MpSetup.chrslots = 0x03; +#else + 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 mpReset(); } else if (g_Vars.perfectbuddynum) { mpReset(); diff --git a/src/game/bondwalk.c b/src/game/bondwalk.c index 8bd907e2e..1d0853c06 100644 --- a/src/game/bondwalk.c +++ b/src/game/bondwalk.c @@ -475,7 +475,7 @@ bool bwalkCalculateNewPositionWithPush(struct coord *delta, f32 rotateamount, bo } } else if (chr->chrflags & CHRCFLAG_PUSHABLE) { if (g_Vars.antiplayernum < 0 - || g_Vars.currentplayer != g_Vars.anti + || PLAYER_IS_NOT_ANTI(g_Vars.currentplayer) || (chr->hidden & CHRHFLAG_ANTINONINTERACTABLE) == 0) { canpush = true; } @@ -809,7 +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 - && g_Vars.currentplayer == g_Vars.anti + && 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 4bf5ccb64..2b5bc41cb 100644 --- a/src/game/chraction.c +++ b/src/game/chraction.c @@ -4351,7 +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 - && aprop == g_Vars.anti->prop + && PROP_IS_FOR_ANTI_PLAYER(aprop) && (chr->hidden & CHRHFLAG_ANTINONINTERACTABLE)) { return; } @@ -4490,7 +4490,9 @@ 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 + && PROP_IS_FOR_ANTI_PLAYER(aprop) + && vprop != g_Vars.bond->prop) { damage *= 100; } } else { @@ -8321,7 +8323,7 @@ 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 && PROP_IS_FOR_ANTI_PLAYER(chr->prop)) { return; } @@ -13556,7 +13558,7 @@ void chraTickBg(void) if (targetprop && (targetprop->type == PROPTYPE_CHR || targetprop->type == PROPTYPE_PLAYER)) { if ((targetprop->type == PROPTYPE_PLAYER - && !(g_Vars.antiplayernum >= 0 && g_Vars.anti && g_Vars.anti->prop == targetprop) + && !(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/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; diff --git a/src/game/endscreen.c b/src/game/endscreen.c index add6d4949..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" } @@ -1700,14 +1721,22 @@ void endscreenPushCoop(void) #endif { // Failed or aborted - if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) { + if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL +#ifndef PLATFORM_N64 + || LOCALPLAYERCOUNT() >= 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 + || LOCALPLAYERCOUNT() >= 3 +#endif + ) { menuPushRootDialog(&g_2PMissionEndscreenCompletedVMenuDialog, MENUROOT_MPENDSCREEN); } else { menuPushRootDialog(&g_2PMissionEndscreenCompletedHMenuDialog, MENUROOT_MPENDSCREEN); @@ -1787,20 +1816,28 @@ 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 - if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) { + if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL +#ifndef PLATFORM_N64 + || LOCALPLAYERCOUNT() >= 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 + || LOCALPLAYERCOUNT() >= 3 +#endif + ) { menuPushRootDialog(&g_2PMissionEndscreenCompletedVMenuDialog, MENUROOT_MPENDSCREEN); } else { menuPushRootDialog(&g_2PMissionEndscreenCompletedHMenuDialog, MENUROOT_MPENDSCREEN); @@ -1810,20 +1847,28 @@ 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 - if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL) { + if (optionsGetScreenSplit() == SCREENSPLIT_VERTICAL +#ifndef PLATFORM_N64 + || LOCALPLAYERCOUNT() >= 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 + || LOCALPLAYERCOUNT() >= 3 +#endif + ) { menuPushRootDialog(&g_2PMissionEndscreenFailedVMenuDialog, MENUROOT_MPENDSCREEN); } else { menuPushRootDialog(&g_2PMissionEndscreenFailedHMenuDialog, MENUROOT_MPENDSCREEN); 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/mainmenu.c b/src/game/mainmenu.c index 1e84f2daf..be898224d 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,7 +758,27 @@ MenuItemHandlerResult menuhandlerAcceptMission(s32 operation, struct menuitem *i } g_Vars.coopplayernum = -1; + 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; + + setNumPlayers(plrcount); + g_MpSetup.chrslots = contmask; +#endif } else { // Solo g_Vars.bondplayernum = 0; @@ -1545,6 +1566,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}; @@ -1611,6 +1634,100 @@ 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) + + // 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: + data->dropdown.value = plrcount; + break; + case MENUOP_GETOPTIONTEXT: + return (s32) (labels[labelidx]); + 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" @@ -4854,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/ingame.c b/src/game/mplayer/ingame.c index 390860a46..4286739f0 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 + || LOCALPLAYERCOUNT() >= 3 +#endif + ) { menuPushRootDialog(&g_2PMissionPauseVMenuDialog, MENUROOT_MPPAUSE); } else { menuPushRootDialog(&g_2PMissionPauseHMenuDialog, MENUROOT_MPPAUSE); diff --git a/src/game/mplayer/mplayer.c b/src/game/mplayer/mplayer.c index 7f27e0165..9336747fb 100644 --- a/src/game/mplayer/mplayer.c +++ b/src/game/mplayer/mplayer.c @@ -256,7 +256,15 @@ 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 + // 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; tmp = g_PlayerConfigsArray[MAX_PLAYERS]; @@ -1333,7 +1341,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)) - && !(g_Vars.antiplayernum >= 0 && ((g_Vars.currentplayer != g_Vars.anti || g_InCutscene))) + && !(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 6c54d5bde..87346e8b9 100644 --- a/src/game/player.c +++ b/src/game/player.c @@ -606,7 +606,7 @@ void playerStartNewLife(void) if (cmd); if (cmd); - if (g_Vars.antiplayernum < 0 || g_Vars.currentplayer != g_Vars.anti) { + if (g_Vars.antiplayernum < 0 || PLAYER_IS_NOT_ANTI(g_Vars.currentplayer)) { while (cmd[0] != INTROCMD_END) { switch (cmd[0]) { case INTROCMD_SPAWN: @@ -980,7 +980,7 @@ void playerSpawn(void) } if (g_Vars.mplayerisrunning) { - if (g_Vars.antiplayernum >= 0 && g_Vars.currentplayer == g_Vars.anti) { + if (g_Vars.antiplayernum >= 0 && PLAYER_IS_ANTI(g_Vars.currentplayer)) { numsqdists = 0; force = false; @@ -1182,7 +1182,7 @@ void playerChooseBodyAndHead(s32 *bodynum, s32 *headnum, s32 *arg2) bool solo; if (g_Vars.antiplayernum >= 0 - && g_Vars.currentplayer == g_Vars.anti + && PLAYER_IS_ANTI(g_Vars.currentplayer) && g_Vars.antiheadnum >= 0 && g_Vars.antibodynum >= 0) { *headnum = g_Vars.antiheadnum; @@ -1514,7 +1514,7 @@ void playerTickChrBody(void) #if VERSION >= VERSION_NTSC_1_0 if (g_Vars.antiplayernum >= 0 - && g_Vars.currentplayer == g_Vars.anti + && PLAYER_IS_ANTI(g_Vars.currentplayer) && g_Vars.currentplayer->vv_eyeheight > 159) { g_Vars.currentplayer->vv_eyeheight = 159; } @@ -4713,7 +4713,7 @@ Gfx *playerRenderHud(Gfx *gdl) chr->chrflags |= CHRCFLAG_HIDDEN; } - if (g_Vars.antiplayernum >= 0 && g_Vars.currentplayer == g_Vars.anti) { + 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 6e941dd9c..6e3e79aa2 100644 --- a/src/game/playerreset.c +++ b/src/game/playerreset.c @@ -186,7 +186,8 @@ 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 && PLAYER_IS_NOT_ANTI(g_Vars.currentplayer)) { + modelmgrLoadProjectileModeldefs(cmd->param1); if (cmd->param2 >= 0) { @@ -213,7 +214,7 @@ 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 && 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 a8aab35e0..b84f50214 100644 --- a/src/game/prop.c +++ b/src/game/prop.c @@ -2362,7 +2362,8 @@ void propsTestForPickup(void) if (g_Vars.currentplayer->bondmovemode != MOVEMODE_CUTSCENE && !g_PlayerInvincible - && g_Vars.currentplayer != g_Vars.anti) { + && PLAYER_IS_NOT_ANTI(g_Vars.currentplayer) + ) { 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..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"); @@ -15942,7 +15942,10 @@ 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 + || PLAYER_IS_NOT_ANTI(g_Vars.currentplayer) + || (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..69880383d 100644 --- a/src/include/constants.h +++ b/src/include/constants.h @@ -20,8 +20,13 @@ #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 + 2) +#define MAX_MPPLAYERCONFIGS (MAX_PLAYERS + MAX_COOPCHRS) #define MAX_OBJECTIVES 10 #define MAX_LOCAL_PLAYERS 4 #define MAX_PLAYERS 8 @@ -81,6 +86,20 @@ #define PLAYERCOUNT() 1 #endif +#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 + #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))))