quakec/source/server/player/last_stand.qc

327 lines
8.8 KiB
C++
Raw Normal View History

/*
server/player/last_stand.qc
Functionality pertaining to Player's Last Stand mode and the act
of getting out of it.
Copyright (C) 2021-2023 NZ:P Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
float() PollPlayersAlive;
void() EndGameSetup;
void() SpectatorSpawn;
#define LAST_STAND_DURATION_SEC 100 // Seconds in Last Stand.
// when dead and other players exist and are alive, throw user into spectate mode
void(entity deceased) Player_BecomeSpectator =
{
if (!deceased.downed)
return;
/*if (self.beingrevived)
{
self.think = startspectate;
self.nextthink = time + 0.1;
return;
}*/
deceased.downedloop = 0;
deceased.beingrevived = false;
deceased.model = "";
setmodel(deceased, deceased.model);
deceased.health = 100;
deceased.weaponmodel = "";
deceased.weapon2model = "";
deceased.weapon = 0;
deceased.downed = 0;
deceased.frame = 0;
UpdateVmodel(deceased.weaponmodel, GetWepSkin(deceased.weapon));
UpdateV2model(deceased.weapon2model, GetWepSkin(deceased.weapon));
entity tempe;
tempe = self;
self = deceased;
SwitchWeapon(self.weapon);
SpectatorSpawn();
self = tempe;
}
void() ReviveTracker_Update =
{
// Add to the die timer..
self.ltime += 0.1;
// Bleed Out Timer
if (self.ltime >= LAST_STAND_DURATION_SEC) {
// Get rid of the Revive Icon from HUDs and make them a spectator.
DisableReviveIcon(self.owner.electro_targeted);
Player_BecomeSpectator(self.owner);
// Prevent any leakage
remove(self);
} else {
self.nextthink = time + 0.1;
}
}
void(entity deceased) SpawnReviveTracker =
{
entity revive_tracker = spawn();
revive_tracker.owner = deceased;
revive_tracker.think = ReviveTracker_Update;
revive_tracker.nextthink = time + 0.1;
}
void() Player_ReviveTrigger =
{
if (other.classname != "player" || (self.beingrevived == true && self.firer != other))
return;
// perform a trace to make sure they're always facing the revivee
vector source;
makevectors(self.angles);
source = other.origin - '0 0 12';
traceline(source, source + v_forward*50, 0, other);
other.active_door = trace_ent;
centerprint(other, other.active_door.classname);
if (self.beingrevived == false && other.active_door == self)
useprint(other, 13, 0, 0);
}
void() Player_EnterLastStand =
{
// 'Pro Gamer Move' achievement.
if (rounds <= 1 && self.currentmag == 0 &&
self.currentmag2 == 0 && self.currentammo == 0 &&
self.secondarymag == 0 && self.secondarymag2 == 0 &&
self.secondaryammo == 0) {
GiveAchievement(9, self);
}
// Play Last Stand Animation
PAnim_GetDown1();
// Force the player to prone.
if (self.stance == 2) self.new_ofs_z = self.view_ofs_z - 42;
if (self.stance == 1) self.new_ofs_z = self.view_ofs_z - 24;
self.stance = 0;
// Get Rid of Mule Kick Weapon (FIXME -- this just obliterates the third slot)
self.thirdweapon = 0;
// Calculate the loss in points, take away points from downed Player.
float point_difference;
point_difference = self.points;
point_difference -= 10*rint((self.points*0.95)/10);
addmoney(self, point_difference * -1, false);
self.requirespower = point_difference;
// Broadcast that the player has downed.
BroadcastMessage(time + 3, 2, self.netname);
// Different hitbox for Last Stand
setsize(self, '-16 -16 -32', '16 16 40');
// Reset state
self.velocity = self.zoom = 0;
self.downed = true;
self.dive_delay = 0;
self.movetype = MOVETYPE_WALK;
// Determine if we should End the Game.
float players_still_alive = PollPlayersAlive();
if ((coop && !players_still_alive) || (!coop && !(self.perks & P_REVIVE))) {
EndGameSetup();
return;
} else {
self.health = 19;
}
// Initiate Self-Revive on Solo
if ((self.perks & P_REVIVE) && !coop) {
self.progress_bar = 10 + time;
self.progress_bar_time = 10;
self.progress_bar_percent = 1;
}
// Take away weapons and Perks
self.perks = 0;
SetPerk(self, self.perks);
self.weaponbk = self.weapon;
self.currentammobk = self.currentammo;
self.currentmagbk = self.currentmag;
self.currentmagbk2 = self.currentmag2;
if(Util_PlayerHasWeapon(self, W_BIATCH, false) ||
Util_PlayerHasWeapon(self, W_RAY, true) ||
Util_PlayerHasWeapon(self, W_357, true)) {
float weapon_slot;
float total_ammo;
total_ammo = 0;
weapon_slot = Util_PlayerHasWeapon(self, W_RAY, true);
if (weapon_slot == 0) weapon_slot = Util_PlayerHasWeapon(self, W_BIATCH, false);
if (weapon_slot == 0) weapon_slot = Util_PlayerHasWeapon(self, W_357, true);
switch(weapon_slot) {
case 1:
total_ammo = self.currentmag + self.currentmag2 + self.currentammo;
break;
case 2:
total_ammo = self.secondarymag + self.secondarymag2 + self.secondaryammo;
self.weapon = self.secondaryweapon;
break;
}
// If it's greater than the mag size, we can fill the magazine.
if (total_ammo > getWeaponMag(self.weapon)) {
self.currentmag = getWeaponMag(self.weapon);
// subtract it from the total ammo
total_ammo -= self.currentmag;
} else {
self.currentmag = total_ammo;
total_ammo = 0;
}
// Check for dual wield mag too
if (IsDualWeapon(self.weapon)) {
if (total_ammo > getWeaponMag(self.weapon)) {
self.currentmag2 = getWeaponMag(self.weapon);
// subtract it from the total ammo
total_ammo -= self.currentmag2;
} else {
self.currentmag2 = total_ammo;
total_ammo = 0;
}
}
// Ray Gun has a special case where we DON'T fill its reserve
if (self.weapon != W_RAY && self.weapon != W_PORTER) {
// Now see if the reserve ammo is more than max downed capacity
if (total_ammo > getWeaponMag(self.weapon)*2) {
self.currentammo = getWeaponMag(self.weapon)*2;
} else {
// It's not so just fill it
self.currentammo = total_ammo;
}
} else {
self.currentammo = 0;
}
} else {
if (!coop) {
self.weapon = W_BIATCH;
self.currentammo = 12;
self.currentmag = self.currentmag2 = 6;
} else {
self.weapon = W_COLT;
self.currentmag = 8;
self.currentammo = 16;
}
}
// Play Switch Animation
self.weaponmodel = GetWeaponModel(self.weapon, 0);
SwitchWeapon(self.weapon);
float startframe = GetFrame(self.weapon,TAKE_OUT_START);
float endframe = GetFrame(self.weapon,TAKE_OUT_END);
Set_W_Frame (startframe, endframe, 0, 0, 0, SUB_Null, self.weaponmodel, false, S_BOTH);
// Spawn Revive Sprite and Revive Tracker in Co-Op
if (coop) {
EnableReviveIcon(revive_index, self.origin + VEC_VIEW_OFS);
SpawnReviveTracker(self);
self.touch = Player_ReviveTrigger;
self.electro_targeted = revive_index;
revive_index++;
}
//self.think = rec_downed;
//self.nextthink = time + 0.1;
}
void() Player_LeaveLastStand =
{
local string modelname;
float startframe;
float endframe;
playgetup(); // animation
self.new_ofs_z = self.view_ofs_z + 42;
self.stance = 2;
self.health = 100;
self.downedloop = 0; // used for death timing vs endgame
self.downed = 0;
self.classname = "player";
// Revert Hitbox
setsize(self, '-16 -16 -32', '16 16 40');
// Take away the ammo that was fired while in last stand.
if (self.weapon != W_COLT) {
if (self.weapon == self.weaponbk) {
self.currentammobk -= self.teslacount;
// Take from the mag if the reserve is empty now
if (self.currentammobk < 0)
self.currentmagbk += self.currentammobk;
self.currentammobk = 0;
} else if (self.weapon == self.secondaryweapon) {
self.secondaryammo -= self.teslacount;
// Take from the mag if the reserve is empty now
if (self.secondaryammo < 0)
self.secondarymag += self.secondaryammo;
self.secondaryammo = 0;
}
}
self.teslacount = 0;
if (!coop) {
addmoney(self, self.requirespower, false);
}
if (self.weaponbk)
{
self.weapon = self.weaponbk;
self.currentammo = self.currentammobk;
self.currentmag = self.currentmagbk;
self.currentmag2 = self.currentmagbk2;
}
modelname = GetWeaponModel(self.weapon, 0);
self.weaponmodel = modelname;
SwitchWeapon(self.weapon);
self.weapon2model = GetWeapon2Model(self.weapon);
self.movetype = MOVETYPE_WALK;
startframe = GetFrame(self.weapon,TAKE_OUT_START);
endframe = GetFrame(self.weapon,TAKE_OUT_END);
Set_W_Frame (startframe, endframe, 0, 0, 0, SUB_Null, modelname, false, S_BOTH);
};