2019-09-19 22:42:45 +00:00
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
/*
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
//-------------------------------------------------------------------------
|
2019-09-21 18:59:54 +00:00
|
|
|
#include "ns.h" // Must come before everything else!
|
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "compat.h"
|
|
|
|
#include "build.h"
|
|
|
|
#include "mmulti.h"
|
|
|
|
#include "actor.h"
|
|
|
|
#include "blood.h"
|
|
|
|
#include "callback.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include "controls.h"
|
|
|
|
#include "demo.h"
|
|
|
|
#include "eventq.h"
|
|
|
|
#include "fx.h"
|
|
|
|
#include "gib.h"
|
2019-06-27 04:33:22 +00:00
|
|
|
#include "globals.h"
|
2019-09-19 22:42:45 +00:00
|
|
|
#include "levels.h"
|
|
|
|
#include "loadsave.h"
|
|
|
|
#include "map2d.h"
|
|
|
|
#include "network.h"
|
|
|
|
#include "player.h"
|
|
|
|
#include "seq.h"
|
|
|
|
#include "sfx.h"
|
|
|
|
#include "sound.h"
|
|
|
|
#include "tile.h"
|
|
|
|
#include "triggers.h"
|
|
|
|
#include "trig.h"
|
|
|
|
#include "view.h"
|
|
|
|
#include "warp.h"
|
|
|
|
#include "weapon.h"
|
|
|
|
#include "common_game.h"
|
2019-09-19 06:10:18 +00:00
|
|
|
#include "messages.h"
|
2019-09-19 22:42:45 +00:00
|
|
|
|
2019-09-22 06:39:22 +00:00
|
|
|
BEGIN_BLD_NS
|
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
PROFILE gProfile[kMaxPlayers];
|
|
|
|
|
|
|
|
PLAYER gPlayer[kMaxPlayers];
|
|
|
|
PLAYER *gMe, *gView;
|
|
|
|
|
2019-09-15 11:59:27 +00:00
|
|
|
bool gBlueFlagDropped = false;
|
|
|
|
bool gRedFlagDropped = false;
|
|
|
|
|
2019-10-19 19:11:39 +00:00
|
|
|
// V = has effect in game, X = no effect in game
|
2019-09-19 22:42:45 +00:00
|
|
|
POWERUPINFO gPowerUpInfo[kMaxPowerUps] = {
|
2019-10-19 19:11:39 +00:00
|
|
|
{ -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)
|
2019-09-19 22:42:45 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
int Handicap[] = {
|
|
|
|
144, 208, 256, 304, 368
|
|
|
|
};
|
|
|
|
|
2019-10-19 19:11:39 +00:00
|
|
|
int gDefaultAccel[] = {
|
|
|
|
|
|
|
|
// normal human
|
|
|
|
0x4000, 0x1200, 0x2000, // stand (front, side, back) / crouch (front, side, back) / swim (front, side, back)
|
|
|
|
// normal beast
|
|
|
|
0x4000, 0x1200, 0x2000, // stand (front, side, back) / crouch (front, side, back) / swim (front, side, back)
|
|
|
|
// shrink human
|
|
|
|
10384, 2108, 2192, // stand (front, side, back) / crouch (front, side, back) / swim (front, side, back)
|
|
|
|
// grown human
|
|
|
|
19384, 5608, 11192 // stand (front, side, back) / crouch (front, side, back) / swim (front, side, back)
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
POSTURE gPosture[4][3] = {
|
|
|
|
|
|
|
|
// normal human
|
|
|
|
{
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
gDefaultAccel[0],
|
|
|
|
gDefaultAccel[0],
|
|
|
|
gDefaultAccel[0],
|
2019-09-19 22:42:45 +00:00
|
|
|
14,
|
|
|
|
17,
|
|
|
|
24,
|
|
|
|
16,
|
|
|
|
32,
|
|
|
|
80,
|
|
|
|
0x1600,
|
|
|
|
0x1200,
|
|
|
|
0xc00,
|
|
|
|
0x90
|
|
|
|
},
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
gDefaultAccel[1],
|
|
|
|
gDefaultAccel[1],
|
|
|
|
gDefaultAccel[1],
|
2019-09-19 22:42:45 +00:00
|
|
|
14,
|
|
|
|
17,
|
|
|
|
24,
|
|
|
|
16,
|
|
|
|
32,
|
|
|
|
80,
|
|
|
|
0x1400,
|
|
|
|
0x1000,
|
|
|
|
-0x600,
|
|
|
|
0xb0
|
|
|
|
},
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
gDefaultAccel[2],
|
|
|
|
gDefaultAccel[2],
|
|
|
|
gDefaultAccel[2],
|
2019-09-19 22:42:45 +00:00
|
|
|
22,
|
|
|
|
28,
|
|
|
|
24,
|
|
|
|
16,
|
|
|
|
16,
|
|
|
|
40,
|
|
|
|
0x800,
|
|
|
|
0x600,
|
|
|
|
-0x600,
|
|
|
|
0xb0
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
// normal beast
|
|
|
|
{
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
gDefaultAccel[3],
|
|
|
|
gDefaultAccel[3],
|
|
|
|
gDefaultAccel[3],
|
2019-09-19 22:42:45 +00:00
|
|
|
14,
|
|
|
|
17,
|
|
|
|
24,
|
|
|
|
16,
|
|
|
|
32,
|
|
|
|
80,
|
|
|
|
0x1600,
|
|
|
|
0x1200,
|
|
|
|
0xc00,
|
|
|
|
0x90
|
|
|
|
},
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
gDefaultAccel[4],
|
|
|
|
gDefaultAccel[4],
|
|
|
|
gDefaultAccel[4],
|
2019-09-19 22:42:45 +00:00
|
|
|
14,
|
|
|
|
17,
|
|
|
|
24,
|
|
|
|
16,
|
|
|
|
32,
|
|
|
|
80,
|
|
|
|
0x1400,
|
|
|
|
0x1000,
|
|
|
|
-0x600,
|
|
|
|
0xb0
|
|
|
|
},
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
gDefaultAccel[5],
|
|
|
|
gDefaultAccel[5],
|
|
|
|
gDefaultAccel[5],
|
2019-09-19 22:42:45 +00:00
|
|
|
22,
|
|
|
|
28,
|
|
|
|
24,
|
|
|
|
16,
|
|
|
|
16,
|
|
|
|
40,
|
|
|
|
0x800,
|
|
|
|
0x600,
|
|
|
|
-0x600,
|
|
|
|
0xb0
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
// shrink human
|
|
|
|
{
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
gDefaultAccel[6],
|
|
|
|
gDefaultAccel[6],
|
|
|
|
gDefaultAccel[6],
|
2019-09-19 22:42:45 +00:00
|
|
|
14,
|
|
|
|
17,
|
|
|
|
24,
|
|
|
|
16,
|
|
|
|
32,
|
|
|
|
80,
|
|
|
|
5632,
|
|
|
|
4608,
|
|
|
|
3072,
|
|
|
|
144
|
|
|
|
},
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
gDefaultAccel[7],
|
|
|
|
gDefaultAccel[7],
|
|
|
|
gDefaultAccel[7],
|
2019-09-19 22:42:45 +00:00
|
|
|
14,
|
|
|
|
17,
|
|
|
|
24,
|
|
|
|
16,
|
|
|
|
32,
|
|
|
|
80,
|
|
|
|
5120,
|
|
|
|
4096,
|
|
|
|
-1536,
|
|
|
|
176
|
|
|
|
},
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
gDefaultAccel[8],
|
|
|
|
gDefaultAccel[8],
|
|
|
|
gDefaultAccel[8],
|
2019-09-19 22:42:45 +00:00
|
|
|
22,
|
|
|
|
28,
|
|
|
|
24,
|
|
|
|
16,
|
|
|
|
16,
|
|
|
|
40,
|
|
|
|
2048,
|
|
|
|
1536,
|
|
|
|
-1536,
|
|
|
|
176
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
// grown human
|
|
|
|
{
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
gDefaultAccel[9],
|
|
|
|
gDefaultAccel[9],
|
|
|
|
gDefaultAccel[9],
|
2019-09-19 22:42:45 +00:00
|
|
|
14,
|
|
|
|
17,
|
|
|
|
24,
|
|
|
|
16,
|
|
|
|
32,
|
|
|
|
80,
|
|
|
|
5632,
|
|
|
|
4608,
|
|
|
|
3072,
|
|
|
|
144
|
|
|
|
},
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
gDefaultAccel[10],
|
|
|
|
gDefaultAccel[10],
|
|
|
|
gDefaultAccel[10],
|
2019-09-19 22:42:45 +00:00
|
|
|
14,
|
|
|
|
17,
|
|
|
|
24,
|
|
|
|
16,
|
|
|
|
32,
|
|
|
|
80,
|
|
|
|
5120,
|
|
|
|
4096,
|
|
|
|
-1536,
|
|
|
|
176
|
|
|
|
},
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
gDefaultAccel[11],
|
|
|
|
gDefaultAccel[11],
|
|
|
|
gDefaultAccel[11],
|
2019-09-19 22:42:45 +00:00
|
|
|
22,
|
|
|
|
28,
|
|
|
|
24,
|
|
|
|
16,
|
|
|
|
16,
|
|
|
|
40,
|
|
|
|
2048,
|
|
|
|
1536,
|
|
|
|
-1536,
|
|
|
|
176
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
AMMOINFO gAmmoInfo[] = {
|
|
|
|
{ 0, -1 },
|
|
|
|
{ 100, -1 },
|
|
|
|
{ 100, 4 },
|
|
|
|
{ 500, 5 },
|
|
|
|
{ 100, -1 },
|
|
|
|
{ 50, -1 },
|
|
|
|
{ 2880, -1 },
|
|
|
|
{ 250, -1 },
|
|
|
|
{ 100, -1 },
|
|
|
|
{ 100, -1 },
|
|
|
|
{ 50, -1 },
|
|
|
|
{ 50, -1 },
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ARMORDATA {
|
|
|
|
int at0;
|
|
|
|
int at4;
|
|
|
|
int at8;
|
|
|
|
int atc;
|
|
|
|
int at10;
|
|
|
|
int at14;
|
|
|
|
};
|
|
|
|
ARMORDATA armorData[5] = {
|
|
|
|
{ 0x320, 0x640, 0x320, 0x640, 0x320, 0x640 },
|
|
|
|
{ 0x640, 0x640, 0, 0x640, 0, 0x640 },
|
|
|
|
{ 0, 0x640, 0x640, 0x640, 0, 0x640 },
|
|
|
|
{ 0, 0x640, 0, 0x640, 0x640, 0x640 },
|
|
|
|
{ 0xc80, 0xc80, 0xc80, 0xc80, 0xc80, 0xc80 }
|
|
|
|
};
|
|
|
|
|
|
|
|
void PlayerSurvive(int, int);
|
2019-09-20 10:11:07 +00:00
|
|
|
void PlayerKneelsOver(int, int);
|
2019-09-19 22:42:45 +00:00
|
|
|
|
|
|
|
int nPlayerSurviveClient = seqRegisterClient(PlayerSurvive);
|
2019-09-20 10:11:07 +00:00
|
|
|
int nPlayerKneelClient = seqRegisterClient(PlayerKneelsOver);
|
2019-09-19 22:42:45 +00:00
|
|
|
|
|
|
|
struct VICTORY {
|
|
|
|
const char *at0;
|
|
|
|
int at4;
|
|
|
|
};
|
|
|
|
|
|
|
|
VICTORY gVictory[] = {
|
|
|
|
{ "%s boned %s like a fish", 4100 },
|
|
|
|
{ "%s castrated %s", 4101 },
|
|
|
|
{ "%s creamed %s", 4102 },
|
|
|
|
{ "%s destroyed %s", 4103 },
|
|
|
|
{ "%s diced %s", 4104 },
|
|
|
|
{ "%s disemboweled %s", 4105 },
|
|
|
|
{ "%s flattened %s", 4106 },
|
|
|
|
{ "%s gave %s Anal Justice", 4107 },
|
|
|
|
{ "%s gave AnAl MaDnEsS to %s", 4108 },
|
|
|
|
{ "%s hurt %s real bad", 4109 },
|
|
|
|
{ "%s killed %s", 4110 },
|
|
|
|
{ "%s made mincemeat out of %s", 4111 },
|
|
|
|
{ "%s massacred %s", 4112 },
|
|
|
|
{ "%s mutilated %s", 4113 },
|
|
|
|
{ "%s reamed %s", 4114 },
|
|
|
|
{ "%s ripped %s a new orifice", 4115 },
|
|
|
|
{ "%s slaughtered %s", 4116 },
|
|
|
|
{ "%s sliced %s", 4117 },
|
|
|
|
{ "%s smashed %s", 4118 },
|
|
|
|
{ "%s sodomized %s", 4119 },
|
|
|
|
{ "%s splattered %s", 4120 },
|
|
|
|
{ "%s squashed %s", 4121 },
|
|
|
|
{ "%s throttled %s", 4122 },
|
|
|
|
{ "%s wasted %s", 4123 },
|
|
|
|
{ "%s body bagged %s", 4124 },
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SUICIDE {
|
|
|
|
const char *at0;
|
|
|
|
int at4;
|
|
|
|
};
|
|
|
|
|
|
|
|
SUICIDE gSuicide[] = {
|
|
|
|
{ "%s is excrement", 4202 },
|
|
|
|
{ "%s is hamburger", 4203 },
|
|
|
|
{ "%s suffered scrotum separation", 4204 },
|
|
|
|
{ "%s volunteered for population control", 4206 },
|
|
|
|
{ "%s has suicided", 4207 },
|
|
|
|
};
|
|
|
|
|
|
|
|
struct DAMAGEINFO {
|
|
|
|
int at0;
|
|
|
|
int at4[3];
|
|
|
|
int at10[3];
|
|
|
|
};
|
|
|
|
|
|
|
|
DAMAGEINFO damageInfo[7] = {
|
|
|
|
{ -1, 731, 732, 733, 710, 710, 710 },
|
|
|
|
{ 1, 742, 743, 744, 711, 711, 711 },
|
|
|
|
{ 0, 731, 732, 733, 712, 712, 712 },
|
|
|
|
{ 1, 731, 732, 733, 713, 713, 713 },
|
|
|
|
{ -1, 724, 724, 724, 714, 714, 714 },
|
|
|
|
{ 2, 731, 732, 733, 715, 715, 715 },
|
|
|
|
{ 0, 0, 0, 0, 0, 0, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
int powerupCheck(PLAYER *pPlayer, int nPowerUp)
|
|
|
|
{
|
|
|
|
dassert(pPlayer != NULL);
|
|
|
|
dassert(nPowerUp >= 0 && nPowerUp < kMaxPowerUps);
|
|
|
|
int nPack = powerupToPackItem(nPowerUp);
|
|
|
|
if (nPack >= 0 && !packItemActive(pPlayer, nPack))
|
|
|
|
return 0;
|
|
|
|
return pPlayer->at202[nPowerUp];
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isGrown(spritetype* pSprite) {
|
2019-10-19 19:11:39 +00:00
|
|
|
if (powerupCheck(&gPlayer[pSprite->type - kDudePlayer1], kPwUpGrowShroom) > 0) return true;
|
|
|
|
else if (pSprite->extra >= 0 && xsprite[pSprite->extra].scale >= 512) return true;
|
|
|
|
else return false;
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool isShrinked(spritetype* pSprite) {
|
2019-10-19 19:11:39 +00:00
|
|
|
if (powerupCheck(&gPlayer[pSprite->type - kDudePlayer1], kPwUpShrinkShroom) > 0) return true;
|
|
|
|
else if (pSprite->extra >= 0 && xsprite[pSprite->extra].scale > 0 && xsprite[pSprite->extra].scale <= 128) return true;
|
|
|
|
else return false;
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool shrinkPlayerSize(PLAYER* pPlayer, int divider) {
|
2019-08-17 02:19:48 +00:00
|
|
|
pPlayer->pXSprite->scale = 256/divider;
|
2019-09-19 22:42:45 +00:00
|
|
|
playerSetRace(pPlayer, kModeHumanShrink);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool growPlayerSize(PLAYER* pPlayer, int multiplier) {
|
2019-08-17 02:19:48 +00:00
|
|
|
pPlayer->pXSprite->scale = 256*multiplier;
|
2019-09-19 22:42:45 +00:00
|
|
|
playerSetRace(pPlayer, kModeHumanGrown);
|
2019-10-19 19:11:39 +00:00
|
|
|
viewSetSystemMessage("%d", pPlayer->pXSprite->scale);
|
2019-09-19 22:42:45 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool resetPlayerSize(PLAYER* pPlayer) {
|
|
|
|
playerSetRace(pPlayer, kModeHuman);
|
|
|
|
pPlayer->pXSprite->scale = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void deactivateSizeShrooms(PLAYER* pPlayer) {
|
2019-10-19 19:11:39 +00:00
|
|
|
powerupDeactivate(pPlayer, kPwUpGrowShroom);
|
|
|
|
pPlayer->at202[kPwUpGrowShroom] = 0;
|
2019-09-19 22:42:45 +00:00
|
|
|
|
2019-10-19 19:11:39 +00:00
|
|
|
powerupDeactivate(pPlayer, kPwUpShrinkShroom);
|
|
|
|
pPlayer->at202[kPwUpShrinkShroom] = 0;
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
|
2019-10-19 19:11:39 +00:00
|
|
|
|
|
|
|
PLAYER* getPlayerById(short id) {
|
|
|
|
if (id > 0) {
|
|
|
|
for (int i = connecthead; i >= 0; i = connectpoint2[i]) {
|
|
|
|
if (id < kMaxPlayers && id == i + 1) return &gPlayer[i]; // relative to connected players
|
|
|
|
else if (id >= kDudePlayer1 && id <= kDudePlayer8 && id == gPlayer[i].pSprite->type) // absolute type
|
|
|
|
return &gPlayer[i];
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
char powerupActivate(PLAYER *pPlayer, int nPowerUp)
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
if (powerupCheck(pPlayer, nPowerUp) > 0 && gPowerUpInfo[nPowerUp].pickupOnce)
|
2019-09-19 22:42:45 +00:00
|
|
|
return 0;
|
|
|
|
if (!pPlayer->at202[nPowerUp])
|
2019-10-19 19:11:39 +00:00
|
|
|
pPlayer->at202[nPowerUp] = gPowerUpInfo[nPowerUp].bonusTime;
|
2019-09-19 22:42:45 +00:00
|
|
|
int nPack = powerupToPackItem(nPowerUp);
|
|
|
|
if (nPack >= 0)
|
|
|
|
pPlayer->packInfo[nPack].at0 = 1;
|
2019-10-11 21:59:39 +00:00
|
|
|
|
|
|
|
switch (nPowerUp + kItemBase) {
|
|
|
|
case kItemModernMapLevel:
|
|
|
|
if (gModernMap) gFullMap = true;
|
|
|
|
break;
|
|
|
|
case kItemShroomShrink:
|
|
|
|
if (!gModernMap) break;
|
|
|
|
else if (isGrown(pPlayer->pSprite)) deactivateSizeShrooms(pPlayer);
|
|
|
|
else shrinkPlayerSize(pPlayer, 2);
|
|
|
|
break;
|
|
|
|
case kItemShroomGrow:
|
|
|
|
if (!gModernMap) break;
|
|
|
|
else if (isShrinked(pPlayer->pSprite)) deactivateSizeShrooms(pPlayer);
|
|
|
|
else {
|
|
|
|
growPlayerSize(pPlayer, 2);
|
2019-10-19 19:11:39 +00:00
|
|
|
if (powerupCheck(&gPlayer[pPlayer->pSprite->type - kDudePlayer1], kPwUpShadowCloak) > 0) {
|
|
|
|
powerupDeactivate(pPlayer, kPwUpShadowCloak);
|
|
|
|
pPlayer->at202[kPwUpShadowCloak] = 0;
|
2019-10-11 21:59:39 +00:00
|
|
|
}
|
2019-09-19 22:42:45 +00:00
|
|
|
|
2019-10-11 21:59:39 +00:00
|
|
|
if (ceilIsTooLow(pPlayer->pSprite))
|
|
|
|
actDamageSprite(pPlayer->pSprite->xvel, pPlayer->pSprite, DAMAGE_TYPE_3, 65535);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kItemFeatherFall:
|
|
|
|
case kItemJumpBoots:
|
|
|
|
pPlayer->ata1[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->ata1[i]++;
|
|
|
|
break;
|
|
|
|
case kItemDivingSuit: // diving suit
|
|
|
|
pPlayer->ata1[4]++;
|
|
|
|
if (pPlayer == gMe && gGameOptions.nGameType == 0)
|
|
|
|
sfxSetReverb(1);
|
|
|
|
break;
|
|
|
|
case kItemGasMask:
|
|
|
|
pPlayer->ata1[4]++;
|
|
|
|
break;
|
|
|
|
case kItemArmorAsbest:
|
|
|
|
pPlayer->ata1[1]++;
|
|
|
|
break;
|
|
|
|
case kItemTwoGuns:
|
|
|
|
pPlayer->atc.newWeapon = pPlayer->atbd;
|
|
|
|
WeaponRaise(pPlayer);
|
|
|
|
break;
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
sfxPlay3DSound(pPlayer->pSprite, 776, -1, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void powerupDeactivate(PLAYER *pPlayer, int nPowerUp)
|
|
|
|
{
|
|
|
|
int nPack = powerupToPackItem(nPowerUp);
|
|
|
|
if (nPack >= 0)
|
|
|
|
pPlayer->packInfo[nPack].at0 = 0;
|
2019-10-11 21:59:39 +00:00
|
|
|
|
|
|
|
switch (nPowerUp + kItemBase) {
|
|
|
|
case kItemShroomShrink:
|
|
|
|
if (gModernMap) {
|
|
|
|
resetPlayerSize(pPlayer);
|
|
|
|
if (ceilIsTooLow(pPlayer->pSprite))
|
|
|
|
actDamageSprite(pPlayer->pSprite->xvel, pPlayer->pSprite, DAMAGE_TYPE_3, 65535);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case kItemShroomGrow:
|
|
|
|
if (gModernMap) resetPlayerSize(pPlayer);
|
|
|
|
break;
|
|
|
|
case kItemFeatherFall:
|
|
|
|
case kItemJumpBoots:
|
|
|
|
pPlayer->ata1[0]--;
|
|
|
|
break;
|
|
|
|
case kItemDeathMask:
|
|
|
|
for (int i = 0; i < 7; i++)
|
|
|
|
pPlayer->ata1[i]--;
|
|
|
|
break;
|
|
|
|
case kItemDivingSuit:
|
|
|
|
pPlayer->ata1[4]--;
|
|
|
|
if (pPlayer == gMe && VanillaMode() ? true : pPlayer->at202[24] == 0)
|
|
|
|
sfxSetReverb(0);
|
|
|
|
break;
|
|
|
|
case kItemReflectShots:
|
|
|
|
if (pPlayer == gMe && VanillaMode() ? true : pPlayer->packInfo[1].at0 == 0)
|
|
|
|
sfxSetReverb(0);
|
|
|
|
break;
|
|
|
|
case kItemGasMask:
|
|
|
|
pPlayer->ata1[4]--;
|
|
|
|
break;
|
|
|
|
case kItemArmorAsbest:
|
|
|
|
pPlayer->ata1[1]--;
|
|
|
|
break;
|
|
|
|
case kItemTwoGuns:
|
|
|
|
pPlayer->atc.newWeapon = pPlayer->atbd;
|
|
|
|
WeaponRaise(pPlayer);
|
|
|
|
break;
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void powerupSetState(PLAYER *pPlayer, int nPowerUp, char bState)
|
|
|
|
{
|
|
|
|
if (!bState)
|
|
|
|
powerupActivate(pPlayer, nPowerUp);
|
|
|
|
else
|
|
|
|
powerupDeactivate(pPlayer, nPowerUp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void powerupProcess(PLAYER *pPlayer)
|
|
|
|
{
|
|
|
|
pPlayer->at31d = ClipLow(pPlayer->at31d-4, 0);
|
|
|
|
for (int i = kMaxPowerUps-1; i >= 0; i--)
|
|
|
|
{
|
|
|
|
int nPack = powerupToPackItem(i);
|
|
|
|
if (nPack >= 0)
|
|
|
|
{
|
|
|
|
if (pPlayer->packInfo[nPack].at0)
|
|
|
|
{
|
|
|
|
pPlayer->at202[i] = ClipLow(pPlayer->at202[i]-4, 0);
|
|
|
|
if (pPlayer->at202[i])
|
2019-10-19 19:11:39 +00:00
|
|
|
pPlayer->packInfo[nPack].at1 = (100*pPlayer->at202[i])/gPowerUpInfo[i].bonusTime;
|
2019-09-19 22:42:45 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
powerupDeactivate(pPlayer, i);
|
|
|
|
if (pPlayer->at321 == nPack)
|
|
|
|
pPlayer->at321 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (pPlayer->at202[i] > 0)
|
|
|
|
{
|
|
|
|
pPlayer->at202[i] = ClipLow(pPlayer->at202[i]-4, 0);
|
|
|
|
if (!pPlayer->at202[i])
|
|
|
|
powerupDeactivate(pPlayer, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void powerupClear(PLAYER *pPlayer)
|
|
|
|
{
|
|
|
|
for (int i = kMaxPowerUps-1; i >= 0; i--)
|
|
|
|
{
|
|
|
|
pPlayer->at202[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void powerupInit(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int packItemToPowerup(int nPack)
|
|
|
|
{
|
|
|
|
int nPowerUp = -1;
|
2019-10-19 19:11:39 +00:00
|
|
|
switch (nPack) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
nPowerUp = kPwUpDivingSuit;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
nPowerUp = kPwUpCrystalBall;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
nPowerUp = kPwUpBeastVision;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
nPowerUp = kPwUpJumpBoots;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ThrowError("Unhandled pack item %d", nPack);
|
|
|
|
break;
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
return nPowerUp;
|
|
|
|
}
|
|
|
|
|
|
|
|
int powerupToPackItem(int nPowerUp)
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
switch (nPowerUp) {
|
|
|
|
case kPwUpDivingSuit:
|
|
|
|
return 1;
|
|
|
|
case kPwUpCrystalBall:
|
|
|
|
return 2;
|
|
|
|
case kPwUpBeastVision:
|
|
|
|
return 3;
|
|
|
|
case kPwUpJumpBoots:
|
|
|
|
return 4;
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char packAddItem(PLAYER *pPlayer, unsigned int nPack)
|
|
|
|
{
|
|
|
|
if (nPack <= 4)
|
|
|
|
{
|
|
|
|
if (pPlayer->packInfo[nPack].at1 >= 100)
|
|
|
|
return 0;
|
|
|
|
pPlayer->packInfo[nPack].at1 = 100;
|
|
|
|
int nPowerUp = packItemToPowerup(nPack);
|
|
|
|
if (nPowerUp >= 0)
|
2019-10-19 19:11:39 +00:00
|
|
|
pPlayer->at202[nPowerUp] = gPowerUpInfo[nPowerUp].bonusTime;
|
2019-09-19 22:42:45 +00:00
|
|
|
if (pPlayer->at321 == -1)
|
|
|
|
pPlayer->at321 = nPack;
|
|
|
|
if (!pPlayer->packInfo[pPlayer->at321].at1)
|
|
|
|
pPlayer->at321 = nPack;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ThrowError("Unhandled pack item %d", nPack);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int packCheckItem(PLAYER *pPlayer, int nPack)
|
|
|
|
{
|
|
|
|
return pPlayer->packInfo[nPack].at1;
|
|
|
|
}
|
|
|
|
|
|
|
|
char packItemActive(PLAYER *pPlayer, int nPack)
|
|
|
|
{
|
|
|
|
return pPlayer->packInfo[nPack].at0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void packUseItem(PLAYER *pPlayer, int nPack)
|
|
|
|
{
|
|
|
|
char v4 = 0;
|
|
|
|
int nPowerUp = -1;
|
|
|
|
if (pPlayer->packInfo[nPack].at1 > 0)
|
|
|
|
{
|
|
|
|
switch (nPack)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
{
|
|
|
|
XSPRITE *pXSprite = pPlayer->pXSprite;
|
|
|
|
unsigned int health = pXSprite->health>>4;
|
|
|
|
if (health < 100)
|
|
|
|
{
|
|
|
|
int heal = ClipHigh(100-health, pPlayer->packInfo[0].at1);
|
|
|
|
actHealDude(pXSprite, heal, 100);
|
|
|
|
pPlayer->packInfo[0].at1 -= heal;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 1:
|
|
|
|
v4 = 1;
|
2019-10-19 19:11:39 +00:00
|
|
|
nPowerUp = kPwUpDivingSuit;
|
2019-09-19 22:42:45 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
v4 = 1;
|
2019-10-19 19:11:39 +00:00
|
|
|
nPowerUp = kPwUpCrystalBall;
|
2019-09-19 22:42:45 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
v4 = 1;
|
2019-10-19 19:11:39 +00:00
|
|
|
nPowerUp = kPwUpBeastVision;
|
2019-09-19 22:42:45 +00:00
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
v4 = 1;
|
2019-10-19 19:11:39 +00:00
|
|
|
nPowerUp = kPwUpJumpBoots;
|
2019-09-19 22:42:45 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ThrowError("Unhandled pack item %d", nPack);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pPlayer->at31d = 0;
|
|
|
|
if (v4)
|
|
|
|
powerupSetState(pPlayer, nPowerUp, pPlayer->packInfo[nPack].at0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void packPrevItem(PLAYER *pPlayer)
|
|
|
|
{
|
|
|
|
if (pPlayer->at31d > 0)
|
|
|
|
{
|
|
|
|
for (int nPrev = ClipLow(pPlayer->at321-1,0); nPrev >= 0; nPrev--)
|
|
|
|
{
|
|
|
|
if (pPlayer->packInfo[nPrev].at1)
|
|
|
|
{
|
|
|
|
pPlayer->at321 = nPrev;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pPlayer->at31d = 600;
|
|
|
|
}
|
|
|
|
|
|
|
|
void packNextItem(PLAYER *pPlayer)
|
|
|
|
{
|
|
|
|
if (pPlayer->at31d > 0)
|
|
|
|
{
|
|
|
|
for (int nNext = ClipHigh(pPlayer->at321+1,5); nNext < 5; nNext++)
|
|
|
|
{
|
|
|
|
if (pPlayer->packInfo[nNext].at1)
|
|
|
|
{
|
|
|
|
pPlayer->at321 = nNext;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pPlayer->at31d = 600;
|
|
|
|
}
|
|
|
|
|
|
|
|
char playerSeqPlaying(PLAYER * pPlayer, int nSeq)
|
|
|
|
{
|
|
|
|
int nCurSeq = seqGetID(3, pPlayer->pSprite->extra);
|
|
|
|
if (pPlayer->pDudeInfo->seqStartID+nSeq == nCurSeq && seqGetStatus(3,pPlayer->pSprite->extra) >= 0)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void playerSetRace(PLAYER *pPlayer, int nLifeMode)
|
|
|
|
{
|
|
|
|
dassert(nLifeMode >= kModeHuman && nLifeMode <= kModeHumanGrown);
|
|
|
|
DUDEINFO *pDudeInfo = pPlayer->pDudeInfo;
|
|
|
|
*pDudeInfo = gPlayerTemplate[nLifeMode];
|
|
|
|
pPlayer->at5f = nLifeMode;
|
|
|
|
|
|
|
|
// By NoOne: don't forget to change clipdist for grow and shrink modes
|
|
|
|
pPlayer->pSprite->clipdist = pDudeInfo->clipdist;
|
|
|
|
|
|
|
|
for (int i = 0; i < 7; i++)
|
|
|
|
pDudeInfo->at70[i] = mulscale8(Handicap[gProfile[pPlayer->at57].skill], pDudeInfo->startDamage[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void playerSetGodMode(PLAYER *pPlayer, char bGodMode)
|
|
|
|
{
|
|
|
|
if (bGodMode)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 7; i++)
|
|
|
|
pPlayer->ata1[i]++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 7; i++)
|
|
|
|
pPlayer->ata1[i]--;
|
|
|
|
}
|
|
|
|
pPlayer->at31a = bGodMode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void playerResetInertia(PLAYER *pPlayer)
|
|
|
|
{
|
|
|
|
POSTURE *pPosture = &gPosture[pPlayer->at5f][pPlayer->at2f];
|
|
|
|
pPlayer->at67 = pPlayer->pSprite->z-pPosture->at24;
|
|
|
|
pPlayer->at6f = pPlayer->pSprite->z-pPosture->at28;
|
|
|
|
viewBackupView(pPlayer->at57);
|
|
|
|
}
|
|
|
|
|
2019-09-07 16:39:26 +00:00
|
|
|
void playerCorrectInertia(PLAYER* pPlayer, vec3_t const *oldpos)
|
|
|
|
{
|
|
|
|
pPlayer->at67 += pPlayer->pSprite->z-oldpos->z;
|
|
|
|
pPlayer->at6f += pPlayer->pSprite->z-oldpos->z;
|
|
|
|
viewCorrectViewOffsets(pPlayer->at57, oldpos);
|
|
|
|
}
|
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
void playerResetPowerUps(PLAYER* pPlayer)
|
|
|
|
{
|
2019-10-19 19:11:39 +00:00
|
|
|
for (int i = 0; i < kMaxPowerUps; i++) {
|
|
|
|
if (!VanillaMode() && (i == kPwUpJumpBoots || i == kPwUpDivingSuit || i == kPwUpCrystalBall || i == kPwUpBeastVision))
|
2019-09-19 22:42:45 +00:00
|
|
|
continue;
|
|
|
|
pPlayer->at202[i] = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void playerStart(int nPlayer)
|
|
|
|
{
|
|
|
|
PLAYER* pPlayer = &gPlayer[nPlayer];
|
|
|
|
GINPUT* pInput = &pPlayer->atc;
|
|
|
|
ZONE* pStartZone = NULL;
|
|
|
|
|
|
|
|
// normal start position
|
|
|
|
if (gGameOptions.nGameType <= 1)
|
|
|
|
pStartZone = &gStartZone[nPlayer];
|
|
|
|
|
|
|
|
// By NoOne: let's check if there is positions of teams is specified
|
|
|
|
// if no, pick position randomly, just like it works in vanilla.
|
|
|
|
else if (gGameOptions.nGameType == 3 && gTeamsSpawnUsed == true) {
|
|
|
|
int maxRetries = 5;
|
|
|
|
while (maxRetries-- > 0) {
|
|
|
|
if (pPlayer->at2ea == 0) pStartZone = &gStartZoneTeam1[Random(3)];
|
|
|
|
else pStartZone = &gStartZoneTeam2[Random(3)];
|
|
|
|
|
|
|
|
if (maxRetries != 0) {
|
|
|
|
// check if there is no spawned player in selected zone
|
|
|
|
for (int i = headspritesect[pStartZone->sectnum]; i >= 0; i = nextspritesect[i]) {
|
|
|
|
spritetype* pSprite = &sprite[i];
|
|
|
|
if (pStartZone->x == pSprite->x && pStartZone->y == pSprite->y && IsPlayerSprite(pSprite)) {
|
|
|
|
pStartZone = NULL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pStartZone != NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pStartZone = &gStartZone[Random(8)];
|
|
|
|
}
|
|
|
|
|
|
|
|
spritetype *pSprite = actSpawnSprite(pStartZone->sectnum, pStartZone->x, pStartZone->y, pStartZone->z, 6, 1);
|
|
|
|
dassert(pSprite->extra > 0 && pSprite->extra < kMaxXSprites);
|
|
|
|
XSPRITE *pXSprite = &xsprite[pSprite->extra];
|
|
|
|
pPlayer->pSprite = pSprite;
|
|
|
|
pPlayer->pXSprite = pXSprite;
|
|
|
|
pPlayer->at5b = pSprite->index;
|
|
|
|
DUDEINFO *pDudeInfo = &dudeInfo[kDudePlayer1 + nPlayer - kDudeBase];
|
|
|
|
pPlayer->pDudeInfo = pDudeInfo;
|
|
|
|
playerSetRace(pPlayer, kModeHuman);
|
|
|
|
seqSpawn(pDudeInfo->seqStartID, 3, pSprite->extra, -1);
|
|
|
|
if (pPlayer == gMe)
|
|
|
|
SetBitString(show2dsprite, pSprite->index);
|
|
|
|
int top, bottom;
|
|
|
|
GetSpriteExtents(pSprite, &top, &bottom);
|
|
|
|
pSprite->z -= bottom - pSprite->z;
|
|
|
|
pSprite->pal = 11+(pPlayer->at2ea&3);
|
|
|
|
pPlayer->angold = pSprite->ang = pStartZone->ang;
|
|
|
|
pPlayer->q16ang = fix16_from_int(pSprite->ang);
|
|
|
|
pSprite->type = kDudePlayer1+nPlayer;
|
|
|
|
pSprite->clipdist = pDudeInfo->clipdist;
|
2019-09-30 07:18:01 +00:00
|
|
|
pSprite->flags = 15;
|
2019-09-19 22:42:45 +00:00
|
|
|
pXSprite->burnTime = 0;
|
|
|
|
pXSprite->burnSource = -1;
|
|
|
|
pPlayer->pXSprite->health = pDudeInfo->startHealth<<4;
|
|
|
|
pPlayer->pSprite->cstat &= (unsigned short)~32768;
|
|
|
|
pPlayer->at63 = 0;
|
|
|
|
pPlayer->q16horiz = 0;
|
|
|
|
pPlayer->q16slopehoriz = 0;
|
|
|
|
pPlayer->q16look = 0;
|
|
|
|
pPlayer->at83 = 0;
|
|
|
|
pPlayer->at2ee = -1;
|
|
|
|
pPlayer->at2f2 = 1200;
|
|
|
|
pPlayer->at2f6 = 0;
|
|
|
|
pPlayer->at2fa = 0;
|
|
|
|
pPlayer->at2fe = 0;
|
|
|
|
pPlayer->at302 = 0;
|
|
|
|
pPlayer->at306 = 0;
|
|
|
|
pPlayer->at30a = 0;
|
|
|
|
pPlayer->at30e = 0;
|
|
|
|
pPlayer->at312 = 0;
|
|
|
|
pPlayer->at316 = 0;
|
|
|
|
pPlayer->at2f = 0;
|
|
|
|
pPlayer->voodooTarget = -1;
|
|
|
|
pPlayer->at34e = 0;
|
|
|
|
pPlayer->at352 = 0;
|
|
|
|
pPlayer->at356 = 0;
|
|
|
|
playerResetInertia(pPlayer);
|
|
|
|
pPlayer->at73 = 0;
|
|
|
|
pPlayer->at1ca.dx = 0x4000;
|
|
|
|
pPlayer->at1ca.dy = 0;
|
|
|
|
pPlayer->at1ca.dz = 0;
|
|
|
|
pPlayer->at1d6 = -1;
|
|
|
|
pPlayer->at6b = pPlayer->at73;
|
2019-09-20 10:11:07 +00:00
|
|
|
if (!(gGameOptions.nGameType == 1 && gGameOptions.bKeepKeysOnRespawn))
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
pPlayer->at88[i] = gGameOptions.nGameType >= 2;
|
2019-09-19 22:42:45 +00:00
|
|
|
pPlayer->at90 = 0;
|
|
|
|
for (int i = 0; i < 8; i++)
|
|
|
|
pPlayer->at91[i] = -1;
|
|
|
|
for (int i = 0; i < 7; i++)
|
|
|
|
pPlayer->ata1[i] = 0;
|
|
|
|
if (pPlayer->at31a)
|
|
|
|
playerSetGodMode(pPlayer, 1);
|
|
|
|
gInfiniteAmmo = 0;
|
|
|
|
gFullMap = 0;
|
|
|
|
pPlayer->at1ba = 0;
|
|
|
|
pPlayer->at1fe = 0;
|
|
|
|
pPlayer->atbe = 0;
|
|
|
|
xvel[pSprite->index] = yvel[pSprite->index] = zvel[pSprite->index] = 0;
|
|
|
|
pInput->q16turn = 0;
|
|
|
|
pInput->keyFlags.word = 0;
|
|
|
|
pInput->forward = 0;
|
|
|
|
pInput->strafe = 0;
|
|
|
|
pInput->q16mlook = 0;
|
|
|
|
pInput->buttonFlags.byte = 0;
|
|
|
|
pInput->useFlags.byte = 0;
|
|
|
|
pPlayer->at35a = 0;
|
|
|
|
pPlayer->at37f = 0;
|
|
|
|
pPlayer->at35e = 0;
|
|
|
|
pPlayer->at362 = 0;
|
|
|
|
pPlayer->at366 = 0;
|
|
|
|
pPlayer->at36a = 0;
|
|
|
|
pPlayer->at36e = 0;
|
|
|
|
pPlayer->at372 = 0;
|
|
|
|
pPlayer->atbf = 0;
|
|
|
|
pPlayer->atc3 = 0;
|
|
|
|
pPlayer->at26 = -1;
|
|
|
|
pPlayer->at376 = 0;
|
|
|
|
pPlayer->nWaterPal = 0;
|
|
|
|
playerResetPowerUps(pPlayer);
|
|
|
|
|
|
|
|
if (pPlayer == gMe)
|
|
|
|
{
|
|
|
|
viewInitializePrediction();
|
|
|
|
gViewMap.x = pPlayer->pSprite->x;
|
|
|
|
gViewMap.y = pPlayer->pSprite->y;
|
|
|
|
gViewMap.angle = pPlayer->pSprite->ang;
|
|
|
|
}
|
|
|
|
if (IsUnderwaterSector(pSprite->sectnum))
|
|
|
|
{
|
|
|
|
pPlayer->at2f = 1;
|
2019-10-19 19:11:39 +00:00
|
|
|
pPlayer->pXSprite->medium = kMediumWater;
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void playerReset(PLAYER *pPlayer)
|
|
|
|
{
|
|
|
|
static int dword_136400[] = {
|
|
|
|
3, 4, 2, 8, 9, 10, 7, 1, 1, 1, 1, 1, 1, 1
|
|
|
|
};
|
|
|
|
static int dword_136438[] = {
|
|
|
|
3, 4, 2, 8, 9, 10, 7, 1, 1, 1, 1, 1, 1, 1
|
|
|
|
};
|
|
|
|
dassert(pPlayer != NULL);
|
|
|
|
for (int i = 0; i < 14; i++)
|
|
|
|
{
|
|
|
|
pPlayer->atcb[i] = gInfiniteAmmo;
|
|
|
|
pPlayer->atd9[i] = 0;
|
|
|
|
}
|
|
|
|
pPlayer->atcb[1] = 1;
|
|
|
|
pPlayer->atbd = 0;
|
|
|
|
pPlayer->at2a = -1;
|
|
|
|
pPlayer->atc.newWeapon = 1;
|
|
|
|
for (int i = 0; i < 14; i++)
|
|
|
|
{
|
|
|
|
pPlayer->at111[0][i] = dword_136400[i];
|
|
|
|
pPlayer->at111[1][i] = dword_136438[i];
|
|
|
|
}
|
|
|
|
for (int i = 0; i < 12; i++)
|
|
|
|
{
|
|
|
|
if (gInfiniteAmmo)
|
|
|
|
pPlayer->at181[i] = gAmmoInfo[i].at0;
|
|
|
|
else
|
|
|
|
pPlayer->at181[i] = 0;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
pPlayer->at33e[i] = 0;
|
|
|
|
pPlayer->atbf = 0;
|
|
|
|
pPlayer->atc3 = 0;
|
|
|
|
pPlayer->at26 = -1;
|
|
|
|
pPlayer->at1b1 = 0;
|
|
|
|
pPlayer->at321 = -1;
|
|
|
|
for (int i = 0; i < 5; i++)
|
|
|
|
{
|
|
|
|
pPlayer->packInfo[i].at0 = 0;
|
|
|
|
pPlayer->packInfo[i].at1 = 0;
|
|
|
|
}
|
2019-10-19 19:11:39 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
for (int a = 0; a < 3; a++)
|
|
|
|
gPosture[i][a].at0 = gPosture[i][a].at4 = gPosture[i][a].at8 = gDefaultAccel[a];
|
|
|
|
}
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int dword_21EFB0[8];
|
2019-09-07 13:15:39 +00:00
|
|
|
ClockTicks dword_21EFD0[8];
|
2019-09-19 22:42:45 +00:00
|
|
|
|
|
|
|
void playerInit(int nPlayer, unsigned int a2)
|
|
|
|
{
|
|
|
|
PLAYER *pPlayer = &gPlayer[nPlayer];
|
|
|
|
if (!(a2&1))
|
|
|
|
memset(pPlayer, 0, sizeof(PLAYER));
|
|
|
|
pPlayer->at57 = nPlayer;
|
|
|
|
pPlayer->at2ea = nPlayer;
|
|
|
|
if (gGameOptions.nGameType == 3)
|
|
|
|
pPlayer->at2ea = nPlayer&1;
|
|
|
|
pPlayer->at2c6 = 0;
|
|
|
|
memset(dword_21EFB0, 0, sizeof(dword_21EFB0));
|
|
|
|
memset(dword_21EFD0, 0, sizeof(dword_21EFD0));
|
|
|
|
memset(pPlayer->at2ca, 0, sizeof(pPlayer->at2ca));
|
|
|
|
if (!(a2&1))
|
|
|
|
playerReset(pPlayer);
|
|
|
|
}
|
|
|
|
|
|
|
|
char sub_3A158(PLAYER *a1, spritetype *a2)
|
|
|
|
{
|
2019-10-07 19:29:52 +00:00
|
|
|
for (int nSprite = headspritestat[kStatThing]; nSprite >= 0; nSprite = nextspritestat[nSprite])
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
|
|
|
if (a2 && a2->index == nSprite)
|
|
|
|
continue;
|
|
|
|
spritetype *pSprite = &sprite[nSprite];
|
2019-10-11 21:59:39 +00:00
|
|
|
if (pSprite->type == kThingDroppedLifeLeech && actOwnerIdToSpriteId(pSprite->owner) == a1->at5b)
|
2019-09-19 22:42:45 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-10-11 21:59:39 +00:00
|
|
|
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:
|
2019-10-13 22:29:19 +00:00
|
|
|
if (isGrown(pPlayer->pSprite) || !powerupActivate(pPlayer, nType)) return false;
|
|
|
|
break;
|
2019-10-11 21:59:39 +00:00
|
|
|
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;
|
|
|
|
}
|
2019-10-13 22:29:19 +00:00
|
|
|
|
2019-10-11 21:59:39 +00:00
|
|
|
powerupActivate(pPlayer, nType);
|
|
|
|
}
|
2019-09-19 22:42:45 +00:00
|
|
|
break;
|
2019-10-11 21:59:39 +00:00
|
|
|
case kItemFlagABase:
|
|
|
|
case kItemFlagBBase: {
|
|
|
|
if (gGameOptions.nGameType != 3 || pItem->extra <= 0) return 0;
|
|
|
|
XSPRITE * pXItem = &xsprite[pItem->extra];
|
|
|
|
if (pItem->type == kItemFlagABase) {
|
|
|
|
if (pPlayer->at2ea == 1) {
|
|
|
|
if ((pPlayer->at90 & 1) == 0 && pXItem->state) {
|
2019-09-19 22:42:45 +00:00
|
|
|
pPlayer->at90 |= 1;
|
|
|
|
pPlayer->at91[0] = pItem->index;
|
2019-10-11 21:59:39 +00:00
|
|
|
trTriggerSprite(pItem->index, pXItem, kCmdOff);
|
2019-09-19 22:42:45 +00:00
|
|
|
sprintf(buffer, "%s stole Blue Flag", gProfile[pPlayer->at57].name);
|
|
|
|
sndStartSample(8007, 255, 2, 0);
|
|
|
|
viewSetMessage(buffer);
|
|
|
|
}
|
|
|
|
}
|
2019-10-11 21:59:39 +00:00
|
|
|
|
|
|
|
if (pPlayer->at2ea == 0) {
|
|
|
|
|
|
|
|
if ((pPlayer->at90 & 1) != 0 && !pXItem->state) {
|
2019-09-19 22:42:45 +00:00
|
|
|
pPlayer->at90 &= ~1;
|
|
|
|
pPlayer->at91[0] = -1;
|
2019-10-11 21:59:39 +00:00
|
|
|
trTriggerSprite(pItem->index, pXItem, kCmdOn);
|
2019-09-19 22:42:45 +00:00
|
|
|
sprintf(buffer, "%s returned Blue Flag", gProfile[pPlayer->at57].name);
|
|
|
|
sndStartSample(8003, 255, 2, 0);
|
|
|
|
viewSetMessage(buffer);
|
|
|
|
}
|
2019-10-11 21:59:39 +00:00
|
|
|
|
|
|
|
if ((pPlayer->at90 & 2) != 0 && pXItem->state) {
|
2019-09-19 22:42:45 +00:00
|
|
|
pPlayer->at90 &= ~2;
|
|
|
|
pPlayer->at91[1] = -1;
|
|
|
|
dword_21EFB0[pPlayer->at2ea] += 10;
|
|
|
|
dword_21EFD0[pPlayer->at2ea] += 240;
|
2019-10-11 21:59:39 +00:00
|
|
|
evSend(0, 0, 81, kCmdOn);
|
2019-09-19 22:42:45 +00:00
|
|
|
sprintf(buffer, "%s captured Red Flag!", gProfile[pPlayer->at57].name);
|
|
|
|
sndStartSample(8001, 255, 2, 0);
|
|
|
|
viewSetMessage(buffer);
|
|
|
|
#if 0
|
|
|
|
if (dword_28E3D4 == 3 && myconnectindex == connecthead)
|
|
|
|
{
|
|
|
|
sprintf(buffer, "frag A killed B\n");
|
|
|
|
sub_7AC28(buffer);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
2019-10-11 21:59:39 +00:00
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
2019-10-11 21:59:39 +00:00
|
|
|
else if (pItem->type == kItemFlagBBase) {
|
|
|
|
|
|
|
|
if (pPlayer->at2ea == 0) {
|
|
|
|
if ((pPlayer->at90 & 2) == 0 && pXItem->state) {
|
2019-09-19 22:42:45 +00:00
|
|
|
pPlayer->at90 |= 2;
|
|
|
|
pPlayer->at91[1] = pItem->index;
|
2019-10-11 21:59:39 +00:00
|
|
|
trTriggerSprite(pItem->index, pXItem, kCmdOff);
|
2019-09-19 22:42:45 +00:00
|
|
|
sprintf(buffer, "%s stole Red Flag", gProfile[pPlayer->at57].name);
|
|
|
|
sndStartSample(8006, 255, 2, 0);
|
|
|
|
viewSetMessage(buffer);
|
|
|
|
}
|
|
|
|
}
|
2019-10-11 21:59:39 +00:00
|
|
|
|
|
|
|
if (pPlayer->at2ea == 1) {
|
|
|
|
if ((pPlayer->at90 & 2) != 0 && !pXItem->state)
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
|
|
|
pPlayer->at90 &= ~2;
|
|
|
|
pPlayer->at91[1] = -1;
|
2019-10-11 21:59:39 +00:00
|
|
|
trTriggerSprite(pItem->index, pXItem, kCmdOn);
|
2019-09-19 22:42:45 +00:00
|
|
|
sprintf(buffer, "%s returned Red Flag", gProfile[pPlayer->at57].name);
|
|
|
|
sndStartSample(8002, 255, 2, 0);
|
|
|
|
viewSetMessage(buffer);
|
|
|
|
}
|
2019-10-11 21:59:39 +00:00
|
|
|
if ((pPlayer->at90 & 1) != 0 && pXItem->state)
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
|
|
|
pPlayer->at90 &= ~1;
|
|
|
|
pPlayer->at91[0] = -1;
|
|
|
|
dword_21EFB0[pPlayer->at2ea] += 10;
|
|
|
|
dword_21EFD0[pPlayer->at2ea] += 240;
|
2019-10-11 21:59:39 +00:00
|
|
|
evSend(0, 0, 80, kCmdOn);
|
2019-09-15 11:59:27 +00:00
|
|
|
sprintf(buffer, "%s captured Blue Flag!", gProfile[pPlayer->at57].name);
|
2019-09-19 22:42:45 +00:00
|
|
|
sndStartSample(8000, 255, 2, 0);
|
|
|
|
viewSetMessage(buffer);
|
|
|
|
#if 0
|
|
|
|
if (dword_28E3D4 == 3 && myconnectindex == connecthead)
|
|
|
|
{
|
|
|
|
sprintf(buffer, "frag B killed A\n");
|
|
|
|
sub_7AC28(buffer);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
2019-10-11 21:59:39 +00:00
|
|
|
case kItemFlagA:
|
|
|
|
if (gGameOptions.nGameType != 3) return 0;
|
|
|
|
evKill(pItem->index, 3, kCallbackReturnFlag);
|
|
|
|
pPlayer->at90 |= 1;
|
|
|
|
pPlayer->at91[0] = pItem->index;
|
|
|
|
gBlueFlagDropped = false;
|
|
|
|
break;
|
|
|
|
case kItemFlagB:
|
|
|
|
if (gGameOptions.nGameType != 3) return 0;
|
|
|
|
evKill(pItem->index, 3, kCallbackReturnFlag);
|
|
|
|
pPlayer->at90 |= 2;
|
|
|
|
pPlayer->at91[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->at33e[1] < pArmorData->atc) {
|
|
|
|
pPlayer->at33e[1] = ClipHigh(pPlayer->at33e[1]+pArmorData->at8, pArmorData->atc);
|
|
|
|
pickedUp = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pPlayer->at33e[0] < pArmorData->at4) {
|
|
|
|
pPlayer->at33e[0] = ClipHigh(pPlayer->at33e[0]+pArmorData->at0, pArmorData->at4);
|
|
|
|
pickedUp = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pPlayer->at33e[2] < pArmorData->at14) {
|
|
|
|
pPlayer->at33e[2] = ClipHigh(pPlayer->at33e[2]+pArmorData->at10, pArmorData->at14);
|
|
|
|
pickedUp = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pickedUp) return 0;
|
|
|
|
pickupSnd = 779;
|
|
|
|
break;
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
2019-10-11 21:59:39 +00:00
|
|
|
case kItemCrystalBall:
|
|
|
|
if (gGameOptions.nGameType == 0 || !packAddItem(pPlayer, gItemData[nType].at8)) return 0;
|
|
|
|
break;
|
|
|
|
case kItemKeySkull:
|
|
|
|
case kItemKeyEye:
|
|
|
|
case kItemKeyFire:
|
|
|
|
case kItemKeyDagger:
|
|
|
|
case kItemKeySpider:
|
|
|
|
case kItemKeyMoon:
|
|
|
|
case kItemKeyKey7:
|
|
|
|
if (pPlayer->at88[pItem->type-99]) return 0;
|
|
|
|
pPlayer->at88[pItem->type-99] = 1;
|
|
|
|
pickupSnd = 781;
|
|
|
|
break;
|
|
|
|
case kItemHealthMedPouch:
|
|
|
|
case kItemHealthLifeEssense:
|
|
|
|
case kItemHealthLifeSeed:
|
|
|
|
case kItemHealthRedPotion: {
|
2019-10-19 19:11:39 +00:00
|
|
|
int addPower = gPowerUpInfo[nType].bonusTime;
|
2019-10-11 21:59:39 +00:00
|
|
|
// by NoOne: allow custom amount for item
|
|
|
|
if (gModernMap && sprite[pItem->xvel].extra >= 0 && xsprite[sprite[pItem->xvel].extra].data1 > 0)
|
|
|
|
addPower = xsprite[sprite[pItem->xvel].extra].data1;
|
|
|
|
|
2019-10-19 19:11:39 +00:00
|
|
|
if (!actHealDude(pXSprite, addPower, gPowerUpInfo[nType].maxTime)) return 0;
|
2019-10-11 21:59:39 +00:00
|
|
|
return 1;
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
2019-10-11 21:59:39 +00:00
|
|
|
case kItemHealthDoctorBag:
|
|
|
|
case kItemJumpBoots:
|
|
|
|
case kItemDivingSuit:
|
|
|
|
case kItemBeastVision:
|
|
|
|
if (!packAddItem(pPlayer, gItemData[nType].at8)) return 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (!powerupActivate(pPlayer, nType)) return 0;
|
|
|
|
return 1;
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
2019-10-11 21:59:39 +00:00
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
sfxPlay3DSound(pSprite->x, pSprite->y, pSprite->z, pickupSnd, pSprite->sectnum);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-10-11 21:59:39 +00:00
|
|
|
char PickupAmmo(PLAYER* pPlayer, spritetype* pAmmo) {
|
|
|
|
AMMOITEMDATA* pAmmoItemData = &gAmmoItemData[pAmmo->type - kItemAmmoBase];
|
2019-09-19 22:42:45 +00:00
|
|
|
int nAmmoType = pAmmoItemData->ata;
|
|
|
|
|
|
|
|
if (pPlayer->at181[nAmmoType] >= gAmmoInfo[nAmmoType].at0) return 0;
|
2019-10-11 21:59:39 +00:00
|
|
|
else if (!gModernMap || pAmmo->extra < 0 || xsprite[pAmmo->extra].data1 <= 0)
|
2019-09-19 22:42:45 +00:00
|
|
|
pPlayer->at181[nAmmoType] = ClipHigh(pPlayer->at181[nAmmoType]+pAmmoItemData->at8, gAmmoInfo[nAmmoType].at0);
|
|
|
|
// by NoOne: allow custom amount for item
|
|
|
|
else
|
|
|
|
pPlayer->at181[nAmmoType] = ClipHigh(pPlayer->at181[nAmmoType] + xsprite[pAmmo->extra].data1, gAmmoInfo[nAmmoType].at0);
|
|
|
|
|
2019-10-11 21:59:39 +00:00
|
|
|
if (pAmmoItemData->atb) pPlayer->atcb[pAmmoItemData->atb] = 1;
|
2019-09-19 22:42:45 +00:00
|
|
|
sfxPlay3DSound(pPlayer->pSprite, 782, -1, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-10-11 21:59:39 +00:00
|
|
|
char PickupWeapon(PLAYER *pPlayer, spritetype *pWeapon) {
|
|
|
|
WEAPONITEMDATA *pWeaponItemData = &gWeaponItemData[pWeapon->type - kItemWeaponBase];
|
2019-09-19 22:42:45 +00:00
|
|
|
int nWeaponType = pWeaponItemData->at8;
|
|
|
|
int nAmmoType = pWeaponItemData->ata;
|
2019-10-11 21:59:39 +00:00
|
|
|
if (!pPlayer->atcb[nWeaponType] || gGameOptions.nWeaponSettings == 2 || gGameOptions.nWeaponSettings == 3) {
|
|
|
|
if (pWeapon->type == kItemWeaponLifeLeech && gGameOptions.nGameType > 1 && sub_3A158(pPlayer, NULL))
|
2019-09-19 22:42:45 +00:00
|
|
|
return 0;
|
|
|
|
pPlayer->atcb[nWeaponType] = 1;
|
|
|
|
if (nAmmoType == -1) return 0;
|
|
|
|
// By NoOne: allow to set custom ammo count for weapon pickups
|
2019-10-11 21:59:39 +00:00
|
|
|
if (!gModernMap || pWeapon->extra < 0 || xsprite[pWeapon->extra].data1 <= 0)
|
2019-09-19 22:42:45 +00:00
|
|
|
pPlayer->at181[nAmmoType] = ClipHigh(pPlayer->at181[nAmmoType] + pWeaponItemData->atc, gAmmoInfo[nAmmoType].at0);
|
|
|
|
else
|
|
|
|
pPlayer->at181[nAmmoType] = ClipHigh(pPlayer->at181[nAmmoType] + xsprite[pWeapon->extra].data1, gAmmoInfo[nAmmoType].at0);
|
|
|
|
|
|
|
|
int nNewWeapon = WeaponUpgrade(pPlayer, nWeaponType);
|
2019-10-11 21:59:39 +00:00
|
|
|
if (nNewWeapon != pPlayer->atbd) {
|
2019-09-19 22:42:45 +00:00
|
|
|
pPlayer->atc3 = 0;
|
|
|
|
pPlayer->atbe = nNewWeapon;
|
|
|
|
}
|
|
|
|
sfxPlay3DSound(pPlayer->pSprite, 777, -1, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
2019-09-21 11:02:17 +00:00
|
|
|
|
|
|
|
if (!actGetRespawnTime(pWeapon) || nAmmoType == -1 || pPlayer->at181[nAmmoType] >= gAmmoInfo[nAmmoType].at0) return 0;
|
2019-10-11 21:59:39 +00:00
|
|
|
else if (!gModernMap || pWeapon->extra < 0 || xsprite[pWeapon->extra].data1 <= 0)
|
2019-09-21 11:02:17 +00:00
|
|
|
pPlayer->at181[nAmmoType] = ClipHigh(pPlayer->at181[nAmmoType]+pWeaponItemData->atc, gAmmoInfo[nAmmoType].at0);
|
|
|
|
else
|
|
|
|
pPlayer->at181[nAmmoType] = ClipHigh(pPlayer->at181[nAmmoType] + xsprite[pWeapon->extra].data1, gAmmoInfo[nAmmoType].at0);
|
|
|
|
|
2019-09-19 22:42:45 +00:00
|
|
|
sfxPlay3DSound(pPlayer->pSprite, 777, -1, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PickUp(PLAYER *pPlayer, spritetype *pSprite)
|
|
|
|
{
|
|
|
|
char buffer[80];
|
|
|
|
int nType = pSprite->type;
|
|
|
|
char pickedUp = 0;
|
|
|
|
int customMsg = -1;
|
|
|
|
|
2019-10-11 21:59:39 +00:00
|
|
|
if (gModernMap) { // by NoOne: allow custom INI message instead "Picked up"
|
|
|
|
XSPRITE* pXSprite = (pSprite->extra >= 0) ? &xsprite[pSprite->extra] : NULL;
|
|
|
|
if (pXSprite != NULL && pXSprite->txID != 3 && pXSprite->lockMsg > 0)
|
|
|
|
customMsg = pXSprite->lockMsg;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nType >= kItemBase && nType <= kItemMax) {
|
2019-09-21 11:02:17 +00:00
|
|
|
pickedUp = PickupItem(pPlayer, pSprite);
|
|
|
|
if (pickedUp && customMsg == -1) sprintf(buffer, "Picked up %s", gItemText[nType - 100]);
|
2019-10-11 21:59:39 +00:00
|
|
|
|
|
|
|
} else if (nType >= kItemAmmoBase && nType < kItemAmmoMax) {
|
2019-09-21 11:02:17 +00:00
|
|
|
pickedUp = PickupAmmo(pPlayer, pSprite);
|
2019-10-11 21:59:39 +00:00
|
|
|
if (pickedUp && customMsg == -1) sprintf(buffer, "Picked up %s", gAmmoText[nType - kItemAmmoBase]);
|
|
|
|
|
|
|
|
} else if (nType >= kItemWeaponBase && nType < kItemWeaponMax) {
|
2019-09-21 11:02:17 +00:00
|
|
|
pickedUp = PickupWeapon(pPlayer, pSprite);
|
2019-10-11 21:59:39 +00:00
|
|
|
if (pickedUp && customMsg == -1) sprintf(buffer, "Picked up %s", gWeaponText[nType - kItemWeaponBase]);
|
2019-09-21 11:02:17 +00:00
|
|
|
}
|
|
|
|
|
2019-10-11 21:59:39 +00:00
|
|
|
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->at377 = 30;
|
|
|
|
if (pPlayer == gMe) {
|
|
|
|
if (customMsg > 0) trTextOver(customMsg - 1);
|
|
|
|
else viewSetMessage(buffer, 0, MESSAGE_PRIORITY_PICKUP);
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2019-10-11 21:59:39 +00:00
|
|
|
for (int nSprite = headspritestat[kStatItem]; nSprite >= 0; nSprite = nNextSprite) {
|
2019-09-19 22:42:45 +00:00
|
|
|
spritetype *pItem = &sprite[nSprite];
|
|
|
|
nNextSprite = nextspritestat[nSprite];
|
2019-09-30 07:18:01 +00:00
|
|
|
if (pItem->flags&32)
|
2019-09-19 22:42:45 +00:00
|
|
|
continue;
|
|
|
|
int dx = klabs(x-pItem->x)>>4;
|
|
|
|
if (dx > 48)
|
|
|
|
continue;
|
|
|
|
int dy = klabs(y-pItem->y)>>4;
|
|
|
|
if (dy > 48)
|
|
|
|
continue;
|
|
|
|
int top, bottom;
|
|
|
|
GetSpriteExtents(pSprite, &top, &bottom);
|
|
|
|
int vb = 0;
|
|
|
|
if (pItem->z < top)
|
|
|
|
vb = (top-pItem->z)>>8;
|
|
|
|
else if (pItem->z > bottom)
|
|
|
|
vb = (pItem->z-bottom)>>8;
|
|
|
|
if (vb > 32)
|
|
|
|
continue;
|
|
|
|
if (approxDist(dx,dy) > 48)
|
|
|
|
continue;
|
|
|
|
GetSpriteExtents(pItem, &top, &bottom);
|
|
|
|
if (cansee(x, y, z, nSector, pItem->x, pItem->y, pItem->z, pItem->sectnum)
|
|
|
|
|| cansee(x, y, z, nSector, pItem->x, pItem->y, top, pItem->sectnum)
|
|
|
|
|| cansee(x, y, z, nSector, pItem->x, pItem->y, bottom, pItem->sectnum))
|
|
|
|
PickUp(pPlayer, pItem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int ActionScan(PLAYER *pPlayer, int *a2, int *a3)
|
|
|
|
{
|
|
|
|
*a2 = 0;
|
|
|
|
*a3 = 0;
|
|
|
|
spritetype *pSprite = pPlayer->pSprite;
|
|
|
|
int x = Cos(pSprite->ang)>>16;
|
|
|
|
int y = Sin(pSprite->ang)>>16;
|
|
|
|
int z = pPlayer->at83;
|
|
|
|
int hit = HitScan(pSprite, pPlayer->at67, 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;
|
2019-10-07 19:29:52 +00:00
|
|
|
if (*a3 > 0 && sprite[*a2].statnum == kStatThing)
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
|
|
|
spritetype *pSprite = &sprite[*a2];
|
|
|
|
XSPRITE *pXSprite = &xsprite[*a3];
|
2019-10-11 21:59:39 +00:00
|
|
|
if (pSprite->type == kThingDroppedLifeLeech)
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
|
|
|
if (gGameOptions.nGameType > 1 && sub_3A158(pPlayer, pSprite))
|
|
|
|
return -1;
|
|
|
|
pXSprite->data4 = pPlayer->at57;
|
|
|
|
pXSprite->isTriggered = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (*a3 > 0 && xsprite[*a3].Push)
|
|
|
|
return 3;
|
2019-10-07 19:29:52 +00:00
|
|
|
if (sprite[*a2].statnum == kStatDude)
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
|
|
|
spritetype *pSprite = &sprite[*a2];
|
|
|
|
XSPRITE *pXSprite = &xsprite[*a3];
|
|
|
|
int nMass = dudeInfo[pSprite->type-kDudeBase].mass;
|
|
|
|
if (nMass)
|
|
|
|
{
|
|
|
|
int t2 = divscale(0xccccc, nMass, 8);
|
|
|
|
xvel[*a2] += mulscale16(x, t2);
|
|
|
|
yvel[*a2] += mulscale16(y, t2);
|
|
|
|
zvel[*a2] += mulscale16(z, t2);
|
|
|
|
}
|
|
|
|
if (pXSprite->Push && !pXSprite->state && !pXSprite->isTriggered)
|
2019-10-11 21:59:39 +00:00
|
|
|
trTriggerSprite(*a2, pXSprite, kCmdSpritePush);
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
case 4:
|
|
|
|
*a2 = gHitInfo.hitwall;
|
|
|
|
*a3 = wall[*a2].extra;
|
|
|
|
if (*a3 > 0 && xwall[*a3].triggerPush)
|
|
|
|
return 0;
|
|
|
|
if (wall[*a2].nextsector >= 0)
|
|
|
|
{
|
|
|
|
*a2 = wall[*a2].nextsector;
|
|
|
|
*a3 = sector[*a2].extra;
|
|
|
|
if (*a3 > 0 && xsector[*a3].Wallpush)
|
|
|
|
return 6;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
*a2 = gHitInfo.hitsect;
|
|
|
|
*a3 = sector[*a2].extra;
|
|
|
|
if (*a3 > 0 && xsector[*a3].Push)
|
|
|
|
return 6;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*a2 = pSprite->sectnum;
|
|
|
|
*a3 = sector[*a2].extra;
|
|
|
|
if (*a3 > 0 && xsector[*a3].Push)
|
|
|
|
return 6;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProcessInput(PLAYER *pPlayer)
|
|
|
|
{
|
|
|
|
spritetype *pSprite = pPlayer->pSprite;
|
|
|
|
XSPRITE *pXSprite = pPlayer->pXSprite;
|
|
|
|
int nSprite = pPlayer->at5b;
|
|
|
|
POSTURE *pPosture = &gPosture[pPlayer->at5f][pPlayer->at2f];
|
|
|
|
GINPUT *pInput = &pPlayer->atc;
|
|
|
|
pPlayer->at2e = pInput->syncFlags.run;
|
|
|
|
if (pInput->buttonFlags.byte || pInput->forward || pInput->strafe || pInput->q16turn)
|
|
|
|
pPlayer->at30a = 0;
|
|
|
|
else if (pPlayer->at30a >= 0)
|
|
|
|
pPlayer->at30a += 4;
|
|
|
|
WeaponProcess(pPlayer);
|
|
|
|
if (pXSprite->health == 0)
|
|
|
|
{
|
|
|
|
char bSeqStat = playerSeqPlaying(pPlayer, 16);
|
|
|
|
if (pPlayer->at2ee != -1)
|
|
|
|
{
|
|
|
|
pPlayer->angold = pSprite->ang = getangle(sprite[pPlayer->at2ee].x - pSprite->x, sprite[pPlayer->at2ee].y - pSprite->y);
|
|
|
|
pPlayer->q16ang = fix16_from_int(pSprite->ang);
|
|
|
|
}
|
|
|
|
pPlayer->at1fe += 4;
|
|
|
|
if (!bSeqStat)
|
|
|
|
{
|
|
|
|
if (bVanilla)
|
|
|
|
pPlayer->q16horiz = fix16_from_int(mulscale16(0x8000-(Cos(ClipHigh(pPlayer->at1fe*8, 1024))>>15), 120));
|
|
|
|
else
|
|
|
|
pPlayer->q16horiz = mulscale16(0x8000-(Cos(ClipHigh(pPlayer->at1fe*8, 1024))>>15), F16(120));
|
|
|
|
}
|
|
|
|
if (pPlayer->atbd)
|
|
|
|
pInput->newWeapon = pPlayer->atbd;
|
|
|
|
if (pInput->keyFlags.action)
|
|
|
|
{
|
|
|
|
if (bSeqStat)
|
|
|
|
{
|
|
|
|
if (pPlayer->at1fe > 360)
|
|
|
|
seqSpawn(pPlayer->pDudeInfo->seqStartID+14, 3, pPlayer->pSprite->extra, nPlayerSurviveClient);
|
|
|
|
}
|
|
|
|
else if (seqGetStatus(3, pPlayer->pSprite->extra) < 0)
|
|
|
|
{
|
|
|
|
if (pPlayer->pSprite)
|
2019-10-11 21:59:39 +00:00
|
|
|
pPlayer->pSprite->type = kThingBloodChunks;
|
2019-10-07 19:29:52 +00:00
|
|
|
actPostSprite(pPlayer->at5b, kStatThing);
|
2019-09-19 22:42:45 +00:00
|
|
|
seqSpawn(pPlayer->pDudeInfo->seqStartID+15, 3, pPlayer->pSprite->extra, -1);
|
|
|
|
playerReset(pPlayer);
|
|
|
|
if (gGameOptions.nGameType == 0 && numplayers == 1)
|
|
|
|
{
|
|
|
|
if (gDemo.at0)
|
|
|
|
gDemo.Close();
|
|
|
|
pInput->keyFlags.restart = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
playerStart(pPlayer->at57);
|
|
|
|
}
|
|
|
|
pInput->keyFlags.action = 0;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (pPlayer->at2f == 1)
|
|
|
|
{
|
|
|
|
int x = Cos(pSprite->ang);
|
|
|
|
int y = Sin(pSprite->ang);
|
|
|
|
if (pInput->forward)
|
|
|
|
{
|
|
|
|
int forward = pInput->forward;
|
|
|
|
if (forward > 0)
|
|
|
|
forward = mulscale8(pPosture->at0, forward);
|
|
|
|
else
|
|
|
|
forward = mulscale8(pPosture->at8, forward);
|
|
|
|
xvel[nSprite] += mulscale30(forward, x);
|
|
|
|
yvel[nSprite] += mulscale30(forward, y);
|
|
|
|
}
|
|
|
|
if (pInput->strafe)
|
|
|
|
{
|
|
|
|
int strafe = pInput->strafe;
|
|
|
|
strafe = mulscale8(pPosture->at4, strafe);
|
|
|
|
xvel[nSprite] += mulscale30(strafe, y);
|
|
|
|
yvel[nSprite] -= mulscale30(strafe, x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (pXSprite->height < 256)
|
|
|
|
{
|
|
|
|
int speed = 0x10000;
|
|
|
|
if (pXSprite->height > 0)
|
|
|
|
speed -= divscale16(pXSprite->height, 256);
|
|
|
|
int x = Cos(pSprite->ang);
|
|
|
|
int y = Sin(pSprite->ang);
|
|
|
|
if (pInput->forward)
|
|
|
|
{
|
|
|
|
int forward = pInput->forward;
|
|
|
|
if (forward > 0)
|
|
|
|
forward = mulscale8(pPosture->at0, forward);
|
|
|
|
else
|
|
|
|
forward = mulscale8(pPosture->at8, forward);
|
|
|
|
if (pXSprite->height)
|
|
|
|
forward = mulscale16(forward, speed);
|
|
|
|
xvel[nSprite] += mulscale30(forward, x);
|
|
|
|
yvel[nSprite] += mulscale30(forward, y);
|
|
|
|
}
|
|
|
|
if (pInput->strafe)
|
|
|
|
{
|
|
|
|
int strafe = pInput->strafe;
|
|
|
|
strafe = mulscale8(pPosture->at4, strafe);
|
|
|
|
if (pXSprite->height)
|
|
|
|
strafe = mulscale16(strafe, speed);
|
|
|
|
xvel[nSprite] += mulscale30(strafe, y);
|
|
|
|
yvel[nSprite] -= mulscale30(strafe, x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pInput->q16turn)
|
|
|
|
pPlayer->q16ang = (pPlayer->q16ang+pInput->q16turn)&0x7ffffff;
|
|
|
|
if (pInput->keyFlags.spin180)
|
|
|
|
{
|
|
|
|
if (!pPlayer->at316)
|
|
|
|
pPlayer->at316 = -1024;
|
|
|
|
pInput->keyFlags.spin180 = 0;
|
|
|
|
}
|
|
|
|
if (pPlayer->at316 < 0)
|
|
|
|
{
|
|
|
|
int speed;
|
|
|
|
if (pPlayer->at2f == 1)
|
|
|
|
speed = 64;
|
|
|
|
else
|
|
|
|
speed = 128;
|
|
|
|
pPlayer->at316 = ClipLow(pPlayer->at316+speed, 0);
|
|
|
|
pPlayer->q16ang += fix16_from_int(speed);
|
|
|
|
}
|
|
|
|
pPlayer->q16ang = (pPlayer->q16ang+fix16_from_int(pSprite->ang-pPlayer->angold))&0x7ffffff;
|
|
|
|
pPlayer->angold = pSprite->ang = fix16_to_int(pPlayer->q16ang);
|
|
|
|
if (!pInput->buttonFlags.jump)
|
|
|
|
pPlayer->at31c = 0;
|
|
|
|
|
|
|
|
switch (pPlayer->at2f)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
if (pInput->buttonFlags.jump)
|
|
|
|
zvel[nSprite] -= 0x5b05;
|
|
|
|
if (pInput->buttonFlags.crouch)
|
|
|
|
zvel[nSprite] += 0x5b05;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (!pInput->buttonFlags.crouch)
|
|
|
|
pPlayer->at2f = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (!pPlayer->at31c && pInput->buttonFlags.jump && pXSprite->height == 0)
|
|
|
|
{
|
|
|
|
sfxPlay3DSound(pSprite, 700, 0, 0);
|
|
|
|
if (packItemActive(pPlayer, 4))
|
|
|
|
zvel[nSprite] = -0x175555;
|
|
|
|
else
|
|
|
|
zvel[nSprite] = -0xbaaaa;
|
|
|
|
|
|
|
|
|
|
|
|
if (isShrinked(pPlayer->pSprite)) zvel[nSprite] -= -200000;
|
|
|
|
else if (isGrown(pPlayer->pSprite)) zvel[nSprite] += -250000;
|
|
|
|
|
|
|
|
pPlayer->at31c = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (pInput->buttonFlags.crouch)
|
|
|
|
pPlayer->at2f = 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (pInput->keyFlags.action)
|
|
|
|
{
|
|
|
|
int a2, a3;
|
|
|
|
int hit = ActionScan(pPlayer, &a2, &a3);
|
|
|
|
switch (hit)
|
|
|
|
{
|
|
|
|
case 6:
|
|
|
|
if (a3 > 0 && a3 <= 2048)
|
|
|
|
{
|
|
|
|
XSECTOR *pXSector = &xsector[a3];
|
|
|
|
int key = pXSector->Key;
|
|
|
|
if (pXSector->locked && pPlayer == gMe)
|
|
|
|
{
|
|
|
|
viewSetMessage("It's locked");
|
|
|
|
sndStartSample(3062, 255, 2, 0);
|
|
|
|
}
|
|
|
|
if (!key || pPlayer->at88[key])
|
2019-10-11 21:59:39 +00:00
|
|
|
trTriggerSector(a2, pXSector, kCmdSpritePush);
|
2019-09-19 22:42:45 +00:00
|
|
|
else if (pPlayer == gMe)
|
|
|
|
{
|
|
|
|
viewSetMessage("That requires a key.");
|
|
|
|
sndStartSample(3063, 255, 2, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0:
|
|
|
|
{
|
|
|
|
XWALL *pXWall = &xwall[a3];
|
|
|
|
int key = pXWall->key;
|
|
|
|
if (pXWall->locked && pPlayer == gMe)
|
|
|
|
{
|
|
|
|
viewSetMessage("It's locked");
|
|
|
|
sndStartSample(3062, 255, 2, 0);
|
|
|
|
}
|
|
|
|
if (!key || pPlayer->at88[key])
|
2019-10-11 21:59:39 +00:00
|
|
|
trTriggerWall(a2, pXWall, kCmdWallPush);
|
2019-09-19 22:42:45 +00:00
|
|
|
else if (pPlayer == gMe)
|
|
|
|
{
|
|
|
|
viewSetMessage("That requires a key.");
|
|
|
|
sndStartSample(3063, 255, 2, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
XSPRITE *pXSprite = &xsprite[a3];
|
|
|
|
int key = pXSprite->key;
|
|
|
|
if (pXSprite->locked && pPlayer == gMe && pXSprite->lockMsg)
|
|
|
|
trTextOver(pXSprite->lockMsg);
|
|
|
|
if (!key || pPlayer->at88[key])
|
2019-10-11 21:59:39 +00:00
|
|
|
trTriggerSprite(a2, pXSprite, kCmdSpritePush);
|
2019-09-19 22:42:45 +00:00
|
|
|
else if (pPlayer == gMe)
|
|
|
|
{
|
|
|
|
viewSetMessage("That requires a key.");
|
|
|
|
sndStartSample(3063, 255, 2, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pPlayer->at372 > 0)
|
|
|
|
pPlayer->at372 = ClipLow(pPlayer->at372-4*(6-gGameOptions.nDifficulty), 0);
|
|
|
|
if (pPlayer->at372 <= 0 && pPlayer->at376)
|
|
|
|
{
|
2019-10-11 21:59:39 +00:00
|
|
|
spritetype *pSprite2 = actSpawnDude(pPlayer->pSprite, kDudeHand, pPlayer->pSprite->clipdist<<1, 0);
|
2019-09-19 22:42:45 +00:00
|
|
|
pSprite2->ang = (pPlayer->pSprite->ang+1024)&2047;
|
|
|
|
int nSprite = pPlayer->pSprite->index;
|
|
|
|
int x = Cos(pPlayer->pSprite->ang)>>16;
|
|
|
|
int y = Sin(pPlayer->pSprite->ang)>>16;
|
|
|
|
xvel[pSprite2->index] = xvel[nSprite] + mulscale14(0x155555, x);
|
|
|
|
yvel[pSprite2->index] = yvel[nSprite] + mulscale14(0x155555, y);
|
|
|
|
zvel[pSprite2->index] = zvel[nSprite];
|
|
|
|
pPlayer->at376 = 0;
|
|
|
|
}
|
|
|
|
pInput->keyFlags.action = 0;
|
|
|
|
}
|
|
|
|
if (bVanilla)
|
|
|
|
{
|
|
|
|
if (pInput->keyFlags.lookCenter && !pInput->buttonFlags.lookUp && !pInput->buttonFlags.lookDown)
|
|
|
|
{
|
|
|
|
if (pPlayer->q16look < 0)
|
|
|
|
pPlayer->q16look = fix16_min(pPlayer->q16look+F16(4), F16(0));
|
|
|
|
if (pPlayer->q16look > 0)
|
|
|
|
pPlayer->q16look = fix16_max(pPlayer->q16look-F16(4), F16(0));
|
|
|
|
if (!pPlayer->q16look)
|
|
|
|
pInput->keyFlags.lookCenter = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pInput->buttonFlags.lookUp)
|
|
|
|
pPlayer->q16look = fix16_min(pPlayer->q16look+F16(4), F16(60));
|
|
|
|
if (pInput->buttonFlags.lookDown)
|
|
|
|
pPlayer->q16look = fix16_max(pPlayer->q16look-F16(4), F16(-60));
|
|
|
|
}
|
|
|
|
pPlayer->q16look = fix16_clamp(pPlayer->q16look+pInput->q16mlook, F16(-60), F16(60));
|
|
|
|
if (pPlayer->q16look > 0)
|
|
|
|
pPlayer->q16horiz = fix16_from_int(mulscale30(120, Sin(fix16_to_int(pPlayer->q16look)<<3)));
|
|
|
|
else if (pPlayer->q16look < 0)
|
|
|
|
pPlayer->q16horiz = fix16_from_int(mulscale30(180, Sin(fix16_to_int(pPlayer->q16look)<<3)));
|
|
|
|
else
|
|
|
|
pPlayer->q16horiz = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CONSTEXPR int upAngle = 289;
|
|
|
|
CONSTEXPR int downAngle = -347;
|
|
|
|
CONSTEXPR double lookStepUp = 4.0*upAngle/60.0;
|
|
|
|
CONSTEXPR double lookStepDown = -4.0*downAngle/60.0;
|
|
|
|
if (pInput->keyFlags.lookCenter && !pInput->buttonFlags.lookUp && !pInput->buttonFlags.lookDown)
|
|
|
|
{
|
|
|
|
if (pPlayer->q16look < 0)
|
|
|
|
pPlayer->q16look = fix16_min(pPlayer->q16look+F16(lookStepDown), F16(0));
|
|
|
|
if (pPlayer->q16look > 0)
|
|
|
|
pPlayer->q16look = fix16_max(pPlayer->q16look-F16(lookStepUp), F16(0));
|
|
|
|
if (!pPlayer->q16look)
|
|
|
|
pInput->keyFlags.lookCenter = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pInput->buttonFlags.lookUp)
|
|
|
|
pPlayer->q16look = fix16_min(pPlayer->q16look+F16(lookStepUp), F16(upAngle));
|
|
|
|
if (pInput->buttonFlags.lookDown)
|
|
|
|
pPlayer->q16look = fix16_max(pPlayer->q16look-F16(lookStepDown), F16(downAngle));
|
|
|
|
}
|
|
|
|
pPlayer->q16look = fix16_clamp(pPlayer->q16look+(pInput->q16mlook<<3), F16(downAngle), F16(upAngle));
|
|
|
|
pPlayer->q16horiz = fix16_from_float(100.f*tanf(fix16_to_float(pPlayer->q16look)*fPI/1024.f));
|
|
|
|
}
|
|
|
|
int nSector = pSprite->sectnum;
|
2019-06-28 12:24:46 +00:00
|
|
|
int florhit = gSpriteHit[pSprite->extra].florhit & 0xc000;
|
2019-09-19 22:42:45 +00:00
|
|
|
char va;
|
|
|
|
if (pXSprite->height < 16 && (florhit == 0x4000 || florhit == 0))
|
|
|
|
va = 1;
|
|
|
|
else
|
|
|
|
va = 0;
|
|
|
|
if (va && (sector[nSector].floorstat&2))
|
|
|
|
{
|
|
|
|
int z1 = getflorzofslope(nSector, pSprite->x, pSprite->y);
|
|
|
|
int x2 = pSprite->x+mulscale30(64, Cos(pSprite->ang));
|
|
|
|
int y2 = pSprite->y+mulscale30(64, Sin(pSprite->ang));
|
|
|
|
short nSector2 = nSector;
|
|
|
|
updatesector(x2, y2, &nSector2);
|
|
|
|
if (nSector2 == nSector)
|
|
|
|
{
|
|
|
|
int z2 = getflorzofslope(nSector2, x2, y2);
|
|
|
|
pPlayer->q16slopehoriz = interpolate(pPlayer->q16slopehoriz, fix16_from_int(z1-z2)>>3, 0x4000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pPlayer->q16slopehoriz = interpolate(pPlayer->q16slopehoriz, F16(0), 0x4000);
|
|
|
|
if (klabs(pPlayer->q16slopehoriz) < 4)
|
|
|
|
pPlayer->q16slopehoriz = 0;
|
|
|
|
}
|
|
|
|
pPlayer->at83 = (-fix16_to_int(pPlayer->q16horiz))<<7;
|
|
|
|
if (pInput->keyFlags.prevItem)
|
|
|
|
{
|
|
|
|
pInput->keyFlags.prevItem = 0;
|
|
|
|
packPrevItem(pPlayer);
|
|
|
|
}
|
|
|
|
if (pInput->keyFlags.nextItem)
|
|
|
|
{
|
|
|
|
pInput->keyFlags.nextItem = 0;
|
|
|
|
packNextItem(pPlayer);
|
|
|
|
}
|
|
|
|
if (pInput->keyFlags.useItem)
|
|
|
|
{
|
|
|
|
pInput->keyFlags.useItem = 0;
|
|
|
|
if (pPlayer->packInfo[pPlayer->at321].at1 > 0)
|
|
|
|
packUseItem(pPlayer, pPlayer->at321);
|
|
|
|
}
|
|
|
|
if (pInput->useFlags.useBeastVision)
|
|
|
|
{
|
|
|
|
pInput->useFlags.useBeastVision = 0;
|
|
|
|
if (pPlayer->packInfo[3].at1 > 0)
|
|
|
|
packUseItem(pPlayer, 3);
|
|
|
|
}
|
|
|
|
if (pInput->useFlags.useCrystalBall)
|
|
|
|
{
|
|
|
|
pInput->useFlags.useCrystalBall = 0;
|
|
|
|
if (pPlayer->packInfo[2].at1 > 0)
|
|
|
|
packUseItem(pPlayer, 2);
|
|
|
|
}
|
|
|
|
if (pInput->useFlags.useJumpBoots)
|
|
|
|
{
|
|
|
|
pInput->useFlags.useJumpBoots = 0;
|
|
|
|
if (pPlayer->packInfo[4].at1 > 0)
|
|
|
|
packUseItem(pPlayer, 4);
|
|
|
|
}
|
|
|
|
if (pInput->useFlags.useMedKit)
|
|
|
|
{
|
|
|
|
pInput->useFlags.useMedKit = 0;
|
|
|
|
if (pPlayer->packInfo[0].at1 > 0)
|
|
|
|
packUseItem(pPlayer, 0);
|
|
|
|
}
|
|
|
|
if (pInput->keyFlags.holsterWeapon)
|
|
|
|
{
|
|
|
|
pInput->keyFlags.holsterWeapon = 0;
|
|
|
|
if (pPlayer->atbd)
|
|
|
|
{
|
|
|
|
WeaponLower(pPlayer);
|
|
|
|
viewSetMessage("Holstering weapon");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CheckPickUp(pPlayer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void playerProcess(PLAYER *pPlayer)
|
|
|
|
{
|
|
|
|
spritetype *pSprite = pPlayer->pSprite;
|
|
|
|
int nSprite = pPlayer->at5b;
|
|
|
|
int nXSprite = pSprite->extra;
|
|
|
|
XSPRITE *pXSprite = pPlayer->pXSprite;
|
|
|
|
POSTURE *pPosture = &gPosture[pPlayer->at5f][pPlayer->at2f];
|
|
|
|
powerupProcess(pPlayer);
|
|
|
|
int top, bottom;
|
|
|
|
GetSpriteExtents(pSprite, &top, &bottom);
|
|
|
|
int dzb = (bottom-pSprite->z)/4;
|
|
|
|
int dzt = (pSprite->z-top)/4;
|
|
|
|
int dw = pSprite->clipdist<<2;
|
|
|
|
if (!gNoClip)
|
|
|
|
{
|
|
|
|
short nSector = pSprite->sectnum;
|
|
|
|
if (pushmove_old(&pSprite->x, &pSprite->y, &pSprite->z, &nSector, dw, dzt, dzb, CLIPMASK0) == -1)
|
|
|
|
actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_0, 500<<4);
|
|
|
|
if (pSprite->sectnum != nSector)
|
|
|
|
{
|
|
|
|
if (nSector == -1)
|
|
|
|
{
|
|
|
|
nSector = pSprite->sectnum;
|
|
|
|
actDamageSprite(nSprite, pSprite, DAMAGE_TYPE_0, 500<<4);
|
|
|
|
}
|
|
|
|
dassert(nSector >= 0 && nSector < kMaxSectors);
|
|
|
|
ChangeSpriteSect(nSprite, nSector);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ProcessInput(pPlayer);
|
|
|
|
int nSpeed = approxDist(xvel[nSprite], yvel[nSprite]);
|
|
|
|
pPlayer->at6b = interpolate(pPlayer->at6b, zvel[nSprite], 0x7000);
|
|
|
|
int dz = pPlayer->pSprite->z-pPosture->at24-pPlayer->at67;
|
|
|
|
if (dz > 0)
|
|
|
|
pPlayer->at6b += mulscale16(dz<<8, 0xa000);
|
|
|
|
else
|
|
|
|
pPlayer->at6b += mulscale16(dz<<8, 0x1800);
|
|
|
|
pPlayer->at67 += pPlayer->at6b>>8;
|
|
|
|
pPlayer->at73 = interpolate(pPlayer->at73, zvel[nSprite], 0x5000);
|
|
|
|
dz = pPlayer->pSprite->z-pPosture->at28-pPlayer->at6f;
|
|
|
|
if (dz > 0)
|
|
|
|
pPlayer->at73 += mulscale16(dz<<8, 0x8000);
|
|
|
|
else
|
|
|
|
pPlayer->at73 += mulscale16(dz<<8, 0xc00);
|
|
|
|
pPlayer->at6f += pPlayer->at73>>8;
|
|
|
|
pPlayer->at37 = ClipLow(pPlayer->at37-4, 0);
|
|
|
|
nSpeed >>= 16;
|
|
|
|
if (pPlayer->at2f == 1)
|
|
|
|
{
|
|
|
|
pPlayer->at3b = (pPlayer->at3b+17)&2047;
|
|
|
|
pPlayer->at4b = (pPlayer->at4b+17)&2047;
|
|
|
|
pPlayer->at3f = mulscale30(pPosture->at14*10, Sin(pPlayer->at3b*2));
|
|
|
|
pPlayer->at43 = mulscale30(pPosture->at18*pPlayer->at37, Sin(pPlayer->at3b-256));
|
|
|
|
pPlayer->at4f = mulscale30(pPosture->at1c*pPlayer->at37, Sin(pPlayer->at4b*2));
|
|
|
|
pPlayer->at53 = mulscale30(pPosture->at20*pPlayer->at37, Sin(pPlayer->at4b-0x155));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pXSprite->height < 256)
|
|
|
|
{
|
|
|
|
pPlayer->at3b = (pPlayer->at3b+pPosture->atc[pPlayer->at2e]*4) & 2047;
|
|
|
|
pPlayer->at4b = (pPlayer->at4b+(pPosture->atc[pPlayer->at2e]*4)/2) & 2047;
|
|
|
|
if (pPlayer->at2e)
|
|
|
|
{
|
|
|
|
if (pPlayer->at37 < 60)
|
|
|
|
pPlayer->at37 = ClipHigh(pPlayer->at37+nSpeed, 60);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (pPlayer->at37 < 30)
|
|
|
|
pPlayer->at37 = ClipHigh(pPlayer->at37+nSpeed, 30);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pPlayer->at3f = mulscale30(pPosture->at14*pPlayer->at37, Sin(pPlayer->at3b*2));
|
|
|
|
pPlayer->at43 = mulscale30(pPosture->at18*pPlayer->at37, Sin(pPlayer->at3b-256));
|
|
|
|
pPlayer->at4f = mulscale30(pPosture->at1c*pPlayer->at37, Sin(pPlayer->at4b*2));
|
|
|
|
pPlayer->at53 = mulscale30(pPosture->at20*pPlayer->at37, Sin(pPlayer->at4b-0x155));
|
|
|
|
}
|
|
|
|
pPlayer->at35a = 0;
|
|
|
|
pPlayer->at37f = ClipLow(pPlayer->at37f-4, 0);
|
|
|
|
pPlayer->at35e = ClipLow(pPlayer->at35e-4, 0);
|
|
|
|
pPlayer->at362 = ClipLow(pPlayer->at362-4, 0);
|
|
|
|
pPlayer->at366 = ClipLow(pPlayer->at366-4, 0);
|
|
|
|
pPlayer->at36a = ClipLow(pPlayer->at36a-4, 0);
|
|
|
|
pPlayer->at377 = ClipLow(pPlayer->at377-4, 0);
|
|
|
|
if (pPlayer == gMe && gMe->pXSprite->health == 0)
|
|
|
|
pPlayer->at376 = 0;
|
|
|
|
if (!pXSprite->health)
|
|
|
|
return;
|
|
|
|
pPlayer->at87 = 0;
|
|
|
|
if (pPlayer->at2f == 1)
|
|
|
|
{
|
|
|
|
pPlayer->at87 = 1;
|
|
|
|
int nSector = pSprite->sectnum;
|
|
|
|
int nLink = gLowerLink[nSector];
|
2019-10-19 19:11:39 +00:00
|
|
|
if (nLink > 0 && (sprite[nLink].type == kMarkerLowGoo || sprite[nLink].type == kMarkerLowWater))
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
|
|
|
if (getceilzofslope(nSector, pSprite->x, pSprite->y) > pPlayer->at67)
|
|
|
|
pPlayer->at87 = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!pPlayer->at87)
|
|
|
|
{
|
|
|
|
pPlayer->at2f2 = 1200;
|
|
|
|
pPlayer->at36e = 0;
|
|
|
|
if (packItemActive(pPlayer, 1))
|
|
|
|
packUseItem(pPlayer, 1);
|
|
|
|
}
|
|
|
|
int nType = kDudePlayer1-kDudeBase;
|
|
|
|
switch (pPlayer->at2f)
|
|
|
|
{
|
|
|
|
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->at6f-pPlayer->pSprite->z, a3, a4, a5, a6);
|
|
|
|
}
|
|
|
|
|
|
|
|
spritetype * playerFireThing(PLAYER *pPlayer, int a2, int a3, int thingType, int a5)
|
|
|
|
{
|
|
|
|
dassert(thingType >= kThingBase && thingType < kThingMax);
|
|
|
|
return actFireThing(pPlayer->pSprite, a2, pPlayer->at6f-pPlayer->pSprite->z, pPlayer->at83+a3, thingType, a5);
|
|
|
|
}
|
|
|
|
|
|
|
|
void playerFrag(PLAYER *pKiller, PLAYER *pVictim)
|
|
|
|
{
|
|
|
|
dassert(pKiller != NULL);
|
|
|
|
dassert(pVictim != NULL);
|
|
|
|
|
|
|
|
char buffer[128] = "";
|
|
|
|
int nKiller = pKiller->pSprite->type-kDudePlayer1;
|
|
|
|
dassert(nKiller >= 0 && nKiller < kMaxPlayers);
|
|
|
|
int nVictim = pVictim->pSprite->type-kDudePlayer1;
|
|
|
|
dassert(nVictim >= 0 && nVictim < kMaxPlayers);
|
|
|
|
if (myconnectindex == connecthead)
|
|
|
|
{
|
|
|
|
sprintf(buffer, "frag %d killed %d\n", pKiller->at57+1, pVictim->at57+1);
|
|
|
|
sub_7AC28(buffer);
|
|
|
|
buffer[0] = 0;
|
|
|
|
}
|
|
|
|
if (nKiller == nVictim)
|
|
|
|
{
|
|
|
|
pVictim->at2ee = -1;
|
2019-09-21 09:54:56 +00:00
|
|
|
if (VanillaMode() || gGameOptions.nGameType != 1)
|
|
|
|
{
|
|
|
|
pVictim->at2c6--;
|
|
|
|
pVictim->at2ca[nVictim]--;
|
|
|
|
}
|
2019-09-19 22:42:45 +00:00
|
|
|
if (gGameOptions.nGameType == 3)
|
|
|
|
dword_21EFB0[pVictim->at2ea]--;
|
|
|
|
int nMessage = Random(5);
|
|
|
|
int nSound = gSuicide[nMessage].at4;
|
|
|
|
if (pVictim == gMe && gMe->at372 <= 0)
|
|
|
|
{
|
|
|
|
sprintf(buffer, "You killed yourself!");
|
|
|
|
if (gGameOptions.nGameType > 0 && nSound >= 0)
|
|
|
|
sndStartSample(nSound, 255, 2, 0);
|
|
|
|
}
|
2019-09-17 12:16:05 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
sprintf(buffer, gSuicide[nMessage].at0, gProfile[nVictim].name);
|
|
|
|
}
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-09-21 09:54:56 +00:00
|
|
|
if (VanillaMode() || gGameOptions.nGameType != 1)
|
|
|
|
{
|
|
|
|
pKiller->at2c6++;
|
|
|
|
pKiller->at2ca[nKiller]++;
|
|
|
|
}
|
2019-09-19 22:42:45 +00:00
|
|
|
if (gGameOptions.nGameType == 3)
|
|
|
|
{
|
|
|
|
if (pKiller->at2ea == pVictim->at2ea)
|
|
|
|
dword_21EFB0[pKiller->at2ea]--;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dword_21EFB0[pKiller->at2ea]++;
|
|
|
|
dword_21EFD0[pKiller->at2ea]+=120;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int nMessage = Random(25);
|
|
|
|
int nSound = gVictory[nMessage].at4;
|
|
|
|
const char* pzMessage = gVictory[nMessage].at0;
|
|
|
|
sprintf(buffer, pzMessage, gProfile[nKiller].name, gProfile[nVictim].name);
|
|
|
|
if (gGameOptions.nGameType > 0 && nSound >= 0 && pKiller == gMe)
|
|
|
|
sndStartSample(nSound, 255, 2, 0);
|
|
|
|
}
|
|
|
|
viewSetMessage(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FragPlayer(PLAYER *pPlayer, int nSprite)
|
|
|
|
{
|
|
|
|
spritetype *pSprite = NULL;
|
|
|
|
if (nSprite >= 0)
|
|
|
|
pSprite = &sprite[nSprite];
|
|
|
|
if (pSprite && IsPlayerSprite(pSprite))
|
|
|
|
{
|
|
|
|
PLAYER *pKiller = &gPlayer[pSprite->type-kDudePlayer1];
|
|
|
|
playerFrag(pKiller, pPlayer);
|
|
|
|
int nTeam1 = pKiller->at2ea&1;
|
|
|
|
int nTeam2 = pPlayer->at2ea&1;
|
|
|
|
if (nTeam1 == 0)
|
|
|
|
{
|
|
|
|
if (nTeam1 != nTeam2)
|
2019-10-11 21:59:39 +00:00
|
|
|
evSend(0, 0, 15, kCmdToggle);
|
2019-09-19 22:42:45 +00:00
|
|
|
else
|
2019-10-11 21:59:39 +00:00
|
|
|
evSend(0, 0, 16, kCmdToggle);
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (nTeam1 == nTeam2)
|
2019-10-11 21:59:39 +00:00
|
|
|
evSend(0, 0, 16, kCmdToggle);
|
2019-09-19 22:42:45 +00:00
|
|
|
else
|
2019-10-11 21:59:39 +00:00
|
|
|
evSend(0, 0, 15, kCmdToggle);
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int playerDamageArmor(PLAYER *pPlayer, DAMAGE_TYPE nType, int nDamage)
|
|
|
|
{
|
|
|
|
DAMAGEINFO *pDamageInfo = &damageInfo[nType];
|
|
|
|
int nArmorType = pDamageInfo->at0;
|
|
|
|
if (nArmorType >= 0 && pPlayer->at33e[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->at33e[nArmorType];
|
|
|
|
int t = scale(v8, 0, 3200, nDamage/4, (nDamage*7)/8);
|
|
|
|
v8 -= t;
|
|
|
|
nDamage -= t;
|
|
|
|
pPlayer->at33e[nArmorType] = ClipLow(v8, 0);
|
|
|
|
}
|
|
|
|
return nDamage;
|
|
|
|
}
|
|
|
|
|
|
|
|
spritetype *sub_40A94(PLAYER *pPlayer, int a2)
|
|
|
|
{
|
|
|
|
char buffer[80];
|
|
|
|
spritetype *pSprite = NULL;
|
|
|
|
switch (a2)
|
|
|
|
{
|
2019-10-11 21:59:39 +00:00
|
|
|
case kItemFlagA:
|
2019-09-19 22:42:45 +00:00
|
|
|
pPlayer->at90 &= ~1;
|
2019-10-11 21:59:39 +00:00
|
|
|
pSprite = actDropObject(pPlayer->pSprite, kItemFlagA);
|
2019-09-19 22:42:45 +00:00
|
|
|
if (pSprite)
|
|
|
|
pSprite->owner = pPlayer->at91[0];
|
2019-09-15 11:59:27 +00:00
|
|
|
gBlueFlagDropped = true;
|
2019-09-19 22:42:45 +00:00
|
|
|
sprintf(buffer, "%s dropped Blue Flag", gProfile[pPlayer->at57].name);
|
|
|
|
sndStartSample(8005, 255, 2, 0);
|
|
|
|
viewSetMessage(buffer);
|
|
|
|
break;
|
2019-10-11 21:59:39 +00:00
|
|
|
case kItemFlagB:
|
2019-09-19 22:42:45 +00:00
|
|
|
pPlayer->at90 &= ~2;
|
2019-10-11 21:59:39 +00:00
|
|
|
pSprite = actDropObject(pPlayer->pSprite, kItemFlagB);
|
2019-09-19 22:42:45 +00:00
|
|
|
if (pSprite)
|
|
|
|
pSprite->owner = pPlayer->at91[1];
|
2019-09-15 11:59:27 +00:00
|
|
|
gRedFlagDropped = true;
|
2019-09-19 22:42:45 +00:00
|
|
|
sprintf(buffer, "%s dropped Red Flag", gProfile[pPlayer->at57].name);
|
|
|
|
sndStartSample(8004, 255, 2, 0);
|
|
|
|
viewSetMessage(buffer);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return pSprite;
|
|
|
|
}
|
|
|
|
|
|
|
|
int playerDamageSprite(int nSource, PLAYER *pPlayer, DAMAGE_TYPE nDamageType, int nDamage)
|
|
|
|
{
|
|
|
|
dassert(nSource < kMaxSprites);
|
|
|
|
dassert(pPlayer != NULL);
|
|
|
|
if (pPlayer->ata1[nDamageType])
|
|
|
|
return 0;
|
|
|
|
nDamage = playerDamageArmor(pPlayer, nDamageType, nDamage);
|
|
|
|
pPlayer->at366 = ClipHigh(pPlayer->at366+(nDamage>>3), 600);
|
|
|
|
|
|
|
|
spritetype *pSprite = pPlayer->pSprite;
|
|
|
|
XSPRITE *pXSprite = pPlayer->pXSprite;
|
|
|
|
int nXSprite = pSprite->extra;
|
|
|
|
int nXSector = sector[pSprite->sectnum].extra;
|
|
|
|
DUDEINFO *pDudeInfo = &dudeInfo[pSprite->type-kDudeBase];
|
|
|
|
int nDeathSeqID = -1;
|
2019-09-20 10:11:07 +00:00
|
|
|
int nKneelingPlayer = -1;
|
2019-09-19 22:42:45 +00:00
|
|
|
int nSprite = pSprite->index;
|
|
|
|
char va = playerSeqPlaying(pPlayer, 16);
|
|
|
|
if (!pXSprite->health)
|
|
|
|
{
|
|
|
|
if (va)
|
|
|
|
{
|
|
|
|
switch (nDamageType)
|
|
|
|
{
|
|
|
|
case DAMAGE_TYPE_5:
|
|
|
|
nDeathSeqID = 18;
|
|
|
|
sfxPlay3DSound(pSprite, 716, 0, 0);
|
|
|
|
break;
|
|
|
|
case DAMAGE_TYPE_3:
|
|
|
|
GibSprite(pSprite, GIBTYPE_7, NULL, NULL);
|
|
|
|
GibSprite(pSprite, GIBTYPE_15, NULL, NULL);
|
|
|
|
pPlayer->pSprite->cstat |= 32768;
|
|
|
|
nDeathSeqID = 17;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
int top, bottom;
|
|
|
|
GetSpriteExtents(pSprite, &top, &bottom);
|
|
|
|
CGibPosition gibPos(pSprite->x, pSprite->y, top);
|
|
|
|
CGibVelocity gibVel(xvel[pSprite->index]>>1, yvel[pSprite->index]>>1, -0xccccc);
|
|
|
|
GibSprite(pSprite, GIBTYPE_27, &gibPos, &gibVel);
|
|
|
|
GibSprite(pSprite, GIBTYPE_7, NULL, NULL);
|
|
|
|
fxSpawnBlood(pSprite, nDamage<<4);
|
|
|
|
fxSpawnBlood(pSprite, nDamage<<4);
|
|
|
|
nDeathSeqID = 17;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int nHealth = pXSprite->health-nDamage;
|
|
|
|
pXSprite->health = ClipLow(nHealth, 0);
|
|
|
|
if (pXSprite->health > 0 && pXSprite->health < 16)
|
|
|
|
{
|
|
|
|
nDamageType = DAMAGE_TYPE_2;
|
|
|
|
pXSprite->health = 0;
|
|
|
|
nHealth = -25;
|
|
|
|
}
|
|
|
|
if (pXSprite->health > 0)
|
|
|
|
{
|
|
|
|
DAMAGEINFO *pDamageInfo = &damageInfo[nDamageType];
|
|
|
|
int nSound;
|
|
|
|
if (nDamage >= (10<<4))
|
|
|
|
nSound = pDamageInfo->at4[0];
|
|
|
|
else
|
|
|
|
nSound = pDamageInfo->at4[Random(3)];
|
2019-10-19 19:11:39 +00:00
|
|
|
if (nDamageType == DAMAGE_TYPE_4 && pXSprite->medium == kMediumWater && !pPlayer->at376)
|
2019-09-19 22:42:45 +00:00
|
|
|
nSound = 714;
|
|
|
|
sfxPlay3DSound(pSprite, nSound, 0, 6);
|
|
|
|
return nDamage;
|
|
|
|
}
|
|
|
|
sfxKill3DSound(pPlayer->pSprite, -1, 441);
|
2019-10-11 21:59:39 +00:00
|
|
|
if (gGameOptions.nGameType == 3 && pPlayer->at90) {
|
|
|
|
if (pPlayer->at90&1) sub_40A94(pPlayer, kItemFlagA);
|
|
|
|
if (pPlayer->at90&2) sub_40A94(pPlayer, kItemFlagB);
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
pPlayer->at1fe = 0;
|
|
|
|
pPlayer->at1b1 = 0;
|
|
|
|
pPlayer->atbd = 0;
|
|
|
|
pPlayer->at2ee = nSource;
|
|
|
|
pPlayer->at34e = 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;
|
2019-09-20 10:11:07 +00:00
|
|
|
nKneelingPlayer = nPlayerKneelClient;
|
2019-10-19 19:11:39 +00:00
|
|
|
powerupActivate(pPlayer, kPwUpDeliriumShroom);
|
2019-09-19 22:42:45 +00:00
|
|
|
pXSprite->target = nSource;
|
2019-10-11 21:59:39 +00:00
|
|
|
evPost(pSprite->index, 3, 15, kCallbackFinishHim);
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
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)
|
2019-10-11 21:59:39 +00:00
|
|
|
trTriggerSector(pSprite->sectnum, &xsector[nXSector], kCmdSectorExit);
|
2019-09-30 07:18:01 +00:00
|
|
|
pSprite->flags |= 7;
|
2019-09-19 22:42:45 +00:00
|
|
|
for (int p = connecthead; p >= 0; p = connectpoint2[p])
|
|
|
|
{
|
|
|
|
if (gPlayer[p].at2ee == nSprite && gPlayer[p].at1fe > 0)
|
|
|
|
gPlayer[p].at2ee = -1;
|
|
|
|
}
|
|
|
|
FragPlayer(pPlayer, nSource);
|
2019-10-11 21:59:39 +00:00
|
|
|
trTriggerSprite(nSprite, pXSprite, kCmdOff);
|
2019-09-19 22:42:45 +00:00
|
|
|
}
|
|
|
|
dassert(gSysRes.Lookup(pDudeInfo->seqStartID + nDeathSeqID, "SEQ") != NULL);
|
2019-09-20 10:11:07 +00:00
|
|
|
seqSpawn(pDudeInfo->seqStartID+nDeathSeqID, 3, nXSprite, nKneelingPlayer);
|
2019-09-19 22:42:45 +00:00
|
|
|
return nDamage;
|
|
|
|
}
|
|
|
|
|
|
|
|
int UseAmmo(PLAYER *pPlayer, int nAmmoType, int nDec)
|
|
|
|
{
|
|
|
|
if (gInfiniteAmmo)
|
|
|
|
return 9999;
|
|
|
|
if (nAmmoType == -1)
|
|
|
|
return 9999;
|
|
|
|
pPlayer->at181[nAmmoType] = ClipLow(pPlayer->at181[nAmmoType]-nDec, 0);
|
|
|
|
return pPlayer->at181[nAmmoType];
|
|
|
|
}
|
|
|
|
|
|
|
|
void sub_41250(PLAYER *pPlayer)
|
|
|
|
{
|
|
|
|
int v4 = pPlayer->at1be.dz;
|
|
|
|
int dz = pPlayer->at6f-pPlayer->pSprite->z;
|
|
|
|
if (UseAmmo(pPlayer, 9, 0) < 8)
|
|
|
|
{
|
|
|
|
pPlayer->at34e = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
{
|
|
|
|
int ang1 = (pPlayer->at352+pPlayer->at356)&2047;
|
|
|
|
actFireVector(pPlayer->pSprite, 0, dz, Cos(ang1)>>16, Sin(ang1)>>16, v4, VECTOR_TYPE_21);
|
|
|
|
int ang2 = (pPlayer->at352+2048-pPlayer->at356)&2047;
|
|
|
|
actFireVector(pPlayer->pSprite, 0, dz, Cos(ang2)>>16, Sin(ang2)>>16, v4, VECTOR_TYPE_21);
|
|
|
|
}
|
|
|
|
pPlayer->at34e = ClipLow(pPlayer->at34e-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)
|
|
|
|
{
|
2019-09-22 07:53:42 +00:00
|
|
|
if (!gGameOptions.bFriendlyFire && IsTargetTeammate(pPlayer, &sprite[pHit->florhit & 0x3fff]))
|
|
|
|
return;
|
2019-09-19 22:42:45 +00:00
|
|
|
char nSurf = tileGetSurfType(pHit->florhit);
|
|
|
|
if (nSurf)
|
|
|
|
sfxPlay3DSound(pSprite, surfaceSound[nSurf], -1, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlayerSurvive(int, int nXSprite)
|
|
|
|
{
|
|
|
|
char buffer[80];
|
|
|
|
XSPRITE *pXSprite = &xsprite[nXSprite];
|
|
|
|
int nSprite = pXSprite->reference;
|
|
|
|
spritetype *pSprite = &sprite[nSprite];
|
|
|
|
actHealDude(pXSprite, 1, 2);
|
|
|
|
if (gGameOptions.nGameType > 0 && numplayers > 1)
|
|
|
|
{
|
|
|
|
sfxPlay3DSound(pSprite, 3009, 0, 6);
|
|
|
|
if (IsPlayerSprite(pSprite))
|
|
|
|
{
|
|
|
|
PLAYER *pPlayer = &gPlayer[pSprite->type-kDudePlayer1];
|
|
|
|
if (pPlayer == gMe)
|
|
|
|
viewSetMessage("I LIVE...AGAIN!!");
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sprintf(buffer, "%s lives again!", gProfile[pPlayer->at57].name);
|
|
|
|
viewSetMessage(buffer);
|
|
|
|
}
|
|
|
|
pPlayer->atc.newWeapon = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-20 10:11:07 +00:00
|
|
|
void PlayerKneelsOver(int, int nXSprite)
|
2019-09-19 22:42:45 +00:00
|
|
|
{
|
|
|
|
XSPRITE *pXSprite = &xsprite[nXSprite];
|
|
|
|
for (int p = connecthead; p >= 0; p = connectpoint2[p])
|
|
|
|
{
|
|
|
|
if (gPlayer[p].pXSprite == pXSprite)
|
|
|
|
{
|
|
|
|
PLAYER *pPlayer = &gPlayer[p];
|
|
|
|
playerDamageSprite(pPlayer->at2ee, pPlayer, DAMAGE_TYPE_5, 500<<4);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class PlayerLoadSave : public LoadSave
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual void Load(void);
|
|
|
|
virtual void Save(void);
|
|
|
|
};
|
|
|
|
|
|
|
|
void PlayerLoadSave::Load(void)
|
|
|
|
{
|
|
|
|
Read(dword_21EFB0, sizeof(dword_21EFB0));
|
|
|
|
Read(&gNetPlayers, sizeof(gNetPlayers));
|
|
|
|
Read(&gProfile, sizeof(gProfile));
|
|
|
|
Read(&gPlayer, sizeof(gPlayer));
|
|
|
|
for (int i = 0; i < gNetPlayers; i++)
|
|
|
|
{
|
|
|
|
gPlayer[i].pSprite = &sprite[gPlayer[i].at5b];
|
|
|
|
gPlayer[i].pXSprite = &xsprite[gPlayer[i].pSprite->extra];
|
|
|
|
gPlayer[i].pDudeInfo = &dudeInfo[gPlayer[i].pSprite->type-kDudeBase];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlayerLoadSave::Save(void)
|
|
|
|
{
|
|
|
|
Write(dword_21EFB0, sizeof(dword_21EFB0));
|
|
|
|
Write(&gNetPlayers, sizeof(gNetPlayers));
|
|
|
|
Write(&gProfile, sizeof(gProfile));
|
|
|
|
Write(&gPlayer, sizeof(gPlayer));
|
|
|
|
}
|
|
|
|
|
|
|
|
static PlayerLoadSave *myLoadSave;
|
|
|
|
|
|
|
|
void PlayerLoadSaveConstruct(void)
|
|
|
|
{
|
|
|
|
myLoadSave = new PlayerLoadSave();
|
|
|
|
}
|
2019-09-22 06:39:22 +00:00
|
|
|
|
|
|
|
END_BLD_NS
|