//------------------------------------------------------------------------- /* Copyright (C) 2010-2019 EDuke32 developers and contributors Copyright (C) 2019 Nuke.YKT This file is part of NBlood. NBlood is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //------------------------------------------------------------------------- #include "ns.h" // Must come before everything else! #include #include #include "compat.h" #include "build.h" #include "mmulti.h" #include "actor.h" #include "blood.h" #include "callback.h" #include "config.h" #include "controls.h" #include "demo.h" #include "eventq.h" #include "fx.h" #include "gib.h" #include "globals.h" #include "levels.h" #include "loadsave.h" #include "map2d.h" #include "network.h" #include "player.h" #include "seq.h" #include "sfx.h" #include "sound.h" #include "tile.h" #include "triggers.h" #include "trig.h" #include "view.h" #include "warp.h" #include "weapon.h" #include "common_game.h" #include "messages.h" #include "gstrings.h" BEGIN_BLD_NS PROFILE gProfile[kMaxPlayers]; PLAYER gPlayer[kMaxPlayers]; PLAYER *gMe, *gView; bool gBlueFlagDropped = false; bool gRedFlagDropped = false; // V = has effect in game, X = no effect in game POWERUPINFO gPowerUpInfo[kMaxPowerUps] = { { -1, 1, 1, 1 }, // 00: V keys { -1, 1, 1, 1 }, // 01: V keys { -1, 1, 1, 1 }, // 02: V keys { -1, 1, 1, 1 }, // 03: V keys { -1, 1, 1, 1 }, // 04: V keys { -1, 1, 1, 1 }, // 05: V keys { -1, 1, 1, 1 }, // 06: V keys { -1, 0, 100, 100 }, // 07: V doctor's bag { -1, 0, 50, 100 }, // 08: V medicine pouch { -1, 0, 20, 100 }, // 09: V life essense { -1, 0, 100, 200 }, // 10: V life seed { -1, 0, 2, 200 }, // 11: V red potion { 783, 0, 3600, 432000 }, // 12: V feather fall { 896, 0, 3600, 432000 }, // 13: V cloak of invisibility { 825, 1, 3600, 432000 }, // 14: V death mask (invulnerability) { 827, 0, 3600, 432000 }, // 15: V jump boots { 828, 0, 3600, 432000 }, // 16: X raven flight { 829, 0, 3600, 1728000 }, // 17: V guns akimbo { 830, 0, 3600, 432000 }, // 18: V diving suit { 831, 0, 3600, 432000 }, // 19: V gas mask { -1, 0, 3600, 432000 }, // 20: X clone { 2566, 0, 3600, 432000 }, // 21: V crystal ball { 836, 0, 3600, 432000 }, // 22: X decoy { 853, 0, 3600, 432000 }, // 23: V doppleganger { 2428, 0, 3600, 432000 }, // 24: V reflective shots { 839, 0, 3600, 432000 }, // 25: V beast vision { 768, 0, 3600, 432000 }, // 26: X cloak of shadow (useless) { 840, 0, 3600, 432000 }, // 27: X rage shroom { 841, 0, 900, 432000 }, // 28: V delirium shroom { 842, 0, 3600, 432000 }, // 29: V grow shroom (gModernMap only) { 843, 0, 3600, 432000 }, // 30: V shrink shroom (gModernMap only) { -1, 0, 3600, 432000 }, // 31: X death mask (useless) { -1, 0, 3600, 432000 }, // 32: X wine goblet { -1, 0, 3600, 432000 }, // 33: X wine bottle { -1, 0, 3600, 432000 }, // 34: X skull grail { -1, 0, 3600, 432000 }, // 35: X silver grail { -1, 0, 3600, 432000 }, // 36: X tome { -1, 0, 3600, 432000 }, // 37: X black chest { -1, 0, 3600, 432000 }, // 38: X wooden chest { 837, 1, 3600, 432000 }, // 39: V asbestos armor { -1, 0, 1, 432000 }, // 40: V basic armor { -1, 0, 1, 432000 }, // 41: V body armor { -1, 0, 1, 432000 }, // 42: V fire armor { -1, 0, 1, 432000 }, // 43: V spirit armor { -1, 0, 1, 432000 }, // 44: V super armor { 0, 0, 0, 0 }, // 45: ? unknown { 0, 0, 0, 0 }, // 46: ? unknown { 0, 0, 0, 0 }, // 47: ? unknown { 0, 0, 0, 0 }, // 48: ? unknown { 0, 0, 0, 0 }, // 49: X dummy { 833, 1, 1, 1 } // 50: V kModernItemLevelMap (gModernMap only) }; int Handicap[] = { 144, 208, 256, 304, 368 }; /*int gDefaultAccel[] = { // normal human 0x4000, 0x1200, 0x2000, // stand (front, side, back) / swim (front, side, back) / crouch (front, side, back) // normal beast 0x4000, 0x1200, 0x2000, // stand (front, side, back) / swim (front, side, back) / crouch (front, side, back) // shrink human 10384, 2108, 2192, // stand (front, side, back) / swim (front, side, back) / crouch (front, side, back) // grown human 19384, 5608, 11192, // stand (front, side, back) / swim (front, side, back) / crouch (front, side, back) }; int gDefaultJumpZ[] = { // normal human -0xbaaaa, -0x175555, 0x5b05, 0, 0, 0, // stand (normal jump, pwup jump) / swim (normal jump, pwup jump) / crouch (normal jump, pwup jump) // normal beast -0xbaaaa, -0x175555, 0x5b05, 0, 0, 0, // stand (normal jump, pwup jump) / swim (normal jump, pwup jump) / crouch (normal jump, pwup jump) // shrink human -564586, -1329173, 0x5b05, 0, 0, 0, // stand (normal jump, pwup jump) / swim (normal jump, pwup jump) / crouch (normal jump, pwup jump) // grown human -1014586, -1779173, 0x5b05, 0, 0, 0, // stand (normal jump, pwup jump) / swim (normal jump, pwup jump) / crouch (normal jump, pwup jump) };*/ POSTURE gPostureDefaults[kModeMax][kPostureMax] = { // normal human { { 0x4000, 0x4000, 0x4000, 14, 17, 24, 16, 32, 80, 0x1600, 0x1200, 0xc00, 0x90, -0xbaaaa, -0x175555 }, { 0x1200, 0x1200, 0x1200, 14, 17, 24, 16, 32, 80, 0x1400, 0x1000, -0x600, 0xb0, 0x5b05, 0 }, { 0x2000, 0x2000, 0x2000, 22, 28, 24, 16, 16, 40, 0x800, 0x600, -0x600, 0xb0, 0, 0 }, }, // normal beast { { 0x4000, 0x4000, 0x4000, 14, 17, 24, 16, 32, 80, 0x1600, 0x1200, 0xc00, 0x90, -0xbaaaa, -0x175555 }, { 0x1200, 0x1200, 0x1200, 14, 17, 24, 16, 32, 80, 0x1400, 0x1000, -0x600, 0xb0, 0x5b05, 0 }, { 0x2000, 0x2000, 0x2000, 22, 28, 24, 16, 16, 40, 0x800, 0x600, -0x600, 0xb0, 0, 0 }, }, // shrink human { { 10384, 10384, 10384, 14, 17, 24, 16, 32, 80, 5632, 4608, 3072, 144, -564586, -1329173 }, { 2108, 2108, 2108, 14, 17, 24, 16, 32, 80, 5120, 4096, -1536, 176, 0x5b05, 0 }, { 2192, 2192, 2192, 22, 28, 24, 16, 16, 40, 2048, 1536, -1536, 176, 0, 0 }, }, // grown human { { 19384, 19384, 19384, 14, 17, 24, 16, 32, 80, 5632, 4608, 3072, 144, -1014586, -1779173 }, { 5608, 5608, 5608, 14, 17, 24, 16, 32, 80, 5120, 4096, -1536, 176, 0x5b05, 0 }, { 11192, 11192, 11192, 22, 28, 24, 16, 16, 40, 2048, 1536, -1536, 176, 0, 0 }, }, }; AMMOINFO gAmmoInfo[] = { { 0, -1 }, { 100, -1 }, { 100, 4 }, { 500, 5 }, { 100, -1 }, { 50, -1 }, { 2880, -1 }, { 250, -1 }, { 100, -1 }, { 100, -1 }, { 50, -1 }, { 50, -1 }, }; struct ARMORDATA { int at0; int at4; int at8; int atc; int at10; int at14; }; ARMORDATA armorData[5] = { { 0x320, 0x640, 0x320, 0x640, 0x320, 0x640 }, { 0x640, 0x640, 0, 0x640, 0, 0x640 }, { 0, 0x640, 0x640, 0x640, 0, 0x640 }, { 0, 0x640, 0, 0x640, 0x640, 0x640 }, { 0xc80, 0xc80, 0xc80, 0xc80, 0xc80, 0xc80 } }; void PlayerSurvive(int, int); void PlayerKneelsOver(int, int); int nPlayerSurviveClient = seqRegisterClient(PlayerSurvive); int nPlayerKneelClient = seqRegisterClient(PlayerKneelsOver); struct VICTORY { const char *at0; int at4; }; VICTORY gVictory[] = { { "%s boned %s like a fish", 4100 }, { "%s castrated %s", 4101 }, { "%s creamed %s", 4102 }, { "%s destroyed %s", 4103 }, { "%s diced %s", 4104 }, { "%s disemboweled %s", 4105 }, { "%s flattened %s", 4106 }, { "%s gave %s Anal Justice", 4107 }, { "%s gave AnAl MaDnEsS to %s", 4108 }, { "%s hurt %s real bad", 4109 }, { "%s killed %s", 4110 }, { "%s made mincemeat out of %s", 4111 }, { "%s massacred %s", 4112 }, { "%s mutilated %s", 4113 }, { "%s reamed %s", 4114 }, { "%s ripped %s a new orifice", 4115 }, { "%s slaughtered %s", 4116 }, { "%s sliced %s", 4117 }, { "%s smashed %s", 4118 }, { "%s sodomized %s", 4119 }, { "%s splattered %s", 4120 }, { "%s squashed %s", 4121 }, { "%s throttled %s", 4122 }, { "%s wasted %s", 4123 }, { "%s body bagged %s", 4124 }, }; struct SUICIDE { const char *at0; int at4; }; SUICIDE gSuicide[] = { { "%s is excrement", 4202 }, { "%s is hamburger", 4203 }, { "%s suffered scrotum separation", 4204 }, { "%s volunteered for population control", 4206 }, { "%s has suicided", 4207 }, }; struct DAMAGEINFO { int at0; int at4[3]; int at10[3]; }; DAMAGEINFO damageInfo[7] = { { -1, 731, 732, 733, 710, 710, 710 }, { 1, 742, 743, 744, 711, 711, 711 }, { 0, 731, 732, 733, 712, 712, 712 }, { 1, 731, 732, 733, 713, 713, 713 }, { -1, 724, 724, 724, 714, 714, 714 }, { 2, 731, 732, 733, 715, 715, 715 }, { 0, 0, 0, 0, 0, 0, 0 } }; TRPLAYERCTRL gPlayerCtrl[kMaxPlayers]; QAV* qavSceneLoad(int qavId) { QAV* pQav = NULL; DICTNODE* hQav = gSysRes.Lookup(qavId, "QAV"); if (hQav) pQav = (QAV*)gSysRes.Lock(hQav); else viewSetSystemMessage("Failed to load QAV animation #%d", qavId); return pQav; } void qavSceneDraw(PLAYER* pPlayer, int a2, int a3, int a4, int a5) { if (pPlayer == NULL || pPlayer->sceneQav == -1) return; QAVSCENE* pQavScene = &gPlayerCtrl[pPlayer->nPlayer].qavScene; spritetype* pSprite = &sprite[pQavScene->index]; if (pQavScene->qavResrc != NULL) { QAV* pQAV = pQavScene->qavResrc; int v4 = (pPlayer->weaponTimer == 0) ? (int)totalclock % pQAV->at10 : pQAV->at10 - pPlayer->weaponTimer; int flags = 2; int nInv = powerupCheck(pPlayer, kPwUpShadowCloak); if (nInv >= 120 * 8 || (nInv != 0 && ((int)totalclock & 32))) { a2 = -128; flags |= 1; } // draw as weapon if (!(pSprite->flags & kModernTypeFlag1)) { pQAV->x = a3; pQAV->y = a4; pQAV->Draw(v4, flags, a2, a5); // draw fullscreen (currently 4:3 only) } else { int wx1 = windowxy1.x, wy1 = windowxy1.y, wx2 = windowxy2.x, wy2 = windowxy2.y; windowxy2.x = xdim - 1; windowxy2.y = ydim - 1; windowxy1.x = windowxy1.y = 0; pQAV->Draw(v4, flags, a2, a5); windowxy1.x = wx1; windowxy1.y = wy1; windowxy2.x = wx2; windowxy2.y = wy2; } } } void qavScenePlay(PLAYER* pPlayer) { if (pPlayer == NULL || pPlayer->sceneQav == -1) return; QAVSCENE* pQavScene = &gPlayerCtrl[pPlayer->nPlayer].qavScene; if (pQavScene->qavResrc != NULL) { QAV* pQAV = pQavScene->qavResrc; pQAV->nSprite = pPlayer->pSprite->index; int nTicks = pQAV->at10 - pPlayer->weaponTimer; pQAV->Play(nTicks - 4, nTicks, pPlayer->qavCallback, pPlayer); } } int powerupCheck(PLAYER *pPlayer, int nPowerUp) { dassert(pPlayer != NULL); dassert(nPowerUp >= 0 && nPowerUp < kMaxPowerUps); int nPack = powerupToPackItem(nPowerUp); if (nPack >= 0 && !packItemActive(pPlayer, nPack)) return 0; return pPlayer->pwUpTime[nPowerUp]; } bool isGrown(spritetype* pSprite) { if (powerupCheck(&gPlayer[pSprite->type - kDudePlayer1], kPwUpGrowShroom) > 0) return true; else if (pSprite->extra >= 0 && xsprite[pSprite->extra].scale >= 512) return true; else return false; } bool isShrinked(spritetype* pSprite) { if (powerupCheck(&gPlayer[pSprite->type - kDudePlayer1], kPwUpShrinkShroom) > 0) return true; else if (pSprite->extra >= 0 && xsprite[pSprite->extra].scale > 0 && xsprite[pSprite->extra].scale <= 128) return true; else return false; } bool shrinkPlayerSize(PLAYER* pPlayer, int divider) { pPlayer->pXSprite->scale = 256/divider; playerSetRace(pPlayer, kModeHumanShrink); return true; } bool growPlayerSize(PLAYER* pPlayer, int multiplier) { pPlayer->pXSprite->scale = 256*multiplier; playerSetRace(pPlayer, kModeHumanGrown); return true; } bool resetPlayerSize(PLAYER* pPlayer) { playerSetRace(pPlayer, kModeHuman); pPlayer->pXSprite->scale = 0; return true; } void deactivateSizeShrooms(PLAYER* pPlayer) { powerupDeactivate(pPlayer, kPwUpGrowShroom); pPlayer->pwUpTime[kPwUpGrowShroom] = 0; powerupDeactivate(pPlayer, kPwUpShrinkShroom); pPlayer->pwUpTime[kPwUpShrinkShroom] = 0; } PLAYER* getPlayerById(short id) { // relative to connected players if (id >= 1 && id <= kMaxPlayers) { id = id - 1; for (int i = connecthead; i >= 0; i = connectpoint2[i]) { if (id == gPlayer[i].nPlayer) return &gPlayer[i]; } // absolute sprite type } else if (id >= kDudePlayer1 && id <= kDudePlayer8) { for (int i = connecthead; i >= 0; i = connectpoint2[i]) { if (id == gPlayer[i].pSprite->type) return &gPlayer[i]; } } viewSetSystemMessage("There is no player id #%d", id); return NULL; } char powerupActivate(PLAYER *pPlayer, int nPowerUp) { if (powerupCheck(pPlayer, nPowerUp) > 0 && gPowerUpInfo[nPowerUp].pickupOnce) return 0; if (!pPlayer->pwUpTime[nPowerUp]) pPlayer->pwUpTime[nPowerUp] = gPowerUpInfo[nPowerUp].bonusTime; int nPack = powerupToPackItem(nPowerUp); if (nPack >= 0) pPlayer->packSlots[nPack].isActive = 1; switch (nPowerUp + kItemBase) { case kItemModernMapLevel: if (gModernMap) gFullMap = true; break; case kItemShroomShrink: if (!gModernMap) break; else if (isGrown(pPlayer->pSprite)) deactivateSizeShrooms(pPlayer); else shrinkPlayerSize(pPlayer, 2); break; case kItemShroomGrow: if (!gModernMap) break; else if (isShrinked(pPlayer->pSprite)) deactivateSizeShrooms(pPlayer); else { growPlayerSize(pPlayer, 2); if (powerupCheck(&gPlayer[pPlayer->pSprite->type - kDudePlayer1], kPwUpShadowCloak) > 0) { powerupDeactivate(pPlayer, kPwUpShadowCloak); pPlayer->pwUpTime[kPwUpShadowCloak] = 0; } if (ceilIsTooLow(pPlayer->pSprite)) actDamageSprite(pPlayer->pSprite->xvel, pPlayer->pSprite, DAMAGE_TYPE_3, 65535); } break; case kItemFeatherFall: case kItemJumpBoots: pPlayer->damageControl[0]++; break; case kItemReflectShots: // reflective shots if (pPlayer == gMe && gGameOptions.nGameType == 0) sfxSetReverb2(1); break; case kItemDeathMask: for (int i = 0; i < 7; i++) pPlayer->damageControl[i]++; break; case kItemDivingSuit: // diving suit pPlayer->damageControl[4]++; if (pPlayer == gMe && gGameOptions.nGameType == 0) sfxSetReverb(1); break; case kItemGasMask: pPlayer->damageControl[4]++; break; case kItemArmorAsbest: pPlayer->damageControl[1]++; break; case kItemTwoGuns: pPlayer->input.newWeapon = pPlayer->curWeapon; WeaponRaise(pPlayer); break; } sfxPlay3DSound(pPlayer->pSprite, 776, -1, 0); return 1; } void powerupDeactivate(PLAYER *pPlayer, int nPowerUp) { int nPack = powerupToPackItem(nPowerUp); if (nPack >= 0) pPlayer->packSlots[nPack].isActive = 0; switch (nPowerUp + kItemBase) { case kItemShroomShrink: if (gModernMap) { resetPlayerSize(pPlayer); if (ceilIsTooLow(pPlayer->pSprite)) actDamageSprite(pPlayer->pSprite->xvel, pPlayer->pSprite, DAMAGE_TYPE_3, 65535); } break; case kItemShroomGrow: if (gModernMap) resetPlayerSize(pPlayer); break; case kItemFeatherFall: case kItemJumpBoots: pPlayer->damageControl[0]--; break; case kItemDeathMask: for (int i = 0; i < 7; i++) pPlayer->damageControl[i]--; break; case kItemDivingSuit: pPlayer->damageControl[4]--; if (pPlayer == gMe && VanillaMode() ? true : pPlayer->pwUpTime[24] == 0) sfxSetReverb(0); break; case kItemReflectShots: if (pPlayer == gMe && VanillaMode() ? true : pPlayer->packSlots[1].isActive == 0) sfxSetReverb(0); break; case kItemGasMask: pPlayer->damageControl[4]--; break; case kItemArmorAsbest: pPlayer->damageControl[1]--; break; case kItemTwoGuns: pPlayer->input.newWeapon = pPlayer->curWeapon; WeaponRaise(pPlayer); break; } } void powerupSetState(PLAYER *pPlayer, int nPowerUp, char bState) { if (!bState) powerupActivate(pPlayer, nPowerUp); else powerupDeactivate(pPlayer, nPowerUp); } void powerupProcess(PLAYER *pPlayer) { pPlayer->packItemTime = ClipLow(pPlayer->packItemTime-4, 0); for (int i = kMaxPowerUps-1; i >= 0; i--) { int nPack = powerupToPackItem(i); if (nPack >= 0) { if (pPlayer->packSlots[nPack].isActive) { pPlayer->pwUpTime[i] = ClipLow(pPlayer->pwUpTime[i]-4, 0); if (pPlayer->pwUpTime[i]) pPlayer->packSlots[nPack].curAmount = (100*pPlayer->pwUpTime[i])/gPowerUpInfo[i].bonusTime; else { powerupDeactivate(pPlayer, i); if (pPlayer->packItemId == nPack) pPlayer->packItemId = 0; } } } else if (pPlayer->pwUpTime[i] > 0) { pPlayer->pwUpTime[i] = ClipLow(pPlayer->pwUpTime[i]-4, 0); if (!pPlayer->pwUpTime[i]) powerupDeactivate(pPlayer, i); } } } void powerupClear(PLAYER *pPlayer) { for (int i = kMaxPowerUps-1; i >= 0; i--) { pPlayer->pwUpTime[i] = 0; } } void powerupInit(void) { } int packItemToPowerup(int nPack) { int nPowerUp = -1; switch (nPack) { case 0: break; case 1: nPowerUp = kPwUpDivingSuit; break; case 2: nPowerUp = kPwUpCrystalBall; break; case 3: nPowerUp = kPwUpBeastVision; break; case 4: nPowerUp = kPwUpJumpBoots; break; default: ThrowError("Unhandled pack item %d", nPack); break; } return nPowerUp; } int powerupToPackItem(int nPowerUp) { switch (nPowerUp) { case kPwUpDivingSuit: return 1; case kPwUpCrystalBall: return 2; case kPwUpBeastVision: return 3; case kPwUpJumpBoots: return 4; } return -1; } char packAddItem(PLAYER *pPlayer, unsigned int nPack) { if (nPack <= 4) { if (pPlayer->packSlots[nPack].curAmount >= 100) return 0; pPlayer->packSlots[nPack].curAmount = 100; int nPowerUp = packItemToPowerup(nPack); if (nPowerUp >= 0) pPlayer->pwUpTime[nPowerUp] = gPowerUpInfo[nPowerUp].bonusTime; if (pPlayer->packItemId == -1) pPlayer->packItemId = nPack; if (!pPlayer->packSlots[pPlayer->packItemId].curAmount) pPlayer->packItemId = nPack; } else ThrowError("Unhandled pack item %d", nPack); return 1; } int packCheckItem(PLAYER *pPlayer, int nPack) { return pPlayer->packSlots[nPack].curAmount; } char packItemActive(PLAYER *pPlayer, int nPack) { return pPlayer->packSlots[nPack].isActive; } void packUseItem(PLAYER *pPlayer, int nPack) { char v4 = 0; int nPowerUp = -1; if (pPlayer->packSlots[nPack].curAmount > 0) { switch (nPack) { case 0: { XSPRITE *pXSprite = pPlayer->pXSprite; unsigned int health = pXSprite->health>>4; if (health < 100) { int heal = ClipHigh(100-health, pPlayer->packSlots[0].curAmount); actHealDude(pXSprite, heal, 100); pPlayer->packSlots[0].curAmount -= heal; } break; } case 1: v4 = 1; nPowerUp = kPwUpDivingSuit; break; case 2: v4 = 1; nPowerUp = kPwUpCrystalBall; break; case 3: v4 = 1; nPowerUp = kPwUpBeastVision; break; case 4: v4 = 1; nPowerUp = kPwUpJumpBoots; break; default: ThrowError("Unhandled pack item %d", nPack); return; } } pPlayer->packItemTime = 0; if (v4) powerupSetState(pPlayer, nPowerUp, pPlayer->packSlots[nPack].isActive); } void packPrevItem(PLAYER *pPlayer) { if (pPlayer->packItemTime > 0) { for (int nPrev = ClipLow(pPlayer->packItemId-1,0); nPrev >= 0; nPrev--) { if (pPlayer->packSlots[nPrev].curAmount) { pPlayer->packItemId = nPrev; break; } } } pPlayer->packItemTime = 600; } void packNextItem(PLAYER *pPlayer) { if (pPlayer->packItemTime > 0) { for (int nNext = ClipHigh(pPlayer->packItemId+1,5); nNext < 5; nNext++) { if (pPlayer->packSlots[nNext].curAmount) { pPlayer->packItemId = nNext; break; } } } pPlayer->packItemTime = 600; } char playerSeqPlaying(PLAYER * pPlayer, int nSeq) { int nCurSeq = seqGetID(3, pPlayer->pSprite->extra); if (pPlayer->pDudeInfo->seqStartID+nSeq == nCurSeq && seqGetStatus(3,pPlayer->pSprite->extra) >= 0) return 1; return 0; } void playerSetRace(PLAYER *pPlayer, int nLifeMode) { dassert(nLifeMode >= kModeHuman && nLifeMode <= kModeHumanGrown); DUDEINFO *pDudeInfo = pPlayer->pDudeInfo; *pDudeInfo = gPlayerTemplate[nLifeMode]; pPlayer->lifeMode = nLifeMode; // By NoOne: don't forget to change clipdist for grow and shrink modes pPlayer->pSprite->clipdist = pDudeInfo->clipdist; for (int i = 0; i < 7; i++) pDudeInfo->at70[i] = mulscale8(Handicap[gProfile[pPlayer->nPlayer].skill], pDudeInfo->startDamage[i]); } void playerSetGodMode(PLAYER *pPlayer, char bGodMode) { if (bGodMode) { for (int i = 0; i < 7; i++) pPlayer->damageControl[i]++; } else { for (int i = 0; i < 7; i++) pPlayer->damageControl[i]--; } pPlayer->godMode = bGodMode; } void playerResetInertia(PLAYER *pPlayer) { POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture]; pPlayer->zView = pPlayer->pSprite->z-pPosture->eyeAboveZ; pPlayer->zWeapon = pPlayer->pSprite->z-pPosture->weaponAboveZ; viewBackupView(pPlayer->nPlayer); } void playerCorrectInertia(PLAYER* pPlayer, vec3_t const *oldpos) { pPlayer->zView += pPlayer->pSprite->z-oldpos->z; pPlayer->zWeapon += pPlayer->pSprite->z-oldpos->z; viewCorrectViewOffsets(pPlayer->nPlayer, oldpos); } void playerResetPowerUps(PLAYER* pPlayer) { for (int i = 0; i < kMaxPowerUps; i++) { if (!VanillaMode() && (i == kPwUpJumpBoots || i == kPwUpDivingSuit || i == kPwUpCrystalBall || i == kPwUpBeastVision)) continue; pPlayer->pwUpTime[i] = 0; } } void playerStart(int nPlayer) { PLAYER* pPlayer = &gPlayer[nPlayer]; GINPUT* pInput = &pPlayer->input; ZONE* pStartZone = NULL; // normal start position if (gGameOptions.nGameType <= 1) pStartZone = &gStartZone[nPlayer]; // By NoOne: let's check if there is positions of teams is specified // if no, pick position randomly, just like it works in vanilla. else if (gModernMap && gGameOptions.nGameType == 3 && gTeamsSpawnUsed == true) { int maxRetries = 5; while (maxRetries-- > 0) { if (pPlayer->teamId == 0) pStartZone = &gStartZoneTeam1[Random(3)]; else pStartZone = &gStartZoneTeam2[Random(3)]; if (maxRetries != 0) { // check if there is no spawned player in selected zone for (int i = headspritesect[pStartZone->sectnum]; i >= 0; i = nextspritesect[i]) { spritetype* pSprite = &sprite[i]; if (pStartZone->x == pSprite->x && pStartZone->y == pSprite->y && IsPlayerSprite(pSprite)) { pStartZone = NULL; break; } } } if (pStartZone != NULL) break; } } else { pStartZone = &gStartZone[Random(8)]; } spritetype *pSprite = actSpawnSprite(pStartZone->sectnum, pStartZone->x, pStartZone->y, pStartZone->z, 6, 1); dassert(pSprite->extra > 0 && pSprite->extra < kMaxXSprites); XSPRITE *pXSprite = &xsprite[pSprite->extra]; pPlayer->pSprite = pSprite; pPlayer->pXSprite = pXSprite; pPlayer->nSprite = pSprite->index; DUDEINFO *pDudeInfo = &dudeInfo[kDudePlayer1 + nPlayer - kDudeBase]; pPlayer->pDudeInfo = pDudeInfo; playerSetRace(pPlayer, kModeHuman); playerResetPosture(pPlayer); seqSpawn(pDudeInfo->seqStartID, 3, pSprite->extra, -1); if (pPlayer == gMe) SetBitString(show2dsprite, pSprite->index); int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); pSprite->z -= bottom - pSprite->z; pSprite->pal = 11+(pPlayer->teamId&3); pPlayer->angold = pSprite->ang = pStartZone->ang; pPlayer->q16ang = fix16_from_int(pSprite->ang); pSprite->type = kDudePlayer1+nPlayer; pSprite->clipdist = pDudeInfo->clipdist; pSprite->flags = 15; pXSprite->burnTime = 0; pXSprite->burnSource = -1; pPlayer->pXSprite->health = pDudeInfo->startHealth<<4; pPlayer->pSprite->cstat &= (unsigned short)~32768; pPlayer->bloodlust = 0; pPlayer->q16horiz = 0; pPlayer->q16slopehoriz = 0; pPlayer->q16look = 0; pPlayer->slope = 0; pPlayer->fraggerId = -1; pPlayer->underwaterTime = 1200; pPlayer->bloodTime = 0; pPlayer->gooTime = 0; pPlayer->wetTime = 0; pPlayer->bubbleTime = 0; pPlayer->at306 = 0; pPlayer->restTime = 0; pPlayer->kickPower = 0; pPlayer->laughCount = 0; pPlayer->spin = 0; pPlayer->posture = 0; pPlayer->voodooTarget = -1; pPlayer->voodooTargets = 0; pPlayer->voodooVar1 = 0; pPlayer->vodooVar2 = 0; playerResetInertia(pPlayer); pPlayer->zWeaponVel = 0; pPlayer->relAim.dx = 0x4000; pPlayer->relAim.dy = 0; pPlayer->relAim.dz = 0; pPlayer->aimTarget = -1; pPlayer->zViewVel = pPlayer->zWeaponVel; if (!(gGameOptions.nGameType == 1 && gGameOptions.bKeepKeysOnRespawn)) for (int i = 0; i < 8; i++) pPlayer->hasKey[i] = gGameOptions.nGameType >= 2; pPlayer->hasFlag = 0; for (int i = 0; i < 8; i++) pPlayer->used2[i] = -1; for (int i = 0; i < 7; i++) pPlayer->damageControl[i] = 0; if (pPlayer->godMode) playerSetGodMode(pPlayer, 1); gInfiniteAmmo = 0; gFullMap = 0; pPlayer->throwPower = 0; pPlayer->deathTime = 0; pPlayer->nextWeapon = 0; xvel[pSprite->index] = yvel[pSprite->index] = zvel[pSprite->index] = 0; pInput->q16turn = 0; pInput->keyFlags.word = 0; pInput->forward = 0; pInput->strafe = 0; pInput->q16mlook = 0; pInput->buttonFlags.byte = 0; pInput->useFlags.byte = 0; pPlayer->flickerEffect = 0; pPlayer->quakeEffect = 0; pPlayer->tiltEffect = 0; pPlayer->visibility = 0; pPlayer->painEffect = 0; pPlayer->blindEffect = 0; pPlayer->chokeEffect = 0; pPlayer->handTime = 0; pPlayer->weaponTimer = 0; pPlayer->weaponState = 0; pPlayer->weaponQav = -1; playerResetQavScene(pPlayer); // reset qav scene pPlayer->hand = 0; pPlayer->nWaterPal = 0; playerResetPowerUps(pPlayer); if (pPlayer == gMe) { viewInitializePrediction(); gViewMap.x = pPlayer->pSprite->x; gViewMap.y = pPlayer->pSprite->y; gViewMap.angle = pPlayer->pSprite->ang; } if (IsUnderwaterSector(pSprite->sectnum)) { pPlayer->posture = 1; pPlayer->pXSprite->medium = kMediumWater; } } void playerReset(PLAYER *pPlayer) { static int dword_136400[] = { 3, 4, 2, 8, 9, 10, 7, 1, 1, 1, 1, 1, 1, 1 }; static int dword_136438[] = { 3, 4, 2, 8, 9, 10, 7, 1, 1, 1, 1, 1, 1, 1 }; dassert(pPlayer != NULL); for (int i = 0; i < 14; i++) { pPlayer->hasWeapon[i] = gInfiniteAmmo; pPlayer->weaponMode[i] = 0; } pPlayer->hasWeapon[1] = 1; pPlayer->curWeapon = 0; pPlayer->qavCallback = -1; pPlayer->input.newWeapon = 1; for (int i = 0; i < 14; i++) { pPlayer->weaponOrder[0][i] = dword_136400[i]; pPlayer->weaponOrder[1][i] = dword_136438[i]; } for (int i = 0; i < 12; i++) { if (gInfiniteAmmo) pPlayer->ammoCount[i] = gAmmoInfo[i].max; else pPlayer->ammoCount[i] = 0; } for (int i = 0; i < 3; i++) pPlayer->armor[i] = 0; pPlayer->weaponTimer = 0; pPlayer->weaponState = 0; pPlayer->weaponQav = -1; pPlayer->qavLoop = 0; pPlayer->packItemId = -1; for (int i = 0; i < 5; i++) { pPlayer->packSlots[i].isActive = 0; pPlayer->packSlots[i].curAmount = 0; } ///////////////// // reset qav scene playerResetQavScene(pPlayer); // reset posture (mainly required for resetting movement speed and jump height) playerResetPosture(pPlayer); ///////////////// } void playerResetPosture(PLAYER* pPlayer) { memcpy(pPlayer->pPosture, gPostureDefaults, sizeof(gPostureDefaults)); } void playerResetQavScene(PLAYER* pPlayer) { QAVSCENE* pQavScene = &gPlayerCtrl[pPlayer->nPlayer].qavScene; pQavScene->index = pQavScene->causedBy = pPlayer->sceneQav = -1; pQavScene->qavResrc = NULL; } int dword_21EFB0[8]; ClockTicks dword_21EFD0[8]; void playerInit(int nPlayer, unsigned int a2) { PLAYER *pPlayer = &gPlayer[nPlayer]; if (!(a2&1)) memset(pPlayer, 0, sizeof(PLAYER)); pPlayer->nPlayer = nPlayer; pPlayer->teamId = nPlayer; if (gGameOptions.nGameType == 3) pPlayer->teamId = nPlayer&1; pPlayer->fragCount = 0; memset(dword_21EFB0, 0, sizeof(dword_21EFB0)); memset(dword_21EFD0, 0, sizeof(dword_21EFD0)); memset(pPlayer->fragInfo, 0, sizeof(pPlayer->fragInfo)); if (!(a2&1)) playerReset(pPlayer); } char sub_3A158(PLAYER *a1, spritetype *a2) { for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite]) { if (a2 && a2->index == nSprite) continue; spritetype *pSprite = &sprite[nSprite]; if (pSprite->type == kThingDroppedLifeLeech && actOwnerIdToSpriteId(pSprite->owner) == a1->nSprite) return 1; } return 0; } char PickupItem(PLAYER *pPlayer, spritetype *pItem) { spritetype *pSprite = pPlayer->pSprite; XSPRITE *pXSprite = pPlayer->pXSprite; char buffer[80]; int pickupSnd = 775; int nType = pItem->type - kItemBase; switch (pItem->type) { case kItemShadowCloak: if (isGrown(pPlayer->pSprite) || !powerupActivate(pPlayer, nType)) return false; break; case kItemShroomShrink: case kItemShroomGrow: if (gModernMap) { switch (pItem->type) { case kItemShroomShrink: if (isShrinked(pSprite)) return false; break; case kItemShroomGrow: if (isGrown(pSprite)) return false; break; } powerupActivate(pPlayer, nType); } break; case kItemFlagABase: case kItemFlagBBase: { if (gGameOptions.nGameType != 3 || pItem->extra <= 0) return 0; XSPRITE * pXItem = &xsprite[pItem->extra]; if (pItem->type == kItemFlagABase) { if (pPlayer->teamId == 1) { if ((pPlayer->hasFlag & 1) == 0 && pXItem->state) { pPlayer->hasFlag |= 1; pPlayer->used2[0] = pItem->index; trTriggerSprite(pItem->index, pXItem, kCmdOff, pPlayer->nSprite); sprintf(buffer, "%s stole Blue Flag", gProfile[pPlayer->nPlayer].name); sndStartSample(8007, 255, 2, 0); viewSetMessage(buffer); } } if (pPlayer->teamId == 0) { if ((pPlayer->hasFlag & 1) != 0 && !pXItem->state) { pPlayer->hasFlag &= ~1; pPlayer->used2[0] = -1; trTriggerSprite(pItem->index, pXItem, kCmdOn, pPlayer->nSprite); sprintf(buffer, "%s returned Blue Flag", gProfile[pPlayer->nPlayer].name); sndStartSample(8003, 255, 2, 0); viewSetMessage(buffer); } if ((pPlayer->hasFlag & 2) != 0 && pXItem->state) { pPlayer->hasFlag &= ~2; pPlayer->used2[1] = -1; dword_21EFB0[pPlayer->teamId] += 10; dword_21EFD0[pPlayer->teamId] += 240; evSend(0, 0, 81, kCmdOn, pPlayer->nSprite); sprintf(buffer, "%s captured Red Flag!", gProfile[pPlayer->nPlayer].name); sndStartSample(8001, 255, 2, 0); viewSetMessage(buffer); #if 0 if (dword_28E3D4 == 3 && myconnectindex == connecthead) { sprintf(buffer, "frag A killed B\n"); sub_7AC28(buffer); } #endif } } } else if (pItem->type == kItemFlagBBase) { if (pPlayer->teamId == 0) { if ((pPlayer->hasFlag & 2) == 0 && pXItem->state) { pPlayer->hasFlag |= 2; pPlayer->used2[1] = pItem->index; trTriggerSprite(pItem->index, pXItem, kCmdOff, pPlayer->nSprite); sprintf(buffer, "%s stole Red Flag", gProfile[pPlayer->nPlayer].name); sndStartSample(8006, 255, 2, 0); viewSetMessage(buffer); } } if (pPlayer->teamId == 1) { if ((pPlayer->hasFlag & 2) != 0 && !pXItem->state) { pPlayer->hasFlag &= ~2; pPlayer->used2[1] = -1; trTriggerSprite(pItem->index, pXItem, kCmdOn, pPlayer->nSprite); sprintf(buffer, "%s returned Red Flag", gProfile[pPlayer->nPlayer].name); sndStartSample(8002, 255, 2, 0); viewSetMessage(buffer); } if ((pPlayer->hasFlag & 1) != 0 && pXItem->state) { pPlayer->hasFlag &= ~1; pPlayer->used2[0] = -1; dword_21EFB0[pPlayer->teamId] += 10; dword_21EFD0[pPlayer->teamId] += 240; evSend(0, 0, 80, kCmdOn, pPlayer->nSprite); sprintf(buffer, "%s captured Blue Flag!", gProfile[pPlayer->nPlayer].name); sndStartSample(8000, 255, 2, 0); viewSetMessage(buffer); #if 0 if (dword_28E3D4 == 3 && myconnectindex == connecthead) { sprintf(buffer, "frag B killed A\n"); sub_7AC28(buffer); } #endif } } } } return 0; case kItemFlagA: if (gGameOptions.nGameType != 3) return 0; evKill(pItem->index, 3, kCallbackReturnFlag); pPlayer->hasFlag |= 1; pPlayer->used2[0] = pItem->index; gBlueFlagDropped = false; break; case kItemFlagB: if (gGameOptions.nGameType != 3) return 0; evKill(pItem->index, 3, kCallbackReturnFlag); pPlayer->hasFlag |= 2; pPlayer->used2[1] = pItem->index; gRedFlagDropped = false; break; case kItemArmorBasic: case kItemArmorBody: case kItemArmorFire: case kItemArmorSpirit: case kItemArmorSuper: { ARMORDATA *pArmorData = &armorData[pItem->type - kItemArmorBasic]; bool pickedUp = false; if (pPlayer->armor[1] < pArmorData->atc) { pPlayer->armor[1] = ClipHigh(pPlayer->armor[1]+pArmorData->at8, pArmorData->atc); pickedUp = true; } if (pPlayer->armor[0] < pArmorData->at4) { pPlayer->armor[0] = ClipHigh(pPlayer->armor[0]+pArmorData->at0, pArmorData->at4); pickedUp = true; } if (pPlayer->armor[2] < pArmorData->at14) { pPlayer->armor[2] = ClipHigh(pPlayer->armor[2]+pArmorData->at10, pArmorData->at14); pickedUp = true; } if (!pickedUp) return 0; pickupSnd = 779; break; } case kItemCrystalBall: if (gGameOptions.nGameType == 0 || !packAddItem(pPlayer, gItemData[nType].packSlot)) return 0; break; case kItemKeySkull: case kItemKeyEye: case kItemKeyFire: case kItemKeyDagger: case kItemKeySpider: case kItemKeyMoon: case kItemKeyKey7: if (pPlayer->hasKey[pItem->type-99]) return 0; pPlayer->hasKey[pItem->type-99] = 1; pickupSnd = 781; break; case kItemHealthMedPouch: case kItemHealthLifeEssense: case kItemHealthLifeSeed: case kItemHealthRedPotion: { int addPower = gPowerUpInfo[nType].bonusTime; // by NoOne: allow custom amount for item if (gModernMap && sprite[pItem->xvel].extra >= 0 && xsprite[sprite[pItem->xvel].extra].data1 > 0) addPower = xsprite[sprite[pItem->xvel].extra].data1; if (!actHealDude(pXSprite, addPower, gPowerUpInfo[nType].maxTime)) return 0; return 1; } case kItemHealthDoctorBag: case kItemJumpBoots: case kItemDivingSuit: case kItemBeastVision: if (!packAddItem(pPlayer, gItemData[nType].packSlot)) return 0; break; default: if (!powerupActivate(pPlayer, nType)) return 0; return 1; } sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, pickupSnd, pSprite->sectnum); return 1; } char PickupAmmo(PLAYER* pPlayer, spritetype* pAmmo) { AMMOITEMDATA* pAmmoItemData = &gAmmoItemData[pAmmo->type - kItemAmmoBase]; int nAmmoType = pAmmoItemData->type; if (pPlayer->ammoCount[nAmmoType] >= gAmmoInfo[nAmmoType].max) return 0; else if (!gModernMap || pAmmo->extra < 0 || xsprite[pAmmo->extra].data1 <= 0) pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType]+pAmmoItemData->count, gAmmoInfo[nAmmoType].max); // by NoOne: allow custom amount for item else pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + xsprite[pAmmo->extra].data1, gAmmoInfo[nAmmoType].max); if (pAmmoItemData->weaponType) pPlayer->hasWeapon[pAmmoItemData->weaponType] = 1; sfxPlay3DSound(pPlayer->pSprite, 782, -1, 0); return 1; } char PickupWeapon(PLAYER *pPlayer, spritetype *pWeapon) { WEAPONITEMDATA *pWeaponItemData = &gWeaponItemData[pWeapon->type - kItemWeaponBase]; int nWeaponType = pWeaponItemData->type; int nAmmoType = pWeaponItemData->ammoType; if (!pPlayer->hasWeapon[nWeaponType] || gGameOptions.nWeaponSettings == 2 || gGameOptions.nWeaponSettings == 3) { if (pWeapon->type == kItemWeaponLifeLeech && gGameOptions.nGameType > 1 && sub_3A158(pPlayer, NULL)) return 0; pPlayer->hasWeapon[nWeaponType] = 1; if (nAmmoType == -1) return 0; // By NoOne: allow to set custom ammo count for weapon pickups if (!gModernMap || pWeapon->extra < 0 || xsprite[pWeapon->extra].data1 <= 0) pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + pWeaponItemData->count, gAmmoInfo[nAmmoType].max); else pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + xsprite[pWeapon->extra].data1, gAmmoInfo[nAmmoType].max); int nNewWeapon = WeaponUpgrade(pPlayer, nWeaponType); if (nNewWeapon != pPlayer->curWeapon) { pPlayer->weaponState = 0; pPlayer->nextWeapon = nNewWeapon; } sfxPlay3DSound(pPlayer->pSprite, 777, -1, 0); return 1; } if (!actGetRespawnTime(pWeapon) || nAmmoType == -1 || pPlayer->ammoCount[nAmmoType] >= gAmmoInfo[nAmmoType].max) return 0; else if (!gModernMap || pWeapon->extra < 0 || xsprite[pWeapon->extra].data1 <= 0) pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType]+pWeaponItemData->count, gAmmoInfo[nAmmoType].max); else pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + xsprite[pWeapon->extra].data1, gAmmoInfo[nAmmoType].max); sfxPlay3DSound(pPlayer->pSprite, 777, -1, 0); return 1; } void PickUp(PLAYER *pPlayer, spritetype *pSprite) { const char *msg = nullptr; int nType = pSprite->type; char pickedUp = 0; int customMsg = -1; if (gModernMap) { // by NoOne: allow custom INI message instead "Picked up" XSPRITE* pXSprite = (pSprite->extra >= 0) ? &xsprite[pSprite->extra] : NULL; if (pXSprite != NULL && pXSprite->txID != 3 && pXSprite->lockMsg > 0) customMsg = pXSprite->lockMsg; } if (nType >= kItemBase && nType <= kItemMax) { pickedUp = PickupItem(pPlayer, pSprite); if (pickedUp && customMsg == -1) msg = GStrings(FStringf("TXTB_ITEM%02d", int(nType - kItemBase +1))); } else if (nType >= kItemAmmoBase && nType < kItemAmmoMax) { pickedUp = PickupAmmo(pPlayer, pSprite); if (pickedUp && customMsg == -1) msg = GStrings(FStringf("TXTB_AMMO%02d", int(nType - kItemAmmoBase +1))); } else if (nType >= kItemWeaponBase && nType < kItemWeaponMax) { pickedUp = PickupWeapon(pPlayer, pSprite); if (pickedUp && customMsg == -1) msg = GStrings(FStringf("TXTB_WPN%02d", int(nType - kItemWeaponBase +1))); } if (!pickedUp) return; else if (pSprite->extra > 0) { XSPRITE *pXSprite = &xsprite[pSprite->extra]; if (pXSprite->Pickup) trTriggerSprite(pSprite->index, pXSprite, kCmdSpritePickup, pPlayer->nSprite); } if (!actCheckRespawn(pSprite)) actPostSprite(pSprite->index, kStatFree); pPlayer->pickupEffect = 30; if (pPlayer == gMe) { if (customMsg > 0) trTextOver(customMsg - 1); else if (msg) viewSetMessage(msg, 0, MESSAGE_PRIORITY_PICKUP); } } void CheckPickUp(PLAYER *pPlayer) { spritetype *pSprite = pPlayer->pSprite; int x = pSprite->x; int y = pSprite->y; int z = pSprite->z; int nSector = pSprite->sectnum; int nNextSprite; for (int nSprite = headspritestat[kStatItem]; nSprite >= 0; nSprite = nNextSprite) { spritetype *pItem = &sprite[nSprite]; nNextSprite = nextspritestat[nSprite]; if (pItem->flags&32) continue; int dx = klabs(x-pItem->x)>>4; if (dx > 48) continue; int dy = klabs(y-pItem->y)>>4; if (dy > 48) continue; int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); int vb = 0; if (pItem->z < top) vb = (top-pItem->z)>>8; else if (pItem->z > bottom) vb = (pItem->z-bottom)>>8; if (vb > 32) continue; if (approxDist(dx,dy) > 48) continue; GetSpriteExtents(pItem, &top, &bottom); if (cansee(x, y, z, nSector, pItem->x, pItem->y, pItem->z, pItem->sectnum) || cansee(x, y, z, nSector, pItem->x, pItem->y, top, pItem->sectnum) || cansee(x, y, z, nSector, pItem->x, pItem->y, bottom, pItem->sectnum)) PickUp(pPlayer, pItem); } } int ActionScan(PLAYER *pPlayer, int *a2, int *a3) { *a2 = 0; *a3 = 0; spritetype *pSprite = pPlayer->pSprite; int x = Cos(pSprite->ang)>>16; int y = Sin(pSprite->ang)>>16; int z = pPlayer->slope; int hit = HitScan(pSprite, pPlayer->zView, x, y, z, 0x10000040, 128); int hitDist = approxDist(pSprite->x-gHitInfo.hitx, pSprite->y-gHitInfo.hity)>>4; if (hitDist < 64) { switch (hit) { case 3: *a2 = gHitInfo.hitsprite; *a3 = sprite[*a2].extra; if (*a3 > 0 && sprite[*a2].statnum == kStatThing) { spritetype *pSprite = &sprite[*a2]; XSPRITE *pXSprite = &xsprite[*a3]; if (pSprite->type == kThingDroppedLifeLeech) { if (gGameOptions.nGameType > 1 && sub_3A158(pPlayer, pSprite)) return -1; pXSprite->data4 = pPlayer->nPlayer; pXSprite->isTriggered = 0; } } if (*a3 > 0 && xsprite[*a3].Push) return 3; if (sprite[*a2].statnum == kStatDude) { spritetype *pSprite = &sprite[*a2]; XSPRITE *pXSprite = &xsprite[*a3]; int nMass = dudeInfo[pSprite->type-kDudeBase].mass; if (nMass) { int t2 = divscale(0xccccc, nMass, 8); xvel[*a2] += mulscale16(x, t2); yvel[*a2] += mulscale16(y, t2); zvel[*a2] += mulscale16(z, t2); } if (pXSprite->Push && !pXSprite->state && !pXSprite->isTriggered) trTriggerSprite(*a2, pXSprite, kCmdSpritePush, pPlayer->nSprite); } break; case 0: case 4: *a2 = gHitInfo.hitwall; *a3 = wall[*a2].extra; if (*a3 > 0 && xwall[*a3].triggerPush) return 0; if (wall[*a2].nextsector >= 0) { *a2 = wall[*a2].nextsector; *a3 = sector[*a2].extra; if (*a3 > 0 && xsector[*a3].Wallpush) return 6; } break; case 1: case 2: *a2 = gHitInfo.hitsect; *a3 = sector[*a2].extra; if (*a3 > 0 && xsector[*a3].Push) return 6; break; } } *a2 = pSprite->sectnum; *a3 = sector[*a2].extra; if (*a3 > 0 && xsector[*a3].Push) return 6; return -1; } void ProcessInput(PLAYER *pPlayer) { spritetype *pSprite = pPlayer->pSprite; XSPRITE *pXSprite = pPlayer->pXSprite; int nSprite = pPlayer->nSprite; POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture]; GINPUT *pInput = &pPlayer->input; pPlayer->isRunning = pInput->syncFlags.run; if (pInput->buttonFlags.byte || pInput->forward || pInput->strafe || pInput->q16turn) pPlayer->restTime = 0; else if (pPlayer->restTime >= 0) pPlayer->restTime += 4; WeaponProcess(pPlayer); if (pXSprite->health == 0) { char bSeqStat = playerSeqPlaying(pPlayer, 16); if (pPlayer->fraggerId != -1) { pPlayer->angold = pSprite->ang = getangle(sprite[pPlayer->fraggerId].x - pSprite->x, sprite[pPlayer->fraggerId].y - pSprite->y); pPlayer->q16ang = fix16_from_int(pSprite->ang); } pPlayer->deathTime += 4; if (!bSeqStat) { if (bVanilla) pPlayer->q16horiz = fix16_from_int(mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime*8, 1024))>>15), 120)); else pPlayer->q16horiz = mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime*8, 1024))>>15), F16(120)); } if (pPlayer->curWeapon) pInput->newWeapon = pPlayer->curWeapon; if (pInput->keyFlags.action) { if (bSeqStat) { if (pPlayer->deathTime > 360) seqSpawn(pPlayer->pDudeInfo->seqStartID+14, 3, pPlayer->pSprite->extra, nPlayerSurviveClient); } else if (seqGetStatus(3, pPlayer->pSprite->extra) < 0) { if (pPlayer->pSprite) pPlayer->pSprite->type = kThingBloodChunks; actPostSprite(pPlayer->nSprite, kStatThing); seqSpawn(pPlayer->pDudeInfo->seqStartID+15, 3, pPlayer->pSprite->extra, -1); playerReset(pPlayer); if (gGameOptions.nGameType == 0 && numplayers == 1) { if (gDemo.at0) gDemo.Close(); pInput->keyFlags.restart = 1; } else playerStart(pPlayer->nPlayer); } pInput->keyFlags.action = 0; } return; } if (pPlayer->posture == 1) { int x = Cos(pSprite->ang); int y = Sin(pSprite->ang); if (pInput->forward) { int forward = pInput->forward; if (forward > 0) forward = mulscale8(pPosture->frontAccel, forward); else forward = mulscale8(pPosture->backAccel, forward); xvel[nSprite] += mulscale30(forward, x); yvel[nSprite] += mulscale30(forward, y); } if (pInput->strafe) { int strafe = pInput->strafe; strafe = mulscale8(pPosture->sideAccel, strafe); xvel[nSprite] += mulscale30(strafe, y); yvel[nSprite] -= mulscale30(strafe, x); } } else if (pXSprite->height < 256) { int speed = 0x10000; if (pXSprite->height > 0) speed -= divscale16(pXSprite->height, 256); int x = Cos(pSprite->ang); int y = Sin(pSprite->ang); if (pInput->forward) { int forward = pInput->forward; if (forward > 0) forward = mulscale8(pPosture->frontAccel, forward); else forward = mulscale8(pPosture->backAccel, forward); if (pXSprite->height) forward = mulscale16(forward, speed); xvel[nSprite] += mulscale30(forward, x); yvel[nSprite] += mulscale30(forward, y); } if (pInput->strafe) { int strafe = pInput->strafe; strafe = mulscale8(pPosture->sideAccel, strafe); if (pXSprite->height) strafe = mulscale16(strafe, speed); xvel[nSprite] += mulscale30(strafe, y); yvel[nSprite] -= mulscale30(strafe, x); } } if (pInput->q16turn) pPlayer->q16ang = (pPlayer->q16ang+pInput->q16turn)&0x7ffffff; if (pInput->keyFlags.spin180) { if (!pPlayer->spin) pPlayer->spin = -1024; pInput->keyFlags.spin180 = 0; } if (pPlayer->spin < 0) { int speed; if (pPlayer->posture == 1) speed = 64; else speed = 128; pPlayer->spin = ClipLow(pPlayer->spin+speed, 0); pPlayer->q16ang += fix16_from_int(speed); } pPlayer->q16ang = (pPlayer->q16ang+fix16_from_int(pSprite->ang-pPlayer->angold))&0x7ffffff; pPlayer->angold = pSprite->ang = fix16_to_int(pPlayer->q16ang); if (!pInput->buttonFlags.jump) pPlayer->cantJump = 0; switch (pPlayer->posture) { case 1: if (pInput->buttonFlags.jump) zvel[nSprite] -= pPosture->normalJumpZ;//0x5b05; if (pInput->buttonFlags.crouch) zvel[nSprite] += pPosture->normalJumpZ;//0x5b05; break; case 2: if (!pInput->buttonFlags.crouch) pPlayer->posture = 0; break; default: if (!pPlayer->cantJump && pInput->buttonFlags.jump && pXSprite->height == 0) { sfxPlay3DSound(pSprite, 700, 0, 0); if (packItemActive(pPlayer, 4)) zvel[nSprite] = pPosture->pwupJumpZ; //-0x175555; else zvel[nSprite] = pPosture->normalJumpZ; //-0xbaaaa; pPlayer->cantJump = 1; } if (pInput->buttonFlags.crouch) pPlayer->posture = 2; break; } if (pInput->keyFlags.action) { int a2, a3; int hit = ActionScan(pPlayer, &a2, &a3); switch (hit) { case 6: if (a3 > 0 && a3 <= 2048) { XSECTOR *pXSector = &xsector[a3]; int key = pXSector->Key; if (pXSector->locked && pPlayer == gMe) { viewSetMessage(GStrings("TXTB_LOCKED")); sndStartSample(3062, 255, 2, 0); } if (!key || pPlayer->hasKey[key]) trTriggerSector(a2, pXSector, kCmdSpritePush, nSprite); else if (pPlayer == gMe) { viewSetMessage(GStrings("TXTB_KEY")); sndStartSample(3063, 255, 2, 0); } } break; case 0: { XWALL *pXWall = &xwall[a3]; int key = pXWall->key; if (pXWall->locked && pPlayer == gMe) { viewSetMessage(GStrings("TXTB_LOCKED")); sndStartSample(3062, 255, 2, 0); } if (!key || pPlayer->hasKey[key]) trTriggerWall(a2, pXWall, kCmdWallPush, pPlayer->nSprite); else if (pPlayer == gMe) { viewSetMessage(GStrings("TXTB_KEY")); sndStartSample(3063, 255, 2, 0); } break; } case 3: { XSPRITE *pXSprite = &xsprite[a3]; int key = pXSprite->key; if (pXSprite->locked && pPlayer == gMe && pXSprite->lockMsg) trTextOver(pXSprite->lockMsg); if (!key || pPlayer->hasKey[key]) trTriggerSprite(a2, pXSprite, kCmdSpritePush, pPlayer->nSprite); else if (pPlayer == gMe) { viewSetMessage(GStrings("TXTB_KEY")); sndStartSample(3063, 255, 2, 0); } break; } } if (pPlayer->handTime > 0) pPlayer->handTime = ClipLow(pPlayer->handTime-4*(6-gGameOptions.nDifficulty), 0); if (pPlayer->handTime <= 0 && pPlayer->hand) { spritetype *pSprite2 = actSpawnDude(pPlayer->pSprite, kDudeHand, pPlayer->pSprite->clipdist<<1, 0); pSprite2->ang = (pPlayer->pSprite->ang+1024)&2047; int nSprite = pPlayer->pSprite->index; int x = Cos(pPlayer->pSprite->ang)>>16; int y = Sin(pPlayer->pSprite->ang)>>16; xvel[pSprite2->index] = xvel[nSprite] + mulscale14(0x155555, x); yvel[pSprite2->index] = yvel[nSprite] + mulscale14(0x155555, y); zvel[pSprite2->index] = zvel[nSprite]; pPlayer->hand = 0; } pInput->keyFlags.action = 0; } if (bVanilla) { if (pInput->keyFlags.lookCenter && !pInput->buttonFlags.lookUp && !pInput->buttonFlags.lookDown) { if (pPlayer->q16look < 0) pPlayer->q16look = fix16_min(pPlayer->q16look+F16(4), F16(0)); if (pPlayer->q16look > 0) pPlayer->q16look = fix16_max(pPlayer->q16look-F16(4), F16(0)); if (!pPlayer->q16look) pInput->keyFlags.lookCenter = 0; } else { if (pInput->buttonFlags.lookUp) pPlayer->q16look = fix16_min(pPlayer->q16look+F16(4), F16(60)); if (pInput->buttonFlags.lookDown) pPlayer->q16look = fix16_max(pPlayer->q16look-F16(4), F16(-60)); } pPlayer->q16look = fix16_clamp(pPlayer->q16look+pInput->q16mlook, F16(-60), F16(60)); if (pPlayer->q16look > 0) pPlayer->q16horiz = fix16_from_int(mulscale30(120, Sin(fix16_to_int(pPlayer->q16look)<<3))); else if (pPlayer->q16look < 0) pPlayer->q16horiz = fix16_from_int(mulscale30(180, Sin(fix16_to_int(pPlayer->q16look)<<3))); else pPlayer->q16horiz = 0; } else { CONSTEXPR int upAngle = 289; CONSTEXPR int downAngle = -347; CONSTEXPR double lookStepUp = 4.0*upAngle/60.0; CONSTEXPR double lookStepDown = -4.0*downAngle/60.0; if (pInput->keyFlags.lookCenter && !pInput->buttonFlags.lookUp && !pInput->buttonFlags.lookDown) { if (pPlayer->q16look < 0) pPlayer->q16look = fix16_min(pPlayer->q16look+F16(lookStepDown), F16(0)); if (pPlayer->q16look > 0) pPlayer->q16look = fix16_max(pPlayer->q16look-F16(lookStepUp), F16(0)); if (!pPlayer->q16look) pInput->keyFlags.lookCenter = 0; } else { if (pInput->buttonFlags.lookUp) pPlayer->q16look = fix16_min(pPlayer->q16look+F16(lookStepUp), F16(upAngle)); if (pInput->buttonFlags.lookDown) pPlayer->q16look = fix16_max(pPlayer->q16look-F16(lookStepDown), F16(downAngle)); } pPlayer->q16look = fix16_clamp(pPlayer->q16look+(pInput->q16mlook<<3), F16(downAngle), F16(upAngle)); pPlayer->q16horiz = fix16_from_float(100.f*tanf(fix16_to_float(pPlayer->q16look)*fPI/1024.f)); } int nSector = pSprite->sectnum; int florhit = gSpriteHit[pSprite->extra].florhit & 0xc000; char va; if (pXSprite->height < 16 && (florhit == 0x4000 || florhit == 0)) va = 1; else va = 0; if (va && (sector[nSector].floorstat&2)) { int z1 = getflorzofslope(nSector, pSprite->x, pSprite->y); int x2 = pSprite->x+mulscale30(64, Cos(pSprite->ang)); int y2 = pSprite->y+mulscale30(64, Sin(pSprite->ang)); short nSector2 = nSector; updatesector(x2, y2, &nSector2); if (nSector2 == nSector) { int z2 = getflorzofslope(nSector2, x2, y2); pPlayer->q16slopehoriz = interpolate(pPlayer->q16slopehoriz, fix16_from_int(z1-z2)>>3, 0x4000); } } else { pPlayer->q16slopehoriz = interpolate(pPlayer->q16slopehoriz, F16(0), 0x4000); if (klabs(pPlayer->q16slopehoriz) < 4) pPlayer->q16slopehoriz = 0; } pPlayer->slope = (-fix16_to_int(pPlayer->q16horiz))<<7; if (pInput->keyFlags.prevItem) { pInput->keyFlags.prevItem = 0; packPrevItem(pPlayer); } if (pInput->keyFlags.nextItem) { pInput->keyFlags.nextItem = 0; packNextItem(pPlayer); } if (pInput->keyFlags.useItem) { pInput->keyFlags.useItem = 0; if (pPlayer->packSlots[pPlayer->packItemId].curAmount > 0) packUseItem(pPlayer, pPlayer->packItemId); } if (pInput->useFlags.useBeastVision) { pInput->useFlags.useBeastVision = 0; if (pPlayer->packSlots[3].curAmount > 0) packUseItem(pPlayer, 3); } if (pInput->useFlags.useCrystalBall) { pInput->useFlags.useCrystalBall = 0; if (pPlayer->packSlots[2].curAmount > 0) packUseItem(pPlayer, 2); } if (pInput->useFlags.useJumpBoots) { pInput->useFlags.useJumpBoots = 0; if (pPlayer->packSlots[4].curAmount > 0) packUseItem(pPlayer, 4); } if (pInput->useFlags.useMedKit) { pInput->useFlags.useMedKit = 0; if (pPlayer->packSlots[0].curAmount > 0) packUseItem(pPlayer, 0); } if (pInput->keyFlags.holsterWeapon) { pInput->keyFlags.holsterWeapon = 0; if (pPlayer->curWeapon) { WeaponLower(pPlayer); viewSetMessage("Holstering weapon"); } } CheckPickUp(pPlayer); } void playerProcess(PLAYER *pPlayer) { spritetype *pSprite = pPlayer->pSprite; int nSprite = pPlayer->nSprite; int nXSprite = pSprite->extra; XSPRITE *pXSprite = pPlayer->pXSprite; POSTURE* pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture]; powerupProcess(pPlayer); int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); int dzb = (bottom-pSprite->z)/4; int dzt = (pSprite->z-top)/4; int dw = pSprite->clipdist<<2; if (!gNoClip) { short nSector = pSprite->sectnum; if (pushmove_old(&pSprite->x, &pSprite->y, &pSprite->z, &nSector, dw, dzt, dzb, CLIPMASK0) == -1) actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_0, 500<<4); if (pSprite->sectnum != nSector) { if (nSector == -1) { nSector = pSprite->sectnum; actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_0, 500<<4); } dassert(nSector >= 0 && nSector < kMaxSectors); ChangeSpriteSect(nSprite, nSector); } } ProcessInput(pPlayer); int nSpeed = approxDist(xvel[nSprite], yvel[nSprite]); pPlayer->zViewVel = interpolate(pPlayer->zViewVel, zvel[nSprite], 0x7000); int dz = pPlayer->pSprite->z-pPosture->eyeAboveZ-pPlayer->zView; if (dz > 0) pPlayer->zViewVel += mulscale16(dz<<8, 0xa000); else pPlayer->zViewVel += mulscale16(dz<<8, 0x1800); pPlayer->zView += pPlayer->zViewVel>>8; pPlayer->zWeaponVel = interpolate(pPlayer->zWeaponVel, zvel[nSprite], 0x5000); dz = pPlayer->pSprite->z-pPosture->weaponAboveZ-pPlayer->zWeapon; if (dz > 0) pPlayer->zWeaponVel += mulscale16(dz<<8, 0x8000); else pPlayer->zWeaponVel += mulscale16(dz<<8, 0xc00); pPlayer->zWeapon += pPlayer->zWeaponVel>>8; pPlayer->bobPhase = ClipLow(pPlayer->bobPhase-4, 0); nSpeed >>= 16; if (pPlayer->posture == 1) { pPlayer->bobAmp = (pPlayer->bobAmp+17)&2047; pPlayer->swayAmp = (pPlayer->swayAmp+17)&2047; pPlayer->bobHeight = mulscale30(pPosture->bobV*10, Sin(pPlayer->bobAmp*2)); pPlayer->bobWidth = mulscale30(pPosture->bobH*pPlayer->bobPhase, Sin(pPlayer->bobAmp-256)); pPlayer->swayHeight = mulscale30(pPosture->swayV*pPlayer->bobPhase, Sin(pPlayer->swayAmp*2)); pPlayer->swayWidth = mulscale30(pPosture->swayH*pPlayer->bobPhase, Sin(pPlayer->swayAmp-0x155)); } else { if (pXSprite->height < 256) { pPlayer->bobAmp = (pPlayer->bobAmp+pPosture->pace[pPlayer->isRunning]*4) & 2047; pPlayer->swayAmp = (pPlayer->swayAmp+(pPosture->pace[pPlayer->isRunning]*4)/2) & 2047; if (pPlayer->isRunning) { if (pPlayer->bobPhase < 60) pPlayer->bobPhase = ClipHigh(pPlayer->bobPhase+nSpeed, 60); } else { if (pPlayer->bobPhase < 30) pPlayer->bobPhase = ClipHigh(pPlayer->bobPhase+nSpeed, 30); } } pPlayer->bobHeight = mulscale30(pPosture->bobV*pPlayer->bobPhase, Sin(pPlayer->bobAmp*2)); pPlayer->bobWidth = mulscale30(pPosture->bobH*pPlayer->bobPhase, Sin(pPlayer->bobAmp-256)); pPlayer->swayHeight = mulscale30(pPosture->swayV*pPlayer->bobPhase, Sin(pPlayer->swayAmp*2)); pPlayer->swayWidth = mulscale30(pPosture->swayH*pPlayer->bobPhase, Sin(pPlayer->swayAmp-0x155)); } pPlayer->flickerEffect = 0; pPlayer->quakeEffect = ClipLow(pPlayer->quakeEffect-4, 0); pPlayer->tiltEffect = ClipLow(pPlayer->tiltEffect-4, 0); pPlayer->visibility = ClipLow(pPlayer->visibility-4, 0); pPlayer->painEffect = ClipLow(pPlayer->painEffect-4, 0); pPlayer->blindEffect = ClipLow(pPlayer->blindEffect-4, 0); pPlayer->pickupEffect = ClipLow(pPlayer->pickupEffect-4, 0); if (pPlayer == gMe && gMe->pXSprite->health == 0) pPlayer->hand = 0; if (!pXSprite->health) return; pPlayer->isUnderwater = 0; if (pPlayer->posture == 1) { pPlayer->isUnderwater = 1; int nSector = pSprite->sectnum; int nLink = gLowerLink[nSector]; if (nLink > 0 && (sprite[nLink].type == kMarkerLowGoo || sprite[nLink].type == kMarkerLowWater)) { if (getceilzofslope(nSector, pSprite->x, pSprite->y) > pPlayer->zView) pPlayer->isUnderwater = 0; } } if (!pPlayer->isUnderwater) { pPlayer->underwaterTime = 1200; pPlayer->chokeEffect = 0; if (packItemActive(pPlayer, 1)) packUseItem(pPlayer, 1); } int nType = kDudePlayer1-kDudeBase; switch (pPlayer->posture) { case 1: seqSpawn(dudeInfo[nType].seqStartID+9, 3, nXSprite, -1); break; case 2: seqSpawn(dudeInfo[nType].seqStartID+10, 3, nXSprite, -1); break; default: if (!nSpeed) seqSpawn(dudeInfo[nType].seqStartID, 3, nXSprite, -1); else seqSpawn(dudeInfo[nType].seqStartID+8, 3, nXSprite, -1); break; } } spritetype *playerFireMissile(PLAYER *pPlayer, int a2, int a3, int a4, int a5, int a6) { return actFireMissile(pPlayer->pSprite, a2, pPlayer->zWeapon-pPlayer->pSprite->z, a3, a4, a5, a6); } spritetype * playerFireThing(PLAYER *pPlayer, int a2, int a3, int thingType, int a5) { dassert(thingType >= kThingBase && thingType < kThingMax); return actFireThing(pPlayer->pSprite, a2, pPlayer->zWeapon-pPlayer->pSprite->z, pPlayer->slope+a3, thingType, a5); } void playerFrag(PLAYER *pKiller, PLAYER *pVictim) { dassert(pKiller != NULL); dassert(pVictim != NULL); char buffer[128] = ""; int nKiller = pKiller->pSprite->type-kDudePlayer1; dassert(nKiller >= 0 && nKiller < kMaxPlayers); int nVictim = pVictim->pSprite->type-kDudePlayer1; dassert(nVictim >= 0 && nVictim < kMaxPlayers); if (myconnectindex == connecthead) { sprintf(buffer, "frag %d killed %d\n", pKiller->nPlayer+1, pVictim->nPlayer+1); sub_7AC28(buffer); buffer[0] = 0; } if (nKiller == nVictim) { pVictim->fraggerId = -1; if (VanillaMode() || gGameOptions.nGameType != 1) { pVictim->fragCount--; pVictim->fragInfo[nVictim]--; } if (gGameOptions.nGameType == 3) dword_21EFB0[pVictim->teamId]--; int nMessage = Random(5); int nSound = gSuicide[nMessage].at4; if (pVictim == gMe && gMe->handTime <= 0) { sprintf(buffer, GStrings("TXTB_KILLSELF")); if (gGameOptions.nGameType > 0 && nSound >= 0) sndStartSample(nSound, 255, 2, 0); } else { sprintf(buffer, gSuicide[nMessage].at0, gProfile[nVictim].name); } } else { if (VanillaMode() || gGameOptions.nGameType != 1) { pKiller->fragCount++; pKiller->fragInfo[nKiller]++; } if (gGameOptions.nGameType == 3) { if (pKiller->teamId == pVictim->teamId) dword_21EFB0[pKiller->teamId]--; else { dword_21EFB0[pKiller->teamId]++; dword_21EFD0[pKiller->teamId]+=120; } } int nMessage = Random(25); int nSound = gVictory[nMessage].at4; const char* pzMessage = gVictory[nMessage].at0; sprintf(buffer, pzMessage, gProfile[nKiller].name, gProfile[nVictim].name); if (gGameOptions.nGameType > 0 && nSound >= 0 && pKiller == gMe) sndStartSample(nSound, 255, 2, 0); } viewSetMessage(buffer); } void FragPlayer(PLAYER *pPlayer, int nSprite) { spritetype *pSprite = NULL; if (nSprite >= 0) pSprite = &sprite[nSprite]; if (pSprite && IsPlayerSprite(pSprite)) { PLAYER *pKiller = &gPlayer[pSprite->type - kDudePlayer1]; playerFrag(pKiller, pPlayer); int nTeam1 = pKiller->teamId&1; int nTeam2 = pPlayer->teamId&1; if (nTeam1 == 0) { if (nTeam1 != nTeam2) evSend(0, 0, 15, kCmdToggle, pKiller->nSprite); else evSend(0, 0, 16, kCmdToggle, pKiller->nSprite); } else { if (nTeam1 == nTeam2) evSend(0, 0, 16, kCmdToggle, pKiller->nSprite); else evSend(0, 0, 15, kCmdToggle, pKiller->nSprite); } } } int playerDamageArmor(PLAYER *pPlayer, DAMAGE_TYPE nType, int nDamage) { DAMAGEINFO *pDamageInfo = &damageInfo[nType]; int nArmorType = pDamageInfo->at0; if (nArmorType >= 0 && pPlayer->armor[nArmorType]) { #if 0 int vbp = (nDamage*7)/8-nDamage/4; int v8 = pPlayer->at33e[nArmorType]; int t = nDamage/4 + vbp * v8 / 3200; v8 -= t; #endif int v8 = pPlayer->armor[nArmorType]; int t = scale(v8, 0, 3200, nDamage/4, (nDamage*7)/8); v8 -= t; nDamage -= t; pPlayer->armor[nArmorType] = ClipLow(v8, 0); } return nDamage; } spritetype *sub_40A94(PLAYER *pPlayer, int a2) { char buffer[80]; spritetype *pSprite = NULL; switch (a2) { case kItemFlagA: pPlayer->hasFlag &= ~1; pSprite = actDropObject(pPlayer->pSprite, kItemFlagA); if (pSprite) pSprite->owner = pPlayer->used2[0]; gBlueFlagDropped = true; sprintf(buffer, "%s dropped Blue Flag", gProfile[pPlayer->nPlayer].name); sndStartSample(8005, 255, 2, 0); viewSetMessage(buffer); break; case kItemFlagB: pPlayer->hasFlag &= ~2; pSprite = actDropObject(pPlayer->pSprite, kItemFlagB); if (pSprite) pSprite->owner = pPlayer->used2[1]; gRedFlagDropped = true; sprintf(buffer, "%s dropped Red Flag", gProfile[pPlayer->nPlayer].name); sndStartSample(8004, 255, 2, 0); viewSetMessage(buffer); break; } return pSprite; } int playerDamageSprite(int nSource, PLAYER *pPlayer, DAMAGE_TYPE nDamageType, int nDamage) { dassert(nSource < kMaxSprites); dassert(pPlayer != NULL); if (pPlayer->damageControl[nDamageType]) return 0; nDamage = playerDamageArmor(pPlayer, nDamageType, nDamage); pPlayer->painEffect = ClipHigh(pPlayer->painEffect+(nDamage>>3), 600); spritetype *pSprite = pPlayer->pSprite; XSPRITE *pXSprite = pPlayer->pXSprite; int nXSprite = pSprite->extra; int nXSector = sector[pSprite->sectnum].extra; DUDEINFO *pDudeInfo = &dudeInfo[pSprite->type-kDudeBase]; int nDeathSeqID = -1; int nKneelingPlayer = -1; int nSprite = pSprite->index; char va = playerSeqPlaying(pPlayer, 16); if (!pXSprite->health) { if (va) { switch (nDamageType) { case DAMAGE_TYPE_5: nDeathSeqID = 18; sfxPlay3DSound(pSprite, 716, 0, 0); break; case DAMAGE_TYPE_3: GibSprite(pSprite, GIBTYPE_7, NULL, NULL); GibSprite(pSprite, GIBTYPE_15, NULL, NULL); pPlayer->pSprite->cstat |= 32768; nDeathSeqID = 17; break; default: { int top, bottom; GetSpriteExtents(pSprite, &top, &bottom); CGibPosition gibPos(pSprite->x, pSprite->y, top); CGibVelocity gibVel(xvel[pSprite->index]>>1, yvel[pSprite->index]>>1, -0xccccc); GibSprite(pSprite, GIBTYPE_27, &gibPos, &gibVel); GibSprite(pSprite, GIBTYPE_7, NULL, NULL); fxSpawnBlood(pSprite, nDamage<<4); fxSpawnBlood(pSprite, nDamage<<4); nDeathSeqID = 17; break; } } } } else { int nHealth = pXSprite->health-nDamage; pXSprite->health = ClipLow(nHealth, 0); if (pXSprite->health > 0 && pXSprite->health < 16) { nDamageType = DAMAGE_TYPE_2; pXSprite->health = 0; nHealth = -25; } if (pXSprite->health > 0) { DAMAGEINFO *pDamageInfo = &damageInfo[nDamageType]; int nSound; if (nDamage >= (10<<4)) nSound = pDamageInfo->at4[0]; else nSound = pDamageInfo->at4[Random(3)]; if (nDamageType == DAMAGE_TYPE_4 && pXSprite->medium == kMediumWater && !pPlayer->hand) nSound = 714; sfxPlay3DSound(pSprite, nSound, 0, 6); return nDamage; } sfxKill3DSound(pPlayer->pSprite, -1, 441); if (gGameOptions.nGameType == 3 && pPlayer->hasFlag) { if (pPlayer->hasFlag&1) sub_40A94(pPlayer, kItemFlagA); if (pPlayer->hasFlag&2) sub_40A94(pPlayer, kItemFlagB); } pPlayer->deathTime = 0; pPlayer->qavLoop = 0; pPlayer->curWeapon = 0; pPlayer->fraggerId = nSource; pPlayer->voodooTargets = 0; if (nDamageType == DAMAGE_TYPE_3 && nDamage < (9<<4)) nDamageType = DAMAGE_TYPE_0; switch (nDamageType) { case DAMAGE_TYPE_3: sfxPlay3DSound(pSprite, 717, 0, 0); GibSprite(pSprite, GIBTYPE_7, NULL, NULL); GibSprite(pSprite, GIBTYPE_15, NULL, NULL); pPlayer->pSprite->cstat |= 32768; nDeathSeqID = 2; break; case DAMAGE_TYPE_1: sfxPlay3DSound(pSprite, 718, 0, 0); nDeathSeqID = 3; break; case DAMAGE_TYPE_4: nDeathSeqID = 1; break; default: if (nHealth < -20 && gGameOptions.nGameType >= 2 && Chance(0x4000)) { DAMAGEINFO *pDamageInfo = &damageInfo[nDamageType]; sfxPlay3DSound(pSprite, pDamageInfo->at10[0], 0, 2); nDeathSeqID = 16; nKneelingPlayer = nPlayerKneelClient; powerupActivate(pPlayer, kPwUpDeliriumShroom); pXSprite->target = nSource; evPost(pSprite->index, 3, 15, kCallbackFinishHim); } else { sfxPlay3DSound(pSprite, 716, 0, 0); nDeathSeqID = 1; } break; } } if (nDeathSeqID < 0) return nDamage; if (nDeathSeqID != 16) { powerupClear(pPlayer); if (nXSector > 0 && xsector[nXSector].Exit) trTriggerSector(pSprite->sectnum, &xsector[nXSector], kCmdSectorExit, nSprite); pSprite->flags |= 7; for (int p = connecthead; p >= 0; p = connectpoint2[p]) { if (gPlayer[p].fraggerId == nSprite && gPlayer[p].deathTime > 0) gPlayer[p].fraggerId = -1; } FragPlayer(pPlayer, nSource); trTriggerSprite(nSprite, pXSprite, kCmdOff, nSource); } dassert(gSysRes.Lookup(pDudeInfo->seqStartID + nDeathSeqID, "SEQ") != NULL); seqSpawn(pDudeInfo->seqStartID+nDeathSeqID, 3, nXSprite, nKneelingPlayer); return nDamage; } int UseAmmo(PLAYER *pPlayer, int nAmmoType, int nDec) { if (gInfiniteAmmo) return 9999; if (nAmmoType == -1) return 9999; pPlayer->ammoCount[nAmmoType] = ClipLow(pPlayer->ammoCount[nAmmoType]-nDec, 0); return pPlayer->ammoCount[nAmmoType]; } void sub_41250(PLAYER *pPlayer) { int v4 = pPlayer->aim.dz; int dz = pPlayer->zWeapon-pPlayer->pSprite->z; if (UseAmmo(pPlayer, 9, 0) < 8) { pPlayer->voodooTargets = 0; return; } for (int i = 0; i < 4; i++) { int ang1 = (pPlayer->voodooVar1+pPlayer->vodooVar2)&2047; actFireVector(pPlayer->pSprite, 0, dz, Cos(ang1)>>16, Sin(ang1)>>16, v4, VECTOR_TYPE_21); int ang2 = (pPlayer->voodooVar1+2048-pPlayer->vodooVar2)&2047; actFireVector(pPlayer->pSprite, 0, dz, Cos(ang2)>>16, Sin(ang2)>>16, v4, VECTOR_TYPE_21); } pPlayer->voodooTargets = ClipLow(pPlayer->voodooTargets-1, 0); } void playerLandingSound(PLAYER *pPlayer) { static int surfaceSound[] = { -1, 600, 601, 602, 603, 604, 605, 605, 605, 600, 605, 605, 605, 604, 603 }; spritetype *pSprite = pPlayer->pSprite; SPRITEHIT *pHit = &gSpriteHit[pSprite->extra]; if (pHit->florhit) { if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, &sprite[pHit->florhit & 0x3fff])) return; char nSurf = tileGetSurfType(pHit->florhit); if (nSurf) sfxPlay3DSound(pSprite, surfaceSound[nSurf], -1, 0); } } void PlayerSurvive(int, int nXSprite) { char buffer[80]; XSPRITE *pXSprite = &xsprite[nXSprite]; int nSprite = pXSprite->reference; spritetype *pSprite = &sprite[nSprite]; actHealDude(pXSprite, 1, 2); if (gGameOptions.nGameType > 0 && numplayers > 1) { sfxPlay3DSound(pSprite, 3009, 0, 6); if (IsPlayerSprite(pSprite)) { PLAYER *pPlayer = &gPlayer[pSprite->type-kDudePlayer1]; if (pPlayer == gMe) viewSetMessage(GStrings("TXT_LIVEAGAIM")); else { sprintf(buffer, "%s lives again!", gProfile[pPlayer->nPlayer].name); viewSetMessage(buffer); } pPlayer->input.newWeapon = 1; } } } void PlayerKneelsOver(int, int nXSprite) { XSPRITE *pXSprite = &xsprite[nXSprite]; for (int p = connecthead; p >= 0; p = connectpoint2[p]) { if (gPlayer[p].pXSprite == pXSprite) { PLAYER *pPlayer = &gPlayer[p]; playerDamageSprite(pPlayer->fraggerId, pPlayer, DAMAGE_TYPE_5, 500<<4); return; } } } class PlayerLoadSave : public LoadSave { public: virtual void Load(void); virtual void Save(void); }; void PlayerLoadSave::Load(void) { const char buffer[2048] = ""; Read(dword_21EFB0, sizeof(dword_21EFB0)); Read(&gNetPlayers, sizeof(gNetPlayers)); Read(&gProfile, sizeof(gProfile)); Read(&gPlayer, sizeof(gPlayer)); Read((void*)&buffer, sizeof(kPlayerCtrlSigStart)); Read(&gPlayerCtrl, sizeof(gPlayerCtrl)); Read((void*)&buffer, sizeof(kPlayerCtrlSigEnd)); for (int i = 0; i < gNetPlayers; i++) { gPlayer[i].pSprite = &sprite[gPlayer[i].nSprite]; gPlayer[i].pXSprite = &xsprite[gPlayer[i].pSprite->extra]; gPlayer[i].pDudeInfo = &dudeInfo[gPlayer[i].pSprite->type-kDudeBase]; // by NoOne: load qav scene if (gPlayer[i].sceneQav != -1) { if (gPlayerCtrl[i].qavScene.qavResrc == NULL) gPlayer[i].sceneQav = -1; else { QAV* pQav = qavSceneLoad(gPlayer[i].sceneQav); if (pQav) { gPlayerCtrl[i].qavScene.qavResrc = pQav; gPlayerCtrl[i].qavScene.qavResrc->Preload(); } else { gPlayer[i].sceneQav = -1; } } } // by NoOne: load posture info /*for (int a = 0; a < kModeMax; a++) { for (int b = 0; b < kPostureMax; b++) { gPosture[a][b] = gPlayerCtrl[i].posture[a][b]; } }*/ } } void PlayerLoadSave::Save(void) { Write(dword_21EFB0, sizeof(dword_21EFB0)); Write(&gNetPlayers, sizeof(gNetPlayers)); Write(&gProfile, sizeof(gProfile)); Write(&gPlayer, sizeof(gPlayer)); ////// by NoOne: copy posture to TRPLAYERCTRL before saving the game /*for (int i = 0; i < gNetPlayers; i++) { for (int a = 0; a < kModeMax; a++) { for (int b = 0; b < kPostureMax; b++) { gPlayerCtrl[i].posture[a][b] = gPosture[a][b]; } } }*/ Write((void*)kPlayerCtrlSigStart, sizeof(kPlayerCtrlSigStart)); Write(&gPlayerCtrl, sizeof(gPlayerCtrl)); Write((void*)kPlayerCtrlSigEnd, sizeof(kPlayerCtrlSigEnd)); ////// } static PlayerLoadSave *myLoadSave; void PlayerLoadSaveConstruct(void) { myLoadSave = new PlayerLoadSave(); } END_BLD_NS