mirror of
https://github.com/DrBeef/Raze.git
synced 2025-02-17 17:31:19 +00:00
Par for course I'd say - instead of checking for replaced player actors at the only time it really matters - i.e. when respawning, it was encoded into the stored value, making the entire thing very messy. For mow, multiplayer handling is not needed, but when it does, better go over all actors and manually substitute the two relevant fields, i.e. sprite.owner and xsprite.burnsource.
2364 lines
78 KiB
C++
2364 lines
78 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 "mmulti.h"
|
|
#include "actor.h"
|
|
#include "blood.h"
|
|
#include "callback.h"
|
|
#include "eventq.h"
|
|
#include "fx.h"
|
|
#include "gib.h"
|
|
#include "globals.h"
|
|
#include "levels.h"
|
|
#include "player.h"
|
|
#include "seq.h"
|
|
#include "sound.h"
|
|
#include "triggers.h"
|
|
#include "view.h"
|
|
#include "common_game.h"
|
|
#include "messages.h"
|
|
#include "nnexts.h"
|
|
#include "gstrings.h"
|
|
#include "gamestate.h"
|
|
#include "automap.h"
|
|
#include "bloodactor.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->pSprite))
|
|
actDamageSprite(pPlayer->pSprite->index, pPlayer->pSprite, DAMAGE_TYPE_3, 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->pSprite))
|
|
actDamageSprite(pPlayer->pSprite->index, pPlayer->pSprite, DAMAGE_TYPE_3, 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;
|
|
}
|
|
}
|
|
|
|
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:
|
|
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(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:
|
|
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->at70[i] = mulscale8(Handicap[gSkill], pDudeInfo->startDamage[i]);
|
|
}
|
|
|
|
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)];
|
|
}
|
|
|
|
spritetype *pSprite = actSpawnSprite(pStartZone->sectnum, pStartZone->x, pStartZone->y, pStartZone->z, 6, 1);
|
|
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)
|
|
show2dsprite.Set(pSprite->index);
|
|
int top, bottom;
|
|
GetSpriteExtents(pSprite, &top, &bottom);
|
|
pSprite->z -= bottom - pSprite->z;
|
|
pSprite->pal = 11+(pPlayer->teamId&3);
|
|
pSprite->ang = pStartZone->ang;
|
|
pPlayer->angold = 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 = buildlook(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 = 0;
|
|
xvel[pSprite->index] = yvel[pSprite->index] = zvel[pSprite->index] = 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 = -1;
|
|
#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 = 0;
|
|
pPlayer->qavCallback = -1;
|
|
pPlayer->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;
|
|
}
|
|
#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) {
|
|
|
|
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;
|
|
evSend(0, 0, 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;
|
|
evSend(0, 0, 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;
|
|
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->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(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) {
|
|
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) {
|
|
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(pWeapon) || 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)
|
|
{
|
|
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(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;
|
|
int nSprite;
|
|
StatIterator it(kStatItem);
|
|
while ((nSprite = it.NextIndex()) >= 0)
|
|
{
|
|
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 = CosScale16(pSprite->ang);
|
|
int y = SinScale16(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.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 && 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] += mulscale16(x, t2);
|
|
yvel[*a2] += mulscale16(y, t2);
|
|
zvel[*a2] += mulscale16(z, t2);
|
|
}
|
|
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)
|
|
{
|
|
spritetype *pSprite = pPlayer->pSprite;
|
|
|
|
pPlayer->angle.ang += buildang(pSprite->ang) - pPlayer->angold;
|
|
pPlayer->angold = pPlayer->angle.ang;
|
|
pSprite->ang = pPlayer->angle.ang.asbuild();
|
|
}
|
|
|
|
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;
|
|
|
|
pPlayer->isRunning = !!(pInput->actions & SB_RUN);
|
|
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->angold = bvectangbam(sprite[pPlayer->fraggerId].x - pSprite->x, sprite[pPlayer->fraggerId].y - pSprite->y);
|
|
pSprite->ang = pPlayer->angold.asbuild();
|
|
pPlayer->angle.addadjustment(getincanglebam(pPlayer->angle.ang, pPlayer->angold));
|
|
}
|
|
pPlayer->deathTime += 4;
|
|
if (!bSeqStat)
|
|
pPlayer->horizon.addadjustment(FixedToFloat(mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime<<3, 1024))>>15), gi->playerHorizMax()) - 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 = mulscale8(pPosture->frontAccel, forward);
|
|
else
|
|
forward = mulscale8(pPosture->backAccel, forward);
|
|
xvel[nSprite] += mulscale30(forward, x);
|
|
yvel[nSprite] += mulscale30(forward, y);
|
|
}
|
|
if (pInput->svel)
|
|
{
|
|
int strafe = pInput->svel;
|
|
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->fvel)
|
|
{
|
|
int forward = pInput->fvel;
|
|
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->svel)
|
|
{
|
|
int strafe = pInput->svel;
|
|
strafe = mulscale8(pPosture->sideAccel, strafe);
|
|
if (pXSprite->height)
|
|
strafe = mulscale16(strafe, speed);
|
|
xvel[nSprite] += mulscale30(strafe, y);
|
|
yvel[nSprite] -= mulscale30(strafe, x);
|
|
}
|
|
}
|
|
|
|
if (SyncInput())
|
|
{
|
|
applylook(&pPlayer->angle, 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)
|
|
{
|
|
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 = CosScale16(pPlayer->pSprite->ang);
|
|
int y = SinScale16(pPlayer->pSprite->ang);
|
|
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->actions &= ~SB_OPEN;
|
|
}
|
|
|
|
if (SyncInput())
|
|
{
|
|
sethorizon(&pPlayer->horizon.horiz, pInput->horz, &pInput->actions);
|
|
}
|
|
|
|
// disable synchronised input if set by game.
|
|
resetForcedSyncInput();
|
|
|
|
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->horizon.horizoff = q16horiz(interpolate(pPlayer->horizon.horizoff.asq16(), IntToFixed(z1-z2)>>3, 0x4000));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPlayer->horizon.horizoff = q16horiz(interpolate(pPlayer->horizon.horizoff.asq16(), 0, 0x4000));
|
|
if (klabs(pPlayer->horizon.horizoff.asq16()) < 4)
|
|
pPlayer->horizon.horizoff = q16horiz(0);
|
|
}
|
|
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)
|
|
{
|
|
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);
|
|
}
|
|
assert(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 >>= FRACBITS;
|
|
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)
|
|
{
|
|
bool running = pPlayer->isRunning && pPlayer->restTime <= 10;
|
|
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 = 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)
|
|
{
|
|
assert(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)
|
|
{
|
|
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)
|
|
evSend(0, 0, 15, kCmdToggle);
|
|
else
|
|
evSend(0, 0, 16, kCmdToggle);
|
|
}
|
|
else
|
|
{
|
|
if (nTeam1 == nTeam2)
|
|
evSend(0, 0, 16, kCmdToggle);
|
|
else
|
|
evSend(0, 0, 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)
|
|
{
|
|
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", PlayerName(pPlayer->nPlayer));
|
|
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", PlayerName(pPlayer->nPlayer));
|
|
sndStartSample(8004, 255, 2, 0);
|
|
viewSetMessage(buffer);
|
|
break;
|
|
}
|
|
return pSprite;
|
|
}
|
|
|
|
int playerDamageSprite(int nSource, PLAYER *pPlayer, DAMAGE_TYPE nDamageType, int nDamage)
|
|
{
|
|
assert(nSource < kMaxSprites);
|
|
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;
|
|
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 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->Kills[0];
|
|
else
|
|
nSound = pDamageInfo->Kills[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) flagDropped(pPlayer, kItemFlagA);
|
|
if (pPlayer->hasFlag&2) flagDropped(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);
|
|
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);
|
|
}
|
|
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)
|
|
{
|
|
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, CosScale16(ang1), SinScale16(ang1), v4, VECTOR_TYPE_21);
|
|
int ang2 = (pPlayer->voodooVar1+2048-pPlayer->vodooVar2)&2047;
|
|
actFireVector(pPlayer->pSprite, 0, dz, CosScale16(ang2), SinScale16(ang2), 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, DBloodActor* actor)
|
|
{
|
|
XSPRITE* pXSprite = &actor->x();
|
|
spritetype* pSprite = &actor->s();
|
|
char buffer[80];
|
|
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!", PlayerName(pPlayer->nPlayer));
|
|
viewSetMessage(buffer);
|
|
}
|
|
pPlayer->newWeapon = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
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->fraggerId, pPlayer, DAMAGE_TYPE_5, 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)
|
|
("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)
|
|
("angold", w.angold)
|
|
("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.index)
|
|
("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
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
END_BLD_NS
|