mirror of
https://github.com/ZDoom/Raze.git
synced 2024-12-14 06:31:34 +00:00
9433e9bdb1
- clear QAVSCENE's initiator pointer at the start of a level or when it holds an actor that's about to be deleted. Seems there is no proper bookkeeping here, so until we can GC actors it needs to be done manually.
2486 lines
85 KiB
C++
2486 lines
85 KiB
C++
//-------------------------------------------------------------------------
|
|
/*
|
|
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 <stdlib.h>
|
|
#include <string.h>
|
|
#include "automap.h"
|
|
#include "compat.h"
|
|
#include "build.h"
|
|
|
|
#include "blood.h"
|
|
#include "gstrings.h"
|
|
#include "gamestate.h"
|
|
#include "automap.h"
|
|
|
|
BEGIN_BLD_NS
|
|
|
|
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
|
|
};
|
|
|
|
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 armor0;
|
|
int armor0max;
|
|
int armor1;
|
|
int armor1max;
|
|
int armor2;
|
|
int armor2max;
|
|
};
|
|
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 }
|
|
};
|
|
|
|
|
|
|
|
struct VICTORY {
|
|
const char *message;
|
|
int Kills;
|
|
};
|
|
|
|
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 *message;
|
|
int Kills;
|
|
};
|
|
|
|
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 armorType;
|
|
int Kills[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 }
|
|
};
|
|
|
|
int powerupCheck(PLAYER *pPlayer, int nPowerUp)
|
|
{
|
|
assert(pPlayer != NULL);
|
|
assert(nPowerUp >= 0 && nPowerUp < kMaxPowerUps);
|
|
int nPack = powerupToPackItem(nPowerUp);
|
|
if (nPack >= 0 && !packItemActive(pPlayer, nPack))
|
|
return 0;
|
|
return pPlayer->pwUpTime[nPowerUp];
|
|
}
|
|
|
|
|
|
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) {
|
|
#ifdef NOONE_EXTENSIONS
|
|
case kItemModernMapLevel:
|
|
if (gModernMap) gFullMap = true;
|
|
break;
|
|
case kItemShroomShrink:
|
|
if (!gModernMap) break;
|
|
else if (isGrown(pPlayer->pSprite)) playerDeactivateShrooms(pPlayer);
|
|
else playerSizeShrink(pPlayer, 2);
|
|
break;
|
|
case kItemShroomGrow:
|
|
if (!gModernMap) break;
|
|
else if (isShrinked(pPlayer->pSprite)) playerDeactivateShrooms(pPlayer);
|
|
else {
|
|
playerSizeGrow(pPlayer, 2);
|
|
if (powerupCheck(&gPlayer[pPlayer->pSprite->type - kDudePlayer1], kPwUpShadowCloak) > 0) {
|
|
powerupDeactivate(pPlayer, kPwUpShadowCloak);
|
|
pPlayer->pwUpTime[kPwUpShadowCloak] = 0;
|
|
}
|
|
|
|
if (ceilIsTooLow(pPlayer->actor()))
|
|
actDamageSprite(pPlayer->actor(), pPlayer->actor(), kDamageExplode, 65535);
|
|
}
|
|
break;
|
|
#endif
|
|
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->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) {
|
|
#ifdef NOONE_EXTENSIONS
|
|
case kItemShroomShrink:
|
|
if (gModernMap) {
|
|
playerSizeReset(pPlayer);
|
|
if (ceilIsTooLow(pPlayer->actor()))
|
|
actDamageSprite(pPlayer->actor(), pPlayer->actor(), kDamageExplode, 65535);
|
|
}
|
|
break;
|
|
case kItemShroomGrow:
|
|
if (gModernMap) playerSizeReset(pPlayer);
|
|
break;
|
|
#endif
|
|
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->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;
|
|
}
|
|
}
|
|
|
|
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:
|
|
I_Error("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
|
|
I_Error("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)
|
|
{
|
|
pPlayer->packItemId = nPack;
|
|
|
|
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(pPlayer->actor(), 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:
|
|
I_Error("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 i = 0; i < 2; i++)
|
|
{
|
|
for (int nPrev = pPlayer->packItemId-1; nPrev >= 0; nPrev--)
|
|
{
|
|
if (pPlayer->packSlots[nPrev].curAmount)
|
|
{
|
|
pPlayer->packItemId = nPrev;
|
|
pPlayer->packItemTime = 600;
|
|
return;
|
|
}
|
|
}
|
|
pPlayer->packItemId = 4;
|
|
if (pPlayer->packSlots[4].curAmount) break;
|
|
}
|
|
}
|
|
|
|
pPlayer->packItemTime = 600;
|
|
}
|
|
|
|
void packNextItem(PLAYER* pPlayer)
|
|
{
|
|
if (pPlayer->packItemTime > 0)
|
|
{
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
for (int nNext = pPlayer->packItemId + 1; nNext < 5; nNext++)
|
|
{
|
|
if (pPlayer->packSlots[nNext].curAmount)
|
|
{
|
|
pPlayer->packItemId = nNext;
|
|
pPlayer->packItemTime = 600;
|
|
return;
|
|
}
|
|
}
|
|
pPlayer->packItemId = 0;
|
|
if (pPlayer->packSlots[0].curAmount) 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)
|
|
{
|
|
assert(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->damageVal[i] = MulScale(Handicap[gSkill], pDudeInfo->startDamage[i], 8);
|
|
}
|
|
|
|
void playerSetGodMode(PLAYER *pPlayer, bool bGodMode)
|
|
{
|
|
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 playerResetPosture(PLAYER* pPlayer) {
|
|
memcpy(pPlayer->pPosture, gPostureDefaults, sizeof(gPostureDefaults));
|
|
}
|
|
|
|
void playerStart(int nPlayer, int bNewLevel)
|
|
{
|
|
PLAYER* pPlayer = &gPlayer[nPlayer];
|
|
InputPacket* pInput = &pPlayer->input;
|
|
ZONE* pStartZone = NULL;
|
|
|
|
// normal start position
|
|
if (gGameOptions.nGameType <= 1)
|
|
pStartZone = &gStartZone[nPlayer];
|
|
|
|
#ifdef NOONE_EXTENSIONS
|
|
// 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
|
|
int i;
|
|
SectIterator it(pStartZone->sectnum);
|
|
while ((i = it.NextIndex()) >= 0)
|
|
{
|
|
spritetype* pSprite = &sprite[i];
|
|
if (pStartZone->x == pSprite->x && pStartZone->y == pSprite->y && IsPlayerSprite(pSprite)) {
|
|
pStartZone = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pStartZone != NULL)
|
|
break;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
else {
|
|
pStartZone = &gStartZone[Random(8)];
|
|
}
|
|
|
|
auto actor = actSpawnSprite(pStartZone->sectnum, pStartZone->x, pStartZone->y, pStartZone->z, 6, 1);
|
|
spritetype* pSprite = &actor->s();
|
|
assert(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)
|
|
actor->s().cstat2 |= CSTAT2_SPRITE_MAPPED;
|
|
int top, bottom;
|
|
GetSpriteExtents(pSprite, &top, &bottom);
|
|
pSprite->z -= bottom - pSprite->z;
|
|
pSprite->pal = 11+(pPlayer->teamId&3);
|
|
pSprite->ang = pStartZone->ang;
|
|
pPlayer->angle.ang = buildang(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->horizon.horiz = pPlayer->horizon.horizoff = q16horiz(0);
|
|
pPlayer->slope = 0;
|
|
pPlayer->fraggerId = -1;
|
|
pPlayer->underwaterTime = 1200;
|
|
pPlayer->bubbleTime = 0;
|
|
pPlayer->restTime = 0;
|
|
pPlayer->kickPower = 0;
|
|
pPlayer->laughCount = 0;
|
|
pPlayer->angle.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 && !bNewLevel))
|
|
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 = kWeapNone;
|
|
actor->xvel() = actor->yvel() = actor->zvel() = 0;
|
|
pInput->avel = 0;
|
|
pInput->actions = 0;
|
|
pInput->fvel = 0;
|
|
pInput->svel = 0;
|
|
pInput->horz = 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 = kQAVNone;
|
|
pPlayer->qavLastTick = 0;
|
|
pPlayer->qavTimer = 0;
|
|
#ifdef NOONE_EXTENSIONS
|
|
playerQavSceneReset(pPlayer); // reset qav scene
|
|
|
|
// assign or update player's sprite index for conditions
|
|
if (gModernMap) {
|
|
|
|
int nSprite;
|
|
StatIterator it(kStatModernPlayerLinker);
|
|
while ((nSprite = it.NextIndex()) >= 0)
|
|
{
|
|
XSPRITE* pXCtrl = &xsprite[sprite[nSprite].extra];
|
|
if (pXCtrl->data1 == pPlayer->nPlayer + 1) {
|
|
int nSpriteOld = pXCtrl->sysData1;
|
|
trPlayerCtrlLink(pXCtrl, pPlayer, (nSpriteOld < 0) ? true : false);
|
|
if (nSpriteOld > 0)
|
|
condUpdateObjectIndex(OBJ_SPRITE, nSpriteOld, pXCtrl->sysData1);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
pPlayer->hand = 0;
|
|
pPlayer->nWaterPal = 0;
|
|
playerResetPowerUps(pPlayer);
|
|
|
|
if (pPlayer == gMe)
|
|
{
|
|
viewInitializePrediction();
|
|
}
|
|
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
|
|
};
|
|
assert(pPlayer != NULL);
|
|
for (int i = 0; i < 14; i++)
|
|
{
|
|
pPlayer->hasWeapon[i] = gInfiniteAmmo;
|
|
pPlayer->weaponMode[i] = 0;
|
|
}
|
|
pPlayer->hasWeapon[1] = 1;
|
|
pPlayer->curWeapon = kWeapNone;
|
|
pPlayer->qavCallback = -1;
|
|
pPlayer->newWeapon = kWeapPitchFork;
|
|
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 = kQAVNone;
|
|
pPlayer->qavLoop = 0;
|
|
pPlayer->qavLastTick = 0;
|
|
pPlayer->qavTimer = 0;
|
|
pPlayer->packItemId = -1;
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
pPlayer->packSlots[i].isActive = 0;
|
|
pPlayer->packSlots[i].curAmount = 0;
|
|
}
|
|
#ifdef NOONE_EXTENSIONS
|
|
playerQavSceneReset(pPlayer);
|
|
#endif
|
|
// reset posture (mainly required for resetting movement speed and jump height)
|
|
playerResetPosture(pPlayer);
|
|
|
|
}
|
|
|
|
int team_score[8];
|
|
int team_ticker[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(team_score, 0, sizeof(team_score));
|
|
memset(team_ticker, 0, sizeof(team_ticker));
|
|
memset(pPlayer->fragInfo, 0, sizeof(pPlayer->fragInfo));
|
|
|
|
if (!(a2&1))
|
|
playerReset(pPlayer);
|
|
}
|
|
|
|
char findDroppedLeech(PLAYER *a1, spritetype *a2)
|
|
{
|
|
int nSprite;
|
|
StatIterator it(kStatThing);
|
|
while ((nSprite = it.NextIndex()) >= 0)
|
|
{
|
|
if (a2 && a2->index == nSprite)
|
|
continue;
|
|
spritetype *pSprite = &sprite[nSprite];
|
|
if (pSprite->type == kThingDroppedLifeLeech && pSprite->owner == a1->nSprite)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
char PickupItem(PLAYER *pPlayer, spritetype *pItem) {
|
|
|
|
auto itemactor = &bloodActors[pItem->index];
|
|
spritetype *pSprite = pPlayer->pSprite; XSPRITE *pXSprite = pPlayer->pXSprite;
|
|
char buffer[80]; int pickupSnd = 775; int nType = pItem->type - kItemBase;
|
|
|
|
switch (pItem->type) {
|
|
case kItemShadowCloak:
|
|
#ifdef NOONE_EXTENSIONS
|
|
if (isGrown(pPlayer->pSprite) || !powerupActivate(pPlayer, nType)) return false;
|
|
#else
|
|
if (!powerupActivate(pPlayer, nType)) return false;
|
|
#endif
|
|
break;
|
|
#ifdef NOONE_EXTENSIONS
|
|
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;
|
|
#endif
|
|
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);
|
|
sprintf(buffer, "%s stole Blue Flag", PlayerName(pPlayer->nPlayer));
|
|
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);
|
|
sprintf(buffer, "%s returned Blue Flag", PlayerName(pPlayer->nPlayer));
|
|
sndStartSample(8003, 255, 2, 0);
|
|
viewSetMessage(buffer);
|
|
}
|
|
|
|
if ((pPlayer->hasFlag & 2) != 0 && pXItem->state) {
|
|
pPlayer->hasFlag &= ~2;
|
|
pPlayer->used2[1] = -1;
|
|
team_score[pPlayer->teamId] += 10;
|
|
team_ticker[pPlayer->teamId] += 240;
|
|
evSendGame(81, kCmdOn);
|
|
sprintf(buffer, "%s captured Red Flag!", PlayerName(pPlayer->nPlayer));
|
|
sndStartSample(8001, 255, 2, 0);
|
|
viewSetMessage(buffer);
|
|
}
|
|
}
|
|
|
|
}
|
|
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);
|
|
sprintf(buffer, "%s stole Red Flag", PlayerName(pPlayer->nPlayer));
|
|
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);
|
|
sprintf(buffer, "%s returned Red Flag", PlayerName(pPlayer->nPlayer));
|
|
sndStartSample(8002, 255, 2, 0);
|
|
viewSetMessage(buffer);
|
|
}
|
|
if ((pPlayer->hasFlag & 1) != 0 && pXItem->state)
|
|
{
|
|
pPlayer->hasFlag &= ~1;
|
|
pPlayer->used2[0] = -1;
|
|
team_score[pPlayer->teamId] += 10;
|
|
team_ticker[pPlayer->teamId] += 240;
|
|
evSendGame(80, kCmdOn);
|
|
sprintf(buffer, "%s captured Blue Flag!", PlayerName(pPlayer->nPlayer));
|
|
sndStartSample(8000, 255, 2, 0);
|
|
viewSetMessage(buffer);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
case kItemFlagA: {
|
|
if (gGameOptions.nGameType != 3) return 0;
|
|
gBlueFlagDropped = false;
|
|
const bool enemyTeam = (pPlayer->teamId&1) == 1;
|
|
if (!enemyTeam && (pItem->owner >= 0) && (pItem->owner < kMaxSprites)) {
|
|
pPlayer->hasFlag &= ~1;
|
|
pPlayer->used2[0] = -1;
|
|
spritetype* pOwner = &sprite[pItem->owner];
|
|
XSPRITE* pXOwner = &xsprite[pOwner->extra];
|
|
trTriggerSprite(pOwner->index, pXOwner, kCmdOn);
|
|
sprintf(buffer, "%s returned Blue Flag", PlayerName(pPlayer->nPlayer));
|
|
sndStartSample(8003, 255, 2, 0);
|
|
viewSetMessage(buffer);
|
|
break;
|
|
}
|
|
pPlayer->hasFlag |= 1;
|
|
pPlayer->used2[0] = pItem->owner;
|
|
if (enemyTeam)
|
|
{
|
|
sprintf(buffer, "%s stole Blue Flag", PlayerName(pPlayer->nPlayer));
|
|
sndStartSample(8007, 255, 2, 0);
|
|
viewSetMessage(buffer);
|
|
}
|
|
break;
|
|
}
|
|
case kItemFlagB: {
|
|
if (gGameOptions.nGameType != 3) return 0;
|
|
gRedFlagDropped = false;
|
|
const bool enemyTeam = (pPlayer->teamId&1) == 0;
|
|
if (!enemyTeam && (pItem->owner >= 0) && (pItem->owner < kMaxSprites)) {
|
|
pPlayer->hasFlag &= ~2;
|
|
pPlayer->used2[1] = -1;
|
|
spritetype* pOwner = &sprite[pItem->owner];
|
|
XSPRITE* pXOwner = &xsprite[pOwner->extra];
|
|
trTriggerSprite(pOwner->index, pXOwner, kCmdOn);
|
|
sprintf(buffer, "%s returned Red Flag", PlayerName(pPlayer->nPlayer));
|
|
sndStartSample(8002, 255, 2, 0);
|
|
viewSetMessage(buffer);
|
|
break;
|
|
}
|
|
pPlayer->hasFlag |= 2;
|
|
pPlayer->used2[1] = pItem->owner;
|
|
if (enemyTeam)
|
|
{
|
|
sprintf(buffer, "%s stole Red Flag", PlayerName(pPlayer->nPlayer));
|
|
sndStartSample(8006, 255, 2, 0);
|
|
viewSetMessage(buffer);
|
|
}
|
|
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->armor1max) {
|
|
pPlayer->armor[1] = ClipHigh(pPlayer->armor[1]+pArmorData->armor1, pArmorData->armor1max);
|
|
pickedUp = true;
|
|
}
|
|
|
|
if (pPlayer->armor[0] < pArmorData->armor0max) {
|
|
pPlayer->armor[0] = ClipHigh(pPlayer->armor[0]+pArmorData->armor0, pArmorData->armor0max);
|
|
pickedUp = true;
|
|
}
|
|
|
|
if (pPlayer->armor[2] < pArmorData->armor2max) {
|
|
pPlayer->armor[2] = ClipHigh(pPlayer->armor[2]+pArmorData->armor2, pArmorData->armor2max);
|
|
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;
|
|
#ifdef NOONE_EXTENSIONS
|
|
// allow custom amount for item
|
|
if (gModernMap && sprite[pItem->index].extra >= 0 && xsprite[sprite[pItem->index].extra].data1 > 0)
|
|
addPower = xsprite[sprite[pItem->index].extra].data1;
|
|
#endif
|
|
|
|
if (!actHealDude(pPlayer->actor(), 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) {
|
|
const AMMOITEMDATA* pAmmoItemData = &gAmmoItemData[pAmmo->type - kItemAmmoBase];
|
|
int nAmmoType = pAmmoItemData->type;
|
|
|
|
if (pPlayer->ammoCount[nAmmoType] >= gAmmoInfo[nAmmoType].max) return 0;
|
|
#ifdef NOONE_EXTENSIONS
|
|
else if (gModernMap && pAmmo->extra >= 0 && xsprite[pAmmo->extra].data1 > 0) // allow custom amount for item
|
|
pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + xsprite[pAmmo->extra].data1, gAmmoInfo[nAmmoType].max);
|
|
#endif
|
|
else
|
|
pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType]+pAmmoItemData->count, 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)
|
|
{
|
|
auto actor = &bloodActors[pWeapon->index];
|
|
const 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 && findDroppedLeech(pPlayer, NULL))
|
|
return 0;
|
|
pPlayer->hasWeapon[nWeaponType] = 1;
|
|
if (nAmmoType == -1) return 0;
|
|
// allow to set custom ammo count for weapon pickups
|
|
#ifdef NOONE_EXTENSIONS
|
|
else if (gModernMap && pWeapon->extra >= 0 && xsprite[pWeapon->extra].data1 > 0)
|
|
pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + xsprite[pWeapon->extra].data1, gAmmoInfo[nAmmoType].max);
|
|
#endif
|
|
else
|
|
pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + pWeaponItemData->count, 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(actor) || nAmmoType == -1 || pPlayer->ammoCount[nAmmoType] >= gAmmoInfo[nAmmoType].max) return 0;
|
|
#ifdef NOONE_EXTENSIONS
|
|
else if (gModernMap && pWeapon->extra >= 0 && xsprite[pWeapon->extra].data1 > 0)
|
|
pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType] + xsprite[pWeapon->extra].data1, gAmmoInfo[nAmmoType].max);
|
|
#endif
|
|
else
|
|
pPlayer->ammoCount[nAmmoType] = ClipHigh(pPlayer->ammoCount[nAmmoType]+pWeaponItemData->count, gAmmoInfo[nAmmoType].max);
|
|
|
|
sfxPlay3DSound(pPlayer->pSprite, 777, -1, 0);
|
|
return 1;
|
|
}
|
|
|
|
void PickUp(PLAYER *pPlayer, spritetype *pSprite)
|
|
{
|
|
auto actor = &bloodActors[pSprite->index];
|
|
const char *msg = nullptr;
|
|
int nType = pSprite->type;
|
|
char pickedUp = 0;
|
|
int customMsg = -1;
|
|
#ifdef NOONE_EXTENSIONS
|
|
if (gModernMap) { // 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;
|
|
}
|
|
#endif
|
|
|
|
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);
|
|
}
|
|
|
|
if (!actCheckRespawn(actor))
|
|
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;
|
|
int nSprite;
|
|
StatIterator it(kStatItem);
|
|
while ((nSprite = it.NextIndex()) >= 0)
|
|
{
|
|
spritetype *pItem = &sprite[nSprite];
|
|
nNextSprite = nextspritestat[nSprite];
|
|
if (pItem->flags&32)
|
|
continue;
|
|
int dx = abs(x-pItem->x)>>4;
|
|
if (dx > 48)
|
|
continue;
|
|
int dy = abs(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 = bcos(pSprite->ang);
|
|
int y = bsin(pSprite->ang);
|
|
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.hitactor ? gHitInfo.hitactor->s().index : -1;
|
|
*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 && findDroppedLeech(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 = getDudeInfo(pSprite->type)->mass;
|
|
if (nMass)
|
|
{
|
|
int t2 = DivScale(0xccccc, nMass, 8);
|
|
xvel[*a2] += MulScale(x, t2, 16);
|
|
yvel[*a2] += MulScale(y, t2, 16);
|
|
zvel[*a2] += MulScale(z, t2, 16);
|
|
}
|
|
if (pXSprite->Push && !pXSprite->state && !pXSprite->isTriggered)
|
|
trTriggerSprite(*a2, pXSprite, kCmdSpritePush);
|
|
}
|
|
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;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Player's sprite angle function, called in ProcessInput() or from gi->GetInput() as required.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void UpdatePlayerSpriteAngle(PLAYER *pPlayer)
|
|
{
|
|
pPlayer->pSprite->ang = pPlayer->angle.ang.asbuild();
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Player's slope tilting wrapper function function, called in ProcessInput() or from gi->GetInput() as required.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void doslopetilting(PLAYER* pPlayer, double const scaleAdjust = 1)
|
|
{
|
|
auto* const pSprite = pPlayer->pSprite;
|
|
auto* const pXSprite = pPlayer->pXSprite;
|
|
int const florhit = gSpriteHit[pSprite->extra].florhit.type;
|
|
char const va = pXSprite->height < 16 && (florhit == kHitSector || florhit == 0) ? 1 : 0;
|
|
pPlayer->horizon.calcviewpitch(pSprite->pos.vec2, buildang(pSprite->ang), va, sector[pSprite->sectnum].floorstat & 2, pSprite->sectnum, scaleAdjust);
|
|
}
|
|
|
|
void ProcessInput(PLAYER *pPlayer)
|
|
{
|
|
enum
|
|
{
|
|
Item_MedKit = 0,
|
|
Item_CrystalBall = 1,
|
|
Item_BeastVision = 2,
|
|
Item_JumpBoots = 3
|
|
};
|
|
|
|
pPlayer->horizon.resetadjustment();
|
|
pPlayer->angle.resetadjustment();
|
|
|
|
spritetype *pSprite = pPlayer->pSprite;
|
|
XSPRITE *pXSprite = pPlayer->pXSprite;
|
|
int nSprite = pPlayer->nSprite;
|
|
POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture];
|
|
InputPacket *pInput = &pPlayer->input;
|
|
|
|
// Originally, this was never able to be true due to sloppy input code in the original game.
|
|
// Allow it to become true behind a CVAR to offer an alternate playing experience if desired.
|
|
pPlayer->isRunning = !!(pInput->actions & SB_RUN) && !cl_bloodvanillarun;
|
|
|
|
if ((pInput->actions & SB_BUTTON_MASK) || pInput->fvel || pInput->svel || pInput->avel)
|
|
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->angle.addadjustment(getincanglebam(pPlayer->angle.ang, bvectangbam(sprite[pPlayer->fraggerId].x - pSprite->x, sprite[pPlayer->fraggerId].y - pSprite->y)));
|
|
}
|
|
pPlayer->deathTime += 4;
|
|
if (!bSeqStat)
|
|
pPlayer->horizon.addadjustment(q16horiz(MulScale(0x8000-(Cos(ClipHigh(pPlayer->deathTime<<3, 1024))>>15), gi->playerHorizMax(), 16) - pPlayer->horizon.horiz.asq16()));
|
|
if (pPlayer->curWeapon)
|
|
pInput->setNewWeapon(pPlayer->curWeapon);
|
|
if (pInput->actions & SB_OPEN)
|
|
{
|
|
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)
|
|
{
|
|
gameaction = ga_autoloadgame;
|
|
}
|
|
else
|
|
playerStart(pPlayer->nPlayer);
|
|
}
|
|
pInput->actions &= ~SB_OPEN;
|
|
}
|
|
return;
|
|
}
|
|
if (pPlayer->posture == 1)
|
|
{
|
|
int x = Cos(pSprite->ang);
|
|
int y = Sin(pSprite->ang);
|
|
if (pInput->fvel)
|
|
{
|
|
int forward = pInput->fvel;
|
|
if (forward > 0)
|
|
forward = MulScale(pPosture->frontAccel, forward, 8);
|
|
else
|
|
forward = MulScale(pPosture->backAccel, forward, 8);
|
|
xvel[nSprite] += MulScale(forward, x, 30);
|
|
yvel[nSprite] += MulScale(forward, y, 30);
|
|
}
|
|
if (pInput->svel)
|
|
{
|
|
int strafe = pInput->svel;
|
|
strafe = MulScale(pPosture->sideAccel, strafe, 8);
|
|
xvel[nSprite] += MulScale(strafe, y, 30);
|
|
yvel[nSprite] -= MulScale(strafe, x, 30);
|
|
}
|
|
}
|
|
else if (pXSprite->height < 256)
|
|
{
|
|
int speed = 0x10000;
|
|
if (pXSprite->height > 0)
|
|
speed -= DivScale(pXSprite->height, 256, 16);
|
|
int x = Cos(pSprite->ang);
|
|
int y = Sin(pSprite->ang);
|
|
if (pInput->fvel)
|
|
{
|
|
int forward = pInput->fvel;
|
|
if (forward > 0)
|
|
forward = MulScale(pPosture->frontAccel, forward, 8);
|
|
else
|
|
forward = MulScale(pPosture->backAccel, forward, 8);
|
|
if (pXSprite->height)
|
|
forward = MulScale(forward, speed, 16);
|
|
xvel[nSprite] += MulScale(forward, x, 30);
|
|
yvel[nSprite] += MulScale(forward, y, 30);
|
|
}
|
|
if (pInput->svel)
|
|
{
|
|
int strafe = pInput->svel;
|
|
strafe = MulScale(pPosture->sideAccel, strafe, 8);
|
|
if (pXSprite->height)
|
|
strafe = MulScale(strafe, speed, 16);
|
|
xvel[nSprite] += MulScale(strafe, y, 30);
|
|
yvel[nSprite] -= MulScale(strafe, x, 30);
|
|
}
|
|
}
|
|
|
|
if (SyncInput())
|
|
{
|
|
pPlayer->angle.applyinput(pInput->avel, &pInput->actions);
|
|
}
|
|
|
|
// unconditionally update the player's sprite angle
|
|
// in case game is forcing synchronised input.
|
|
UpdatePlayerSpriteAngle(pPlayer);
|
|
|
|
if (!(pInput->actions & SB_JUMP))
|
|
pPlayer->cantJump = 0;
|
|
|
|
switch (pPlayer->posture) {
|
|
case 1:
|
|
if (pInput->actions & SB_JUMP)
|
|
zvel[nSprite] -= pPosture->normalJumpZ;//0x5b05;
|
|
if (pInput->actions & SB_CROUCH)
|
|
zvel[nSprite] += pPosture->normalJumpZ;//0x5b05;
|
|
break;
|
|
case 2:
|
|
if (!(pInput->actions & SB_CROUCH))
|
|
pPlayer->posture = 0;
|
|
break;
|
|
default:
|
|
if (!pPlayer->cantJump && (pInput->actions & SB_JUMP) && pXSprite->height == 0) {
|
|
#ifdef NOONE_EXTENSIONS
|
|
if ((packItemActive(pPlayer, 4) && pPosture->pwupJumpZ != 0) || pPosture->normalJumpZ != 0)
|
|
#endif
|
|
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->actions & SB_CROUCH)
|
|
pPlayer->posture = 2;
|
|
break;
|
|
}
|
|
if (pInput->actions & SB_OPEN)
|
|
{
|
|
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"));
|
|
auto snd = 3062;
|
|
if (sndCheckPlaying(snd))
|
|
sndStopSample(snd);
|
|
sndStartSample(snd, 255, 2, 0);
|
|
}
|
|
if (!key || pPlayer->hasKey[key])
|
|
trTriggerSector(a2, pXSector, kCmdSpritePush);
|
|
else if (pPlayer == gMe)
|
|
{
|
|
viewSetMessage(GStrings("TXTB_KEY"));
|
|
auto snd = 3063;
|
|
if (sndCheckPlaying(snd))
|
|
sndStopSample(snd);
|
|
sndStartSample(snd, 255, 2, 0);
|
|
}
|
|
}
|
|
break;
|
|
case 0:
|
|
{
|
|
XWALL *pXWall = &xwall[a3];
|
|
int key = pXWall->key;
|
|
if (pXWall->locked && pPlayer == gMe)
|
|
{
|
|
viewSetMessage(GStrings("TXTB_LOCKED"));
|
|
auto snd = 3062;
|
|
if (sndCheckPlaying(snd))
|
|
sndStopSample(snd);
|
|
sndStartSample(snd, 255, 2, 0);
|
|
}
|
|
if (!key || pPlayer->hasKey[key])
|
|
trTriggerWall(a2, pXWall, kCmdWallPush);
|
|
else if (pPlayer == gMe)
|
|
{
|
|
viewSetMessage(GStrings("TXTB_KEY"));
|
|
auto snd = 3063;
|
|
if (sndCheckPlaying(snd))
|
|
sndStopSample(snd);
|
|
sndStartSample(snd, 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);
|
|
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)
|
|
{
|
|
auto pactor = &bloodActors[pPlayer->pSprite->index];
|
|
auto spawned = actSpawnDude(pactor, kDudeHand, pPlayer->pSprite->clipdist<<1, 0);
|
|
spritetype* pSprite2 = &spawned->s();
|
|
pSprite2->ang = (pPlayer->pSprite->ang+1024)&2047;
|
|
int nSprite = pPlayer->pSprite->index;
|
|
int x = bcos(pPlayer->pSprite->ang);
|
|
int y = bsin(pPlayer->pSprite->ang);
|
|
xvel[pSprite2->index] = xvel[nSprite] + MulScale(0x155555, x, 14);
|
|
yvel[pSprite2->index] = yvel[nSprite] + MulScale(0x155555, y, 14);
|
|
zvel[pSprite2->index] = zvel[nSprite];
|
|
pPlayer->hand = 0;
|
|
}
|
|
pInput->actions &= ~SB_OPEN;
|
|
}
|
|
|
|
if (SyncInput())
|
|
{
|
|
pPlayer->horizon.applyinput(pInput->horz, &pInput->actions);
|
|
doslopetilting(pPlayer);
|
|
}
|
|
|
|
// disable synchronised input and input locks if set by game.
|
|
resetForcedSyncInput();
|
|
pPlayer->angle.unlockinput();
|
|
pPlayer->horizon.unlockinput();
|
|
|
|
pPlayer->slope = -pPlayer->horizon.horiz.asq16() >> 9;
|
|
if (pInput->actions & SB_INVPREV)
|
|
{
|
|
pInput->actions&= ~SB_INVPREV;
|
|
packPrevItem(pPlayer);
|
|
}
|
|
if (pInput->actions & SB_INVNEXT)
|
|
{
|
|
pInput->actions &= ~SB_INVNEXT;
|
|
packNextItem(pPlayer);
|
|
}
|
|
if (pInput->actions & SB_INVUSE)
|
|
{
|
|
pInput->actions &= ~SB_INVUSE;
|
|
if (pPlayer->packSlots[pPlayer->packItemId].curAmount > 0)
|
|
packUseItem(pPlayer, pPlayer->packItemId);
|
|
}
|
|
if (pInput->isItemUsed(Item_BeastVision))
|
|
{
|
|
pInput->clearItemUsed(Item_BeastVision);
|
|
if (pPlayer->packSlots[3].curAmount > 0)
|
|
packUseItem(pPlayer, 3);
|
|
}
|
|
if (pInput->isItemUsed(Item_CrystalBall))
|
|
{
|
|
pInput->clearItemUsed(Item_CrystalBall);
|
|
if (pPlayer->packSlots[2].curAmount > 0)
|
|
packUseItem(pPlayer, 2);
|
|
}
|
|
if (pInput->isItemUsed(Item_JumpBoots))
|
|
{
|
|
pInput->clearItemUsed(Item_JumpBoots);
|
|
if (pPlayer->packSlots[4].curAmount > 0)
|
|
packUseItem(pPlayer, 4);
|
|
}
|
|
if (pInput->isItemUsed(Item_MedKit))
|
|
{
|
|
pInput->clearItemUsed(Item_MedKit);
|
|
if (pPlayer->packSlots[0].curAmount > 0)
|
|
packUseItem(pPlayer, 0);
|
|
}
|
|
if (pInput->actions & SB_HOLSTER)
|
|
{
|
|
pInput->actions &= ~SB_HOLSTER;
|
|
if (pPlayer->curWeapon)
|
|
{
|
|
WeaponLower(pPlayer);
|
|
viewSetMessage("Holstering weapon");
|
|
}
|
|
}
|
|
CheckPickUp(pPlayer);
|
|
}
|
|
|
|
void playerProcess(PLAYER *pPlayer)
|
|
{
|
|
auto actor = pPlayer->actor();
|
|
spritetype *pSprite = pPlayer->pSprite;
|
|
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)
|
|
{
|
|
int nSector = pSprite->sectnum;
|
|
if (pushmove(&pSprite->pos, &nSector, dw, dzt, dzb, CLIPMASK0) == -1)
|
|
actDamageSprite(actor, actor, kDamageFall, 500<<4);
|
|
if (pSprite->sectnum != nSector)
|
|
{
|
|
if (nSector == -1)
|
|
{
|
|
nSector = pSprite->sectnum;
|
|
actDamageSprite(actor, actor, kDamageFall, 500<<4);
|
|
}
|
|
assert(nSector >= 0 && nSector < kMaxSectors);
|
|
ChangeSpriteSect(pSprite->index, nSector);
|
|
}
|
|
}
|
|
ProcessInput(pPlayer);
|
|
int nSpeed = approxDist(actor->xvel(), actor->yvel());
|
|
pPlayer->zViewVel = interpolatedvalue(pPlayer->zViewVel, actor->zvel(), 0x7000);
|
|
int dz = pPlayer->pSprite->z-pPosture->eyeAboveZ-pPlayer->zView;
|
|
if (dz > 0)
|
|
pPlayer->zViewVel += MulScale(dz<<8, 0xa000, 16);
|
|
else
|
|
pPlayer->zViewVel += MulScale(dz<<8, 0x1800, 16);
|
|
pPlayer->zView += pPlayer->zViewVel>>8;
|
|
pPlayer->zWeaponVel = interpolatedvalue(pPlayer->zWeaponVel, actor->zvel(), 0x5000);
|
|
dz = pPlayer->pSprite->z-pPosture->weaponAboveZ-pPlayer->zWeapon;
|
|
if (dz > 0)
|
|
pPlayer->zWeaponVel += MulScale(dz<<8, 0x8000, 16);
|
|
else
|
|
pPlayer->zWeaponVel += MulScale(dz<<8, 0xc00, 16);
|
|
pPlayer->zWeapon += pPlayer->zWeaponVel>>8;
|
|
pPlayer->bobPhase = ClipLow(pPlayer->bobPhase-4, 0);
|
|
nSpeed >>= FRACBITS;
|
|
if (pPlayer->posture == 1)
|
|
{
|
|
pPlayer->bobAmp = (pPlayer->bobAmp+17)&2047;
|
|
pPlayer->swayAmp = (pPlayer->swayAmp+17)&2047;
|
|
pPlayer->bobHeight = MulScale(pPosture->bobV*10, Sin(pPlayer->bobAmp*2), 30);
|
|
pPlayer->bobWidth = MulScale(pPosture->bobH*pPlayer->bobPhase, Sin(pPlayer->bobAmp-256), 30);
|
|
pPlayer->swayHeight = MulScale(pPosture->swayV*pPlayer->bobPhase, Sin(pPlayer->swayAmp*2), 30);
|
|
pPlayer->swayWidth = MulScale(pPosture->swayH*pPlayer->bobPhase, Sin(pPlayer->swayAmp-0x155), 30);
|
|
}
|
|
else
|
|
{
|
|
if (pXSprite->height < 256)
|
|
{
|
|
bool running = pPlayer->isRunning && !cl_bloodvanillabobbing;
|
|
pPlayer->bobAmp = (pPlayer->bobAmp+pPosture->pace[running]*4) & 2047;
|
|
pPlayer->swayAmp = (pPlayer->swayAmp+(pPosture->pace[running]*4)/2) & 2047;
|
|
if (running)
|
|
{
|
|
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 = MulScale(pPosture->bobV*pPlayer->bobPhase, Sin(pPlayer->bobAmp*2), 30);
|
|
pPlayer->bobWidth = MulScale(pPosture->bobH*pPlayer->bobPhase, Sin(pPlayer->bobAmp-256), 30);
|
|
pPlayer->swayHeight = MulScale(pPosture->swayV*pPlayer->bobPhase, Sin(pPlayer->swayAmp*2), 30);
|
|
pPlayer->swayWidth = MulScale(pPosture->swayH*pPlayer->bobPhase, Sin(pPlayer->swayAmp-0x155), 30);
|
|
}
|
|
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, actor, -1);
|
|
break;
|
|
case 2:
|
|
seqSpawn(dudeInfo[nType].seqStartID+10, actor, -1);
|
|
break;
|
|
default:
|
|
if (!nSpeed)
|
|
seqSpawn(dudeInfo[nType].seqStartID, actor, -1);
|
|
else
|
|
seqSpawn(dudeInfo[nType].seqStartID+8, actor, -1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
spritetype *playerFireMissile(PLAYER *pPlayer, int a2, int a3, int a4, int a5, int a6)
|
|
{
|
|
auto misl = actFireMissile(&bloodActors[pPlayer->pSprite->index], a2, pPlayer->zWeapon-pPlayer->pSprite->z, a3, a4, a5, a6);
|
|
return misl ? &misl->s() : nullptr;
|
|
}
|
|
|
|
spritetype * playerFireThing(PLAYER *pPlayer, int a2, int a3, int thingType, int a5)
|
|
{
|
|
assert(thingType >= kThingBase && thingType < kThingMax);
|
|
auto misl = actFireThing(&bloodActors[pPlayer->pSprite->index], a2, pPlayer->zWeapon-pPlayer->pSprite->z, pPlayer->slope+a3, thingType, a5);
|
|
return misl ? &misl->s() : nullptr;
|
|
}
|
|
|
|
void playerFrag(PLAYER *pKiller, PLAYER *pVictim)
|
|
{
|
|
assert(pKiller != NULL);
|
|
assert(pVictim != NULL);
|
|
|
|
char buffer[128] = "";
|
|
int nKiller = pKiller->pSprite->type-kDudePlayer1;
|
|
assert(nKiller >= 0 && nKiller < kMaxPlayers);
|
|
int nVictim = pVictim->pSprite->type-kDudePlayer1;
|
|
assert(nVictim >= 0 && nVictim < kMaxPlayers);
|
|
if (nKiller == nVictim)
|
|
{
|
|
pVictim->fraggerId = -1;
|
|
if (VanillaMode() || gGameOptions.nGameType != 1)
|
|
{
|
|
pVictim->fragCount--;
|
|
pVictim->fragInfo[nVictim]--;
|
|
}
|
|
if (gGameOptions.nGameType == 3)
|
|
team_score[pVictim->teamId]--;
|
|
int nMessage = Random(5);
|
|
int nSound = gSuicide[nMessage].Kills;
|
|
if (pVictim == gMe && gMe->handTime <= 0)
|
|
{
|
|
strcpy(buffer, GStrings("TXTB_KILLSELF"));
|
|
if (gGameOptions.nGameType > 0 && nSound >= 0)
|
|
sndStartSample(nSound, 255, 2, 0);
|
|
}
|
|
else
|
|
{
|
|
sprintf(buffer, gSuicide[nMessage].message, PlayerName(nVictim));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (VanillaMode() || gGameOptions.nGameType != 1)
|
|
{
|
|
pKiller->fragCount++;
|
|
pKiller->fragInfo[nKiller]++;
|
|
}
|
|
if (gGameOptions.nGameType == 3)
|
|
{
|
|
if (pKiller->teamId == pVictim->teamId)
|
|
team_score[pKiller->teamId]--;
|
|
else
|
|
{
|
|
team_score[pKiller->teamId]++;
|
|
team_ticker[pKiller->teamId]+=120;
|
|
}
|
|
}
|
|
int nMessage = Random(25);
|
|
int nSound = gVictory[nMessage].Kills;
|
|
const char* pzMessage = gVictory[nMessage].message;
|
|
sprintf(buffer, pzMessage, PlayerName(nKiller), PlayerName(nVictim));
|
|
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)
|
|
evSendGame(15, kCmdToggle);
|
|
else
|
|
evSendGame(16, kCmdToggle);
|
|
}
|
|
else
|
|
{
|
|
if (nTeam1 == nTeam2)
|
|
evSendGame(16, kCmdToggle);
|
|
else
|
|
evSendGame(15, kCmdToggle);
|
|
}
|
|
}
|
|
}
|
|
|
|
int playerDamageArmor(PLAYER *pPlayer, DAMAGE_TYPE nType, int nDamage)
|
|
{
|
|
DAMAGEINFO *pDamageInfo = &damageInfo[nType];
|
|
int nArmorType = pDamageInfo->armorType;
|
|
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 *flagDropped(PLAYER *pPlayer, int a2)
|
|
{
|
|
auto actor = pPlayer->actor();
|
|
char buffer[80];
|
|
spritetype *pSprite = NULL;
|
|
switch (a2)
|
|
{
|
|
case kItemFlagA:
|
|
pPlayer->hasFlag &= ~1;
|
|
pSprite = &actDropObject(actor, kItemFlagA)->s();
|
|
if (pSprite)
|
|
pSprite->owner = pPlayer->used2[0];
|
|
gBlueFlagDropped = true;
|
|
sprintf(buffer, "%s dropped Blue Flag", PlayerName(pPlayer->nPlayer));
|
|
sndStartSample(8005, 255, 2, 0);
|
|
viewSetMessage(buffer);
|
|
break;
|
|
case kItemFlagB:
|
|
pPlayer->hasFlag &= ~2;
|
|
pSprite = &actDropObject(actor, kItemFlagB)->s();
|
|
if (pSprite)
|
|
pSprite->owner = pPlayer->used2[1];
|
|
gRedFlagDropped = true;
|
|
sprintf(buffer, "%s dropped Red Flag", PlayerName(pPlayer->nPlayer));
|
|
sndStartSample(8004, 255, 2, 0);
|
|
viewSetMessage(buffer);
|
|
break;
|
|
}
|
|
return pSprite;
|
|
}
|
|
|
|
int playerDamageSprite(DBloodActor* source, PLAYER *pPlayer, DAMAGE_TYPE nDamageType, int nDamage)
|
|
{
|
|
int nSource = source ? source->s().index : -1;
|
|
assert(pPlayer != NULL);
|
|
if (pPlayer->damageControl[nDamageType] || pPlayer->godMode)
|
|
return 0;
|
|
nDamage = playerDamageArmor(pPlayer, nDamageType, nDamage);
|
|
pPlayer->painEffect = ClipHigh(pPlayer->painEffect+(nDamage>>3), 600);
|
|
|
|
spritetype *pSprite = pPlayer->pSprite;
|
|
XSPRITE *pXSprite = pPlayer->pXSprite;
|
|
auto pActor = &bloodActors[pSprite->index];
|
|
int nXSprite = pSprite->extra;
|
|
int nXSector = sector[pSprite->sectnum].extra;
|
|
DUDEINFO *pDudeInfo = getDudeInfo(pSprite->type);
|
|
int nDeathSeqID = -1;
|
|
int nKneelingPlayer = -1;
|
|
int nSprite = pSprite->index;
|
|
char va = playerSeqPlaying(pPlayer, 16);
|
|
if (!pXSprite->health)
|
|
{
|
|
if (va)
|
|
{
|
|
switch (nDamageType)
|
|
{
|
|
case kDamageSpirit:
|
|
nDeathSeqID = 18;
|
|
sfxPlay3DSound(pSprite, 716, 0, 0);
|
|
break;
|
|
case kDamageExplode:
|
|
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(pActor, nDamage<<4);
|
|
fxSpawnBlood(pActor, nDamage<<4);
|
|
nDeathSeqID = 17;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int nHealth = pXSprite->health-nDamage;
|
|
pXSprite->health = ClipLow(nHealth, 0);
|
|
if (pXSprite->health > 0 && pXSprite->health < 16)
|
|
{
|
|
nDamageType = kDamageBullet;
|
|
pXSprite->health = 0;
|
|
nHealth = -25;
|
|
}
|
|
if (pXSprite->health > 0)
|
|
{
|
|
DAMAGEINFO *pDamageInfo = &damageInfo[nDamageType];
|
|
int nSound;
|
|
if (nDamage >= (10<<4))
|
|
nSound = pDamageInfo->Kills[0];
|
|
else
|
|
nSound = pDamageInfo->Kills[Random(3)];
|
|
if (nDamageType == kDamageDrown && 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) flagDropped(pPlayer, kItemFlagA);
|
|
if (pPlayer->hasFlag&2) flagDropped(pPlayer, kItemFlagB);
|
|
}
|
|
pPlayer->deathTime = 0;
|
|
pPlayer->qavLoop = 0;
|
|
pPlayer->curWeapon = kWeapNone;
|
|
pPlayer->fraggerId = nSource;
|
|
pPlayer->voodooTargets = 0;
|
|
if (nDamageType == kDamageExplode && nDamage < (9<<4))
|
|
nDamageType = kDamageFall;
|
|
switch (nDamageType)
|
|
{
|
|
case kDamageExplode:
|
|
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 kDamageBurn:
|
|
sfxPlay3DSound(pSprite, 718, 0, 0);
|
|
nDeathSeqID = 3;
|
|
break;
|
|
case kDamageDrown:
|
|
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_i = nSource;
|
|
evPostActor(&bloodActors[pSprite->index], 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);
|
|
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);
|
|
|
|
#ifdef NOONE_EXTENSIONS
|
|
// allow drop items and keys in multiplayer
|
|
if (gModernMap && gGameOptions.nGameType != 0 && pPlayer->pXSprite->health <= 0) {
|
|
|
|
DBloodActor* pItem = nullptr;
|
|
if (pPlayer->pXSprite->dropMsg && (pItem = actDropItem(pActor, pPlayer->pXSprite->dropMsg)) != NULL)
|
|
evPostActor(pItem, 500, kCallbackRemove);
|
|
|
|
if (pPlayer->pXSprite->key) {
|
|
|
|
int i; // if all players have this key, don't drop it
|
|
for (i = connecthead; i >= 0; i = connectpoint2[i]) {
|
|
if (!gPlayer[i].hasKey[pPlayer->pXSprite->key])
|
|
break;
|
|
}
|
|
|
|
if (i == 0 && (pItem = actDropKey(pActor, (pPlayer->pXSprite->key + kItemKeyBase) - 1)) != NULL)
|
|
evPostActor(pItem, 500, kCallbackRemove);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
#endif
|
|
|
|
}
|
|
assert(getSequence(pDudeInfo->seqStartID + nDeathSeqID) != 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 voodooTarget(PLAYER *pPlayer)
|
|
{
|
|
auto actor = &bloodActors[pPlayer->pSprite->index];
|
|
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(actor, 0, dz, bcos(ang1), bsin(ang1), v4, kVectorVoodoo10);
|
|
int ang2 = (pPlayer->voodooVar1+2048-pPlayer->vodooVar2)&2047;
|
|
actFireVector(actor, 0, dz, bcos(ang2), bsin(ang2), v4, kVectorVoodoo10);
|
|
}
|
|
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.type != kHitNone)
|
|
{
|
|
if (!gGameOptions.bFriendlyFire && pHit->florhit.type == kHitSprite && IsTargetTeammate(pPlayer, &pHit->florhit.actor->s()))
|
|
return;
|
|
int nSurf = tileGetSurfType(pHit->florhit);
|
|
if (nSurf)
|
|
sfxPlay3DSound(pSprite, surfaceSound[nSurf], -1, 0);
|
|
}
|
|
}
|
|
|
|
void PlayerSurvive(int, DBloodActor* actor)
|
|
{
|
|
XSPRITE* pXSprite = &actor->x();
|
|
spritetype* pSprite = &actor->s();
|
|
char buffer[80];
|
|
actHealDude(actor, 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!", PlayerName(pPlayer->nPlayer));
|
|
viewSetMessage(buffer);
|
|
}
|
|
pPlayer->newWeapon = kWeapPitchFork;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlayerKneelsOver(int, DBloodActor* actor)
|
|
{
|
|
XSPRITE* pXSprite = &actor->x();
|
|
for (int p = connecthead; p >= 0; p = connectpoint2[p])
|
|
{
|
|
if (gPlayer[p].pXSprite == pXSprite)
|
|
{
|
|
PLAYER *pPlayer = &gPlayer[p];
|
|
playerDamageSprite(pPlayer->fragger(), pPlayer, kDamageSpirit, 500<<4);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
//
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
FSerializer& Serialize(FSerializer& arc, const char* keyname, Aim& w, Aim* def)
|
|
{
|
|
if (arc.BeginObject(keyname))
|
|
{
|
|
arc("x", w.dx)
|
|
("y", w.dx)
|
|
("z", w.dx)
|
|
.EndObject();
|
|
}
|
|
return arc;
|
|
}
|
|
|
|
FSerializer& Serialize(FSerializer& arc, const char* keyname, PACKINFO& w, PACKINFO* def)
|
|
{
|
|
if (arc.BeginObject(keyname))
|
|
{
|
|
arc("isactive", w.isActive)
|
|
("curamount", w.curAmount)
|
|
.EndObject();
|
|
}
|
|
return arc;
|
|
}
|
|
|
|
FSerializer& Serialize(FSerializer& arc, const char* keyname, POSTURE& w, POSTURE* def)
|
|
{
|
|
if (arc.BeginObject(keyname))
|
|
{
|
|
arc("frontaccel", w.frontAccel, def->frontAccel)
|
|
("sideaccel", w.sideAccel, def->sideAccel)
|
|
("backaccel", w.backAccel, def->backAccel)
|
|
("pace0", w.pace[0], def->pace[0])
|
|
("pace1", w.pace[1], def->pace[1])
|
|
("bobv", w.bobV, def->bobV)
|
|
("bobh", w.bobH, def->bobH)
|
|
("swayv", w.swayV, def->swayV)
|
|
("swayh", w.swayH, def->swayH)
|
|
("eyeabovez", w.eyeAboveZ, def->eyeAboveZ)
|
|
("weaponabovez", w.weaponAboveZ, def->weaponAboveZ)
|
|
("xoffset", w.xOffset, def->xOffset)
|
|
("zoffset", w.zOffset, def->zOffset)
|
|
("normaljumpz", w.normalJumpZ, def->normalJumpZ)
|
|
("pwupjumpz", w.pwupJumpZ, def->pwupJumpZ)
|
|
.EndObject();
|
|
}
|
|
return arc;
|
|
}
|
|
|
|
FSerializer& Serialize(FSerializer& arc, const char* keyname, PLAYER& w, PLAYER* def)
|
|
{
|
|
if (arc.isReading()) playerResetPosture(&w);
|
|
if (arc.BeginObject(keyname))
|
|
{
|
|
arc("spritenum", w.nSprite)
|
|
("horizon", w.horizon)
|
|
("angle", w.angle)
|
|
("newweapon", w.newWeapon)
|
|
("used1", w.used1)
|
|
("weaponqav", w.weaponQav)
|
|
("qavcallback", w.qavCallback)
|
|
("isrunning", w.isRunning)
|
|
("posture", w.posture)
|
|
("sceneqav", w.sceneQav)
|
|
("bobphase", w.bobPhase)
|
|
("bobamp", w.bobAmp)
|
|
("bobheight", w.bobHeight)
|
|
("bobwidth", w.bobWidth)
|
|
("swayphase", w.swayPhase)
|
|
("swayamp", w.swayAmp)
|
|
("swayheight", w.swayHeight)
|
|
("swaywidth", w.swayWidth)
|
|
("nplayer", w.nPlayer)
|
|
("lifemode", w.lifeMode)
|
|
("bloodlust", w.bloodlust)
|
|
("zview", w.zView)
|
|
("zviewvel", w.zViewVel)
|
|
("zweapon", w.zWeapon)
|
|
("zweaponvel", w.zWeaponVel)
|
|
("slope", w.slope)
|
|
("underwater", w.isUnderwater)
|
|
.Array("haskey", w.hasKey, 8)
|
|
("hasflag", w.hasFlag)
|
|
.Array("used2", w.used2, 8)
|
|
.Array("dmgcontrol", w.damageControl, 7)
|
|
("curweapon", w.curWeapon)
|
|
("nextweapon", w.nextWeapon)
|
|
("weapontimer", w.weaponTimer)
|
|
("weaponstate", w.weaponState)
|
|
("weaponammo", w.weaponAmmo)
|
|
.Array("hasweapon", w.hasWeapon, countof(w.hasWeapon))
|
|
.Array("weaponmode", w.weaponMode, countof(w.weaponMode))
|
|
.Array("weaponorder", &w.weaponOrder[0][0], 14*2)
|
|
.Array("ammocount", w.ammoCount, countof(w.ammoCount))
|
|
("qavloop", w.qavLoop)
|
|
("qavlastTick", w.qavLastTick)
|
|
("qavtimer", w.qavTimer)
|
|
("fusetime", w.fuseTime)
|
|
("throwtime", w.throwTime)
|
|
("throwpower", w.throwPower)
|
|
("aim", w.aim)
|
|
("relaim", w.relAim)
|
|
("aimtarget", w.aimTarget)
|
|
("aimtargetscount", w.aimTargetsCount)
|
|
.Array("aimtargets", w.aimTargets, countof(w.aimTargets))
|
|
("deathtime", w.deathTime)
|
|
.Array("pwuptime", w.pwUpTime, countof(w.pwUpTime))
|
|
("fragcount", w.fragCount)
|
|
.Array("fraginfo", w.fragInfo, countof(w.fragInfo))
|
|
("teamid", w.teamId)
|
|
("fraggerid", w.fraggerId)
|
|
("undserwatertime", w.underwaterTime)
|
|
("bubbletime", w.bubbleTime)
|
|
("resttime", w.restTime)
|
|
("kickpower", w.kickPower)
|
|
("laughcount", w.laughCount)
|
|
("godmode", w.godMode)
|
|
("fallscream", w.fallScream)
|
|
("cantjump", w.cantJump)
|
|
("packitemtime", w.packItemTime)
|
|
("packitemid", w.packItemId)
|
|
.Array("packslots", w.packSlots, countof(w.packSlots))
|
|
.Array("armor", w.armor, countof(w.armor))
|
|
("voodootarget", w.voodooTarget)
|
|
("voodootargets", w.voodooTargets)
|
|
("voodoovar1", w.voodooVar1)
|
|
("voodoovar2", w.vodooVar2)
|
|
("flickereffect", w.flickerEffect)
|
|
("tilteffect", w.tiltEffect)
|
|
("visibility", w.visibility)
|
|
("paineffect", w.painEffect)
|
|
("blindeffect", w.blindEffect)
|
|
("chokeeffect", w.chokeEffect)
|
|
("handtime", w.handTime)
|
|
("hand", w.hand)
|
|
("pickupeffect", w.pickupEffect)
|
|
("flasheffect", w.flashEffect)
|
|
("quakeeffect", w.quakeEffect)
|
|
("player_par", w.player_par)
|
|
("waterpal", w.nWaterPal)
|
|
.Array("posturedata", &w.pPosture[0][0], &gPostureDefaults[0][0], kModeMax * kPostureMax) // only save actual changes in this.
|
|
.EndObject();
|
|
}
|
|
return arc;
|
|
}
|
|
|
|
#ifdef NOONE_EXTENSIONS
|
|
FSerializer& Serialize(FSerializer& arc, const char* keyname, TRPLAYERCTRL& w, TRPLAYERCTRL* def)
|
|
{
|
|
if (arc.BeginObject(keyname))
|
|
{
|
|
arc("index", w.qavScene.initiator)
|
|
("dummy", w.qavScene.dummy)
|
|
.EndObject();
|
|
}
|
|
if (arc.isReading()) w.qavScene.qavResrc = nullptr;
|
|
return arc;
|
|
}
|
|
#endif
|
|
|
|
|
|
void SerializePlayers(FSerializer& arc)
|
|
{
|
|
if (arc.BeginObject("players"))
|
|
{
|
|
arc("numplayers", gNetPlayers)
|
|
.Array("teamscore", team_score, gNetPlayers)
|
|
.Array("players", gPlayer, gNetPlayers)
|
|
#ifdef NOONE_EXTENSIONS
|
|
.Array("playerctrl", gPlayerCtrl, gNetPlayers)
|
|
#endif
|
|
.EndObject();
|
|
}
|
|
|
|
if (arc.isReading())
|
|
{
|
|
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];
|
|
|
|
#ifdef NOONE_EXTENSIONS
|
|
// load qav scene
|
|
if (gPlayer[i].sceneQav != -1)
|
|
{
|
|
QAV* pQav = playerQavSceneLoad(gPlayer[i].sceneQav);
|
|
if (pQav)
|
|
{
|
|
gPlayerCtrl[i].qavScene.qavResrc = pQav;
|
|
//gPlayerCtrl[i].qavScene.qavResrc->Preload();
|
|
}
|
|
else
|
|
{
|
|
gPlayer[i].sceneQav = -1;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, newWeapon)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, weaponQav)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, qavCallback)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, isRunning)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, posture) // stand, crouch, swim
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, sceneQav) // by NoOne: used to keep qav id
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, bobPhase)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, bobAmp)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, bobHeight)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, bobWidth)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, swayPhase)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, swayAmp)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, swayHeight)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, swayWidth)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, nPlayer) // Connect id
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, nSprite)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, lifeMode)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, zView)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, zViewVel)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, zWeapon)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, zWeaponVel)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, slope)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, isUnderwater)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, hasKey)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, hasFlag)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, damageControl)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, curWeapon)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, nextWeapon)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, weaponTimer)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, weaponState)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, weaponAmmo) //rename
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, hasWeapon)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, weaponMode)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, weaponOrder)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, ammoCount)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, qavLoop)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, fuseTime)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, throwTime)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, throwPower)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, aim) // world
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, aimTarget) // aim target sprite
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, aimTargetsCount)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, aimTargets)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, deathTime)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, pwUpTime)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, teamId)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, fragCount)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, fragInfo)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, underwaterTime)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, bubbleTime)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, restTime)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, kickPower)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, laughCount)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, godMode)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, fallScream)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, cantJump)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, packItemTime) // pack timer
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, packItemId) // pack id 1: diving suit, 2: crystal ball, 3:
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, packSlots) // at325 1]: diving suit, [2]: crystal ball,
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, armor) // armor
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, voodooTarget)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, flickerEffect)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, tiltEffect)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, visibility)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, painEffect)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, blindEffect)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, chokeEffect)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, handTime)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, hand) // if true, there is hand start choking the player
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, pickupEffect)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, flashEffect) // if true, reduce pPlayer->visibility counter
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, quakeEffect)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, player_par)
|
|
DEFINE_FIELD_X(BloodPlayer, PLAYER, nWaterPal)
|
|
|
|
END_BLD_NS
|