mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00

This improves clarity for using weapon commands. Also the mask was incorrect for protocol above version 8 (mohaas and mohaab)
12830 lines
347 KiB
C++
12830 lines
347 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2024 the OpenMoHAA team
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
OpenMoHAA source code 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.
|
|
|
|
OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
// player.h: Class definition of the player.
|
|
|
|
#include "g_local.h"
|
|
#include "bg_local.h"
|
|
#include "g_spawn.h"
|
|
#include "g_phys.h"
|
|
#include "entity.h"
|
|
#include "consoleevent.h"
|
|
#include "player.h"
|
|
#include "worldspawn.h"
|
|
#include "weapon.h"
|
|
#include "trigger.h"
|
|
#include "scriptmaster.h"
|
|
#include "scriptexception.h"
|
|
#include "navigate.h"
|
|
#include "misc.h"
|
|
#include "earthquake.h"
|
|
#include "gravpath.h"
|
|
#include "armor.h"
|
|
#include "inventoryitem.h"
|
|
#include "gibs.h"
|
|
#include "actor.h"
|
|
#include "object.h"
|
|
#include "characterstate.h"
|
|
#include "weaputils.h"
|
|
#include "dm_manager.h"
|
|
#include "parm.h"
|
|
#include "body.h"
|
|
#include "playerstart.h"
|
|
#include "camera.h"
|
|
#include "weapturret.h"
|
|
#include "vehicleturret.h"
|
|
#include "portableturret.h"
|
|
#include "fixedturret.h"
|
|
|
|
const Vector power_color(0.0, 1.0, 0.0);
|
|
const Vector acolor(1.0, 1.0, 1.0);
|
|
const Vector bcolor(1.0, 0.0, 0.0);
|
|
|
|
//
|
|
// mohaas 2.0 and above
|
|
//
|
|
const char *pInstantMsgEng[6][9] = {
|
|
{"Good job team!",
|
|
"Alright!", "We've done it!",
|
|
"Wooohoo!", "Objective achieved.",
|
|
"We've completed an objective.", "We've lost an objective!",
|
|
"The enemy has overrun our objective!", NULL},
|
|
{"Squad, move in!",
|
|
"Squad, fall back!", "Squad, attack right flank!",
|
|
"Squad, attack left flank!", "Squad, hold this position!",
|
|
"Squad, covering fire!", "Squad, regroup!",
|
|
"Squad, split up!", NULL},
|
|
{"Cover me!",
|
|
"I'll cover you!", "Follow me!",
|
|
"You take point.", "Taking Fire! Need some help!",
|
|
"Get ready to move in on my signal.", "Attack!",
|
|
"Open fire!", NULL},
|
|
{"Yes sir!",
|
|
"No sir!", "Enemy Spotted.",
|
|
"Sniper!", "Grenade! Take Cover!",
|
|
"Area Clear.", "Thanks.",
|
|
"I owe you one.", NULL},
|
|
{"Who wants more?!",
|
|
"Never send boys to do a man's job.", "This is too easy!",
|
|
"You mess with the best, you die like the rest.", "Watch that friendly fire!",
|
|
"Hey! I'm on your team!", "Come on out you cowards!",
|
|
"Where are you hiding?", NULL},
|
|
// Added in 2.30
|
|
{"Guard our jail!",
|
|
"Capture the enemy jail!", "I'm defending our jail!",
|
|
"I'm attacking the enemy jail!", "Rescue the Prisoners!",
|
|
"The enemy is attacking our jail!"}
|
|
};
|
|
|
|
//
|
|
// for mohaa version 1.11 and below
|
|
//
|
|
const char *pInstantMsgEng_ver6[5][9] = {
|
|
{"Squad, move in!",
|
|
"Squad, fall back!", "Squad, attack right flank!",
|
|
"Squad, attack left flank!", "Squad, hold this position!",
|
|
"Squad, covering fire!", "Squad, regroup!",
|
|
"", ""},
|
|
{
|
|
"Cover me!", "I'll cover you!",
|
|
"Follow me!", "You take point.",
|
|
"You take the lead.", "Taking Fire! Need some help!",
|
|
"Charge!", "Attack!",
|
|
"Open fire!", },
|
|
{
|
|
"Yes sir!", "No sir!",
|
|
"Enemy Spotted.", "Sniper!",
|
|
"Grenade! Take Cover!", "Area Clear.",
|
|
"Great Shot!", "Thanks.",
|
|
"I owe you one.", },
|
|
{
|
|
"Is that all you've got?", "I think they are all out of real men!",
|
|
"Go on and run, you yellow-bellies!", "They're a bunch of cowards!",
|
|
"Come back when you've had some target practice!", "Come prepared next time!",
|
|
"Try again!", "I've seen French school girls shoot better!",
|
|
"That made a mess.", },
|
|
{"He's going to get us killed!",
|
|
"A lot of good men are going to die because of his poor leadership", "Good riddance!",
|
|
"That guy is going to get us all killed!", "Hey buddy, get down!",
|
|
"Stay out of my foxhole, pal!", "Find your own hiding place!",
|
|
"Get out of my way!", ""}
|
|
};
|
|
|
|
Event EV_Player_DumpState
|
|
(
|
|
"state",
|
|
EV_CHEAT,
|
|
NULL,
|
|
NULL,
|
|
"Dumps the player's state to the console.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_ForceTorsoState
|
|
(
|
|
"forcetorsostate",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"torsostate",
|
|
"Force the player's torso to a certain state",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_ForceLegsState
|
|
(
|
|
"forcelegsstate",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"legsstate",
|
|
"Force the player's legs to a certain state",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_GiveAllCheat
|
|
(
|
|
"wuss",
|
|
EV_CONSOLE | EV_CHEAT,
|
|
NULL,
|
|
NULL,
|
|
"Gives player all weapons.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_GiveNewWeaponsCheat
|
|
(
|
|
"giveweapon",
|
|
EV_CONSOLE | EV_CHEAT,
|
|
"s",
|
|
"weapon_name",
|
|
"Gives player all weapons.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_EndLevel
|
|
(
|
|
"endlevel",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Called when the player gets to the end of the level.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_DevGodCheat
|
|
(
|
|
"dog",
|
|
EV_CHEAT | EV_CONSOLE,
|
|
"I",
|
|
"god_mode",
|
|
"Sets the god mode cheat or toggles it.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_FullHeal
|
|
(
|
|
"fullheal",
|
|
EV_CHEAT | EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Heals player.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_DevNoTargetCheat
|
|
(
|
|
"notarget",
|
|
EV_CHEAT,
|
|
NULL,
|
|
NULL,
|
|
"Toggles the notarget cheat.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_DevNoClipCheat
|
|
(
|
|
"noclip",
|
|
EV_CHEAT | EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Toggles the noclip cheat.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_PrevItem
|
|
(
|
|
"invprev",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Cycle to player's previous item.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_NextItem
|
|
(
|
|
"invnext",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Cycle to player's next item.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_PrevWeapon
|
|
(
|
|
"weapprev",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Cycle to player's previous weapon.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_NextWeapon
|
|
(
|
|
"weapnext",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Cycle to player's next weapon.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_DropWeapon
|
|
(
|
|
"weapdrop",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Drops the player's current weapon.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Reload
|
|
(
|
|
"reload",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Reloads the player's weapon",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_CorrectWeaponAttachments
|
|
(
|
|
"correctweaponattachments",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"makes sure the weapons is properly attached when interrupting a reload",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_GiveCheat
|
|
(
|
|
"give",
|
|
EV_CONSOLE | EV_CHEAT,
|
|
"sI",
|
|
"name amount",
|
|
"Gives the player the specified thing (weapon, ammo, item, etc.) and optionally the amount.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_GiveWeaponCheat
|
|
(
|
|
"giveweapon",
|
|
EV_CONSOLE | EV_CHEAT,
|
|
"s",
|
|
"weapon_name",
|
|
"Gives the player the specified weapon.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_GameVersion
|
|
(
|
|
"gameversion",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Prints the game version.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Fov
|
|
(
|
|
"fov",
|
|
EV_CONSOLE,
|
|
"F",
|
|
"fov",
|
|
"Sets the fov.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Dead
|
|
(
|
|
"dead",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Called when the player is dead.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SpawnEntity
|
|
(
|
|
"spawn",
|
|
EV_CHEAT,
|
|
"sSSSSSSSS",
|
|
"entityname keyname1 value1 keyname2 value2 keyname3 value3 keyname4 value4",
|
|
"Spawns an entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SpawnActor
|
|
(
|
|
"actor",
|
|
EV_CHEAT,
|
|
"sSSSSSSSS",
|
|
"modelname keyname1 value1 keyname2 value2 keyname3 value3 keyname4 value4",
|
|
"Spawns an actor.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Respawn
|
|
(
|
|
"respawn",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Respawns the player.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_TestThread
|
|
(
|
|
"testthread",
|
|
EV_CHEAT,
|
|
"sS",
|
|
"scriptfile label",
|
|
"Starts the named thread at label if provided.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_PowerupTimer
|
|
(
|
|
"poweruptimer",
|
|
EV_DEFAULT,
|
|
"ii",
|
|
"poweruptimer poweruptype",
|
|
"Sets the powerup timer and powerup type.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_UpdatePowerupTimer
|
|
(
|
|
"updatepoweruptime",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Called once a second to decrement powerup time.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_ResetState
|
|
(
|
|
"resetstate",
|
|
EV_CHEAT,
|
|
NULL,
|
|
NULL,
|
|
"Reset the player's state table.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_WhatIs
|
|
(
|
|
"whatis",
|
|
EV_CHEAT,
|
|
"i",
|
|
"entity_number",
|
|
"Prints info on the specified entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_ActorInfo
|
|
(
|
|
"actorinfo",
|
|
EV_CHEAT,
|
|
"i",
|
|
"actor_number",
|
|
"Prints info on the specified actor.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_KillEnt
|
|
(
|
|
"killent",
|
|
EV_CHEAT,
|
|
"i",
|
|
"entity_number",
|
|
"Kills the specified entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_KillClass
|
|
(
|
|
"killclass",
|
|
EV_CHEAT,
|
|
"sI",
|
|
"classname except_entity_number",
|
|
"Kills all of the entities in the specified class.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_RemoveEnt
|
|
(
|
|
"removeent",
|
|
EV_CHEAT,
|
|
"i",
|
|
"entity_number",
|
|
"Removes the specified entity.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_RemoveClass
|
|
(
|
|
"removeclass",
|
|
EV_CHEAT,
|
|
"sI",
|
|
"classname except_entity_number",
|
|
"Removes all of the entities in the specified class.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Jump
|
|
(
|
|
"jump",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"height",
|
|
"Makes the player jump.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_AnimLoop_Torso
|
|
(
|
|
"animloop_torso",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Called when the torso animation has finished.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_AnimLoop_Legs
|
|
(
|
|
"animloop_legs",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Called when the legs animation has finished.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_AnimLoop_Pain // Added in 2.0
|
|
(
|
|
"animloop_pain",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Called when the pain animation has finished.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_DoUse
|
|
(
|
|
"usestuff",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Makes the player try to use whatever is in front of her.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_ListInventory
|
|
(
|
|
"listinventory",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"List of the player's inventory.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_ActivateShield
|
|
(
|
|
"activateshield",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Activates the player's shield",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_DeactivateShield
|
|
(
|
|
"deactivateshield",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Deactivates the player's shield",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Turn
|
|
(
|
|
"turn",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"yawangle",
|
|
"Causes player to turn the specified amount.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_TurnUpdate
|
|
(
|
|
"turnupdate",
|
|
EV_DEFAULT,
|
|
"ff",
|
|
"yaw timeleft",
|
|
"Causes player to turn the specified amount.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_TurnLegs
|
|
(
|
|
"turnlegs",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"yawangle",
|
|
"Turns the players legs instantly by the specified amount.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_NextPainTime(
|
|
"nextpaintime",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"seconds",
|
|
"Set the next time the player experiences pain (Current time + seconds specified).",
|
|
EV_NORMAL
|
|
);
|
|
|
|
Event EV_Player_FinishUseAnim
|
|
(
|
|
"finishuseanim",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Fires off all targets associated with a particular useanim.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Holster
|
|
(
|
|
"holster",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Holsters all wielded weapons, or unholsters previously put away weapons",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SafeHolster
|
|
(
|
|
"safeholster",
|
|
EV_CONSOLE,
|
|
"b",
|
|
"putaway",
|
|
"Holsters all wielded weapons, or unholsters previously put away weapons\n"
|
|
"preserves state, so it will not holster or unholster unless necessary",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SafeZoom
|
|
(
|
|
"safezoom",
|
|
EV_DEFAULT,
|
|
"b",
|
|
"zoomin",
|
|
"0 turns off zoom,"
|
|
"and 1 returns zoom to previous setting"
|
|
);
|
|
Event EV_Player_ZoomOff
|
|
(
|
|
"zoomoff",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"makes sure that zoom is off",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_StartUseObject
|
|
(
|
|
"startuseobject",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"starts up the useobject's animations.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_FinishUseObject
|
|
(
|
|
"finishuseobject",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Fires off all targets associated with a particular useobject.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_WatchActor
|
|
(
|
|
"watchactor",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"actor_to_watch",
|
|
"Makes the player's camera watch the specified actor.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_StopWatchingActor
|
|
(
|
|
"stopwatchingactor",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"actor_to_stop_watching",
|
|
"Makes the player's camera stop watching the specified actor.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetDamageMultiplier
|
|
(
|
|
"damage_multiplier",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"damage_multiplier",
|
|
"Sets the current damage multiplier",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_WaitForState
|
|
(
|
|
"waitForState",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"stateToWaitFor",
|
|
"When set, the player will clear waitforplayer when this state is hit\n"
|
|
"in the legs or torso.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_LogStats
|
|
(
|
|
"logstats",
|
|
EV_CHEAT,
|
|
"b",
|
|
"state",
|
|
"Turn on/off the debugging playlog",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_TakePain
|
|
(
|
|
"takepain",
|
|
EV_DEFAULT,
|
|
"b",
|
|
"bool",
|
|
"Set whether or not to take pain",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SkipCinematic
|
|
(
|
|
"skipcinematic",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Skip the current cinematic",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_ResetHaveItem
|
|
(
|
|
"resethaveitem",
|
|
EV_CONSOLE,
|
|
"s",
|
|
"weapon_name",
|
|
"Resets the game var that keeps track that we have gotten this weapon",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_ModifyHeight
|
|
(
|
|
"modheight",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"height",
|
|
"change the maximum height of the player\ncan specify 'stand', 'duck' or 'prone'.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_ModifyHeightFloat // Added in 2.40
|
|
(
|
|
"modheightfloat",
|
|
EV_DEFAULT,
|
|
"ff",
|
|
"height max_z",
|
|
"Specify the view height of the player and the height of his bounding box.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetMovePosFlags
|
|
(
|
|
"moveposflags",
|
|
EV_DEFAULT,
|
|
"sS",
|
|
"position movement",
|
|
"used by the state files to tell the game dll what the player is doing",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_GetPosition
|
|
(
|
|
"getposition",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns the player current position",
|
|
EV_RETURN
|
|
);
|
|
Event EV_Player_GetMovement
|
|
(
|
|
"getmovement",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns the player current movement",
|
|
EV_RETURN
|
|
);
|
|
Event EV_Player_Score
|
|
(
|
|
"score",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Show the score for the current deathmatch game",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_JoinDMTeam
|
|
(
|
|
"join_team",
|
|
EV_CONSOLE,
|
|
"s",
|
|
"team",
|
|
"Join the specified team (allies or axis)",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_AutoJoinDMTeam
|
|
(
|
|
"auto_join_team",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Join the team with fewer players",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_PickWeapon
|
|
(
|
|
"pickweapon",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Pick your weapon.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetInJail // Added in 2.30
|
|
(
|
|
"injail",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"boolean",
|
|
"set to 1 to indicate when player is in jail,"
|
|
"0 when they are free",
|
|
EV_SETTER
|
|
);
|
|
Event EV_Player_GetInJail // Added in 2.30
|
|
(
|
|
"injail",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns 1 if player is in jail,"
|
|
"0 if out",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_GetNationalityPrefix // Added in 2.30
|
|
(
|
|
"nationalityprefix",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"get the three or five letter prefix that denotes the player's nationality",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_IsSpectator // Added in 2.30
|
|
(
|
|
"isSpectator",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Check to see if player is a spectator (non-zero return value)",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_Spectator
|
|
(
|
|
"spectator",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Become a spectator",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_JoinArena
|
|
(
|
|
"join_arena",
|
|
EV_CONSOLE,
|
|
"i",
|
|
"arena_id_num",
|
|
"Join the specified arena",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_LeaveArena
|
|
(
|
|
"leave_arena",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Leave the current arena",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_CreateTeam
|
|
(
|
|
"create_team",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Create a team in the current arena",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_LeaveTeam
|
|
(
|
|
"leave_team",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Leave the current team",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_RefreshArenaUI
|
|
(
|
|
"arena_ui",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Refresh the arena UI",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_CallVote
|
|
(
|
|
"callvote",
|
|
EV_CONSOLE,
|
|
"ss",
|
|
"arg1 arg2",
|
|
"Player calls a vote",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Vote
|
|
(
|
|
"vote",
|
|
EV_CONSOLE,
|
|
"s",
|
|
"arg1",
|
|
"Player votes either yes or no",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_RetrieveVoteOptions // Added in 2.0
|
|
(
|
|
"gvo",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Retrieves the server's vote options file",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_PrimaryDMWeapon
|
|
(
|
|
"primarydmweapon",
|
|
EV_CONSOLE,
|
|
"s",
|
|
"weaptype",
|
|
"Sets the player's primary DM weapon",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_DeadBody
|
|
(
|
|
"deadbody",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Spawn a dead body",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Physics_On
|
|
(
|
|
"physics_on",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"turn player physics on.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Physics_Off
|
|
(
|
|
"physics_off",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"turn player physics off.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_ArmWithWeapons // Added in 2.30
|
|
(
|
|
"armwithweapons",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"give player their primary and secondary weapons.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_GetCurrentDMWeaponType // Added in 2.30
|
|
(
|
|
"getcurrentdmweapontype",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"get the player's current DM weapon type.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_AttachToLadder
|
|
(
|
|
"attachtoladder",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Attaches the sentient to a ladder",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_UnattachFromLadder
|
|
(
|
|
"unattachfromladder",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Unattaches the sentient from a ladder",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_TweakLadderPos
|
|
(
|
|
"tweakladderpos",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Tweaks the player's position on a ladder to be proper",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_EnsureOverLadder
|
|
(
|
|
"ensureoverladder",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Ensures that the player is at the proper height when getting off the top of a ladder",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_EnsureForwardOffLadder
|
|
(
|
|
"ensureforwardoffladder",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Ensures that the player went forward off the ladder.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_JailIsEscaping // Added in 2.30
|
|
(
|
|
"isEscaping",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Return non-zero if escaping or assisting escape",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_JailEscape // Added in 2.30
|
|
(
|
|
"jailescape",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Start the escape from jail animation",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_JailAssistEscape // Added in 2.30
|
|
(
|
|
"jailassistescape",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Start the assist jail escape animation",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_JailEscapeStop // Added in 2.30
|
|
(
|
|
"jailescapestop",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Stop either the escape from jail or assist animation",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_GetIsDisguised
|
|
(
|
|
"is_disguised",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"zero = not disguised"
|
|
"non-zero = disguised",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_GetHasDisguise
|
|
(
|
|
"has_disguise",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"zero = does not have a disguise,"
|
|
"non - zero = has a disguise",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_SetHasDisguise
|
|
(
|
|
"has_disguise",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"is_disguised",
|
|
"zero = does not have a disguise,"
|
|
"non - zero = has a disguise",
|
|
EV_SETTER
|
|
);
|
|
Event EV_Player_ObjectiveCount
|
|
(
|
|
"objective",
|
|
EV_DEFAULT,
|
|
"ii",
|
|
"num_completed out_of",
|
|
"Sets the number of objectives completed and the total number of objectives",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Stats
|
|
(
|
|
"stats",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Display the MissionLog.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Teleport
|
|
(
|
|
"tele",
|
|
EV_CHEAT | EV_CONSOLE,
|
|
"v",
|
|
"location",
|
|
"Teleport to location",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Face
|
|
(
|
|
"face",
|
|
EV_CHEAT | EV_CONSOLE,
|
|
"v",
|
|
"angles",
|
|
"Force angles to specified vector",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Coord
|
|
(
|
|
"coord",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"Prints out current location and angles",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_TestAnim
|
|
(
|
|
"testplayeranim",
|
|
EV_CHEAT,
|
|
"fS",
|
|
"weight anim",
|
|
"Plays a test animation on the player",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_StuffText
|
|
(
|
|
"stufftext",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"stuffstrings",
|
|
"Stuffs text to the player's console",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_DMMessage
|
|
(
|
|
"dmmessage",
|
|
EV_CONSOLE,
|
|
"is",
|
|
"mode stuffstrings",
|
|
"sends a DM message to the appropriate players",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_IPrint
|
|
(
|
|
"iprint",
|
|
EV_CONSOLE,
|
|
"sI",
|
|
"string bold",
|
|
"prints a string to the player,"
|
|
"optionally in bold",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_SetViewangles
|
|
(
|
|
"viewangles",
|
|
EV_DEFAULT,
|
|
"v",
|
|
"newAngles",
|
|
"set the view angles of the entity to newAngles.",
|
|
EV_SETTER
|
|
);
|
|
Event EV_GetViewangles
|
|
(
|
|
"viewangles",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"get the angles of the entity.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_GetUseHeld
|
|
(
|
|
"useheld",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns 1 if this player is holding use,"
|
|
"or 0 if he is not",
|
|
EV_GETTER
|
|
);
|
|
Event EV_GetFireHeld
|
|
(
|
|
"fireheld",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns 1 if this player is holding fire,"
|
|
"or 0 if he is not",
|
|
EV_GETTER
|
|
);
|
|
Event EV_GetPrimaryFireHeld // Added in 2.30
|
|
(
|
|
"primaryfireheld",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns 1 if this player is holding the primary fire, or 0 if not",
|
|
EV_GETTER);
|
|
Event EV_GetSecondaryFireHeld // Added in 2.30
|
|
(
|
|
"secondaryfireheld",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns 1 if this player is holding the secondary fire, or 0 if not",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_GetReady
|
|
(
|
|
"ready",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns 1 if this player is ready,"
|
|
"0 otherwise",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_SetReady
|
|
(
|
|
"ready",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"makes this player ready for the round to start",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetNotReady
|
|
(
|
|
"notready",
|
|
EV_CONSOLE,
|
|
NULL,
|
|
NULL,
|
|
"makes this player not ready for the round to start",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_GetName
|
|
(
|
|
"netname",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns player's name",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_GetDMTeam
|
|
(
|
|
"dmteam",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns 'allies',"
|
|
"'axis',"
|
|
"'spectator',"
|
|
"or 'freeforall'",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_SetViewModelAnim
|
|
(
|
|
"viewmodelanim",
|
|
EV_DEFAULT,
|
|
"sI",
|
|
"name force_restart",
|
|
"Sets the player's view model animation.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_DMDeathDrop
|
|
(
|
|
"dmdeathdrop",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Drops the player inventory in DM after's he's been killed",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Stopwatch
|
|
(
|
|
"stopwatch",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"duration",
|
|
"Starts a stopwatch for a given duration... use 0 to clear the stopwatch",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_EnterIntermission
|
|
(
|
|
"_enterintermission",
|
|
EV_CODEONLY,
|
|
NULL,
|
|
NULL,
|
|
"CODE USE ONLY",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetPerferredWeapon
|
|
(
|
|
"perferredweapon",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"weapon_name",
|
|
"Overrides your preferred weapon that is displayed in the stats screen.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetVoiceType
|
|
(
|
|
"voicetype",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"voice_name",
|
|
"Sets the voice type to use the player.",
|
|
EV_NORMAL
|
|
);
|
|
|
|
Event EV_Player_AddKills // Added in 2.0
|
|
(
|
|
"addkills",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"kills",
|
|
"Give or take kills from the player",
|
|
EV_NORMAL
|
|
);
|
|
|
|
Event EV_Player_KillAxis // Added in 2.30
|
|
(
|
|
"killaxis",
|
|
EV_CHEAT,
|
|
"f",
|
|
"radius",
|
|
"Kills all of the axis that are in the passed radius, or all of them if radius is 0.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_GetTurret // Added in 2.30
|
|
(
|
|
"turret",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Returns the turret the player is using. NULL if player isn't using a turret.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_GetVehicle // Added in 2.30
|
|
(
|
|
"vehicle",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Returns the vehicle the player is using. NULL if player isn't using a vehicle.",
|
|
EV_GETTER
|
|
);
|
|
|
|
////////////////////////////
|
|
//
|
|
// Added in OPM
|
|
//
|
|
////////////////////////////
|
|
Event EV_Player_AddDeaths
|
|
(
|
|
"adddeaths",
|
|
EV_DEFAULT,
|
|
"i",
|
|
"deaths",
|
|
"adds deaths number to player",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_AdminRights
|
|
(
|
|
"adminrights",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns client admin rights",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_IsAdmin
|
|
(
|
|
"isadmin",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"checks if player is logged as admin",
|
|
EV_RETURN
|
|
);
|
|
Event EV_Player_BindWeap
|
|
(
|
|
"bindweap",
|
|
EV_DEFAULT,
|
|
"ei",
|
|
"weapon handnum",
|
|
"binds weapon to player and sets him as weapon owner",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Dive
|
|
(
|
|
"dive",
|
|
EV_DEFAULT,
|
|
"fF",
|
|
"height airborne_duration",
|
|
"Makes the player dive into prone position.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_FreezeControls
|
|
(
|
|
"freezecontrols",
|
|
EV_DEFAULT,
|
|
"b",
|
|
"freeze_state",
|
|
"Blocks or unblocks control input from this player.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_GetConnState
|
|
(
|
|
"getconnstate",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"gets connection state. [DEPRECATED]",
|
|
EV_RETURN
|
|
);
|
|
Event EV_Player_GetDamageMultiplier
|
|
(
|
|
"damage_multiplier",
|
|
EV_DEFAULT,
|
|
"Gets the current damage multiplier",
|
|
NULL,
|
|
NULL,
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_GetKillHandler
|
|
(
|
|
"killhandler",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"label",
|
|
"Gets the player's current killed event handler. Returns NIL if no custom killhandler was set.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_GetKills
|
|
(
|
|
"getkills",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"gets kills number of player",
|
|
EV_RETURN
|
|
);
|
|
Event EV_Player_GetDeaths
|
|
(
|
|
"getdeaths",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"gets deaths number of player",
|
|
EV_RETURN
|
|
);
|
|
Event EV_Player_GetLegsState
|
|
(
|
|
"getlegsstate",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets the player's current legs state name",
|
|
EV_RETURN
|
|
);
|
|
Event EV_Player_GetStateFile
|
|
(
|
|
"statefile",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets the player's current state file.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_GetTorsoState
|
|
(
|
|
"gettorsostate",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets the player's current torso state name",
|
|
EV_RETURN
|
|
);
|
|
Event EV_Player_Inventory
|
|
(
|
|
"inventory",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns player's inventory",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_InventorySet
|
|
(
|
|
"inventory",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"array",
|
|
"Set up the player's inventory",
|
|
EV_SETTER
|
|
);
|
|
Event EV_Player_LeanLeftHeld
|
|
(
|
|
"leanleftheld",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Returns 1 if this player is holding lean left key, or 0 if he is not",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_LeanRightHeld
|
|
(
|
|
"leanrightheld",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns EV_RETURN if this player is holding lean right key, or 0 if he is not",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_MoveSpeedScale
|
|
(
|
|
"moveSpeedScale",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"speed",
|
|
"Sets the player's speed multiplier (default 1.0).",
|
|
EV_SETTER
|
|
);
|
|
Event EV_Player_MoveSpeedScaleGet
|
|
(
|
|
"moveSpeedScale",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets the player's speed multiplier.",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_PlayLocalSound
|
|
(
|
|
"playlocalsound",
|
|
EV_DEFAULT,
|
|
"sBF",
|
|
"soundName loop time",
|
|
"Plays a local sound to the player. The sound must be aliased globally. Requires sv_reborn to be set for stereo "
|
|
"sounds.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_RunHeld
|
|
(
|
|
"runheld",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns 1 if this player is holding run key,"
|
|
"or 0 if he is not",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_SecFireHeld
|
|
(
|
|
"secfireheld",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns 1 if this player is holding secondary fire, or 0 if he is not",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_SetAnimSpeed
|
|
(
|
|
"setanimspeed",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"speed",
|
|
"set the player's animation speed multiplier (default 1.0).",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetFov
|
|
(
|
|
"setfov",
|
|
EV_DEFAULT,
|
|
"f",
|
|
"fov",
|
|
"set the player's fov (default 80).",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetKillHandler
|
|
(
|
|
"killhandler",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"label",
|
|
"Replace the player's killed event by a new scripted handler. None or an empty string will revert to the default "
|
|
"killed event handler.",
|
|
EV_SETTER
|
|
);
|
|
Event EV_Player_SetSpeed
|
|
(
|
|
"setspeed",
|
|
EV_DEFAULT,
|
|
"fI",
|
|
"speed index",
|
|
"Sets the player's speed multiplier (default 1.0). Index specify which array value will be used (maximum 4).",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetStateFile
|
|
(
|
|
"statefile",
|
|
EV_DEFAULT,
|
|
"S",
|
|
"statefile",
|
|
"Sets the player's current state file (setting NIL, NULL or an empty string will revert to the global statefile).",
|
|
EV_SETTER
|
|
);
|
|
Event EV_Player_SetTeam
|
|
(
|
|
"setteam",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"team_name",
|
|
"sets the player's team without respawning.\n"
|
|
"Available team names are 'none', 'spectator', 'freeforall', 'axis' and 'allies'.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_HideEnt
|
|
(
|
|
"hideent",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"entity",
|
|
"Hides the specified entity to the player.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_ShowEnt
|
|
(
|
|
"showent",
|
|
EV_DEFAULT,
|
|
"e",
|
|
"entity",
|
|
"Shows the specified entity to the player.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_StopLocalSound
|
|
(
|
|
"stoplocalsound",
|
|
EV_DEFAULT,
|
|
"sF",
|
|
"soundName time",
|
|
"Stops the specified sound.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Userinfo
|
|
(
|
|
"userinfo",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"returns userinfo string",
|
|
EV_GETTER
|
|
);
|
|
Event EV_Player_ViewModelGetAnim
|
|
(
|
|
"viewmodelgetanim",
|
|
EV_DEFAULT,
|
|
"B",
|
|
"fullanim",
|
|
"Gets the player's current view model animation.",
|
|
EV_RETURN
|
|
);
|
|
Event EV_Player_ViewModelAnimFinished
|
|
(
|
|
"viewmodelanimfinished",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"True if the player's current view model finished its animation.",
|
|
EV_RETURN
|
|
);
|
|
Event EV_Player_ViewModelAnimValid
|
|
(
|
|
"viewmodelanimvalid",
|
|
EV_DEFAULT,
|
|
"sB",
|
|
"anim fullanim",
|
|
"True if the view model animation is valid.",
|
|
EV_RETURN
|
|
);
|
|
|
|
#ifdef OPM_FEATURES
|
|
Event EV_Player_Earthquake
|
|
(
|
|
"earthquake2",
|
|
EV_DEFAULT,
|
|
"ffbbVF",
|
|
"duration magnitude no_rampup no_rampdown location radius",
|
|
"Create a smooth realistic earthquake for a player. Requires sv_reborn to be set.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_Replicate
|
|
(
|
|
"replicate",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"variable",
|
|
"Replicate a variable to the client (needs patch 1.12).",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetClientFlag
|
|
(
|
|
"setclientflag",
|
|
EV_DEFAULT,
|
|
"s",
|
|
"name",
|
|
"Calls a flag to the script client.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetEntityShader
|
|
(
|
|
"setentshader",
|
|
EV_DEFAULT,
|
|
"es",
|
|
"entity shadername",
|
|
"Sets an entity shader for this player. An empty string will revert to the normal entity shader.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetLocalSoundRate
|
|
(
|
|
"setlocalsoundrate",
|
|
EV_DEFAULT,
|
|
"sfF",
|
|
"name rate time",
|
|
"Sets the local sound rate.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_SetViewModelAnimSpeed
|
|
(
|
|
"setvmaspeed",
|
|
EV_DEFAULT,
|
|
"sf",
|
|
"name speed",
|
|
"Sets the player's animation speed when playing it.",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_VisionSetBlur
|
|
(
|
|
"visionsetblur",
|
|
EV_DEFAULT,
|
|
"fF",
|
|
"level transition_time",
|
|
"Sets the player's blur level. Level is a fraction from 0-1",
|
|
EV_NORMAL
|
|
);
|
|
Event EV_Player_VisionGetNaked
|
|
(
|
|
"visiongetnaked",
|
|
EV_DEFAULT,
|
|
NULL,
|
|
NULL,
|
|
"Gets the player's current naked-eye vision.",
|
|
EV_RETURN
|
|
);
|
|
Event EV_Player_VisionSetNaked
|
|
(
|
|
"visionsetnaked",
|
|
EV_DEFAULT,
|
|
"sFF",
|
|
"vision_name transition_time phase",
|
|
"Sets the player's naked-eye vision. Optionally give a transition time from the current vision. If vision_name is "
|
|
"an empty string, it will revert to the current global vision.",
|
|
EV_NORMAL
|
|
);
|
|
#endif
|
|
|
|
qboolean TryPush(int entnum, vec3_t move_origin, vec3_t move_end);
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
PLAYER
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
CLASS_DECLARATION(Sentient, Player, "player") {
|
|
{&EV_Vehicle_Enter, &Player::EnterVehicle },
|
|
{&EV_Vehicle_Exit, &Player::ExitVehicle },
|
|
{&EV_Turret_Enter, &Player::EnterTurret },
|
|
{&EV_Turret_Exit, &Player::ExitTurret },
|
|
{&EV_Player_EndLevel, &Player::EndLevel },
|
|
{&EV_Player_PrevItem, &Player::SelectPreviousItem },
|
|
{&EV_Player_NextItem, &Player::SelectNextItem },
|
|
{&EV_Player_PrevWeapon, &Player::SelectPreviousWeapon },
|
|
{&EV_Player_NextWeapon, &Player::SelectNextWeapon },
|
|
{&EV_Player_DropWeapon, &Player::DropCurrentWeapon },
|
|
{&EV_Player_Reload, &Player::PlayerReload },
|
|
{&EV_Player_CorrectWeaponAttachments, &Player::EventCorrectWeaponAttachments},
|
|
{&EV_Player_GiveCheat, &Player::GiveCheat },
|
|
{&EV_Player_GiveWeaponCheat, &Player::GiveWeaponCheat },
|
|
{&EV_Player_GiveAllCheat, &Player::GiveAllCheat },
|
|
{&EV_Player_GiveNewWeaponsCheat, &Player::GiveNewWeaponsCheat },
|
|
{&EV_Player_DevGodCheat, &Player::GodCheat },
|
|
{&EV_Player_FullHeal, &Player::FullHeal },
|
|
{&EV_Player_DevNoTargetCheat, &Player::NoTargetCheat },
|
|
{&EV_Player_DevNoClipCheat, &Player::NoclipCheat },
|
|
{&EV_Player_GameVersion, &Player::GameVersion },
|
|
{&EV_Player_DumpState, &Player::DumpState },
|
|
{&EV_Player_ForceTorsoState, &Player::ForceTorsoState },
|
|
{&EV_Player_ForceLegsState, &Player::ForceLegsState },
|
|
{&EV_Player_Fov, &Player::EventSetSelectedFov },
|
|
{&EV_Kill, &Player::Kill },
|
|
{&EV_Player_Dead, &Player::Dead },
|
|
{&EV_Player_SpawnEntity, &Player::SpawnEntity },
|
|
{&EV_Player_SpawnActor, &Player::SpawnActor },
|
|
{&EV_Player_Respawn, &Player::Respawn },
|
|
{&EV_Player_DoUse, &Player::DoUse },
|
|
{&EV_Pain, &Player::Pain },
|
|
{&EV_Killed, &Player::Killed },
|
|
{&EV_GotKill, &Player::GotKill },
|
|
{&EV_Player_TestThread, &Player::TestThread },
|
|
{&EV_Player_PowerupTimer, &Player::SetPowerupTimer },
|
|
{&EV_Player_UpdatePowerupTimer, &Player::UpdatePowerupTimer },
|
|
{&EV_Player_ResetState, &Player::ResetState },
|
|
{&EV_Player_WhatIs, &Player::WhatIs },
|
|
{&EV_Player_ActorInfo, &Player::ActorInfo },
|
|
{&EV_Player_KillEnt, &Player::KillEnt },
|
|
{&EV_Player_RemoveEnt, &Player::RemoveEnt },
|
|
{&EV_Player_KillClass, &Player::KillClass },
|
|
{&EV_Player_RemoveClass, &Player::RemoveClass },
|
|
{&EV_Player_AnimLoop_Legs, &Player::EndAnim_Legs },
|
|
{&EV_Player_AnimLoop_Torso, &Player::EndAnim_Torso },
|
|
{&EV_Player_AnimLoop_Pain, &Player::EndAnim_Pain },
|
|
{&EV_Player_Jump, &Player::Jump },
|
|
{&EV_Sentient_JumpXY, &Player::JumpXY },
|
|
{&EV_Player_ListInventory, &Player::ListInventoryEvent },
|
|
{&EV_Player_NextPainTime, &Player::NextPainTime },
|
|
{&EV_Player_Turn, &Player::Turn },
|
|
{&EV_Player_TurnUpdate, &Player::TurnUpdate },
|
|
{&EV_Player_TurnLegs, &Player::TurnLegs },
|
|
{&EV_Player_FinishUseAnim, &Player::FinishUseAnim },
|
|
{&EV_Player_Holster, &Player::HolsterToggle },
|
|
{&EV_Player_SafeHolster, &Player::Holster },
|
|
{&EV_Player_SafeZoom, &Player::SafeZoomed },
|
|
{&EV_Player_ZoomOff, &Player::ZoomOffEvent },
|
|
{&EV_Player_StartUseObject, &Player::StartUseObject },
|
|
{&EV_Player_FinishUseObject, &Player::FinishUseObject },
|
|
{&EV_Player_WatchActor, &Player::WatchActor },
|
|
{&EV_Player_StopWatchingActor, &Player::StopWatchingActor },
|
|
{&EV_Player_SetDamageMultiplier, &Player::SetDamageMultiplier },
|
|
{&EV_Player_WaitForState, &Player::WaitForState },
|
|
{&EV_Player_LogStats, &Player::LogStats },
|
|
{&EV_Player_TakePain, &Player::SetTakePain },
|
|
{&EV_Player_SkipCinematic, &Player::SkipCinematic },
|
|
{&EV_Player_ResetHaveItem, &Player::ResetHaveItem },
|
|
{&EV_Show, &Player::PlayerShowModel },
|
|
{&EV_Player_ModifyHeight, &Player::ModifyHeight },
|
|
{&EV_Player_ModifyHeightFloat, &Player::ModifyHeightFloat },
|
|
{&EV_Player_SetMovePosFlags, &Player::SetMovePosFlags },
|
|
{&EV_Player_GetPosition, &Player::GetPositionForScript },
|
|
{&EV_Player_GetMovement, &Player::GetMovementForScript },
|
|
{&EV_Player_Teleport, &Player::EventTeleport },
|
|
{&EV_Player_Face, &Player::EventFace },
|
|
{&EV_Player_Coord, &Player::EventCoord },
|
|
{&EV_Player_TestAnim, &Player::EventTestAnim },
|
|
{&EV_Player_JailIsEscaping, &Player::EventGetIsEscaping },
|
|
{&EV_Player_JailEscapeStop, &Player::EventJailEscapeStop },
|
|
{&EV_Player_JailAssistEscape, &Player::EventJailAssistEscape },
|
|
{&EV_Player_JailEscape, &Player::EventJailEscape },
|
|
{&EV_Player_Score, &Player::Score },
|
|
{&EV_Player_JoinDMTeam, &Player::Join_DM_Team },
|
|
{&EV_Player_AutoJoinDMTeam, &Player::Auto_Join_DM_Team },
|
|
{&EV_Player_LeaveTeam, &Player::Leave_DM_Team },
|
|
{&EV_Player_IsSpectator, &Player::GetIsSpectator },
|
|
{&EV_Player_GetNationalityPrefix, &Player::GetNationalityPrefix },
|
|
{&EV_Player_SetInJail, &Player::EventSetInJail },
|
|
{&EV_Player_GetInJail, &Player::EventGetInJail },
|
|
{&EV_Player_Spectator, &Player::Spectator },
|
|
{&EV_Player_PickWeapon, &Player::PickWeaponEvent },
|
|
{&EV_Player_CallVote, &Player::CallVote },
|
|
{&EV_Player_Vote, &Player::Vote },
|
|
{&EV_Player_RetrieveVoteOptions, &Player::RetrieveVoteOptions },
|
|
{&EV_Player_PrimaryDMWeapon, &Player::EventPrimaryDMWeapon },
|
|
{&EV_Player_DeadBody, &Player::DeadBody },
|
|
{&EV_Player_ArmWithWeapons, &Player::ArmWithWeapons },
|
|
{&EV_Player_GetCurrentDMWeaponType, &Player::EventGetCurrentDMWeaponType },
|
|
{&EV_Player_Physics_On, &Player::PhysicsOn },
|
|
{&EV_Player_Physics_Off, &Player::PhysicsOff },
|
|
{&EV_Player_AttachToLadder, &Player::AttachToLadder },
|
|
{&EV_Player_UnattachFromLadder, &Player::UnattachFromLadder },
|
|
{&EV_Player_TweakLadderPos, &Player::TweakLadderPos },
|
|
{&EV_Player_EnsureOverLadder, &Player::EnsureOverLadder },
|
|
{&EV_Player_EnsureForwardOffLadder, &Player::EnsureForwardOffLadder },
|
|
{&EV_Damage, &Player::ArmorDamage },
|
|
{&EV_Player_GetIsDisguised, &Player::GetIsDisguised },
|
|
{&EV_Player_GetHasDisguise, &Player::GetHasDisguise },
|
|
{&EV_Player_SetHasDisguise, &Player::SetHasDisguise },
|
|
{&EV_Player_ObjectiveCount, &Player::SetObjectiveCount },
|
|
{&EV_Player_Stats, &Player::Stats },
|
|
{&EV_Player_StuffText, &Player::EventStuffText },
|
|
{&EV_Player_DMMessage, &Player::EventDMMessage },
|
|
{&EV_Player_IPrint, &Player::EventIPrint },
|
|
{&EV_SetViewangles, &Player::SetViewangles },
|
|
{&EV_GetViewangles, &Player::GetViewangles },
|
|
{&EV_GetUseHeld, &Player::EventGetUseHeld },
|
|
{&EV_GetFireHeld, &Player::EventGetFireHeld },
|
|
{&EV_GetPrimaryFireHeld, &Player::EventGetPrimaryFireHeld },
|
|
{&EV_GetSecondaryFireHeld, &Player::EventGetSecondaryFireHeld },
|
|
{&EV_Player_GetReady, &Player::EventGetReady },
|
|
{&EV_Player_SetReady, &Player::EventSetReady },
|
|
{&EV_Player_SetNotReady, &Player::EventSetNotReady },
|
|
{&EV_Player_GetDMTeam, &Player::EventGetDMTeam },
|
|
{&EV_Player_GetName, &Player::EventGetNetName },
|
|
{&EV_Player_SetViewModelAnim, &Player::EventSetViewModelAnim },
|
|
{&EV_Player_DMDeathDrop, &Player::EventDMDeathDrop },
|
|
{&EV_Player_Stopwatch, &Player::EventStopwatch },
|
|
{&EV_Player_EnterIntermission, &Player::EventEnterIntermission },
|
|
{&EV_Player_SetPerferredWeapon, &Player::EventSetPerferredWeapon },
|
|
{&EV_Player_SetVoiceType, &Player::EventSetVoiceType },
|
|
{&EV_Player_AddKills, &Player::EventAddKills },
|
|
{&EV_Player_KillAxis, &Player::EventKillAxis },
|
|
{&EV_Player_GetTurret, &Player::EventGetTurret },
|
|
{&EV_Player_GetVehicle, &Player::EventGetVehicle },
|
|
|
|
{&EV_Player_AddDeaths, &Player::AddDeaths },
|
|
{&EV_Player_AdminRights, &Player::AdminRights },
|
|
{&EV_Player_BindWeap, &Player::BindWeap },
|
|
{&EV_Player_Dive, &Player::Dive },
|
|
{&EV_Player_DMMessage, &Player::EventDMMessage },
|
|
{&EV_Player_FreezeControls, &Player::FreezeControls },
|
|
{&EV_Player_SetTeam, &Player::EventSetTeam },
|
|
{&EV_Player_GetConnState, &Player::GetConnState },
|
|
{&EV_Player_GetDamageMultiplier, &Player::GetDamageMultiplier },
|
|
{&EV_Player_GetDeaths, &Player::GetDeaths },
|
|
{&EV_Player_GetKillHandler, &Player::GetKillHandler },
|
|
{&EV_Player_GetKills, &Player::GetKills },
|
|
{&EV_Player_GetLegsState, &Player::GetLegsState },
|
|
{&EV_Player_GetStateFile, &Player::GetStateFile },
|
|
{&EV_Player_GetTorsoState, &Player::GetTorsoState },
|
|
{&EV_Player_HideEnt, &Player::HideEntity },
|
|
{&EV_Player_Inventory, &Player::Inventory },
|
|
{&EV_Player_InventorySet, &Player::InventorySet },
|
|
{&EV_Player_IsSpectator, &Player::GetIsSpectator },
|
|
{&EV_Player_IsAdmin, &Player::IsAdmin },
|
|
{&EV_Player_LeanLeftHeld, &Player::LeanLeftHeld },
|
|
{&EV_Player_LeanRightHeld, &Player::LeanRightHeld },
|
|
{&EV_Player_MoveSpeedScale, &Player::SetSpeed },
|
|
{&EV_Player_MoveSpeedScaleGet, &Player::GetMoveSpeedScale },
|
|
{&EV_Player_PlayLocalSound, &Player::PlayLocalSound },
|
|
{&EV_Player_RunHeld, &Player::RunHeld },
|
|
{&EV_Player_SecFireHeld, &Player::SecFireHeld },
|
|
{&EV_Player_SetAnimSpeed, &Player::SetAnimSpeed },
|
|
{&EV_Player_SetKillHandler, &Player::SetKillHandler },
|
|
{&EV_Player_SetSpeed, &Player::SetSpeed },
|
|
{&EV_Player_SetStateFile, &Player::SetStateFile },
|
|
{&EV_Player_ShowEnt, &Player::ShowEntity },
|
|
{&EV_Player_Spectator, &Player::Spectator },
|
|
{&EV_Player_StopLocalSound, &Player::StopLocalSound },
|
|
{&EV_Player_Userinfo, &Player::Userinfo },
|
|
{&EV_Player_ViewModelAnimFinished, &Player::EventGetViewModelAnimFinished},
|
|
{&EV_Player_ViewModelGetAnim, &Player::EventGetViewModelAnim },
|
|
{&EV_Player_ViewModelAnimValid, &Player::EventGetViewModelAnimValid },
|
|
#ifdef OPM_FEATURES
|
|
{&EV_Player_Earthquake, &Player::EventEarthquake },
|
|
{&EV_Player_SetClientFlag, &Player::SetClientFlag },
|
|
{&EV_Player_SetEntityShader, &Player::SetEntityShader },
|
|
{&EV_Player_SetLocalSoundRate, &Player::SetLocalSoundRate },
|
|
{&EV_Player_SetViewModelAnimSpeed, &Player::SetVMASpeed },
|
|
{&EV_Player_VisionGetNaked, &Player::VisionGetNaked },
|
|
{&EV_Player_VisionSetBlur, &Player::VisionSetBlur },
|
|
{&EV_Player_VisionSetNaked, &Player::VisionSetNaked },
|
|
#endif
|
|
{NULL, NULL }
|
|
};
|
|
|
|
movecontrolfunc_t Player::MoveStartFuncs[] = {
|
|
NULL, // MOVECONTROL_USER, // Quake style
|
|
NULL, // MOVECONTROL_LEGS, // Quake style, legs state system active
|
|
NULL, // MOVECONTROL_USER_MOVEANIM, // Quake style, legs state system active
|
|
NULL, // MOVECONTROL_ANIM, // move based on animation, with full collision testing
|
|
NULL, // MOVECONTROL_ABSOLUTE, // move based on animation, with full collision testing but no turning
|
|
NULL, // MOVECONTROL_HANGING, // move based on animation, with full collision testing, hanging
|
|
NULL, // MOVECONTROL_ROPE_GRAB
|
|
NULL, // MOVECONTROL_ROPE_RELEASE
|
|
NULL, // MOVECONTROL_ROPE_MOVE
|
|
NULL, // MOVECONTROL_PICKUPENEMY
|
|
&Player::StartPush, // MOVECONTROL_PUSH
|
|
NULL, // MOVECONTROL_CLIMBWALL
|
|
&Player::StartUseAnim, // MOVECONTROL_USEANIM
|
|
NULL, // MOVECONTROL_CROUCH
|
|
&Player::StartLoopUseAnim, // MOVECONTROL_LOOPUSEANIM
|
|
&Player::SetupUseObject, // MOVECONTROL_USEOBJECT
|
|
NULL, // MOVECONTROL_COOLOBJECT
|
|
};
|
|
|
|
Player::Player()
|
|
{
|
|
//
|
|
// set the entity type
|
|
//
|
|
entflags |= ECF_PLAYER;
|
|
|
|
mCurTrailOrigin = 0;
|
|
mLastTrailTime = 0;
|
|
m_pLastSpawnpoint = NULL;
|
|
|
|
voted = false;
|
|
m_fInvulnerableTimeElapsed = 0;
|
|
m_voiceType = PVT_NONE_SET;
|
|
m_fTalkTime = 0;
|
|
num_deaths = 0;
|
|
num_kills = 0;
|
|
num_won_matches = 0;
|
|
num_lost_matches = 0;
|
|
num_team_kills = 0;
|
|
m_iLastNumTeamKills = 0;
|
|
m_bTempSpectator = false;
|
|
m_bSpectator = false;
|
|
m_bSpectatorSwitching = false;
|
|
m_bAllowFighting = false;
|
|
m_bReady = false;
|
|
m_iPlayerSpectating = 0;
|
|
dm_team = TEAM_NONE;
|
|
m_fTeamSelectTime = -30;
|
|
votecount = 0;
|
|
m_fNextVoteOptionTime = 0;
|
|
m_fWeapSelectTime = 0;
|
|
|
|
fAttackerDispTime = 0;
|
|
m_iInfoClient = 0;
|
|
m_iInfoClientHealth = 0;
|
|
m_fInfoClientTime = 0;
|
|
|
|
m_bDeathSpectator = false;
|
|
m_fSpawnTimeLeft = 0;
|
|
m_bWaitingForRespawn = 0;
|
|
m_bShouldRespawn = false;
|
|
last_camera_type = -1;
|
|
m_fLastInvulnerableTime = 0;
|
|
m_iInvulnerableTimeRemaining = 0;
|
|
m_fLastVoteTime = 0;
|
|
|
|
//
|
|
// Added in OPM
|
|
//====
|
|
#ifdef OPM_FEATURES
|
|
m_bShowingHint = false;
|
|
#endif
|
|
m_fpsTiki = NULL;
|
|
m_bConnected = false;
|
|
|
|
m_iInstantMessageTime = 0;
|
|
m_iTextChatTime = 0;
|
|
//====
|
|
|
|
if (LoadingSavegame) {
|
|
return;
|
|
}
|
|
|
|
actor_camera_right = false;
|
|
starting_actor_camera_right = false;
|
|
useanim_numloops = 1;
|
|
move_right_vel = 0;
|
|
|
|
m_iInfoClient = -1;
|
|
m_iInfoClientHealth = 0;
|
|
m_fInfoClientTime = 0.0;
|
|
|
|
music_current_volume = -1;
|
|
music_saved_volume = -1;
|
|
music_volume_fade_time = -1;
|
|
|
|
feetfalling = false;
|
|
edict->s.eType = ET_PLAYER;
|
|
buttons = 0;
|
|
new_buttons = 0;
|
|
server_new_buttons = 0;
|
|
respawn_time = -1.0;
|
|
|
|
//
|
|
// State
|
|
//
|
|
statemap_Legs = NULL;
|
|
statemap_Torso = NULL;
|
|
m_fPartBlends[0] = 0;
|
|
m_fPartBlends[1] = 0;
|
|
m_iPartSlot[legs] = 0;
|
|
m_iPartSlot[torso] = 2;
|
|
partBlendMult[0] = 0;
|
|
partBlendMult[1] = 0;
|
|
m_fPainBlend = 0;
|
|
animdone_Pain = false;
|
|
|
|
m_fLastDeltaTime = level.time;
|
|
|
|
camera = NULL;
|
|
atobject = NULL;
|
|
atobject_dist = 0;
|
|
toucheduseanim = NULL;
|
|
useitem_in_use = NULL;
|
|
|
|
damage_blood = 0;
|
|
damage_count = 0;
|
|
damage_from = vec_zero;
|
|
damage_alpha = 0;
|
|
damage_yaw = 0;
|
|
|
|
fAttackerDispTime = 0;
|
|
pAttackerDistPointer = NULL;
|
|
last_attack_button = 0;
|
|
attack_blocked = false;
|
|
canfall = false;
|
|
|
|
move_left_vel = 0;
|
|
move_right_vel = 0;
|
|
move_backward_vel = 0;
|
|
move_forward_vel = 0;
|
|
move_up_vel = 0;
|
|
move_down_vel = 0;
|
|
|
|
moveresult = 0;
|
|
animspeed = 0;
|
|
airspeed = 200.0f;
|
|
weapons_holstered_by_code = false;
|
|
actor_camera = NULL;
|
|
|
|
damage_multiplier = 1.0f;
|
|
take_pain = true;
|
|
m_bIsInJail = false;
|
|
dm_team = TEAM_NONE;
|
|
current_team = NULL;
|
|
|
|
m_bTempSpectator = false;
|
|
m_bSpectator = false;
|
|
m_bSpectatorSwitching = false;
|
|
m_bAllowFighting = false;
|
|
m_bReady = true;
|
|
m_fTeamSelectTime = -30;
|
|
m_fTalkTime = 0;
|
|
|
|
num_deaths = 0;
|
|
num_kills = 0;
|
|
num_team_kills = 0;
|
|
m_iLastNumTeamKills = 0;
|
|
num_won_matches = 0;
|
|
num_lost_matches = 0;
|
|
client->ps.voted = false;
|
|
votecount = 0;
|
|
m_fLastVoteTime = 0;
|
|
m_fNextVoteOptionTime = 0;
|
|
m_fWeapSelectTime = 0;
|
|
m_jailstate = JAILSTATE_NONE;
|
|
|
|
SetSelectedFov(atof(Info_ValueForKey(client->pers.userinfo, "fov")));
|
|
SetFov(selectedfov);
|
|
|
|
m_iInZoomMode = 0;
|
|
m_iNumShotsFired = 0;
|
|
m_iNumHits = 0;
|
|
m_iNumGroinShots = 0;
|
|
m_iNumHeadShots = 0;
|
|
m_iNumLeftArmShots = 0;
|
|
m_iNumLeftLegShots = 0;
|
|
m_iNumRightArmShots = 0;
|
|
m_iNumRightLegShots = 0;
|
|
m_iNumTorsoShots = 0;
|
|
|
|
m_sPerferredWeaponOverride = "";
|
|
|
|
SetTargetName("player");
|
|
|
|
Init();
|
|
|
|
for (int i = 0; i < MAX_TRAILS; i++) {
|
|
mvTrail[i] = Vector(0, 0, 0);
|
|
}
|
|
|
|
for (int i = 0; i < MAX_TRAILS; i++) {
|
|
mvTrailEyes[i] = Vector(0, 0, 0);
|
|
}
|
|
|
|
client->ps.pm_flags &= ~PMF_NO_HUD;
|
|
|
|
m_fLastSprintTime = 0;
|
|
m_bHasJumped = false;
|
|
|
|
m_fLastInvulnerableTime = 0;
|
|
m_iInvulnerableTimeRemaining = -1;
|
|
m_fSpawnTimeLeft = 0;
|
|
m_bWaitingForRespawn = false;
|
|
m_bShouldRespawn = false;
|
|
m_bDeathSpectator = false;
|
|
|
|
m_vViewPos = vec_zero;
|
|
|
|
//
|
|
// Added in OPM
|
|
//====
|
|
m_bFrozen = false;
|
|
animDoneVM = true;
|
|
m_fVMAtime = 0;
|
|
//====
|
|
|
|
for (int i = 0; i < MAX_SPEED_MULTIPLIERS; i++) {
|
|
speed_multiplier[i] = 1.0f;
|
|
}
|
|
}
|
|
|
|
Player::~Player()
|
|
{
|
|
int i, num;
|
|
Conditional *cond;
|
|
|
|
num = legs_conditionals.NumObjects();
|
|
for (i = num; i > 0; i--) {
|
|
cond = legs_conditionals.ObjectAt(i);
|
|
delete cond;
|
|
}
|
|
|
|
num = torso_conditionals.NumObjects();
|
|
for (i = num; i > 0; i--) {
|
|
cond = torso_conditionals.ObjectAt(i);
|
|
delete cond;
|
|
}
|
|
|
|
legs_conditionals.FreeObjectList();
|
|
torso_conditionals.FreeObjectList();
|
|
|
|
// Added in 2.11
|
|
// Make sure to clean turret stuff up
|
|
// when the player is deleted
|
|
RemoveFromVehiclesAndTurrets();
|
|
|
|
entflags &= ~ECF_PLAYER;
|
|
}
|
|
|
|
static qboolean logfile_started = qfalse;
|
|
|
|
void Player::Init(void)
|
|
{
|
|
InitClient();
|
|
InitPhysics();
|
|
InitPowerups();
|
|
InitWorldEffects();
|
|
InitSound();
|
|
InitView();
|
|
InitState();
|
|
InitEdict();
|
|
InitMaxAmmo();
|
|
InitWeapons();
|
|
InitInventory();
|
|
InitHealth();
|
|
InitStats();
|
|
InitModel();
|
|
InitInvulnerable();
|
|
|
|
LoadStateTable();
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
InitDeathmatch();
|
|
} else if (!LoadingSavegame) {
|
|
ChooseSpawnPoint();
|
|
JoinNearbySquads();
|
|
}
|
|
|
|
// make sure we put the player back into the world
|
|
link();
|
|
logfile_started = qfalse;
|
|
|
|
// notify scripts for the spawning player
|
|
parm.other = this;
|
|
parm.owner = this;
|
|
level.Unregister(STRING_PLAYERSPAWN);
|
|
|
|
//
|
|
// Added in OPM
|
|
//
|
|
if (!m_bConnected) {
|
|
m_bConnected = true;
|
|
|
|
Event *ev = new Event;
|
|
ev->AddEntity(this);
|
|
scriptedEvents[SE_CONNECTED].Trigger(ev);
|
|
}
|
|
|
|
Spawned();
|
|
}
|
|
|
|
void Player::InitStats(void)
|
|
{
|
|
m_iNumObjectives = 0;
|
|
m_iObjectivesCompleted = 0;
|
|
m_iNumHitsTaken = 0;
|
|
m_iNumEnemiesKilled = 0;
|
|
m_iNumObjectsDestroyed = 0;
|
|
}
|
|
|
|
void Player::InitEdict(void)
|
|
{
|
|
// entity state stuff
|
|
setSolidType(SOLID_BBOX);
|
|
if (m_bSpectator) {
|
|
//
|
|
// 2.0: always noclip when spectating
|
|
//
|
|
setMoveType(MOVETYPE_NOCLIP);
|
|
} else {
|
|
setMoveType(MOVETYPE_WALK);
|
|
}
|
|
|
|
setSize(Vector(-16, -16, 0), Vector(16, 16, 72));
|
|
|
|
edict->clipmask = MASK_PLAYERSOLID;
|
|
edict->r.ownerNum = ENTITYNUM_NONE;
|
|
|
|
// clear entity state values
|
|
edict->s.eFlags = 0;
|
|
edict->s.wasframe = 0;
|
|
|
|
// players have precise shadows
|
|
edict->s.renderfx |= RF_SHADOW_PRECISE | RF_SHADOW;
|
|
}
|
|
|
|
void Player::InitSound(void)
|
|
{
|
|
//
|
|
// reset the music
|
|
//
|
|
client->ps.current_music_mood = mood_normal;
|
|
client->ps.fallback_music_mood = mood_normal;
|
|
ChangeMusic("normal", "normal", false);
|
|
|
|
client->ps.music_volume = 1.0;
|
|
client->ps.music_volume_fade_time = 0.0;
|
|
ChangeMusicVolume(1.0, 0.0);
|
|
|
|
music_forced = false;
|
|
|
|
// Reset the reverb stuff
|
|
|
|
client->ps.reverb_type = eax_generic;
|
|
client->ps.reverb_level = 0;
|
|
SetReverb(client->ps.reverb_type, client->ps.reverb_level);
|
|
}
|
|
|
|
void Player::InitClient(void)
|
|
{
|
|
client_persistant_t saved;
|
|
|
|
// deathmatch wipes most client data every spawn
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
char userinfo[MAX_INFO_STRING];
|
|
char dm_primary[MAX_QPATH];
|
|
float enterTime = client->pers.enterTime;
|
|
teamtype_t team = client->pers.teamnum;
|
|
int round_kills = client->pers.round_kills;
|
|
|
|
memcpy(userinfo, client->pers.userinfo, sizeof(userinfo));
|
|
memcpy(dm_primary, client->pers.dm_primary, sizeof(dm_primary));
|
|
G_InitClientPersistant(client);
|
|
G_ClientUserinfoChanged(edict, userinfo);
|
|
|
|
memcpy(client->pers.dm_primary, dm_primary, sizeof(client->pers.dm_primary));
|
|
client->pers.enterTime = enterTime;
|
|
client->pers.teamnum = team;
|
|
client->pers.round_kills = round_kills;
|
|
}
|
|
|
|
// clear everything but the persistant data and fov
|
|
saved = client->pers;
|
|
|
|
memset(client, 0, sizeof(*client));
|
|
client->pers = saved;
|
|
|
|
client->ps.clientNum = client - game.clients;
|
|
client->lastActiveTime = level.inttime;
|
|
client->ps.commandTime = level.svsTime;
|
|
|
|
SetStopwatch(0);
|
|
|
|
#ifdef OPM_FEATURES
|
|
m_bShowingHint = false;
|
|
#endif
|
|
}
|
|
|
|
void Player::InitState(void)
|
|
{
|
|
gibbed = false;
|
|
pain = 0;
|
|
nextpaintime = 0;
|
|
|
|
m_fMineDist = 1000;
|
|
m_fMineCheckTime = 0;
|
|
m_sDmPrimary = "";
|
|
|
|
knockdown = false;
|
|
pain_dir = PAIN_NONE;
|
|
pain_type = MOD_NONE;
|
|
pain_location = -2;
|
|
takedamage = DAMAGE_AIM;
|
|
deadflag = DEAD_NO;
|
|
flags &= ~FL_TEAMSLAVE;
|
|
flags |= (FL_POSTTHINK | FL_THINK | FL_DIE_EXPLODE | FL_BLOOD);
|
|
m_iMovePosFlags = MPF_POSITION_STANDING;
|
|
|
|
if (!com_blood->integer) {
|
|
flags &= ~(FL_DIE_EXPLODE | FL_BLOOD);
|
|
}
|
|
}
|
|
|
|
void Player::InitHealth(void)
|
|
{
|
|
static cvar_t *pMaxHealth = gi.Cvar_Get("g_maxplayerhealth", "250", 0);
|
|
static cvar_t *pDMHealth = gi.Cvar_Get("g_playerdmhealth", "100", 0);
|
|
|
|
// Don't do anything if we're loading a server game.
|
|
// This is either a loadgame or a restart
|
|
if (LoadingSavegame) {
|
|
return;
|
|
}
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER && !g_realismmode->integer) {
|
|
max_health = pMaxHealth->integer;
|
|
} else if (g_gametype->integer != GT_SINGLE_PLAYER && pDMHealth->integer > 0) {
|
|
max_health = pDMHealth->integer;
|
|
} else {
|
|
// reset the health values
|
|
max_health = 100;
|
|
}
|
|
|
|
health = max_health;
|
|
|
|
// 2.0:
|
|
// Make sure to clear the heal rate and the dead flag when respawning
|
|
//
|
|
m_fHealRate = 0;
|
|
edict->s.eFlags &= ~EF_DEAD;
|
|
|
|
// Fixed in OPM
|
|
// This avoid losing weapons when dying and then immediately respawning
|
|
CancelEventsOfType(EV_Player_DMDeathDrop);
|
|
// And this prevents the player from dying when respawning immediately after getting killed
|
|
CancelEventsOfType(EV_Player_Dead);
|
|
}
|
|
|
|
void Player::InitModel(void)
|
|
{
|
|
// 2.0:
|
|
// Make sure to detach from any object before initializing
|
|
// To prevent any glitches
|
|
RemoveFromVehiclesAndTurrets();
|
|
UnattachFromLadder(NULL);
|
|
|
|
gi.clearmodel(edict);
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
setModel("models/player/" + str(g_playermodel->string) + ".tik");
|
|
} else if (dm_team == TEAM_AXIS) {
|
|
if (Q_stricmpn(client->pers.dm_playergermanmodel, "german", 6)
|
|
&& Q_stricmpn(client->pers.dm_playergermanmodel, "axis", 4)
|
|
//
|
|
// 2.30 models
|
|
//
|
|
&& Q_stricmpn(client->pers.dm_playergermanmodel, "it", 2)
|
|
&& Q_stricmpn(client->pers.dm_playergermanmodel, "sc", 2)) {
|
|
setModel("models/player/german_wehrmacht_soldier.tik");
|
|
} else {
|
|
setModel("models/player/" + str(client->pers.dm_playergermanmodel) + ".tik");
|
|
}
|
|
} else {
|
|
if (Q_stricmpn(client->pers.dm_playermodel, "american", 8)
|
|
&& Q_stricmpn(client->pers.dm_playermodel, "allied", 6)) {
|
|
setModel("models/player/american_army.tik");
|
|
} else {
|
|
setModel("models/player/" + str(client->pers.dm_playermodel) + ".tik");
|
|
}
|
|
}
|
|
|
|
//
|
|
// Fallback to a default model if not found
|
|
//
|
|
if (!edict->tiki) {
|
|
if (dm_team == TEAM_AXIS) {
|
|
setModel("models/player/german_wehrmacht_soldier.tik");
|
|
} else {
|
|
setModel("models/player/american_army.tik");
|
|
}
|
|
}
|
|
|
|
SetControllerTag(HEAD_TAG, gi.Tag_NumForName(edict->tiki, "Bip01 Head"));
|
|
SetControllerTag(TORSO_TAG, gi.Tag_NumForName(edict->tiki, "Bip01 Spine2"));
|
|
SetControllerTag(ARMS_TAG, gi.Tag_NumForName(edict->tiki, "Bip01 Spine1"));
|
|
SetControllerTag(PELVIS_TAG, gi.Tag_NumForName(edict->tiki, "Bip01 Pelvis"));
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER && IsSpectator()) {
|
|
hideModel();
|
|
} else {
|
|
showModel();
|
|
}
|
|
|
|
if (GetActiveWeapon(WEAPON_MAIN)) {
|
|
// Show the arms
|
|
edict->s.eFlags &= ~EF_UNARMED;
|
|
} else {
|
|
edict->s.eFlags |= EF_UNARMED;
|
|
}
|
|
|
|
edict->s.eFlags &= ~EF_ANY_TEAM;
|
|
|
|
if (dm_team == TEAM_ALLIES) {
|
|
edict->s.eFlags |= EF_ALLIES;
|
|
} else if (dm_team == TEAM_AXIS) {
|
|
edict->s.eFlags |= EF_AXIS;
|
|
}
|
|
|
|
G_SetClientConfigString(edict);
|
|
|
|
client->ps.iViewModelAnim = 0;
|
|
client->ps.iViewModelAnimChanged = 0;
|
|
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
if (dm_team == TEAM_AXIS) {
|
|
if (m_voiceType <= PVT_AXIS_START || m_voiceType >= PVT_AXIS_END) {
|
|
m_voiceType = PVT_AXIS_GERMAN;
|
|
}
|
|
} else {
|
|
if (m_voiceType <= PVT_ALLIED_START || m_voiceType >= PVT_ALLIED_END) {
|
|
m_voiceType = PVT_ALLIED_AMERICAN;
|
|
}
|
|
}
|
|
} else {
|
|
if (dm_team == TEAM_AXIS) {
|
|
if (m_voiceType >= PVT_AXIS_END) {
|
|
m_voiceType = PVT_AXIS_AXIS4;
|
|
}
|
|
} else {
|
|
if (m_voiceType >= PVT_ALLIED_END) {
|
|
m_voiceType = PVT_ALLIED_PILOT;
|
|
}
|
|
}
|
|
}
|
|
|
|
InitModelFps();
|
|
}
|
|
|
|
void Player::InitModelFps(void)
|
|
{
|
|
char model_name[MAX_STRING_TOKENS];
|
|
char *model_replace;
|
|
|
|
Q_strncpyz(model_name, model.c_str(), sizeof(model_name));
|
|
size_t len = strlen(model_name);
|
|
|
|
model_replace = model_name + len - 4;
|
|
|
|
Q_strncpyz(model_replace, "_fps.tik", sizeof(model_name) - (model_replace - model_name));
|
|
|
|
m_fpsTiki = gi.modeltiki(model_name);
|
|
}
|
|
|
|
void Player::InitPhysics(void)
|
|
{
|
|
// Physics stuff
|
|
oldvelocity = vec_zero;
|
|
velocity = vec_zero;
|
|
old_v_angle = v_angle;
|
|
gravity = 1.0;
|
|
falling = false;
|
|
mediumimpact = false;
|
|
hardimpact = false;
|
|
setContents(CONTENTS_BODY);
|
|
mass = 500;
|
|
memset(&last_ucmd, 0, sizeof(last_ucmd));
|
|
|
|
client->ps.groundTrace.fraction = 1.0f;
|
|
|
|
// Added in OPM
|
|
// Prevent the player from being stuck
|
|
flags &= ~FL_PARTIAL_IMMOBILE;
|
|
}
|
|
|
|
void Player::InitPowerups(void)
|
|
{
|
|
// powerups
|
|
poweruptimer = 0;
|
|
poweruptype = 0;
|
|
}
|
|
|
|
void Player::InitWorldEffects(void)
|
|
{
|
|
// world effects
|
|
next_painsound_time = 0;
|
|
}
|
|
|
|
void Player::InitMaxAmmo(void)
|
|
{
|
|
GiveAmmo("pistol", 0, 200);
|
|
GiveAmmo("rifle", 0, 200);
|
|
GiveAmmo("smg", 0, 300);
|
|
GiveAmmo("mg", 0, 500);
|
|
GiveAmmo("grenade", 0, 5);
|
|
GiveAmmo("agrenade", 0, 5);
|
|
GiveAmmo("heavy", 0, 5);
|
|
GiveAmmo("shotgun", 0, 50);
|
|
|
|
if (g_target_game >= target_game_e::TG_MOHTT) {
|
|
//
|
|
// Team tactics ammunition
|
|
//
|
|
GiveAmmo("landmine", 0, 5);
|
|
}
|
|
|
|
if (g_target_game >= target_game_e::TG_MOHTA) {
|
|
//
|
|
// Team assault ammunition
|
|
//
|
|
GiveAmmo("smokegrenade", 0, 5);
|
|
GiveAmmo("asmokegrenade", 0, 5);
|
|
GiveAmmo("riflegrenade", 0, 3);
|
|
}
|
|
}
|
|
|
|
void Player::InitWeapons(void)
|
|
{
|
|
// Don't do anything if we're loading a server game.
|
|
// This is either a loadgame or a restart
|
|
if (LoadingSavegame) {
|
|
return;
|
|
}
|
|
|
|
// Added in OPM
|
|
// This fixes a bug where player can charge then go to spectator or respawn.
|
|
// The grenade would immediately explode when firing
|
|
charge_start_time = 0;
|
|
}
|
|
|
|
void Player::InitInventory(void) {}
|
|
|
|
void Player::InitView(void)
|
|
{
|
|
// view stuff
|
|
camera = NULL;
|
|
v_angle = vec_zero;
|
|
SetViewAngles(v_angle);
|
|
viewheight = DEFAULT_VIEWHEIGHT;
|
|
|
|
// blend stuff
|
|
damage_blend = vec_zero;
|
|
}
|
|
|
|
void Player::ChooseSpawnPoint(void)
|
|
{
|
|
// set up the player's spawn location
|
|
PlayerStart *p = SelectSpawnPoint(this);
|
|
setOrigin(p->origin + Vector(0, 0, 1));
|
|
origin.copyTo(edict->s.origin2);
|
|
edict->s.renderfx |= RF_FRAMELERP;
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER && !IsSpectator()) {
|
|
KillBox(this);
|
|
}
|
|
|
|
setAngles(p->angles);
|
|
SetViewAngles(p->angles);
|
|
SetupView();
|
|
|
|
VectorCopy(origin, client->ps.vEyePos);
|
|
client->ps.vEyePos[2] += client->ps.viewheight;
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
for (int i = 1; i <= 4; i++) {
|
|
Event *ev = new Event(EV_SetViewangles);
|
|
ev->AddVector(p->angles);
|
|
PostEvent(ev, level.frametime * i);
|
|
}
|
|
}
|
|
|
|
if (p->m_bDeleteOnSpawn) {
|
|
delete p;
|
|
} else {
|
|
p->Unregister(STRING_SPAWN);
|
|
m_pLastSpawnpoint = p;
|
|
}
|
|
}
|
|
|
|
void Player::EndLevel(Event *ev)
|
|
{
|
|
InitPowerups();
|
|
if (health > max_health) {
|
|
health = max_health;
|
|
}
|
|
|
|
if (health < 1) {
|
|
health = 1;
|
|
}
|
|
}
|
|
|
|
void Player::Respawn(Event *ev)
|
|
{
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
bool bOldVoted;
|
|
|
|
if (health <= 0.0f) {
|
|
DeadBody(NULL);
|
|
hideModel();
|
|
}
|
|
|
|
respawn_time = level.time;
|
|
|
|
// This is not present in MOHAA
|
|
ProcessEvent(EV_Player_UnattachFromLadder);
|
|
RemoveFromVehiclesAndTurrets();
|
|
|
|
FreeInventory();
|
|
|
|
// Save the previous vote value
|
|
bOldVoted = client->ps.voted;
|
|
Init();
|
|
client->ps.voted = bOldVoted;
|
|
client->ps.pm_flags |= PMF_RESPAWNED;
|
|
|
|
SetInvulnerable();
|
|
|
|
// Clear the center message
|
|
gi.centerprintf(edict, " ");
|
|
m_bShouldRespawn = false;
|
|
} else {
|
|
if (g_lastsave->string && *g_lastsave->string) {
|
|
gi.SendConsoleCommand("loadlastgame\n");
|
|
} else {
|
|
gi.SendConsoleCommand("restart\n");
|
|
}
|
|
|
|
logfile_started = qfalse;
|
|
}
|
|
|
|
//
|
|
// Added in OPM
|
|
//
|
|
Unregister(STRING_RESPAWN);
|
|
}
|
|
|
|
void Player::SetDeltaAngles(void)
|
|
{
|
|
int i;
|
|
|
|
// Use v_angle since we may be in a camera
|
|
for (i = 0; i < 3; i++) {
|
|
client->ps.delta_angles[i] = ANGLE2SHORT(v_angle[i]);
|
|
}
|
|
}
|
|
|
|
void Player::Obituary(Entity *attacker, Entity *inflictor, int meansofdeath, int iLocation)
|
|
{
|
|
str s1;
|
|
str s2;
|
|
qboolean bDispLocation;
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
return;
|
|
}
|
|
|
|
s1 = "x";
|
|
s2 = "x";
|
|
bDispLocation = qfalse;
|
|
|
|
if (attacker == this) {
|
|
//
|
|
// Player killed themselves
|
|
//
|
|
switch (meansofdeath) {
|
|
case MOD_SUICIDE:
|
|
s1 = "took himself out of commision";
|
|
break;
|
|
case MOD_LAVA:
|
|
s1 = "was burned to a crisp";
|
|
break;
|
|
case MOD_SLIME:
|
|
s1 = "was melted to nothing";
|
|
break;
|
|
case MOD_FALLING:
|
|
s1 = "cratered";
|
|
break;
|
|
case MOD_EXPLOSION:
|
|
s1 = "blew himself up";
|
|
break;
|
|
case MOD_GRENADE:
|
|
if (G_Random() >= 0.5f) {
|
|
s1 = "played catch with himself";
|
|
} else {
|
|
s1 = "tripped on his own grenade";
|
|
}
|
|
break;
|
|
case MOD_ROCKET:
|
|
s1 = "rocketed himself";
|
|
break;
|
|
case MOD_BULLET:
|
|
if (iLocation > -1) {
|
|
s1 = "shot himself";
|
|
} else {
|
|
s1 = "shot himself in the";
|
|
bDispLocation = qtrue;
|
|
}
|
|
break;
|
|
case MOD_FAST_BULLET:
|
|
if (iLocation == HITLOC_GENERAL || iLocation == HITLOC_MISS) {
|
|
s1 = "shot himself";
|
|
} else {
|
|
s1 = "shot himself in the";
|
|
bDispLocation = qtrue;
|
|
}
|
|
break;
|
|
case MOD_LANDMINE:
|
|
s1 = "was hoist on his own pitard";
|
|
break;
|
|
default:
|
|
s1 = "died";
|
|
break;
|
|
}
|
|
|
|
if (bDispLocation && g_obituarylocation->integer) {
|
|
str szConv2 = s2 + " in the " + G_LocationNumToDispString(iLocation);
|
|
|
|
if (dedicated->integer) {
|
|
gi.Printf("%s %s\n", client->pers.netname, gi.LV_ConvertString(szConv2.c_str()));
|
|
}
|
|
|
|
G_PrintDeathMessage(s1, szConv2.c_str(), "x", client->pers.netname, this, "s");
|
|
} else {
|
|
if (dedicated->integer) {
|
|
gi.Printf("%s %s\n", client->pers.netname, gi.LV_ConvertString(s1.c_str()));
|
|
}
|
|
|
|
G_PrintDeathMessage(s1.c_str(), s2.c_str(), "x", client->pers.netname, this, "s");
|
|
}
|
|
} else if (attacker && attacker->client) {
|
|
//
|
|
// Killed by another player
|
|
//
|
|
Weapon *pAttackerWeap = NULL;
|
|
|
|
if (attacker->IsSubclassOfPlayer()) {
|
|
pAttackerWeap = ((Player *)attacker)->GetActiveWeapon(WEAPON_MAIN);
|
|
}
|
|
|
|
switch (meansofdeath) {
|
|
case MOD_CRUSH:
|
|
case MOD_CRUSH_EVERY_FRAME:
|
|
s1 = "was crushed by";
|
|
break;
|
|
case MOD_TELEFRAG:
|
|
s1 = "was telefragged by";
|
|
break;
|
|
case MOD_LAVA:
|
|
case MOD_FIRE:
|
|
case MOD_ON_FIRE:
|
|
s1 = "was burned up by";
|
|
break;
|
|
case MOD_SLIME:
|
|
s1 = "was melted by";
|
|
break;
|
|
case MOD_FALLING:
|
|
s1 = "was pushed over the edge by";
|
|
break;
|
|
case MOD_EXPLOSION:
|
|
s1 = "was blown away by";
|
|
break;
|
|
case MOD_GRENADE:
|
|
if (G_Random() >= 0.5f) {
|
|
s1 = "tripped on";
|
|
s2 = "'s grenade";
|
|
} else {
|
|
s1 = "is picking";
|
|
s2 = "'s shrapnel out of his teeth";
|
|
}
|
|
break;
|
|
case MOD_ROCKET:
|
|
s1 = "took";
|
|
if (G_Random() >= 0.5f) {
|
|
s2 = "'s rocket right in the kisser";
|
|
} else {
|
|
s2 = "'s rocket in the face";
|
|
}
|
|
break;
|
|
case MOD_IMPACT:
|
|
s1 = "was knocked out by";
|
|
break;
|
|
case MOD_BULLET:
|
|
case MOD_FAST_BULLET:
|
|
s1 = "was shot by";
|
|
|
|
if (pAttackerWeap) {
|
|
if (pAttackerWeap->GetWeaponClass() & WEAPON_CLASS_PISTOL) {
|
|
s1 = "was gunned down by";
|
|
} else if (pAttackerWeap->GetWeaponClass() & WEAPON_CLASS_RIFLE) {
|
|
if (pAttackerWeap->GetZoom()) {
|
|
s1 = "was sniped by";
|
|
} else {
|
|
s1 = "was rifled by";
|
|
}
|
|
} else if (pAttackerWeap->GetWeaponClass() & WEAPON_CLASS_SMG) {
|
|
s1 = "was perforated by";
|
|
s2 = "'s' SMG";
|
|
} else if (pAttackerWeap->GetWeaponClass() & WEAPON_CLASS_MG) {
|
|
s1 = "was machine-gunned by";
|
|
}
|
|
}
|
|
|
|
if (iLocation > -1) {
|
|
bDispLocation = qtrue;
|
|
}
|
|
break;
|
|
case MOD_VEHICLE:
|
|
s1 = "was run over by";
|
|
break;
|
|
case MOD_IMPALE:
|
|
s1 = "was impaled by";
|
|
break;
|
|
case MOD_BASH:
|
|
if (G_Random() >= 0.5f) {
|
|
s1 = "was bashed by";
|
|
} else {
|
|
s1 = "was clubbed by";
|
|
}
|
|
break;
|
|
case MOD_SHOTGUN:
|
|
if (G_Random() >= 0.5f) {
|
|
s1 = "was hunted down by";
|
|
} else {
|
|
s1 = "was pumped full of buckshot by";
|
|
}
|
|
break;
|
|
case MOD_LANDMINE:
|
|
s1 = "stepped on";
|
|
s2 = "'s landmine";
|
|
break;
|
|
default:
|
|
s1 = "was killed by";
|
|
break;
|
|
}
|
|
|
|
if (bDispLocation && g_obituarylocation->integer) {
|
|
str szConv2 = s2 + " in the " + G_LocationNumToDispString(iLocation);
|
|
|
|
G_PrintDeathMessage(
|
|
s1.c_str(), szConv2.c_str(), attacker->client->pers.netname, client->pers.netname, this, "p"
|
|
);
|
|
|
|
if (dedicated->integer) {
|
|
str szLoc1, szLoc2;
|
|
|
|
szLoc1 = gi.LV_ConvertString(s1.c_str());
|
|
if (s2 == 'x') {
|
|
gi.Printf("%s %s %s\n", client->pers.netname, szLoc1.c_str(), attacker->client->pers.netname);
|
|
} else {
|
|
szLoc2 = gi.LV_ConvertString(szConv2.c_str());
|
|
gi.Printf(
|
|
"%s %s %s%s\n",
|
|
client->pers.netname,
|
|
szLoc1.c_str(),
|
|
attacker->client->pers.netname,
|
|
szLoc2.c_str()
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
G_PrintDeathMessage(
|
|
s1.c_str(), s2.c_str(), attacker->client->pers.netname, client->pers.netname, this, "p"
|
|
);
|
|
|
|
if (dedicated->integer) {
|
|
str szLoc1, szLoc2;
|
|
|
|
szLoc1 = gi.LV_ConvertString(s1.c_str());
|
|
if (s2 == 'x') {
|
|
gi.Printf("%s %s %s\n", client->pers.netname, szLoc1.c_str(), attacker->client->pers.netname);
|
|
} else {
|
|
szLoc2 = gi.LV_ConvertString(s2.c_str());
|
|
gi.Printf(
|
|
"%s %s %s%s\n",
|
|
client->pers.netname,
|
|
szLoc1.c_str(),
|
|
attacker->client->pers.netname,
|
|
szLoc2.c_str()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// No attacker and not self
|
|
//
|
|
switch (meansofdeath) {
|
|
case MOD_LAVA:
|
|
s1 = "was burned to a crisp";
|
|
break;
|
|
case MOD_SLIME:
|
|
s1 = "was melted to nothing";
|
|
break;
|
|
case MOD_FALLING:
|
|
s1 = "cratered";
|
|
break;
|
|
case MOD_EXPLOSION:
|
|
s1 = "blew up";
|
|
break;
|
|
case MOD_GRENADE:
|
|
s1 = "caught some shrapnel";
|
|
break;
|
|
case MOD_ROCKET:
|
|
s1 = "caught a rocket";
|
|
break;
|
|
case MOD_BULLET:
|
|
case MOD_FAST_BULLET:
|
|
if (iLocation == HITLOC_GENERAL || iLocation == HITLOC_MISS) {
|
|
s1 = "was shot";
|
|
} else {
|
|
s1 = "was shot in the";
|
|
bDispLocation = qtrue;
|
|
}
|
|
break;
|
|
case MOD_LANDMINE:
|
|
s1 = "stepped on a land mine";
|
|
break;
|
|
default:
|
|
s1 = "died";
|
|
break;
|
|
}
|
|
|
|
if (bDispLocation && g_obituarylocation->integer) {
|
|
str szConv2 = s2 + " in the " + G_LocationNumToDispString(iLocation);
|
|
|
|
G_PrintDeathMessage(s1.c_str(), szConv2.c_str(), "x", client->pers.netname, this, "w");
|
|
|
|
if (dedicated->integer) {
|
|
gi.Printf("%s %s\n", client->pers.netname, gi.LV_ConvertString(s1.c_str()));
|
|
}
|
|
} else {
|
|
G_PrintDeathMessage(s1.c_str(), s2.c_str(), "x", client->pers.netname, this, "w");
|
|
|
|
if (dedicated->integer) {
|
|
gi.Printf("%s %s\n", client->pers.netname, gi.LV_ConvertString(s1.c_str()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::Dead(Event *ev)
|
|
{
|
|
if (deadflag == DEAD_DEAD) {
|
|
return;
|
|
}
|
|
|
|
health = 0;
|
|
deadflag = DEAD_DEAD;
|
|
|
|
edict->s.renderfx &= ~RF_SHADOW;
|
|
server_new_buttons = 0;
|
|
|
|
CancelEventsOfType(EV_Player_Dead);
|
|
|
|
// stop animating
|
|
StopPartAnimating(legs);
|
|
|
|
// pause the torso anim
|
|
PausePartAnim(torso);
|
|
|
|
partAnim[torso] = "";
|
|
|
|
if (m_fPainBlend != 0) {
|
|
// Clear pain blend
|
|
StopAnimating(ANIMSLOT_PAIN);
|
|
edict->s.frameInfo[ANIMSLOT_PAIN].weight = 0;
|
|
m_fPainBlend = 0;
|
|
animdone_Pain = false;
|
|
}
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
if (dmManager.AllowRespawn()) {
|
|
respawn_time = level.time + 1.0f;
|
|
} else {
|
|
respawn_time = level.time + 2.0f;
|
|
}
|
|
} else if (level.current_map && *level.current_map) {
|
|
G_BeginIntermission(level.current_map, TRANS_LEVEL);
|
|
} else {
|
|
respawn_time = level.time + 1.f;
|
|
}
|
|
|
|
ZoomOff();
|
|
|
|
if (ShouldForceSpectatorOnDeath()) {
|
|
m_bDeathSpectator = true;
|
|
|
|
Spectator();
|
|
SetPlayerSpectateRandom();
|
|
}
|
|
}
|
|
|
|
void Player::Killed(Event *ev)
|
|
{
|
|
Entity *attacker;
|
|
Entity *inflictor;
|
|
int meansofdeath;
|
|
int location;
|
|
Event *event;
|
|
|
|
//
|
|
// Added in OPM
|
|
// This one is openmohaa-specific
|
|
// Custom killed event will do the job
|
|
//
|
|
if (m_killedLabel.IsSet()) {
|
|
event = new Event(0, ev->NumArgs());
|
|
for (int i = 1; i <= ev->NumArgs(); i++) {
|
|
event->AddValue(ev->GetValue(i));
|
|
}
|
|
m_killedLabel.Execute(this, event);
|
|
delete event;
|
|
|
|
Unregister(STRING_DEATH);
|
|
return;
|
|
}
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
current_team->AddDeaths(this, 1);
|
|
} else {
|
|
AddDeaths(1);
|
|
}
|
|
|
|
attacker = ev->GetEntity(1);
|
|
inflictor = ev->GetEntity(3);
|
|
meansofdeath = ev->GetInteger(9);
|
|
location = ev->GetInteger(10);
|
|
|
|
if (attacker && inflictor) {
|
|
Obituary(attacker, inflictor, meansofdeath, location);
|
|
}
|
|
|
|
RemoveFromVehiclesAndTurrets();
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER && attacker && attacker->IsSubclassOfPlayer()) {
|
|
static_cast<Player *>(attacker)->KilledPlayerInDeathmatch(this, (meansOfDeath_t)meansofdeath);
|
|
}
|
|
|
|
deadflag = DEAD_DYING;
|
|
health = 0;
|
|
|
|
event = new Event(EV_Pain, 10);
|
|
|
|
event->AddEntity(attacker);
|
|
event->AddFloat(ev->GetFloat(2));
|
|
event->AddEntity(inflictor);
|
|
event->AddVector(ev->GetVector(4));
|
|
event->AddVector(ev->GetVector(5));
|
|
event->AddVector(ev->GetVector(6));
|
|
event->AddInteger(ev->GetInteger(7));
|
|
event->AddInteger(ev->GetInteger(8));
|
|
event->AddInteger(ev->GetInteger(9));
|
|
event->AddInteger(ev->GetInteger(10));
|
|
|
|
ProcessEvent(event);
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
if (HasItem("Binoculars")) {
|
|
takeItem("Binoculars");
|
|
}
|
|
|
|
PostEvent(EV_Player_DMDeathDrop, 0.1f);
|
|
edict->s.eFlags |= EF_DEAD;
|
|
}
|
|
|
|
edict->clipmask = MASK_DEADSOLID;
|
|
setContents(CONTENTS_CORPSE);
|
|
setSolidType(SOLID_NOT);
|
|
setMoveType(MOVETYPE_TOSS);
|
|
|
|
angles.x = 0;
|
|
angles.z = 0;
|
|
setAngles(angles);
|
|
|
|
//
|
|
// change music
|
|
//
|
|
ChangeMusic("failure", "normal", true);
|
|
|
|
takedamage = DAMAGE_NO;
|
|
|
|
// Post a dead event just in case
|
|
PostEvent(EV_Player_Dead, 5.0f);
|
|
ZoomOff();
|
|
|
|
if (g_voiceChat->integer) {
|
|
if (m_voiceType == PVT_ALLIED_MANON) {
|
|
//
|
|
// manon_death doesn't exist in 2.0 anymore.
|
|
// The code is left just in case
|
|
//
|
|
Sound("manon_death", CHAN_VOICE, -1.0f, 160, NULL, -1.0f, 1, 0, 1, 1200);
|
|
} else {
|
|
Sound("player_death");
|
|
}
|
|
} else {
|
|
Sound("player_death");
|
|
}
|
|
|
|
if (m_fPainBlend) {
|
|
//
|
|
// 2.0: No more pain animation after death
|
|
//
|
|
animdone_Pain = true;
|
|
}
|
|
|
|
//
|
|
// Added in OPM
|
|
// Scripted events
|
|
//
|
|
event = new Event(0, 11);
|
|
|
|
event->AddEntity(ev->GetEntity(1));
|
|
event->AddFloat(ev->GetFloat(2));
|
|
event->AddEntity(ev->GetEntity(3));
|
|
event->AddVector(ev->GetVector(4));
|
|
event->AddVector(ev->GetVector(5));
|
|
event->AddVector(ev->GetVector(6));
|
|
event->AddInteger(ev->GetInteger(7));
|
|
event->AddInteger(ev->GetInteger(8));
|
|
event->AddInteger(ev->GetInteger(9));
|
|
event->AddInteger(ev->GetInteger(10));
|
|
event->AddEntity(this);
|
|
|
|
scriptedEvents[SE_KILL].Trigger(event);
|
|
|
|
Unregister(STRING_DEATH);
|
|
}
|
|
|
|
void Player::EventDMDeathDrop(Event *ev)
|
|
{
|
|
Weapon *weapon = GetActiveWeapon(WEAPON_MAIN);
|
|
SpawnArgs args;
|
|
ClassDef *cls;
|
|
|
|
if (!m_bDontDropWeapons && weapon && weapon->IsSubclassOfWeapon()) {
|
|
weapon->Drop();
|
|
}
|
|
|
|
args.setArg("model", "models/items/dm_50_healthbox.tik");
|
|
|
|
cls = args.getClassDef();
|
|
if (cls) {
|
|
Item *item = (Item *)cls->newInstance();
|
|
if (item) {
|
|
if (item->IsSubclassOfItem()) {
|
|
item->setModel("models/items/dm_50_healthbox.tik");
|
|
|
|
item->SetOwner(this);
|
|
item->ProcessPendingEvents();
|
|
item->Drop();
|
|
} else {
|
|
// useless and not pickupable, delete it
|
|
delete item;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeInventory();
|
|
}
|
|
|
|
void Player::EventStopwatch(Event *ev)
|
|
{
|
|
stopWatchType_t eType = SWT_NORMAL;
|
|
|
|
int iDuration = ev->GetInteger(1);
|
|
if (iDuration < 0) {
|
|
ScriptError("duration < 0");
|
|
}
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
eType = static_cast<stopWatchType_t>(ev->GetInteger(2));
|
|
} else {
|
|
eType = SWT_NORMAL;
|
|
}
|
|
|
|
SetStopwatch(iDuration, eType);
|
|
}
|
|
|
|
void Player::SetStopwatch(int iDuration, stopWatchType_t type)
|
|
{
|
|
int iStartTime;
|
|
char szCmd[256];
|
|
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
if (type != SWT_NORMAL) {
|
|
iStartTime = (int)(level.svsFloatTime * 1000.f);
|
|
} else {
|
|
iStartTime = 0;
|
|
if (iDuration) {
|
|
iStartTime = ceil(level.svsFloatTime * 1000.f);
|
|
}
|
|
}
|
|
|
|
Com_sprintf(szCmd, sizeof(szCmd), "stopwatch %i %i %i", iStartTime, iDuration, type);
|
|
} else {
|
|
iStartTime = 0;
|
|
if (iDuration) {
|
|
iStartTime = (int)level.svsFloatTime;
|
|
}
|
|
|
|
Com_sprintf(szCmd, sizeof(szCmd), "stopwatch %i %i", iStartTime, iDuration);
|
|
}
|
|
|
|
gi.SendServerCommand(edict - g_entities, szCmd);
|
|
}
|
|
|
|
void Player::KilledPlayerInDeathmatch(Player *killed, meansOfDeath_t meansofdeath)
|
|
{
|
|
DM_Team *pDMTeam;
|
|
|
|
pDMTeam = killed->GetDM_Team();
|
|
|
|
if (meansofdeath == MOD_TELEFRAG) {
|
|
//
|
|
// Added in OPM
|
|
// Telefrag isn't the fault of anyone
|
|
// so don't count any kill
|
|
//
|
|
return;
|
|
}
|
|
|
|
if (killed == this) {
|
|
pDMTeam->AddKills(this, -1);
|
|
gi.SendServerCommand(
|
|
edict - g_entities, "print \"" HUD_MESSAGE_WHITE "%s\n\"", gi.LV_ConvertString("You killed yourself")
|
|
);
|
|
|
|
return;
|
|
}
|
|
|
|
if (pDMTeam == GetDM_Team() && g_gametype->integer >= GT_TEAM) {
|
|
//
|
|
// A teammate was killed
|
|
//
|
|
current_team->AddKills(this, -1);
|
|
num_team_kills++;
|
|
} else {
|
|
current_team->AddKills(this, 1);
|
|
}
|
|
|
|
gi.SendServerCommand(
|
|
edict - g_entities,
|
|
"print \"" HUD_MESSAGE_WHITE "%s %s\n\"",
|
|
gi.LV_ConvertString("You killed"),
|
|
killed->client->pers.netname
|
|
);
|
|
}
|
|
|
|
void Player::Pain(Event *ev)
|
|
{
|
|
float damage, yawdiff;
|
|
Entity *attacker;
|
|
int meansofdeath;
|
|
Vector dir, pos, attack_angle;
|
|
int iLocation;
|
|
|
|
attacker = ev->GetEntity(1);
|
|
damage = ev->GetFloat(2);
|
|
pos = ev->GetVector(4);
|
|
dir = ev->GetVector(5);
|
|
meansofdeath = ev->GetInteger(9);
|
|
iLocation = ev->GetInteger(10);
|
|
|
|
if (!damage && !knockdown) {
|
|
return;
|
|
}
|
|
|
|
client->ps.stats[STAT_LAST_PAIN] = damage;
|
|
|
|
// Determine direction
|
|
attack_angle = dir.toAngles();
|
|
yawdiff = angles[YAW] - attack_angle[YAW] + 180;
|
|
yawdiff = AngleNormalize180(yawdiff);
|
|
|
|
if (yawdiff > -45 && yawdiff < 45) {
|
|
pain_dir = PAIN_FRONT;
|
|
} else if (yawdiff < -45 && yawdiff > -135) {
|
|
pain_dir = PAIN_LEFT;
|
|
} else if (yawdiff > 45 && yawdiff < 135) {
|
|
pain_dir = PAIN_RIGHT;
|
|
} else {
|
|
pain_dir = PAIN_REAR;
|
|
}
|
|
|
|
pain_type = (meansOfDeath_t)meansofdeath;
|
|
pain_location = iLocation;
|
|
|
|
// Only set the regular pain level if enough time since last pain has passed
|
|
if (((level.time > nextpaintime) && take_pain) || IsDead()) {
|
|
pain = damage;
|
|
}
|
|
|
|
// add to the damage inflicted on a player this frame
|
|
// the total will be turned into screen blends and view angle kicks
|
|
// at the end of the frame
|
|
damage_blood += damage;
|
|
damage_from += dir * damage;
|
|
damage_yaw = dir.toYaw() * 10.0f;
|
|
|
|
if (damage_yaw == client->ps.stats[STAT_DAMAGEDIR]) {
|
|
if (damage_yaw < 1800.0f) {
|
|
damage_yaw += 1.0f;
|
|
} else {
|
|
damage_yaw -= 1.0f;
|
|
}
|
|
}
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER && attacker && attacker->client && attacker != this) {
|
|
gi.MSG_SetClient(attacker->edict - g_entities);
|
|
if (IsDead()) {
|
|
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_NOTIFY_KILL));
|
|
} else {
|
|
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_NOTIFY_HIT));
|
|
}
|
|
gi.MSG_EndCGM();
|
|
}
|
|
|
|
if (IsDead()) {
|
|
return;
|
|
}
|
|
|
|
if (g_voiceChat->integer) {
|
|
if (m_voiceType == PVT_ALLIED_MANON) {
|
|
//
|
|
// Should have been removed since 2.0
|
|
//
|
|
Sound("manon_pain", CHAN_DIALOG, -1, 160, NULL, -1, 1, 0, 1, 1200);
|
|
} else {
|
|
Sound("player_pain");
|
|
}
|
|
} else {
|
|
Sound("player_pain");
|
|
}
|
|
}
|
|
|
|
void Player::DoUse(Event *ev)
|
|
{
|
|
gentity_t *hit;
|
|
int touch[MAX_GENTITIES];
|
|
int num;
|
|
int i;
|
|
bool bWasInTurretOrVehicle;
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER && IsSpectator()) {
|
|
// Prevent using stuff while spectating
|
|
return;
|
|
}
|
|
|
|
if (IsDead()) {
|
|
// Dead players mustn't use
|
|
return;
|
|
}
|
|
|
|
if (edict->r.svFlags & SVF_NOCLIENT) {
|
|
// Fixed in OPM
|
|
// Clients that are not sent to other clients cannot use objects.
|
|
// Some mods make players non-solid, hide them and turn physics off
|
|
// as a way to spectate other players or for cinematics.
|
|
// This prevent players to use objects such as doors
|
|
return;
|
|
}
|
|
|
|
bWasInTurretOrVehicle = m_pVehicle || m_pTurret;
|
|
|
|
if (bWasInTurretOrVehicle) {
|
|
RemoveFromVehiclesAndTurretsInternal();
|
|
return;
|
|
}
|
|
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
if ((buttons & BUTTON_ATTACKLEFT) || (buttons & BUTTON_ATTACKRIGHT)) {
|
|
//
|
|
// Added in 2.0
|
|
// Only allow use if the player isn't holding attack buttons
|
|
//
|
|
return;
|
|
}
|
|
}
|
|
|
|
num = getUseableEntities(touch, MAX_GENTITIES, true);
|
|
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
// Fixed in 2.0
|
|
// Since 2.0, the loop stops when the player
|
|
// uses a turret, this prevents the turret from being deleted
|
|
// after being attached to the player
|
|
//
|
|
for (i = 0; i < num; i++) {
|
|
hit = &g_entities[touch[i]];
|
|
|
|
if (!hit->inuse) {
|
|
continue;
|
|
}
|
|
|
|
Event *event = new Event(EV_Use);
|
|
event->AddListener(this);
|
|
|
|
hit->entity->ProcessEvent(event);
|
|
|
|
if (m_pVehicle || m_pTurret) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// Backward compatibility
|
|
// It still allows 1.11 SP to work properly
|
|
// Such as in m1l1 when the player must man the mounted machine gun
|
|
for (i = 0; i < num; i++) {
|
|
hit = &g_entities[touch[i]];
|
|
|
|
if (!hit->inuse) {
|
|
continue;
|
|
}
|
|
|
|
Event *event = new Event(EV_Use);
|
|
event->AddListener(this);
|
|
|
|
hit->entity->ProcessEvent(event);
|
|
}
|
|
}
|
|
|
|
if (!bWasInTurretOrVehicle && m_pVehicle) {
|
|
//
|
|
// Added in 2.30
|
|
// Make the vehicle also invincible if the player is invincible
|
|
//
|
|
if (flags & FL_GODMODE) {
|
|
m_pVehicle->flags |= FL_GODMODE;
|
|
} else {
|
|
m_pVehicle->flags &= ~FL_GODMODE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::TouchStuff(pmove_t *pm)
|
|
{
|
|
gentity_t *other;
|
|
Event *event;
|
|
int i;
|
|
int j;
|
|
|
|
//
|
|
// clear out any conditionals that are controlled by touching
|
|
//
|
|
toucheduseanim = NULL;
|
|
|
|
if (getMoveType() != MOVETYPE_NOCLIP) {
|
|
G_TouchTriggers(this);
|
|
}
|
|
|
|
// touch other objects
|
|
for (i = 0; i < pm->numtouch; i++) {
|
|
other = &g_entities[pm->touchents[i]];
|
|
|
|
for (j = 0; j < i; j++) {
|
|
gentity_t *ge = &g_entities[j];
|
|
|
|
if (ge == other) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j != i) {
|
|
// duplicated
|
|
continue;
|
|
}
|
|
|
|
// Don't bother touching the world
|
|
if ((!other->entity) || (other->entity == world)) {
|
|
continue;
|
|
}
|
|
|
|
event = new Event(EV_Touch);
|
|
event->AddEntity(this);
|
|
other->entity->ProcessEvent(event);
|
|
|
|
event = new Event(EV_Touch);
|
|
event->AddEntity(other->entity);
|
|
ProcessEvent(event);
|
|
}
|
|
}
|
|
|
|
void Player::GetMoveInfo(pmove_t *pm)
|
|
{
|
|
moveresult = pm->moveresult;
|
|
|
|
if (!deadflag || (g_gametype->integer != GT_SINGLE_PLAYER && IsSpectator())) {
|
|
v_angle[0] = pm->ps->viewangles[0];
|
|
v_angle[1] = pm->ps->viewangles[1];
|
|
v_angle[2] = pm->ps->viewangles[2];
|
|
|
|
if (moveresult == MOVERESULT_TURNED) {
|
|
angles.y = v_angle[1];
|
|
setAngles(angles);
|
|
SetViewAngles(angles);
|
|
}
|
|
}
|
|
|
|
setOrigin(Vector(pm->ps->origin[0], pm->ps->origin[1], pm->ps->origin[2]));
|
|
|
|
if (pm->ps->groundEntityNum != ENTITYNUM_NONE) {
|
|
float backoff;
|
|
float change;
|
|
int i;
|
|
|
|
backoff = DotProduct(pm->ps->groundTrace.plane.normal, pm->ps->velocity);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
change = pm->ps->groundTrace.plane.normal[i] * backoff;
|
|
pm->ps->velocity[i] -= change;
|
|
}
|
|
}
|
|
|
|
// Set the ground entity
|
|
groundentity = NULL;
|
|
if (pm->ps->groundEntityNum != ENTITYNUM_NONE) {
|
|
groundentity = &g_entities[pm->ps->groundEntityNum];
|
|
airspeed = 200;
|
|
|
|
if (!groundentity->entity || groundentity->entity->getMoveType() == MOVETYPE_NONE) {
|
|
m_vPushVelocity = vec_zero;
|
|
}
|
|
|
|
//
|
|
// Fixed in OPM
|
|
// Disable predictions when the groundentity is moving up/down, looks like shaky otherwise
|
|
if (groundentity->entity && groundentity->entity != this && groundentity->entity->velocity[2] != 0) {
|
|
pm->ps->pm_flags |= PMF_NO_PREDICTION;
|
|
}
|
|
} else if (m_pGlueMaster) {
|
|
// Added in OPM
|
|
// Use the glue master for the ground entity to make the viewmodel will stay still
|
|
pm->ps->groundEntityNum = m_pGlueMaster->entnum;
|
|
}
|
|
|
|
velocity = Vector(pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]);
|
|
|
|
if ((client->ps.pm_flags & PMF_FROZEN) || (client->ps.pm_flags & PMF_NO_MOVE)) {
|
|
velocity = vec_zero;
|
|
} else {
|
|
setSize(pm->mins, pm->maxs);
|
|
viewheight = pm->ps->viewheight;
|
|
}
|
|
|
|
// water type and level is set in the predicted code
|
|
waterlevel = pm->waterlevel;
|
|
watertype = pm->watertype;
|
|
}
|
|
|
|
void Player::SetMoveInfo(pmove_t *pm, usercmd_t *ucmd)
|
|
{
|
|
Vector move;
|
|
|
|
// set up for pmove
|
|
memset(pm, 0, sizeof(pmove_t));
|
|
|
|
velocity.copyTo(client->ps.velocity);
|
|
|
|
pm->ps = &client->ps;
|
|
|
|
if (ucmd) {
|
|
pm->cmd = *ucmd;
|
|
}
|
|
|
|
if (sv_drawtrace->integer <= 1) {
|
|
pm->trace = gi.trace;
|
|
} else {
|
|
pm->trace = &G_PMDrawTrace;
|
|
}
|
|
|
|
pm->tracemask = MASK_PLAYERSOLID;
|
|
pm->pointcontents = gi.pointcontents;
|
|
|
|
pm->ps->origin[0] = origin.x;
|
|
pm->ps->origin[1] = origin.y;
|
|
pm->ps->origin[2] = origin.z;
|
|
|
|
/*
|
|
pm->mins[0] = mins.x;
|
|
pm->mins[1] = mins.y;
|
|
pm->mins[2] = mins.z;
|
|
|
|
pm->maxs[0] = maxs.x;
|
|
pm->maxs[1] = maxs.y;
|
|
pm->maxs[2] = maxs.z;
|
|
*/
|
|
|
|
pm->ps->velocity[0] = velocity.x;
|
|
pm->ps->velocity[1] = velocity.y;
|
|
pm->ps->velocity[2] = velocity.z;
|
|
|
|
pm->pmove_fixed = pmove_fixed->integer;
|
|
pm->pmove_msec = pmove_msec->integer;
|
|
|
|
if (pmove_msec->integer < 8) {
|
|
pm->pmove_msec = 8;
|
|
} else if (pmove_msec->integer > 33) {
|
|
pm->pmove_msec = 33;
|
|
}
|
|
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
//
|
|
// Added in 2.0
|
|
// In multiplayer mode, specify if the player can lean while moving
|
|
//
|
|
if (dmflags->integer & DF_ALLOW_LEAN_MOVEMENT) {
|
|
pm->alwaysAllowLean = qtrue;
|
|
} else {
|
|
pm->alwaysAllowLean = qfalse;
|
|
}
|
|
} else {
|
|
pm->alwaysAllowLean = qfalse;
|
|
}
|
|
|
|
pm->leanMax = 45.f;
|
|
pm->leanAdd = 6.f;
|
|
pm->leanRecoverSpeed = 8.5f;
|
|
pm->leanSpeed = 2.f;
|
|
} else {
|
|
pm->alwaysAllowLean = qtrue;
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
pm->leanMax = 40.f;
|
|
} else {
|
|
// Don't allow lean in single-player, like in the original game
|
|
pm->leanMax = 0;
|
|
}
|
|
|
|
pm->leanAdd = 10.f;
|
|
pm->leanRecoverSpeed = 15.f;
|
|
pm->leanSpeed = 4.f;
|
|
}
|
|
|
|
pm->protocol = g_protocol;
|
|
|
|
// Added in OPM
|
|
// Initialize the ground entity
|
|
pm->ps->groundEntityNum = ENTITYNUM_NONE;
|
|
}
|
|
|
|
pmtype_t Player::GetMovePlayerMoveType(void)
|
|
{
|
|
if (getMoveType() == MOVETYPE_NOCLIP || IsSpectator()) {
|
|
return PM_NOCLIP;
|
|
} else if (deadflag) {
|
|
return PM_DEAD;
|
|
} else if (movecontrol == MOVECONTROL_CLIMBWALL) {
|
|
return PM_CLIMBWALL;
|
|
} else {
|
|
return PM_NORMAL;
|
|
}
|
|
}
|
|
|
|
void Player::CheckGround(void)
|
|
{
|
|
pmove_t pm;
|
|
|
|
SetMoveInfo(&pm, current_ucmd);
|
|
Pmove_GroundTrace(&pm);
|
|
GetMoveInfo(&pm);
|
|
}
|
|
|
|
qboolean Player::AnimMove(Vector& move, Vector *endpos)
|
|
{
|
|
Vector up;
|
|
Vector down;
|
|
trace_t trace;
|
|
int mask;
|
|
Vector start(origin);
|
|
Vector end(origin + move);
|
|
|
|
mask = MASK_PLAYERSOLID;
|
|
|
|
// test the player position if they were a stepheight higher
|
|
trace = G_Trace(start, mins, maxs, end, this, mask, true, "AnimMove");
|
|
if (trace.fraction < 1) {
|
|
if ((movecontrol == MOVECONTROL_HANGING) || (movecontrol == MOVECONTROL_CLIMBWALL)) {
|
|
up = origin;
|
|
up.z += move.z;
|
|
trace = G_Trace(origin, mins, maxs, up, this, mask, true, "AnimMove");
|
|
if (trace.fraction < 1) {
|
|
if (endpos) {
|
|
*endpos = origin;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
origin = trace.endpos;
|
|
end = origin;
|
|
end.x += move.x;
|
|
end.y += move.y;
|
|
|
|
trace = G_Trace(origin, mins, maxs, end, this, mask, true, "AnimMove");
|
|
if (endpos) {
|
|
*endpos = trace.endpos;
|
|
}
|
|
|
|
return (trace.fraction > 0);
|
|
} else {
|
|
return TestMove(move, endpos);
|
|
}
|
|
} else {
|
|
if (endpos) {
|
|
*endpos = trace.endpos;
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
}
|
|
|
|
qboolean Player::TestMove(Vector& move, Vector *endpos)
|
|
{
|
|
trace_t trace;
|
|
Vector pos(origin + move);
|
|
|
|
trace = G_Trace(origin, mins, maxs, pos, this, MASK_PLAYERSOLID, true, "TestMove");
|
|
if (trace.allsolid) {
|
|
// player is completely trapped in another solid
|
|
if (endpos) {
|
|
*endpos = origin;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
if (trace.fraction < 1.0f) {
|
|
Vector up(origin);
|
|
up.z += STEPSIZE;
|
|
|
|
trace = G_Trace(origin, mins, maxs, up, this, MASK_PLAYERSOLID, true, "TestMove");
|
|
if (trace.fraction == 0.0f) {
|
|
if (endpos) {
|
|
*endpos = origin;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
Vector temp(trace.endpos);
|
|
Vector end(temp + move);
|
|
|
|
trace = G_Trace(temp, mins, maxs, end, this, MASK_PLAYERSOLID, true, "TestMove");
|
|
if (trace.fraction == 0.0f) {
|
|
if (endpos) {
|
|
*endpos = origin;
|
|
}
|
|
return qfalse;
|
|
}
|
|
|
|
temp = trace.endpos;
|
|
|
|
Vector down(trace.endpos);
|
|
down.z = origin.z;
|
|
|
|
trace = G_Trace(temp, mins, maxs, down, this, MASK_PLAYERSOLID, true, "TestMove");
|
|
}
|
|
|
|
if (endpos) {
|
|
*endpos = trace.endpos;
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
float Player::TestMoveDist(Vector& move)
|
|
{
|
|
Vector endpos;
|
|
|
|
TestMove(move, &endpos);
|
|
endpos -= origin;
|
|
|
|
return endpos.length();
|
|
}
|
|
|
|
static Vector vec_up = Vector(0, 0, 1);
|
|
|
|
void Player::CheckMoveFlags(void)
|
|
{
|
|
trace_t trace;
|
|
Vector start;
|
|
Vector end;
|
|
float oldsp;
|
|
Vector olddir(oldvelocity.x, oldvelocity.y, 0);
|
|
|
|
//
|
|
// Check if moving forward will cause the player to fall
|
|
//
|
|
start = origin + yaw_forward * 52.0f;
|
|
end = start;
|
|
end.z -= STEPSIZE * 2;
|
|
|
|
trace = G_Trace(start, mins, maxs, end, this, MASK_PLAYERSOLID, true, "CheckMoveFlags");
|
|
canfall = (trace.fraction >= 1.0f);
|
|
|
|
if (!groundentity && !(client->ps.walking)) {
|
|
falling = true;
|
|
hardimpact = false;
|
|
mediumimpact = false;
|
|
} else {
|
|
falling = false;
|
|
mediumimpact = oldvelocity.z <= -180.0f;
|
|
hardimpact = oldvelocity.z < -400.0f;
|
|
}
|
|
|
|
// check for running into walls
|
|
oldsp = VectorNormalize(olddir);
|
|
if ((oldsp > 220.0f) && (velocity * olddir < 2.0f)) {
|
|
moveresult = MOVERESULT_HITWALL;
|
|
}
|
|
|
|
move_forward_vel = DotProduct(yaw_forward, velocity);
|
|
move_backward_vel = -move_forward_vel;
|
|
|
|
if (move_forward_vel < 0.0f) {
|
|
move_forward_vel = 0.0f;
|
|
}
|
|
|
|
if (move_backward_vel < 0.0f) {
|
|
move_backward_vel = 0.0f;
|
|
}
|
|
|
|
move_left_vel = DotProduct(yaw_left, velocity);
|
|
move_right_vel = -move_left_vel;
|
|
|
|
if (move_left_vel < 0.0f) {
|
|
move_left_vel = 0.0f;
|
|
}
|
|
|
|
if (move_right_vel < 0.0f) {
|
|
move_right_vel = 0.0f;
|
|
}
|
|
|
|
move_up_vel = DotProduct(vec_up, velocity);
|
|
move_down_vel = -move_up_vel;
|
|
|
|
if (move_up_vel < 0.0f) {
|
|
move_up_vel = 0.0f;
|
|
}
|
|
|
|
if (move_down_vel < 0.0f) {
|
|
move_down_vel = 0.0f;
|
|
}
|
|
}
|
|
|
|
qboolean Player::CheckMove(Vector& move, Vector *endpos)
|
|
{
|
|
return AnimMove(move, endpos);
|
|
}
|
|
|
|
float Player::CheckMoveDist(Vector& move)
|
|
{
|
|
Vector endpos;
|
|
|
|
CheckMove(move, &endpos);
|
|
endpos -= origin;
|
|
|
|
return endpos.length();
|
|
}
|
|
|
|
void Player::ClientMove(usercmd_t *ucmd)
|
|
{
|
|
pmove_t pm;
|
|
Vector move;
|
|
|
|
#ifdef OPM_FEATURES
|
|
int touch[MAX_GENTITIES];
|
|
int num = getUseableEntities(touch, MAX_GENTITIES, true);
|
|
bool bHintShown = false;
|
|
|
|
for (int i = 0; i < num; i++) {
|
|
Entity *entity = g_entities[touch[i]].entity;
|
|
if (entity && entity->m_HintString.length()) {
|
|
entity->ProcessHint(edict, true);
|
|
bHintShown = true;
|
|
m_bShowingHint = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bHintShown && m_bShowingHint) {
|
|
m_bShowingHint = false;
|
|
|
|
// FIXME: delete
|
|
if (sv_specialgame->integer) {
|
|
gi.MSG_SetClient(edict - g_entities);
|
|
|
|
// Send the hint string once
|
|
gi.MSG_StartCGM(CGM_HINTSTRING);
|
|
gi.MSG_WriteString("");
|
|
gi.MSG_EndCGM();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
oldorigin = origin;
|
|
|
|
client->ps.pm_type = GetMovePlayerMoveType();
|
|
// set move flags
|
|
client->ps.pm_flags &=
|
|
~(PMF_FROZEN | PMF_NO_PREDICTION | PMF_NO_MOVE | PMF_DUCKED | PMF_TURRET | PMF_VIEW_PRONE | PMF_VIEW_DUCK_RUN
|
|
| PMF_VIEW_JUMP_START);
|
|
|
|
if (level.playerfrozen || m_bFrozen) {
|
|
client->ps.pm_flags |= PMF_FROZEN;
|
|
}
|
|
|
|
if ((flags & FL_IMMOBILE) || (flags & FL_PARTIAL_IMMOBILE)) {
|
|
client->ps.pm_flags |= PMF_NO_MOVE;
|
|
client->ps.pm_flags |= PMF_NO_PREDICTION;
|
|
}
|
|
|
|
if (m_pGlueMaster) {
|
|
//
|
|
// Added in 2.0.
|
|
// Disable movement prediction/movement if the player is glued to something
|
|
//
|
|
client->ps.pm_flags |= PMF_NO_PREDICTION;
|
|
client->ps.pm_flags |= PMF_NO_MOVE;
|
|
}
|
|
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
if (maxs.z == 54.0f || maxs.z == 60.0f) {
|
|
client->ps.pm_flags |= PMF_DUCKED;
|
|
} else if (viewheight == JUMP_START_VIEWHEIGHT) {
|
|
client->ps.pm_flags |= PMF_VIEW_JUMP_START;
|
|
}
|
|
} else {
|
|
if (maxs.z == 60.0f) {
|
|
client->ps.pm_flags |= PMF_DUCKED;
|
|
} else if (maxs.z == 54.0f) {
|
|
client->ps.pm_flags |= PMF_DUCKED | PMF_VIEW_PRONE;
|
|
} else if (maxs.z == 20.0f) {
|
|
client->ps.pm_flags |= PMF_VIEW_PRONE;
|
|
} else if (maxs.z == 53.0f) {
|
|
client->ps.pm_flags |= PMF_VIEW_DUCK_RUN;
|
|
} else if (viewheight == JUMP_START_VIEWHEIGHT) {
|
|
client->ps.pm_flags |= PMF_VIEW_JUMP_START;
|
|
}
|
|
}
|
|
|
|
switch (movecontrol) {
|
|
case MOVECONTROL_USER:
|
|
case MOVECONTROL_LEGS:
|
|
case MOVECONTROL_USER_MOVEANIM:
|
|
break;
|
|
|
|
case MOVECONTROL_CROUCH:
|
|
client->ps.pm_flags |= PMF_NO_PREDICTION | PMF_DUCKED | PMF_VIEW_PRONE;
|
|
break;
|
|
|
|
default:
|
|
client->ps.pm_flags |= PMF_NO_PREDICTION;
|
|
}
|
|
|
|
if (movetype == MOVETYPE_NOCLIP) {
|
|
if (!(last_ucmd.buttons & BUTTON_RUN)) {
|
|
client->ps.speed = sv_runspeed->value * sv_walkspeedmult->value;
|
|
} else {
|
|
client->ps.speed = sv_runspeed->value;
|
|
}
|
|
} else if (!groundentity) {
|
|
client->ps.speed = airspeed;
|
|
} else {
|
|
Weapon *pWeap;
|
|
|
|
if (last_ucmd.buttons & BUTTON_RUN) {
|
|
client->ps.speed = GetRunSpeed();
|
|
} else {
|
|
client->ps.speed = sv_runspeed->value * sv_walkspeedmult->value;
|
|
}
|
|
|
|
if (m_iMovePosFlags & MPF_POSITION_CROUCHING) {
|
|
client->ps.speed = (float)client->ps.speed * sv_crouchspeedmult->value;
|
|
}
|
|
|
|
pWeap = GetActiveWeapon(WEAPON_MAIN);
|
|
if (pWeap) {
|
|
//
|
|
// Also use the weapon movement speed
|
|
//
|
|
if (!IsZoomed()) {
|
|
client->ps.speed = (float)client->ps.speed * pWeap->GetMovementSpeed();
|
|
} else {
|
|
client->ps.speed = (float)client->ps.speed * pWeap->GetZoomMovement();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
client->ps.speed = (int)((float)client->ps.speed * sv_dmspeedmult->value);
|
|
}
|
|
|
|
//====
|
|
// Added in OPM
|
|
for (int i = 0; i < MAX_SPEED_MULTIPLIERS; i++) {
|
|
client->ps.speed = (int)((float)client->ps.speed * speed_multiplier[i]);
|
|
}
|
|
//====
|
|
|
|
client->ps.gravity = sv_gravity->value * gravity;
|
|
|
|
if ((movecontrol != MOVECONTROL_ABSOLUTE) && (movecontrol != MOVECONTROL_PUSH)
|
|
&& (movecontrol != MOVECONTROL_CLIMBWALL)) {
|
|
Vector oldpos(origin);
|
|
|
|
SetMoveInfo(&pm, ucmd);
|
|
Pmove(&pm);
|
|
GetMoveInfo(&pm);
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER && groundentity && groundentity->entity
|
|
&& groundentity->entity->IsSubclassOfSentient()) {
|
|
//
|
|
// Added in 2.0
|
|
// If the player is on another sentient, try to make it fall off
|
|
//
|
|
velocity -= Vector(orientation[0]) * (random() * 20.f);
|
|
velocity -= Vector(orientation[1]) * (random() * 10.f);
|
|
}
|
|
|
|
ProcessPmoveEvents(pm.pmoveEvent);
|
|
|
|
// if we're not moving, set the blocked flag in case the user is trying to move
|
|
if ((ucmd->forwardmove || ucmd->rightmove) && ((oldpos - origin).length() < 0.005f)) {
|
|
moveresult = MOVERESULT_BLOCKED;
|
|
}
|
|
if (client->ps.walking && moveresult >= MOVERESULT_BLOCKED) {
|
|
setOrigin(oldpos);
|
|
VectorCopy(origin, client->ps.origin);
|
|
}
|
|
} else {
|
|
if (movecontrol == MOVECONTROL_CLIMBWALL) {
|
|
PM_UpdateViewAngles(&client->ps, ucmd);
|
|
v_angle = client->ps.viewangles;
|
|
} else if (!deadflag) {
|
|
v_angle = client->ps.viewangles;
|
|
}
|
|
|
|
// should collect objects to touch against
|
|
memset(&pm, 0, sizeof(pmove_t));
|
|
|
|
// keep the command time up to date or else the next PMove we run will try to catch up
|
|
client->ps.commandTime = ucmd->serverTime;
|
|
|
|
velocity = vec_zero;
|
|
}
|
|
|
|
if ((getMoveType() != MOVETYPE_NOCLIP) && (client->ps.pm_flags & PMF_NO_PREDICTION)) {
|
|
if ((movecontrol == MOVECONTROL_ABSOLUTE) || (movecontrol == MOVECONTROL_CLIMBWALL)) {
|
|
velocity = vec_zero;
|
|
}
|
|
|
|
if ((movecontrol == MOVECONTROL_ANIM) || (movecontrol == MOVECONTROL_CLIMBWALL)
|
|
|| (movecontrol == MOVECONTROL_USEANIM) || (movecontrol == MOVECONTROL_LOOPUSEANIM)
|
|
|| (movecontrol == MOVECONTROL_USER_MOVEANIM)) {
|
|
Vector delta = vec_zero;
|
|
PlayerAnimDelta(delta);
|
|
|
|
// using PM_NOCLIP for a smooth move
|
|
//client->ps.pm_type = PM_NOCLIP;
|
|
|
|
if (delta != vec_zero) {
|
|
float mat[3][3];
|
|
AngleVectors(angles, mat[0], mat[1], mat[2]);
|
|
MatrixTransformVector(delta, mat, move);
|
|
AnimMove(move, &origin);
|
|
setOrigin(origin);
|
|
CheckGround();
|
|
}
|
|
}
|
|
}
|
|
|
|
m_fLastDeltaTime = level.time;
|
|
|
|
TouchStuff(&pm);
|
|
}
|
|
|
|
void Player::VehicleMove(usercmd_t *ucmd)
|
|
{
|
|
if (!m_pVehicle) {
|
|
return;
|
|
}
|
|
|
|
oldorigin = origin;
|
|
|
|
client->ps.pm_type = GetMovePlayerMoveType();
|
|
|
|
// set move flags
|
|
client->ps.pm_flags &=
|
|
~(PMF_FROZEN | PMF_NO_PREDICTION | PMF_NO_MOVE | PMF_DUCKED | PMF_TURRET | PMF_VIEW_PRONE | PMF_VIEW_DUCK_RUN
|
|
| PMF_VIEW_JUMP_START);
|
|
|
|
// disable prediction
|
|
client->ps.pm_flags |= PMF_TURRET | PMF_NO_PREDICTION;
|
|
|
|
if (level.playerfrozen || m_bFrozen) {
|
|
client->ps.pm_flags |= PMF_FROZEN;
|
|
}
|
|
|
|
client->ps.gravity = gravity * sv_gravity->value;
|
|
|
|
if (m_pVehicle->Drive(current_ucmd)) {
|
|
client->ps.commandTime = ucmd->serverTime;
|
|
// Added in OPM
|
|
// The player can't walk while attached to a vehicle
|
|
client->ps.groundEntityNum = ENTITYNUM_NONE;
|
|
client->ps.walking = false;
|
|
} else {
|
|
ClientMove(ucmd);
|
|
}
|
|
}
|
|
|
|
void Player::TurretMove(usercmd_t *ucmd)
|
|
{
|
|
if (!m_pTurret) {
|
|
return;
|
|
}
|
|
|
|
oldorigin = origin;
|
|
|
|
client->ps.pm_type = GetMovePlayerMoveType();
|
|
|
|
// set move flags
|
|
client->ps.pm_flags &=
|
|
~(PMF_FROZEN | PMF_NO_PREDICTION | PMF_NO_MOVE | PMF_DUCKED | PMF_TURRET | PMF_VIEW_PRONE | PMF_VIEW_DUCK_RUN
|
|
| PMF_VIEW_JUMP_START);
|
|
|
|
// disable prediction
|
|
client->ps.pm_flags |= PMF_TURRET | PMF_NO_PREDICTION;
|
|
if (getMoveType() == MOVETYPE_PORTABLE_TURRET) {
|
|
client->ps.pm_flags |= PMF_TURRET;
|
|
}
|
|
|
|
if (level.playerfrozen || m_bFrozen) {
|
|
client->ps.pm_flags |= PMF_FROZEN;
|
|
}
|
|
|
|
client->ps.gravity = gravity * sv_gravity->value;
|
|
|
|
if (m_pVehicle) {
|
|
// Added in 2.30
|
|
m_pVehicle->PathDrive(current_ucmd);
|
|
}
|
|
|
|
if (m_pTurret->IsSubclassOfTurretGun() && m_pTurret->UserAim(current_ucmd)) {
|
|
client->ps.commandTime = ucmd->serverTime;
|
|
// Added in OPM
|
|
// The player can't walk while attached to a turret
|
|
client->ps.groundEntityNum = ENTITYNUM_NONE;
|
|
client->ps.walking = false;
|
|
} else {
|
|
ClientMove(ucmd);
|
|
}
|
|
}
|
|
|
|
void Player::ClientInactivityTimer(void)
|
|
{
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
return;
|
|
}
|
|
|
|
if (g_inactivekick->integer && g_inactivekick->integer < 60) {
|
|
gi.cvar_set("g_inactiveKick", "60");
|
|
}
|
|
|
|
if (g_inactivespectate->integer && g_inactivespectate->integer < 20) {
|
|
gi.cvar_set("g_inactiveSpectate", "20");
|
|
}
|
|
|
|
if (num_team_kills >= g_teamkillkick->integer) {
|
|
const str message = gi.LV_ConvertString("was removed from the server for killing too many teammates.");
|
|
|
|
//
|
|
// The player reached maximum team kills
|
|
//
|
|
G_PrintToAllClients(va("%s %s\n", client->pers.netname, message.c_str()), 2);
|
|
|
|
if (Q_stricmp(Info_ValueForKey(client->pers.userinfo, "ip"), "localhost")) {
|
|
//
|
|
// Make sure to not kick the local host
|
|
//
|
|
gi.DropClient(client->ps.clientNum, message.c_str());
|
|
} else if (!m_bSpectator) {
|
|
// if it's the host, put it back in spectator mode
|
|
num_team_kills = 0;
|
|
m_iLastNumTeamKills = 0;
|
|
|
|
PostEvent(EV_Player_Spectator, 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (num_team_kills >= g_teamkillwarn->integer && num_team_kills > m_iLastNumTeamKills) {
|
|
const str sWarning = gi.LV_ConvertString("Warning:");
|
|
const str sTeamKills = gi.LV_ConvertString("more team kill(s) and you will be removed from the server.");
|
|
|
|
m_iLastNumTeamKills = num_team_kills;
|
|
|
|
gi.centerprintf(
|
|
edict, "%s %i %s", sWarning.c_str(), g_teamkillkick->integer - num_team_kills, sTeamKills.c_str()
|
|
);
|
|
}
|
|
|
|
if (current_ucmd->buttons & BUTTON_ANY || (!g_inactivespectate->integer && !g_inactivekick->integer)
|
|
|| current_ucmd->forwardmove || current_ucmd->rightmove || current_ucmd->upmove
|
|
|| (m_bTempSpectator && client->lastActiveTime >= level.inttime - 5000)) {
|
|
client->lastActiveTime = level.inttime;
|
|
client->activeWarning = 0;
|
|
return;
|
|
}
|
|
|
|
if (g_inactivekick->integer && client->lastActiveTime < level.inttime - 1000 * g_inactivekick->integer) {
|
|
const char *s = Info_ValueForKey(client->pers.userinfo, "ip");
|
|
|
|
if (Q_stricmp(s, "localhost")) {
|
|
gi.DropClient(client->ps.clientNum, "was dropped for inactivity");
|
|
return;
|
|
}
|
|
|
|
if (m_bSpectator) {
|
|
return;
|
|
}
|
|
|
|
PostEvent(EV_Player_Spectator, 0);
|
|
return;
|
|
}
|
|
|
|
if (g_inactivespectate->integer && client->lastActiveTime < level.inttime - g_inactivespectate->integer * 1000
|
|
&& !m_bSpectator) {
|
|
PostEvent(EV_Player_Spectator, 0);
|
|
return;
|
|
}
|
|
|
|
if (g_inactivekick->integer) {
|
|
static struct {
|
|
int iLevel;
|
|
int iTime;
|
|
} warnkick[7] = {
|
|
{1, 30},
|
|
{8, 15},
|
|
{9, 5 },
|
|
{10, 4 },
|
|
{11, 3 },
|
|
{12, 2 },
|
|
{13, 1 }
|
|
};
|
|
|
|
int iKickWait = g_inactivekick->integer - (level.inttime - client->lastActiveTime) / 1000 - 1;
|
|
|
|
for (int i = 0; i < 7; i++) {
|
|
if (client->activeWarning < warnkick[i].iLevel && iKickWait < warnkick[i].iTime) {
|
|
const str sActionIn = gi.LV_ConvertString("You will be kicked for inactivity in");
|
|
const str sSeconds = gi.LV_ConvertString("seconds");
|
|
|
|
client->activeWarning = warnkick[i].iLevel;
|
|
|
|
gi.centerprintf(edict, "%s %i %s", sActionIn.c_str(), warnkick[i].iTime, sSeconds.c_str());
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_inactivespectate->integer && dm_team != TEAM_SPECTATOR) {
|
|
static struct {
|
|
int iLevel;
|
|
int iTime;
|
|
} warnspectate[6] = {2, 15, 3, 5, 4, 4, 5, 3, 6, 2, 7, 1};
|
|
|
|
int iSpectateWait = g_inactivespectate->integer - (level.inttime - client->lastActiveTime) / 1000 - 1;
|
|
|
|
for (int i = 0; i < 6; i++) {
|
|
if (client->activeWarning < warnspectate[i].iLevel && iSpectateWait < warnspectate[i].iTime) {
|
|
const str sActionIn = gi.LV_ConvertString("You will be moved to spectator for inactivity in");
|
|
const str sSeconds = gi.LV_ConvertString("seconds");
|
|
|
|
client->activeWarning = warnspectate[i].iLevel;
|
|
|
|
gi.centerprintf(edict, "%s %i %s", sActionIn.c_str(), warnspectate[i].iTime, sSeconds.c_str());
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::UpdateEnemies(void)
|
|
{
|
|
float fFov;
|
|
float fMaxDist;
|
|
float fMaxCosSquared;
|
|
Vector vLookDir;
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
return;
|
|
}
|
|
|
|
if (m_pNextSquadMate == this) {
|
|
return;
|
|
}
|
|
|
|
fFov = fov * 0.9f;
|
|
fMaxDist = world->farplane_distance * 0.7867f;
|
|
fMaxCosSquared = 0.0f;
|
|
|
|
AngleVectors(m_vViewAng, vLookDir, NULL, NULL);
|
|
|
|
if (m_Enemy) {
|
|
m_Enemy->m_iAttackerCount -= 3;
|
|
m_Enemy = NULL;
|
|
}
|
|
|
|
for (Sentient *obj = level.m_HeadSentient[0]; obj != NULL; obj = obj->m_NextSentient) {
|
|
Vector vDelta;
|
|
float fDot;
|
|
float fDotSquared;
|
|
|
|
if (CanSee(obj, fFov, fMaxDist, false)) {
|
|
obj->m_fPlayerSightLevel += level.frametime;
|
|
|
|
vDelta = obj->origin - origin;
|
|
fDot = DotProduct(vDelta, vLookDir);
|
|
fDotSquared = fDot * fDot;
|
|
|
|
if (fDotSquared > fMaxCosSquared * vDelta.lengthSquared()) {
|
|
fMaxCosSquared = fDotSquared / vDelta.lengthSquared();
|
|
m_Enemy = obj;
|
|
}
|
|
} else {
|
|
obj->m_fPlayerSightLevel = 0.0f;
|
|
}
|
|
}
|
|
|
|
if (m_Enemy) {
|
|
m_Enemy->m_iAttackerCount += 3;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==============
|
|
ClientThink
|
|
|
|
This will be called once for each client frame, which will
|
|
usually be a couple times for each server frame.
|
|
==============
|
|
*/
|
|
void Player::ClientThink(void)
|
|
{
|
|
// sanity check the command time to prevent speedup cheating
|
|
if (current_ucmd->serverTime > level.svsTime) {
|
|
//
|
|
// we don't want any future commands, these could be from the previous game
|
|
//
|
|
return;
|
|
}
|
|
|
|
if (current_ucmd->serverTime < level.svsTime - 1000) {
|
|
current_ucmd->serverTime = level.svsTime - 1000;
|
|
}
|
|
|
|
if ((current_ucmd->serverTime - client->ps.commandTime) < 1) {
|
|
return;
|
|
}
|
|
|
|
TickSprint();
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER && dm_team == TEAM_SPECTATOR && !IsSpectator()) {
|
|
Spectator();
|
|
}
|
|
|
|
last_ucmd = *current_ucmd;
|
|
server_new_buttons |= current_ucmd->buttons & ~buttons;
|
|
new_buttons = current_ucmd->buttons & ~buttons;
|
|
buttons = current_ucmd->buttons;
|
|
|
|
if (camera) {
|
|
m_vViewPos = camera->origin;
|
|
m_vViewAng = camera->angles;
|
|
} else {
|
|
m_vViewPos[0] = (float)current_eyeinfo->ofs[0] + origin[0];
|
|
m_vViewPos[1] = (float)current_eyeinfo->ofs[1] + origin[1];
|
|
m_vViewPos[2] = (float)current_eyeinfo->ofs[2] + origin[2];
|
|
|
|
m_vViewAng[0] = current_eyeinfo->angles[0];
|
|
m_vViewAng[1] = current_eyeinfo->angles[1];
|
|
m_vViewAng[2] = 0.0f;
|
|
}
|
|
|
|
VectorCopy(m_vViewPos, client->ps.vEyePos);
|
|
|
|
if (!level.intermissiontime) {
|
|
if (new_buttons & BUTTON_ATTACKRIGHT) {
|
|
Weapon *weapon = GetActiveWeapon(WEAPON_MAIN);
|
|
|
|
if (weapon && (weapon->GetZoom())) {
|
|
ToggleZoom(weapon->GetZoom());
|
|
}
|
|
}
|
|
|
|
if (new_buttons & BUTTON_USE) {
|
|
DoUse(NULL);
|
|
}
|
|
|
|
moveresult = MOVERESULT_NONE;
|
|
|
|
if (m_pTurret) {
|
|
TurretMove(current_ucmd);
|
|
} else if (m_pVehicle) {
|
|
VehicleMove(current_ucmd);
|
|
} else {
|
|
ClientMove(current_ucmd);
|
|
}
|
|
|
|
// Save cmd angles so that we can get delta angle movements next frame
|
|
client->cmd_angles[0] = SHORT2ANGLE(current_ucmd->angles[0]);
|
|
client->cmd_angles[1] = SHORT2ANGLE(current_ucmd->angles[1]);
|
|
client->cmd_angles[2] = SHORT2ANGLE(current_ucmd->angles[2]);
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER && g_smoothClients->integer) {
|
|
VectorCopy(client->ps.velocity, edict->s.pos.trDelta);
|
|
edict->s.pos.trTime = client->ps.commandTime;
|
|
} else {
|
|
VectorClear(edict->s.pos.trDelta);
|
|
edict->s.pos.trTime = 0;
|
|
}
|
|
|
|
ClientInactivityTimer();
|
|
} else {
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
client->ps.pm_flags |= PMF_FROZEN;
|
|
client->ps.pm_flags |= PMF_INTERMISSION;
|
|
VectorClear(client->ps.velocity);
|
|
|
|
if (level.time - level.intermissiontime > 5.0f
|
|
&& (new_buttons & (BUTTON_ATTACKLEFT | BUTTON_ATTACKRIGHT))) {
|
|
level.exitintermission = true;
|
|
}
|
|
} else {
|
|
if (level.intermissiontype == TRANS_MISSION_FAILED || IsDead()) {
|
|
gi.cvar_set("g_success", "0");
|
|
gi.cvar_set("g_failed", "1");
|
|
} else {
|
|
gi.cvar_set("g_success", "1");
|
|
gi.cvar_set("g_failed", "0");
|
|
}
|
|
|
|
// prevent getting medals from cheats
|
|
if (g_medal0->modificationCount > 1 || g_medal1->modificationCount > 1 || g_medal2->modificationCount > 1
|
|
|| g_medal3->modificationCount > 1 || g_medal4->modificationCount > 1 || g_medal5->modificationCount > 1
|
|
|| g_medalbt1->modificationCount > 1 || g_medalbt2->modificationCount > 1
|
|
|| g_medalbt3->modificationCount > 1 || g_medalbt4->modificationCount > 1
|
|
|| g_medalbt5->modificationCount > 1 || g_eogmedal0->modificationCount > 1
|
|
|| g_eogmedal1->modificationCount > 1 || g_eogmedal2->modificationCount > 1) {
|
|
gi.cvar_set("g_gotmedal", "1");
|
|
} else {
|
|
gi.cvar_set("g_gotmedal", "0");
|
|
}
|
|
|
|
client->ps.pm_flags |= PMF_FROZEN;
|
|
VectorClear(client->ps.velocity);
|
|
|
|
if (level.time - level.intermissiontime > 4.0f) {
|
|
if (level.intermissiontype) {
|
|
if (level.intermissiontype == TRANS_MISSION_FAILED) {
|
|
if ((new_buttons & BUTTON_ATTACKLEFT) || (new_buttons & BUTTON_ATTACKRIGHT)) {
|
|
G_MissionFailed();
|
|
}
|
|
} else if ((new_buttons & BUTTON_ATTACKLEFT) || (new_buttons & BUTTON_ATTACKRIGHT)) {
|
|
g_medal0->modificationCount = 1;
|
|
g_medal1->modificationCount = 1;
|
|
g_medal2->modificationCount = 1;
|
|
g_medal3->modificationCount = 1;
|
|
g_medal4->modificationCount = 1;
|
|
g_medal5->modificationCount = 1;
|
|
g_medalbt0->modificationCount = 1;
|
|
g_medalbt1->modificationCount = 1;
|
|
g_medalbt2->modificationCount = 1;
|
|
g_medalbt3->modificationCount = 1;
|
|
g_medalbt4->modificationCount = 1;
|
|
g_medalbt5->modificationCount = 1;
|
|
g_eogmedal0->modificationCount = 1;
|
|
g_eogmedal1->modificationCount = 1;
|
|
g_eogmedal2->modificationCount = 1;
|
|
g_medal0->modified = false;
|
|
g_medal1->modified = false;
|
|
g_medal2->modified = false;
|
|
g_medal3->modified = false;
|
|
g_medal4->modified = false;
|
|
g_medal5->modified = false;
|
|
g_eogmedal0->modified = false;
|
|
g_eogmedal1->modified = false;
|
|
g_eogmedal2->modified = false;
|
|
|
|
level.exitintermission = true;
|
|
}
|
|
} else {
|
|
level.exitintermission = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save cmd angles so that we can get delta angle movements next frame
|
|
client->cmd_angles[0] = SHORT2ANGLE(current_ucmd->angles[0]);
|
|
client->cmd_angles[1] = SHORT2ANGLE(current_ucmd->angles[1]);
|
|
client->cmd_angles[2] = SHORT2ANGLE(current_ucmd->angles[2]);
|
|
client->ps.commandTime = current_ucmd->serverTime;
|
|
}
|
|
}
|
|
|
|
void Player::Think(void)
|
|
{
|
|
static cvar_t *g_aimLagTime = NULL;
|
|
|
|
int m_iClientWeaponCommand;
|
|
Event *m_pWeaponCommand = NULL;
|
|
Weapon *pWeap;
|
|
|
|
if (whereami->integer && origin != oldorigin) {
|
|
gi.DPrintf("x %8.2f y %8.2f z %8.2f area %2d\n", origin[0], origin[1], origin[2], edict->r.areanum);
|
|
}
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER && g_playermodel->modified) {
|
|
setModel("models/player/" + str(g_playermodel->string) + ".tik");
|
|
|
|
if (!edict->tiki) {
|
|
setModel("models/player/american_army.tik");
|
|
}
|
|
|
|
g_playermodel->modified = qfalse;
|
|
}
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
m_bIsDisguised = false;
|
|
|
|
if (m_bHasDisguise && !level.m_bAlarm) {
|
|
pWeap = GetActiveWeapon(WEAPON_MAIN);
|
|
|
|
if (!pWeap || pWeap->IsSubclassOfInventoryItem()) {
|
|
m_bIsDisguised = true;
|
|
|
|
for (Sentient *pSent = level.m_HeadSentient[0]; pSent != NULL; pSent = pSent->m_NextSentient) {
|
|
Actor *act = (Actor *)pSent;
|
|
|
|
if (pSent->m_Enemy == this && act->IsAttacking()) {
|
|
m_bIsDisguised = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PathSearch::PlayerCover(this);
|
|
UpdateEnemies();
|
|
}
|
|
|
|
if (movetype == MOVETYPE_NOCLIP) {
|
|
StopPartAnimating(torso);
|
|
SetPartAnim("idle");
|
|
|
|
client->ps.walking = qfalse;
|
|
groundentity = 0;
|
|
} else {
|
|
CheckMoveFlags();
|
|
EvaluateState();
|
|
}
|
|
|
|
oldvelocity = velocity;
|
|
old_v_angle = v_angle;
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
if ((server_new_buttons & BUTTON_ATTACKLEFT) && (!GetActiveWeapon(WEAPON_MAIN)) && !IsDead()
|
|
&& !IsNewActiveWeapon() && !LoadingSavegame) {
|
|
Event *ev = new Event("useweaponclass");
|
|
ev->AddString("item1");
|
|
|
|
ProcessEvent(ev);
|
|
}
|
|
} else {
|
|
// Added in 2.0: Invulnerability and team spawn
|
|
TickInvulnerable();
|
|
TickTeamSpawn();
|
|
|
|
if (deadflag == DEAD_DEAD && level.time > respawn_time) {
|
|
if (dmManager.AllowRespawn() && AllowTeamRespawn()) {
|
|
if (((server_new_buttons & BUTTON_ATTACKLEFT) || (server_new_buttons & BUTTON_ATTACKRIGHT))
|
|
|| (g_forcerespawn->integer > 0 && level.time > g_forcerespawn->integer + respawn_time)) {
|
|
// 2.0
|
|
// Check for team respawn
|
|
//
|
|
if (!m_fSpawnTimeLeft) {
|
|
if (AllowTeamRespawn()) {
|
|
EndSpectator();
|
|
PostEvent(EV_Player_Respawn, 0);
|
|
}
|
|
} else {
|
|
m_bWaitingForRespawn = true;
|
|
}
|
|
}
|
|
} else if (!IsSpectator()) {
|
|
BeginTempSpectator();
|
|
}
|
|
}
|
|
|
|
if (IsSpectator()) {
|
|
if (!m_bTempSpectator && level.time > respawn_time && (server_new_buttons & BUTTON_ATTACKLEFT)) {
|
|
if (current_team && dm_team != TEAM_SPECTATOR) {
|
|
if (client->pers.dm_primary[0]) {
|
|
if ((g_gametype->integer == GT_FFA
|
|
|| (g_gametype->integer >= GT_TEAM && dm_team > TEAM_FREEFORALL))
|
|
&& deadflag != DEAD_DEAD) {
|
|
// 2.0
|
|
// Check for team respawn
|
|
//
|
|
if (!m_fSpawnTimeLeft) {
|
|
if (AllowTeamRespawn()) {
|
|
EndSpectator();
|
|
PostEvent(EV_Player_Respawn, 0);
|
|
}
|
|
} else {
|
|
m_bWaitingForRespawn = true;
|
|
}
|
|
}
|
|
} else if (m_fWeapSelectTime < level.time) {
|
|
m_fWeapSelectTime = level.time + 1.0;
|
|
UserSelectWeapon(false);
|
|
}
|
|
} else if (m_fWeapSelectTime < level.time) {
|
|
m_fWeapSelectTime = level.time + 1.0;
|
|
gi.SendServerCommand(edict - g_entities, "stufftext \"pushmenu_teamselect\"");
|
|
}
|
|
}
|
|
// Removed in 2.0
|
|
//else if (level.time > m_fWeapSelectTime + 10.0) {
|
|
// m_fWeapSelectTime = level.time;
|
|
// gi.centerprintf(edict, "\n\n\n%s", gi.LV_ConvertString("Press fire to join the battle!"));
|
|
//}
|
|
} else if (!client->pers.dm_primary[0]) {
|
|
Spectator();
|
|
if (m_fWeapSelectTime < level.time) {
|
|
m_fWeapSelectTime = level.time + 1.0;
|
|
UserSelectWeapon(false);
|
|
}
|
|
}
|
|
|
|
if (IsSpectator()) {
|
|
if (g_protocol >= PROTOCOL_MOHTA_MIN) {
|
|
if (m_iPlayerSpectating) {
|
|
if (last_ucmd.upmove) {
|
|
if (!m_bSpectatorSwitching) {
|
|
m_bSpectatorSwitching = true;
|
|
|
|
if (last_ucmd.upmove > 0) {
|
|
SetPlayerSpectate(true);
|
|
} else {
|
|
SetPlayerSpectate(false);
|
|
}
|
|
}
|
|
} else {
|
|
m_bSpectatorSwitching = false;
|
|
}
|
|
} else if ((server_new_buttons & BUTTON_USE)) {
|
|
SetPlayerSpectateRandom();
|
|
server_new_buttons &= ~BUTTON_USE;
|
|
}
|
|
} else {
|
|
if ((server_new_buttons & BUTTON_USE)) {
|
|
SetPlayerSpectate(true);
|
|
}
|
|
}
|
|
|
|
if (g_gametype->integer >= GT_TEAM && g_forceteamspectate->integer && GetTeam() > TEAM_FREEFORALL) {
|
|
if (!m_iPlayerSpectating) {
|
|
SetPlayerSpectateRandom();
|
|
} else {
|
|
gentity_t *ent = g_entities + m_iPlayerSpectating - 1;
|
|
|
|
if (!ent->inuse || !ent->entity) {
|
|
// Invalid spectate entity
|
|
SetPlayerSpectateRandom();
|
|
} else if (ent->entity->deadflag >= DEAD_DEAD || static_cast<Player *>(ent->entity)->IsSpectator()
|
|
|| !IsValidSpectatePlayer(static_cast<Player *>(ent->entity))) {
|
|
SetPlayerSpectateRandom();
|
|
}
|
|
}
|
|
} else {
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
// Since 2.0, use = clear spectator
|
|
if (m_iPlayerSpectating && (server_new_buttons & BUTTON_USE)) {
|
|
m_iPlayerSpectating = 0;
|
|
}
|
|
} else {
|
|
// On 1.11 and below, up = clear spectator
|
|
if (last_ucmd.upmove) {
|
|
m_iPlayerSpectating = 0;
|
|
}
|
|
}
|
|
|
|
if (m_iPlayerSpectating) {
|
|
gentity_t *ent = g_entities + m_iPlayerSpectating - 1;
|
|
|
|
if (!ent->inuse || !ent->entity) {
|
|
// Invalid spectate entity
|
|
SetPlayerSpectateRandom();
|
|
} else if (ent->entity->deadflag >= DEAD_DEAD || static_cast<Player *>(ent->entity)->IsSpectator()
|
|
|| !IsValidSpectatePlayer(static_cast<Player *>(ent->entity))) {
|
|
SetPlayerSpectateRandom();
|
|
} else if (g_gametype->integer >= GT_TEAM && GetTeam() > TEAM_FREEFORALL
|
|
&& static_cast<Player *>(ent->entity)->GetTeam() != GetTeam()) {
|
|
SetPlayerSpectateRandom();
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
m_iPlayerSpectating = 0;
|
|
}
|
|
}
|
|
|
|
if (g_logstats->integer) {
|
|
if (!logfile_started) {
|
|
ProcessEvent(EV_Player_LogStats);
|
|
logfile_started = qtrue;
|
|
}
|
|
}
|
|
|
|
if (!IsDead()) {
|
|
m_iClientWeaponCommand = G_GetWeaponCommand(server_new_buttons);
|
|
|
|
switch (m_iClientWeaponCommand) {
|
|
case 0:
|
|
// No command
|
|
break;
|
|
case WEAPON_COMMAND_USE_PISTOL:
|
|
m_pWeaponCommand = new Event(EV_Sentient_UseWeaponClass);
|
|
m_pWeaponCommand->AddString("pistol");
|
|
break;
|
|
case WEAPON_COMMAND_USE_RIFLE:
|
|
m_pWeaponCommand = new Event(EV_Sentient_UseWeaponClass);
|
|
m_pWeaponCommand->AddString("rifle");
|
|
break;
|
|
case WEAPON_COMMAND_USE_SMG:
|
|
m_pWeaponCommand = new Event(EV_Sentient_UseWeaponClass);
|
|
m_pWeaponCommand->AddString("smg");
|
|
break;
|
|
case WEAPON_COMMAND_USE_MG:
|
|
m_pWeaponCommand = new Event(EV_Sentient_UseWeaponClass);
|
|
m_pWeaponCommand->AddString("mg");
|
|
break;
|
|
case WEAPON_COMMAND_USE_GRENADE:
|
|
m_pWeaponCommand = new Event(EV_Sentient_UseWeaponClass);
|
|
m_pWeaponCommand->AddString("grenade");
|
|
break;
|
|
case WEAPON_COMMAND_USE_HEAVY:
|
|
m_pWeaponCommand = new Event(EV_Sentient_UseWeaponClass);
|
|
m_pWeaponCommand->AddString("heavy");
|
|
break;
|
|
case WEAPON_COMMAND_USE_ITEM1:
|
|
m_pWeaponCommand = new Event(EV_Sentient_ToggleItemUse);
|
|
break;
|
|
case WEAPON_COMMAND_USE_ITEM2:
|
|
m_pWeaponCommand = new Event(EV_Sentient_UseWeaponClass);
|
|
m_pWeaponCommand->AddString("item2");
|
|
break;
|
|
case WEAPON_COMMAND_USE_ITEM3:
|
|
m_pWeaponCommand = new Event(EV_Sentient_UseWeaponClass);
|
|
m_pWeaponCommand->AddString("item3");
|
|
break;
|
|
case WEAPON_COMMAND_USE_ITEM4:
|
|
m_pWeaponCommand = new Event(EV_Sentient_UseWeaponClass);
|
|
m_pWeaponCommand->AddString("item4");
|
|
break;
|
|
case WEAPON_COMMAND_USE_PREV_WEAPON:
|
|
m_pWeaponCommand = new Event(EV_Player_PrevWeapon);
|
|
break;
|
|
case WEAPON_COMMAND_USE_NEXT_WEAPON:
|
|
m_pWeaponCommand = new Event(EV_Player_NextWeapon);
|
|
break;
|
|
case WEAPON_COMMAND_USE_LAST_WEAPON:
|
|
m_pWeaponCommand = new Event(EV_Sentient_UseLastWeapon);
|
|
break;
|
|
case WEAPON_COMMAND_HOLSTER:
|
|
m_pWeaponCommand = new Event(EV_Player_Holster);
|
|
break;
|
|
case WEAPON_COMMAND_DROP:
|
|
m_pWeaponCommand = new Event(EV_Player_DropWeapon);
|
|
break;
|
|
default:
|
|
gi.DPrintf("Unrecognized weapon command %d\n", m_iClientWeaponCommand);
|
|
}
|
|
|
|
if (m_pWeaponCommand) {
|
|
PostEvent(m_pWeaponCommand, 0);
|
|
}
|
|
}
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
if (!g_aimLagTime) {
|
|
g_aimLagTime = gi.Cvar_Get("g_aimLagTime", "250", 0);
|
|
}
|
|
|
|
if (mLastTrailTime + g_aimLagTime->integer < level.inttime) {
|
|
mLastTrailTime = level.inttime;
|
|
|
|
mvTrail[0] = centroid;
|
|
mvTrailEyes[0] = centroid;
|
|
mvTrailEyes[0][0] = EyePosition()[0];
|
|
}
|
|
}
|
|
UpdateFootsteps();
|
|
|
|
//
|
|
// Added in 2.0
|
|
// Heal rate
|
|
//
|
|
if (m_fHealRate && !IsDead()) {
|
|
float newrate;
|
|
|
|
if (g_healrate->value && g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
newrate = max_health * (g_healrate->value / 100.f) * level.frametime;
|
|
if (newrate >= m_fHealRate) {
|
|
newrate = m_fHealRate;
|
|
m_fHealRate = 0;
|
|
} else {
|
|
m_fHealRate -= newrate;
|
|
}
|
|
} else {
|
|
newrate = m_fHealRate;
|
|
m_fHealRate = 0;
|
|
}
|
|
|
|
// heal
|
|
health += newrate;
|
|
if (health > max_health) {
|
|
// make sure it doesn't go above the maximum player health
|
|
health = max_health;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Added in 2.0: talk icon
|
|
//
|
|
if (buttons & BUTTON_TALK) {
|
|
edict->s.eFlags |= EF_PLAYER_IN_MENU;
|
|
} else {
|
|
edict->s.eFlags &= ~EF_PLAYER_IN_MENU;
|
|
}
|
|
|
|
if (m_fTalkTime > level.time) {
|
|
edict->s.eFlags |= EF_PLAYER_TALKING;
|
|
} else {
|
|
edict->s.eFlags &= ~EF_PLAYER_TALKING;
|
|
}
|
|
|
|
//
|
|
// Added in OPM
|
|
//
|
|
if (!animDoneVM) {
|
|
int index;
|
|
float anim_time;
|
|
vma_t *vma = vmalist.find(m_sVMcurrent);
|
|
|
|
index = m_fpsTiki == NULL ? -1 : gi.Anim_NumForName(m_fpsTiki, m_sVMAcurrent);
|
|
|
|
if (index >= 0) {
|
|
anim_time = gi.Anim_Time(m_fpsTiki, index);
|
|
|
|
if (m_fVMAtime < anim_time) {
|
|
if (vma) {
|
|
m_fVMAtime += level.frametime * vma->speed;
|
|
} else {
|
|
m_fVMAtime += level.frametime;
|
|
}
|
|
} else {
|
|
animDoneVM = true;
|
|
m_fVMAtime = 0;
|
|
|
|
Notify("viewmodelanim_done");
|
|
}
|
|
} else {
|
|
animDoneVM = true;
|
|
m_fVMAtime = 0;
|
|
|
|
Notify("viewmodelanim_done");
|
|
}
|
|
}
|
|
|
|
server_new_buttons = 0;
|
|
|
|
edict->r.svFlags &= ~(SVF_SINGLECLIENT | SVF_NOTSINGLECLIENT);
|
|
}
|
|
|
|
void Player::InitLegsStateTable(void)
|
|
{
|
|
animdone_Legs = false;
|
|
currentState_Legs = statemap_Legs->FindState("STAND");
|
|
|
|
str legsAnim(currentState_Legs->getLegAnim(*this, &legs_conditionals));
|
|
if (legsAnim == "") {
|
|
StopPartAnimating(legs);
|
|
} else if (legsAnim != "none") {
|
|
SetPartAnim(legsAnim.c_str(), legs);
|
|
}
|
|
}
|
|
|
|
void Player::InitTorsoStateTable(void)
|
|
{
|
|
animdone_Torso = false;
|
|
|
|
currentState_Torso = statemap_Torso->FindState("STAND");
|
|
|
|
str torsoAnim(currentState_Torso->getActionAnim(*this, &torso_conditionals));
|
|
if (torsoAnim == "") {
|
|
StopPartAnimating(torso);
|
|
} else if (torsoAnim != "none") {
|
|
SetPartAnim(torsoAnim.c_str(), torso);
|
|
}
|
|
}
|
|
|
|
void Player::LoadStateTable(void)
|
|
{
|
|
int i;
|
|
Conditional *cond;
|
|
|
|
statemap_Legs = NULL;
|
|
statemap_Torso = NULL;
|
|
|
|
//
|
|
// Free existing conditionals
|
|
//
|
|
for (i = legs_conditionals.NumObjects(); i > 0; i--) {
|
|
cond = legs_conditionals.ObjectAt(i);
|
|
delete cond;
|
|
}
|
|
legs_conditionals.FreeObjectList();
|
|
|
|
for (i = torso_conditionals.NumObjects(); i > 0; i--) {
|
|
cond = torso_conditionals.ObjectAt(i);
|
|
delete cond;
|
|
}
|
|
torso_conditionals.FreeObjectList();
|
|
|
|
statemap_Legs =
|
|
GetStatemap(str(g_statefile->string) + "_Legs.st", (Condition<Class> *)m_conditions, &legs_conditionals, false);
|
|
statemap_Torso = GetStatemap(
|
|
str(g_statefile->string) + "_Torso.st", (Condition<Class> *)m_conditions, &torso_conditionals, false
|
|
);
|
|
|
|
movecontrol = MOVECONTROL_LEGS;
|
|
|
|
InitLegsStateTable();
|
|
InitTorsoStateTable();
|
|
|
|
movecontrol = currentState_Legs->getMoveType();
|
|
if (!movecontrol) {
|
|
movecontrol = MOVECONTROL_LEGS;
|
|
}
|
|
|
|
for (int i = 1; i <= legs_conditionals.NumObjects(); i++) {
|
|
Conditional *c = legs_conditionals.ObjectAt(i);
|
|
|
|
if (Q_stricmp(c->getName(), "PAIN") && !c->parmList.NumObjects()) {
|
|
m_pLegsPainCond = c;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int i = 1; i <= torso_conditionals.NumObjects(); i++) {
|
|
Conditional *c = torso_conditionals.ObjectAt(i);
|
|
|
|
if (Q_stricmp(c->getName(), "PAIN") && !c->parmList.NumObjects()) {
|
|
m_pTorsoPainCond = c;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((movecontrol < (sizeof(MoveStartFuncs) / sizeof(MoveStartFuncs[0]))) && (MoveStartFuncs[movecontrol])) {
|
|
(this->*MoveStartFuncs[movecontrol])();
|
|
}
|
|
|
|
SetViewAngles(v_angle);
|
|
}
|
|
|
|
void Player::ResetState(Event *ev)
|
|
{
|
|
movecontrol = MOVECONTROL_LEGS;
|
|
LoadStateTable();
|
|
}
|
|
|
|
void Player::StartPush(void)
|
|
{
|
|
trace_t trace;
|
|
Vector end(origin + yaw_forward * 64.0f);
|
|
|
|
trace = G_Trace(origin, mins, maxs, end, this, MASK_SOLID, true, "StartPush");
|
|
if (trace.fraction == 1.0f) {
|
|
return;
|
|
}
|
|
v_angle.y = vectoyaw(trace.plane.normal) - 180;
|
|
SetViewAngles(v_angle);
|
|
|
|
setOrigin(trace.endpos - yaw_forward * 0.4f);
|
|
}
|
|
|
|
void Player::StartClimbLadder(void)
|
|
{
|
|
trace_t trace;
|
|
Vector end(origin + yaw_forward * 20.0f);
|
|
|
|
trace = G_Trace(origin, mins, maxs, end, this, MASK_SOLID, true, "StartClimbLadder");
|
|
if ((trace.fraction == 1.0f) || !(trace.surfaceFlags & SURF_LADDER)) {
|
|
return;
|
|
}
|
|
|
|
v_angle.y = vectoyaw(trace.plane.normal) - 180;
|
|
SetViewAngles(v_angle);
|
|
|
|
setOrigin(trace.endpos - yaw_forward * 0.4f);
|
|
}
|
|
|
|
void Player::StartUseAnim(void)
|
|
{
|
|
UseAnim *ua;
|
|
Vector neworg;
|
|
Vector newangles;
|
|
str newanim;
|
|
str state;
|
|
str camera;
|
|
trace_t trace;
|
|
|
|
if (toucheduseanim) {
|
|
ua = (UseAnim *)(Entity *)toucheduseanim;
|
|
} else if (atobject) {
|
|
ua = (UseAnim *)(Entity *)atobject;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
useitem_in_use = ua;
|
|
toucheduseanim = NULL;
|
|
atobject = NULL;
|
|
|
|
if (ua->GetInformation(this, &neworg, &newangles, &newanim, &useanim_numloops, &state, &camera)) {
|
|
trace = G_Trace(origin, mins, maxs, neworg, this, MASK_PLAYERSOLID, true, "StartUseAnim");
|
|
if (trace.startsolid || (trace.fraction < 1.0f)) {
|
|
gi.DPrintf("Move to UseAnim was blocked.\n");
|
|
}
|
|
|
|
if (!trace.startsolid) {
|
|
setOrigin(trace.endpos);
|
|
}
|
|
|
|
setAngles(newangles);
|
|
v_angle.y = newangles.y;
|
|
SetViewAngles(v_angle);
|
|
|
|
movecontrol = MOVECONTROL_ABSOLUTE;
|
|
|
|
if (state.length()) {
|
|
State *newState;
|
|
|
|
newState = statemap_Torso->FindState(state);
|
|
if (newState) {
|
|
EvaluateState(newState);
|
|
} else {
|
|
gi.DPrintf("Could not find state %s on UseAnim\n", state.c_str());
|
|
}
|
|
} else {
|
|
if (currentState_Torso) {
|
|
if (camera.length()) {
|
|
currentState_Torso->setCameraType(camera);
|
|
} else {
|
|
currentState_Torso->setCameraType("behind");
|
|
}
|
|
}
|
|
SetPartAnim(newanim, legs);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::StartLoopUseAnim(void)
|
|
{
|
|
useanim_numloops--;
|
|
}
|
|
|
|
void Player::FinishUseAnim(Event *ev)
|
|
{
|
|
UseAnim *ua;
|
|
|
|
if (!useitem_in_use) {
|
|
return;
|
|
}
|
|
|
|
ua = (UseAnim *)(Entity *)useitem_in_use;
|
|
ua->TriggerTargets(this);
|
|
useitem_in_use = NULL;
|
|
}
|
|
|
|
void Player::SetupUseObject(void)
|
|
{
|
|
UseObject *uo;
|
|
Vector neworg;
|
|
Vector newangles;
|
|
str state;
|
|
trace_t trace;
|
|
|
|
if (atobject) {
|
|
uo = (UseObject *)(Entity *)atobject;
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
useitem_in_use = uo;
|
|
|
|
uo->Setup(this, &neworg, &newangles, &state);
|
|
{
|
|
trace = G_Trace(neworg, mins, maxs, neworg, this, MASK_PLAYERSOLID, true, "SetupUseObject - 1");
|
|
if (trace.startsolid || trace.allsolid) {
|
|
trace = G_Trace(origin, mins, maxs, neworg, this, MASK_PLAYERSOLID, true, "SetupUseObject - 2");
|
|
if (trace.startsolid || (trace.fraction < 1.0f)) {
|
|
gi.DPrintf("Move to UseObject was blocked.\n");
|
|
}
|
|
}
|
|
|
|
if (!trace.startsolid) {
|
|
setOrigin(trace.endpos);
|
|
}
|
|
|
|
setAngles(newangles);
|
|
v_angle.y = newangles.y;
|
|
SetViewAngles(v_angle);
|
|
|
|
movecontrol = MOVECONTROL_ABSOLUTE;
|
|
|
|
if (state.length()) {
|
|
State *newState;
|
|
|
|
newState = statemap_Torso->FindState(state);
|
|
if (newState) {
|
|
EvaluateState(newState);
|
|
} else {
|
|
gi.DPrintf("Could not find state %s on UseObject\n", state.c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::StartUseObject(Event *ev)
|
|
{
|
|
UseObject *uo;
|
|
|
|
if (!useitem_in_use) {
|
|
return;
|
|
}
|
|
|
|
uo = (UseObject *)(Entity *)useitem_in_use;
|
|
uo->Start();
|
|
}
|
|
|
|
void Player::FinishUseObject(Event *ev)
|
|
{
|
|
UseObject *uo;
|
|
|
|
if (!useitem_in_use) {
|
|
return;
|
|
}
|
|
|
|
uo = (UseObject *)(Entity *)useitem_in_use;
|
|
uo->Stop(this);
|
|
useitem_in_use = NULL;
|
|
}
|
|
|
|
void Player::Turn(Event *ev)
|
|
{
|
|
float yaw;
|
|
Vector oldang(v_angle);
|
|
|
|
yaw = ev->GetFloat(1);
|
|
|
|
v_angle[YAW] = (int)(anglemod(v_angle[YAW]) / 22.5f) * 22.5f;
|
|
SetViewAngles(v_angle);
|
|
|
|
if (!CheckMove(vec_zero)) {
|
|
SetViewAngles(oldang);
|
|
return;
|
|
}
|
|
|
|
CancelEventsOfType(EV_Player_TurnUpdate);
|
|
|
|
ev = new Event(EV_Player_TurnUpdate);
|
|
ev->AddFloat(yaw / 5.0f);
|
|
ev->AddFloat(0.5f);
|
|
ProcessEvent(ev);
|
|
}
|
|
|
|
void Player::TurnUpdate(Event *ev)
|
|
{
|
|
float yaw;
|
|
float timeleft;
|
|
Vector oldang(v_angle);
|
|
|
|
yaw = ev->GetFloat(1);
|
|
timeleft = ev->GetFloat(2);
|
|
timeleft -= 0.1f;
|
|
|
|
if (timeleft > 0) {
|
|
ev = new Event(EV_Player_TurnUpdate);
|
|
ev->AddFloat(yaw);
|
|
ev->AddFloat(timeleft);
|
|
PostEvent(ev, 0.1f);
|
|
|
|
v_angle[YAW] += yaw;
|
|
SetViewAngles(v_angle);
|
|
} else {
|
|
v_angle[YAW] = (int)(anglemod(v_angle[YAW]) / 22.5f) * 22.5f;
|
|
SetViewAngles(v_angle);
|
|
}
|
|
|
|
if (!CheckMove(vec_zero)) {
|
|
SetViewAngles(oldang);
|
|
}
|
|
}
|
|
|
|
void Player::TurnLegs(Event *ev)
|
|
{
|
|
float yaw;
|
|
|
|
yaw = ev->GetFloat(1);
|
|
|
|
angles[YAW] += yaw;
|
|
setAngles(angles);
|
|
}
|
|
|
|
void Player::EvaluateState(State *forceTorso, State *forceLegs)
|
|
{
|
|
int count;
|
|
State *laststate_Legs;
|
|
State *laststate_Torso;
|
|
State *startstate_Legs;
|
|
State *startstate_Torso;
|
|
movecontrol_t move;
|
|
|
|
if (getMoveType() == MOVETYPE_NOCLIP) {
|
|
return;
|
|
}
|
|
|
|
if (flags & FL_IMMOBILE) {
|
|
// Don't evaluate state when immobile
|
|
return;
|
|
}
|
|
|
|
if (getMoveType() == MOVETYPE_PORTABLE_TURRET) {
|
|
// Added in 2.0
|
|
// Animations are handled hardcodedly
|
|
currentState_Torso = NULL;
|
|
currentState_Legs = NULL;
|
|
return;
|
|
}
|
|
|
|
// Evaluate the current state.
|
|
// When the state changes, we reevaluate the state so that if the
|
|
// conditions aren't met in the new state, we don't play one frame of
|
|
// the animation for that state before going to the next state.
|
|
startstate_Torso = laststate_Torso = currentState_Torso;
|
|
count = 0;
|
|
do {
|
|
// since we could get into an infinite loop here, do a check
|
|
// to make sure we don't.
|
|
count++;
|
|
if (count > 10) {
|
|
gi.DPrintf("Possible infinite loop in state '%s'\n", currentState_Torso->getName());
|
|
if (count > 20) {
|
|
assert(0);
|
|
gi.Error(ERR_DROP, "Stopping due to possible infinite state loop\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
laststate_Torso = currentState_Torso;
|
|
|
|
if (currentState_Torso) {
|
|
laststate_Torso = currentState_Torso;
|
|
|
|
if (forceTorso) {
|
|
currentState_Torso = forceTorso;
|
|
} else {
|
|
currentState_Torso = currentState_Torso->Evaluate(*this, &torso_conditionals);
|
|
}
|
|
} else {
|
|
// Added in 2.0
|
|
// Switch to the default torso state if it's NULL
|
|
if (forceTorso) {
|
|
currentState_Torso = forceTorso;
|
|
} else {
|
|
currentState_Torso = statemap_Torso->FindState("STAND");
|
|
}
|
|
|
|
laststate_Torso = NULL;
|
|
}
|
|
|
|
if (currentState_Torso) {
|
|
if (laststate_Torso) {
|
|
// Process exit commands of the last state
|
|
laststate_Torso->ProcessExitCommands(this);
|
|
}
|
|
|
|
// Process entry commands of the new state
|
|
currentState_Torso->ProcessEntryCommands(this);
|
|
|
|
if (waitForState.length() && (!waitForState.icmpn(currentState_Torso->getName(), waitForState.length()))) {
|
|
waitForState = "";
|
|
}
|
|
|
|
move = currentState_Torso->getMoveType();
|
|
|
|
// use the current movecontrol
|
|
if (move == MOVECONTROL_NONE) {
|
|
move = movecontrol;
|
|
}
|
|
|
|
str legsAnim;
|
|
str torsoAnim(currentState_Torso->getActionAnim(*this, &torso_conditionals));
|
|
|
|
if (move == MOVECONTROL_LEGS) {
|
|
if (!currentState_Legs) {
|
|
animdone_Legs = false;
|
|
currentState_Legs = statemap_Legs->FindState("STAND");
|
|
legsAnim = currentState_Legs->getLegAnim(*this, &legs_conditionals);
|
|
|
|
if (legsAnim == "") {
|
|
StopPartAnimating(legs);
|
|
} else if (legsAnim != "none") {
|
|
SetPartAnim(legsAnim.c_str(), legs);
|
|
}
|
|
}
|
|
|
|
if (torsoAnim == "none") {
|
|
StopPartAnimating(torso);
|
|
animdone_Torso = true;
|
|
} else if (torsoAnim != "") {
|
|
SetPartAnim(torsoAnim.c_str(), torso);
|
|
}
|
|
} else {
|
|
if (torsoAnim == "none") {
|
|
StopPartAnimating(torso);
|
|
animdone_Torso = true;
|
|
} else if (torsoAnim != "") {
|
|
SetPartAnim(torsoAnim.c_str(), torso);
|
|
}
|
|
|
|
legsAnim = currentState_Torso->getLegAnim(*this, &torso_conditionals);
|
|
|
|
if (legsAnim == "none" || legsAnim == "") {
|
|
StopPartAnimating(legs);
|
|
} else {
|
|
SetPartAnim(legsAnim.c_str(), legs);
|
|
}
|
|
|
|
// Fixed in OPM
|
|
// Clear the legs state, so the torso state can reset it to STAND
|
|
// in subsequent iterations.
|
|
// As the legs animation is stopped, there would be no anim to wait on.
|
|
//
|
|
// This prevents the current legs state to be stuck
|
|
// when the move control is set to non-legs and then to legs
|
|
// in the same iteration before the legs state is being processed.
|
|
currentState_Legs = NULL;
|
|
}
|
|
|
|
if (movecontrol != move) {
|
|
movecontrol = move;
|
|
if ((move < (sizeof(MoveStartFuncs) / sizeof(MoveStartFuncs[0]))) && (MoveStartFuncs[move])) {
|
|
(this->*MoveStartFuncs[move])();
|
|
}
|
|
|
|
if (movecontrol == MOVECONTROL_CLIMBWALL) {
|
|
edict->s.eFlags |= EF_CLIMBWALL;
|
|
} else {
|
|
edict->s.eFlags &= ~EF_CLIMBWALL;
|
|
}
|
|
}
|
|
|
|
SetViewAngles(v_angle);
|
|
} else {
|
|
currentState_Torso = laststate_Torso;
|
|
}
|
|
} while (laststate_Torso != currentState_Torso);
|
|
|
|
// Evaluate the current state.
|
|
// When the state changes, we reevaluate the state so that if the
|
|
// conditions aren't met in the new state, we don't play one frame of
|
|
// the animation for that state before going to the next state.
|
|
startstate_Legs = laststate_Legs = currentState_Legs;
|
|
if (movecontrol == MOVECONTROL_LEGS) {
|
|
count = 0;
|
|
do {
|
|
// since we could get into an infinite loop here, do a check
|
|
// to make sure we don't.
|
|
count++;
|
|
if (count > 10) {
|
|
gi.DPrintf("Possible infinite loop in state '%s'\n", currentState_Legs->getName());
|
|
if (count > 20) {
|
|
assert(0);
|
|
gi.Error(ERR_DROP, "Stopping due to possible infinite state loop\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (currentState_Legs) {
|
|
laststate_Legs = currentState_Legs;
|
|
|
|
if (forceLegs) {
|
|
currentState_Legs = forceLegs;
|
|
} else {
|
|
currentState_Legs = currentState_Legs->Evaluate(*this, &legs_conditionals);
|
|
}
|
|
} else {
|
|
// Added in 2.0
|
|
// Switch to the default legs state if it's NULL
|
|
if (forceLegs) {
|
|
currentState_Legs = forceLegs;
|
|
} else if ((m_iMovePosFlags & MPF_POSITION_CROUCHING)) {
|
|
currentState_Legs = statemap_Legs->FindState("CROUCH_IDLE");
|
|
} else {
|
|
currentState_Legs = statemap_Legs->FindState("STAND");
|
|
}
|
|
|
|
laststate_Legs = NULL;
|
|
}
|
|
|
|
animdone_Legs = false;
|
|
if (currentState_Legs) {
|
|
if (laststate_Legs) {
|
|
// Process exit commands of the last state
|
|
laststate_Legs->ProcessExitCommands(this);
|
|
}
|
|
|
|
// Process entry commands of the new state
|
|
currentState_Legs->ProcessEntryCommands(this);
|
|
|
|
if (waitForState.length()
|
|
&& (!waitForState.icmpn(currentState_Legs->getName(), waitForState.length()))) {
|
|
waitForState = "";
|
|
}
|
|
|
|
str legsAnim(currentState_Legs->getLegAnim(*this, &legs_conditionals));
|
|
|
|
if (legsAnim == "none") {
|
|
StopPartAnimating(legs);
|
|
animdone_Legs = true;
|
|
} else if (legsAnim != "") {
|
|
SetPartAnim(legsAnim, legs);
|
|
}
|
|
} else {
|
|
currentState_Legs = laststate_Legs;
|
|
}
|
|
} while (laststate_Legs != currentState_Legs);
|
|
} else {
|
|
currentState_Legs = NULL;
|
|
}
|
|
|
|
if (g_showplayeranim->integer) {
|
|
if (last_leg_anim_name != AnimName(legs)) {
|
|
gi.DPrintf("Legs change from %s to %s\n", last_leg_anim_name.c_str(), AnimName(legs));
|
|
last_leg_anim_name = AnimName(legs);
|
|
}
|
|
|
|
if (last_torso_anim_name != AnimName(torso)) {
|
|
gi.DPrintf("Torso change from %s to %s\n", last_torso_anim_name.c_str(), AnimName(torso));
|
|
last_torso_anim_name = AnimName(torso);
|
|
}
|
|
}
|
|
|
|
if (g_showplayerstate->integer) {
|
|
if (startstate_Legs != currentState_Legs) {
|
|
gi.DPrintf(
|
|
"Legs change from %s to %s\n",
|
|
startstate_Legs ? startstate_Legs->getName() : "NULL",
|
|
currentState_Legs ? currentState_Legs->getName() : "NULL"
|
|
);
|
|
}
|
|
|
|
if (startstate_Torso != currentState_Torso) {
|
|
gi.DPrintf(
|
|
"Torso change from %s to %s\n",
|
|
startstate_Torso ? startstate_Torso->getName() : "NULL",
|
|
currentState_Torso ? currentState_Torso->getName() : "NULL"
|
|
);
|
|
}
|
|
}
|
|
|
|
// This is so we don't remember pain when we change to a state that has a PAIN condition
|
|
pain = 0;
|
|
}
|
|
|
|
void Player::SelectPreviousItem(Event *ev)
|
|
{
|
|
if (deadflag) {
|
|
return;
|
|
}
|
|
|
|
Item *item = GetActiveWeapon(WEAPON_MAIN);
|
|
|
|
item = PrevItem(item);
|
|
|
|
if (item) {
|
|
useWeapon((Weapon *)item, WEAPON_MAIN);
|
|
}
|
|
}
|
|
|
|
void Player::SelectNextItem(Event *ev)
|
|
{
|
|
if (deadflag) {
|
|
return;
|
|
}
|
|
|
|
Item *item = GetActiveWeapon(WEAPON_MAIN);
|
|
|
|
item = NextItem(item);
|
|
|
|
if (item) {
|
|
useWeapon((Weapon *)item, WEAPON_MAIN);
|
|
}
|
|
}
|
|
|
|
void Player::SelectPreviousWeapon(Event *ev)
|
|
{
|
|
Weapon *weapon;
|
|
Weapon *initialWeapon;
|
|
Weapon *activeWeapon;
|
|
|
|
if (deadflag) {
|
|
return;
|
|
}
|
|
|
|
activeWeapon = GetActiveWeapon(WEAPON_MAIN);
|
|
if (activeWeapon && activeWeapon->IsSubclassOfInventoryItem()) {
|
|
activeWeapon = NULL;
|
|
}
|
|
|
|
if (!activeWeapon) {
|
|
activeWeapon = newActiveWeapon.weapon;
|
|
if (activeWeapon && activeWeapon->IsSubclassOfInventoryItem()) {
|
|
activeWeapon = NULL;
|
|
}
|
|
}
|
|
|
|
if (activeWeapon) {
|
|
// Fixed in OPM
|
|
// Fixes the bug that cause infinite loop when the last weapon has no ammo
|
|
// and the only weapon is an inventory item
|
|
for (weapon = initialWeapon = PreviousWeapon(activeWeapon); weapon && weapon != activeWeapon; ) {
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER || !weapon->IsSubclassOfInventoryItem()) {
|
|
break;
|
|
}
|
|
|
|
weapon = PreviousWeapon(weapon);
|
|
if (weapon == initialWeapon) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
weapon = BestWeapon();
|
|
}
|
|
|
|
if (weapon && weapon != activeWeapon) {
|
|
useWeapon(weapon);
|
|
}
|
|
|
|
if (deadflag) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
void Player::SelectNextWeapon(Event *ev)
|
|
{
|
|
Weapon *weapon;
|
|
Weapon *initialWeapon;
|
|
Weapon *activeWeapon;
|
|
|
|
if (deadflag) {
|
|
return;
|
|
}
|
|
|
|
activeWeapon = GetActiveWeapon(WEAPON_MAIN);
|
|
if (activeWeapon && activeWeapon->IsSubclassOfInventoryItem()) {
|
|
activeWeapon = NULL;
|
|
}
|
|
|
|
if (!activeWeapon) {
|
|
activeWeapon = newActiveWeapon.weapon;
|
|
if (activeWeapon && activeWeapon->IsSubclassOfInventoryItem()) {
|
|
activeWeapon = NULL;
|
|
}
|
|
}
|
|
|
|
if (activeWeapon) {
|
|
// Fixed in OPM
|
|
// Fixes the bug that cause infinite loop when the last weapon has no ammo
|
|
// and the only weapon is an inventory item
|
|
for (weapon = initialWeapon = NextWeapon(activeWeapon); weapon && weapon != activeWeapon; ) {
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER || !weapon->IsSubclassOfInventoryItem()) {
|
|
break;
|
|
}
|
|
|
|
weapon = NextWeapon(weapon);
|
|
if (weapon == initialWeapon) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
weapon = WorstWeapon();
|
|
}
|
|
|
|
if (weapon && weapon != activeWeapon) {
|
|
useWeapon(weapon);
|
|
}
|
|
}
|
|
|
|
void Player::DropCurrentWeapon(Event *ev)
|
|
{
|
|
Weapon *weapon;
|
|
Vector forward;
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
return;
|
|
}
|
|
|
|
weapon = GetActiveWeapon(WEAPON_MAIN);
|
|
|
|
if (!weapon) {
|
|
return;
|
|
}
|
|
|
|
// Don't drop the weapon if we're charging
|
|
if (charge_start_time) {
|
|
return;
|
|
}
|
|
|
|
if ((weapon->GetWeaponClass() & WEAPON_CLASS_ITEM)) {
|
|
SelectNextWeapon(NULL);
|
|
takeItem(weapon->model);
|
|
} else {
|
|
if (weapon->GetCurrentAttachToTag() != "tag_weapon_right") {
|
|
EventCorrectWeaponAttachments(NULL);
|
|
}
|
|
|
|
// This check isn't in MOHAA
|
|
if (!weapon->IsDroppable()) {
|
|
return;
|
|
}
|
|
|
|
weapon->Drop();
|
|
|
|
AngleVectors(m_vViewAng, forward, NULL, NULL);
|
|
|
|
// make the weapon looks like it's thrown
|
|
weapon->velocity = forward * 200.0f;
|
|
|
|
edict->s.eFlags |= EF_UNARMED;
|
|
|
|
SelectNextWeapon(NULL);
|
|
|
|
if (holsteredWeapon == weapon) {
|
|
holsteredWeapon = NULL;
|
|
}
|
|
if (lastActiveWeapon.weapon == weapon) {
|
|
lastActiveWeapon.weapon = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::GiveWeaponCheat(Event *ev)
|
|
{
|
|
giveItem(ev->GetString(1));
|
|
}
|
|
|
|
void Player::GiveCheat(Event *ev)
|
|
{
|
|
str name;
|
|
|
|
if (deadflag) {
|
|
return;
|
|
}
|
|
|
|
name = ev->GetString(1);
|
|
|
|
if (!name.icmp("all")) {
|
|
GiveAllCheat(ev);
|
|
return;
|
|
}
|
|
EventGiveItem(ev);
|
|
}
|
|
|
|
void Player::GiveAllCheat(Event *ev)
|
|
{
|
|
char *buffer;
|
|
char *buf;
|
|
char com_token[MAX_STRING_CHARS];
|
|
|
|
if (deadflag) {
|
|
return;
|
|
}
|
|
|
|
if (gi.FS_ReadFile("global/giveall.scr", (void **)&buf, true) != -1) {
|
|
buffer = buf;
|
|
while (1) {
|
|
Q_strncpyz(com_token, COM_ParseExt(&buffer, qtrue), sizeof(com_token));
|
|
|
|
if (!com_token[0]) {
|
|
break;
|
|
}
|
|
|
|
// Create the event
|
|
ev = new Event(com_token);
|
|
|
|
// get the rest of the line
|
|
while (1) {
|
|
Q_strncpyz(com_token, COM_ParseExt(&buffer, qfalse), sizeof(com_token));
|
|
if (!com_token[0]) {
|
|
break;
|
|
}
|
|
|
|
ev->AddToken(com_token);
|
|
}
|
|
|
|
this->ProcessEvent(ev);
|
|
}
|
|
gi.FS_FreeFile(buf);
|
|
}
|
|
}
|
|
|
|
void Player::GiveNewWeaponsCheat(Event *ev)
|
|
{
|
|
char *buffer;
|
|
char *current;
|
|
const char *token;
|
|
|
|
if (deadflag != DEAD_NO) {
|
|
return;
|
|
}
|
|
|
|
if (gi.FS_ReadFile("global/givenewweapons.scr", (void **)&buffer, qtrue) != -1) {
|
|
return;
|
|
}
|
|
|
|
current = buffer;
|
|
for (;;) {
|
|
Event *event;
|
|
|
|
token = COM_ParseExt(¤t, qtrue);
|
|
if (!token[0]) {
|
|
break;
|
|
}
|
|
|
|
event = new Event(token);
|
|
|
|
for (;;) {
|
|
token = COM_ParseExt(¤t, qfalse);
|
|
if (!token[0]) {
|
|
break;
|
|
}
|
|
|
|
event->AddToken(token);
|
|
}
|
|
|
|
ProcessEvent(event);
|
|
}
|
|
|
|
gi.FS_FreeFile(buffer);
|
|
}
|
|
|
|
void Player::GodCheat(Event *ev)
|
|
{
|
|
const char *msg;
|
|
|
|
if (ev->NumArgs() > 0) {
|
|
if (ev->GetInteger(1)) {
|
|
flags |= FL_GODMODE;
|
|
// Also enable the god mode for the vehicle
|
|
if (m_pVehicle) {
|
|
m_pVehicle->flags |= FL_GODMODE;
|
|
}
|
|
} else {
|
|
flags &= ~FL_GODMODE;
|
|
// Also disable the god mode for the vehicle
|
|
if (m_pVehicle) {
|
|
m_pVehicle->flags &= ~FL_GODMODE;
|
|
}
|
|
}
|
|
} else {
|
|
if (flags & FL_GODMODE) {
|
|
flags &= ~FL_GODMODE;
|
|
if (m_pVehicle) {
|
|
m_pVehicle->flags &= ~FL_GODMODE;
|
|
}
|
|
} else {
|
|
flags |= FL_GODMODE;
|
|
if (m_pVehicle) {
|
|
m_pVehicle->flags |= FL_GODMODE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ev->isSubclassOf(ConsoleEvent)) {
|
|
if (!(flags & FL_GODMODE)) {
|
|
msg = "CHEAT: godmode OFF\n";
|
|
} else {
|
|
msg = "CHEAT: godmode ON\n";
|
|
}
|
|
|
|
gi.SendServerCommand(edict - g_entities, "print \"%s\"", msg);
|
|
}
|
|
}
|
|
|
|
void Player::Kill(Event *ev)
|
|
{
|
|
if ((level.time - respawn_time) < 5) {
|
|
return;
|
|
}
|
|
|
|
flags &= ~FL_GODMODE;
|
|
health = 1;
|
|
Damage(this, this, 10, origin, vec_zero, vec_zero, 0, DAMAGE_NO_PROTECTION, MOD_SUICIDE);
|
|
}
|
|
|
|
void Player::NoTargetCheat(Event *ev)
|
|
{
|
|
const char *msg;
|
|
|
|
flags ^= FL_NOTARGET;
|
|
if (!(flags & FL_NOTARGET)) {
|
|
msg = "notarget OFF\n";
|
|
} else {
|
|
msg = "notarget ON\n";
|
|
}
|
|
|
|
gi.SendServerCommand(edict - g_entities, "print \"%s\"", msg);
|
|
}
|
|
|
|
void Player::NoclipCheat(Event *ev)
|
|
{
|
|
const char *msg;
|
|
|
|
if (m_pVehicle) {
|
|
msg = "Must exit vehicle first\n";
|
|
} else if (m_pTurret) {
|
|
msg = "Must exit turret first\n";
|
|
} else if (getMoveType() == MOVETYPE_NOCLIP) {
|
|
setMoveType(MOVETYPE_WALK);
|
|
msg = "noclip OFF\n";
|
|
|
|
// reset the state machine so that his animations are correct
|
|
ResetState(NULL);
|
|
charge_start_time = 0;
|
|
} else {
|
|
client->ps.feetfalling = false;
|
|
movecontrol = MOVECONTROL_LEGS;
|
|
|
|
setMoveType(MOVETYPE_NOCLIP);
|
|
msg = "noclip ON\n";
|
|
}
|
|
|
|
gi.SendServerCommand(edict - g_entities, "print \"%s\"", msg);
|
|
}
|
|
|
|
void Player::GameVersion(Event *ev)
|
|
{
|
|
gi.SendServerCommand(edict - g_entities, "print \"%s : %s\n\"", GAMEVERSION, __DATE__);
|
|
}
|
|
|
|
void Player::SetFov(float newFov)
|
|
{
|
|
fov = newFov;
|
|
|
|
if (fov < 1) {
|
|
fov = 80;
|
|
} else if (fov > 160) {
|
|
fov = 160;
|
|
}
|
|
}
|
|
|
|
void Player::EventSetSelectedFov(Event *ev)
|
|
{
|
|
float fOldSelectedFov;
|
|
|
|
if (ev->NumArgs() < 1) {
|
|
gi.SendServerCommand(edict - g_entities, "print \"Fov = %d\n\"", (unsigned int)fov);
|
|
return;
|
|
}
|
|
|
|
fOldSelectedFov = selectedfov;
|
|
SetSelectedFov(ev->GetFloat(1));
|
|
if (fov == fOldSelectedFov) {
|
|
SetFov(selectedfov);
|
|
}
|
|
}
|
|
|
|
void Player::SetSelectedFov(float newFov)
|
|
{
|
|
selectedfov = newFov;
|
|
|
|
if (selectedfov < 1) {
|
|
selectedfov = 80;
|
|
} else if (selectedfov > 160) {
|
|
selectedfov = 160;
|
|
}
|
|
|
|
/*
|
|
if( g_gametype->integer != GT_SINGLE_PLAYER && !developer->integer )
|
|
{
|
|
if( selectedfov < 80 )
|
|
{
|
|
selectedfov = 80;
|
|
}
|
|
else if( selectedfov > 80 )
|
|
{
|
|
selectedfov = 80;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
===============
|
|
CalcRoll
|
|
|
|
===============
|
|
*/
|
|
float Player::CalcRoll(void)
|
|
{
|
|
float sign;
|
|
float side;
|
|
float value;
|
|
Vector l;
|
|
|
|
angles.AngleVectors(NULL, &l, NULL);
|
|
side = velocity * l;
|
|
sign = side < 0 ? 4 : -4;
|
|
side = fabs(side);
|
|
|
|
value = sv_rollangle->value;
|
|
|
|
if (side < sv_rollspeed->value) {
|
|
side = side * value / sv_rollspeed->value;
|
|
} else {
|
|
side = value;
|
|
}
|
|
|
|
return side * sign;
|
|
}
|
|
|
|
//
|
|
// PMove Events
|
|
//
|
|
void Player::ProcessPmoveEvents(int event)
|
|
{
|
|
float damage;
|
|
|
|
switch (event) {
|
|
case EV_NONE:
|
|
break;
|
|
case EV_FALL_SHORT:
|
|
case EV_FALL_MEDIUM:
|
|
case EV_FALL_FAR:
|
|
case EV_FALL_FATAL:
|
|
if (event == EV_FALL_FATAL) {
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
damage = 101;
|
|
} else {
|
|
damage = max_health + 1.0f;
|
|
}
|
|
} else if (event == EV_FALL_FAR) {
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
damage = 25;
|
|
} else {
|
|
damage = 20;
|
|
}
|
|
} else if (event == EV_FALL_MEDIUM) {
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
damage = 15;
|
|
} else {
|
|
damage = 10;
|
|
}
|
|
} else {
|
|
damage = 5;
|
|
}
|
|
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
// since 2.0, remove a percentage of the health
|
|
damage = damage * (max_health / 100.0);
|
|
}
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER || !DM_FLAG(DF_NO_FALLING)) {
|
|
Damage(this, this, (int)damage, origin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_FALLING);
|
|
}
|
|
break;
|
|
case EV_TERMINAL_VELOCITY:
|
|
Sound("snd_fall", CHAN_VOICE);
|
|
break;
|
|
case EV_WATER_LEAVE: // foot leaves
|
|
Sound("impact_playerleavewater", CHAN_AUTO);
|
|
break;
|
|
case EV_WATER_UNDER: // head touches
|
|
Sound("impact_playersubmerge", CHAN_AUTO);
|
|
break;
|
|
case EV_WATER_CLEAR: // head leaves
|
|
Sound("snd_gasp", CHAN_LOCAL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
WorldEffects
|
|
=============
|
|
*/
|
|
void Player::WorldEffects(void)
|
|
{
|
|
if (deadflag == DEAD_DEAD || getMoveType() == MOVETYPE_NOCLIP) {
|
|
// if we are dead or no-cliping, no world effects
|
|
return;
|
|
}
|
|
|
|
//
|
|
// check for on fire
|
|
//
|
|
if (on_fire) {
|
|
if (next_painsound_time < level.time) {
|
|
next_painsound_time = level.time + 4;
|
|
Sound("snd_onfire", CHAN_LOCAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
AddBlend
|
|
=============
|
|
*/
|
|
void Player::AddBlend(float r, float g, float b, float a)
|
|
{
|
|
float a2;
|
|
float a3;
|
|
|
|
if (a <= 0) {
|
|
return;
|
|
}
|
|
|
|
// new total alpha
|
|
a2 = blend[3] + (1 - blend[3]) * a;
|
|
|
|
// fraction of color from old
|
|
a3 = blend[3] / a2;
|
|
|
|
blend[0] = blend[0] * a3 + r * (1 - a3);
|
|
blend[1] = blend[1] * a3 + g * (1 - a3);
|
|
blend[2] = blend[2] * a3 + b * (1 - a3);
|
|
blend[3] = a2;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
CalcBlend
|
|
=============
|
|
*/
|
|
void Player::CalcBlend(void)
|
|
{
|
|
int contents;
|
|
Vector vieworg;
|
|
|
|
client->ps.stats[STAT_ADDFADE] = 0;
|
|
blend[0] = blend[1] = blend[2] = blend[3] = 0;
|
|
|
|
// add for contents
|
|
vieworg = m_vViewPos;
|
|
|
|
contents = gi.pointcontents(vieworg, 0);
|
|
|
|
if (contents & CONTENTS_SOLID) {
|
|
// Outside of world
|
|
//AddBlend( 0.8, 0.5, 0.0, 0.2 );
|
|
} else if (contents & CONTENTS_LAVA) {
|
|
AddBlend(level.lava_color[0], level.lava_color[1], level.lava_color[2], level.lava_alpha);
|
|
} else if (contents & CONTENTS_WATER) {
|
|
AddBlend(level.water_color[0], level.water_color[1], level.water_color[2], level.water_alpha);
|
|
}
|
|
|
|
// add for damage
|
|
if (damage_alpha > 0) {
|
|
AddBlend(damage_blend[0], damage_blend[1], damage_blend[2], damage_alpha);
|
|
|
|
// drop the damage value
|
|
damage_alpha -= 0.06f;
|
|
if (damage_alpha < 0) {
|
|
damage_alpha = 0;
|
|
}
|
|
client->ps.blend[0] = blend[0];
|
|
client->ps.blend[1] = blend[1];
|
|
client->ps.blend[2] = blend[2];
|
|
client->ps.blend[3] = blend[3];
|
|
}
|
|
|
|
// Do the cinematic fading
|
|
float alpha = 1;
|
|
|
|
level.m_fade_time -= level.frametime;
|
|
|
|
// Return if we are completely faded in
|
|
if ((level.m_fade_time <= 0) && (level.m_fade_type == fadein)) {
|
|
client->ps.blend[3] = 0 + damage_alpha;
|
|
return;
|
|
}
|
|
|
|
// If we are faded out, and another fade out is coming in, then don't bother
|
|
if ((level.m_fade_time_start > 0) && (level.m_fade_type == fadeout)) {
|
|
if (client->ps.blend[3] >= 1) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (level.m_fade_time_start > 0) {
|
|
alpha = level.m_fade_time / level.m_fade_time_start;
|
|
}
|
|
|
|
if (level.m_fade_type == fadeout) {
|
|
alpha = 1.0f - alpha;
|
|
}
|
|
|
|
if (alpha < 0) {
|
|
alpha = 0;
|
|
}
|
|
|
|
if (alpha > 1) {
|
|
alpha = 1;
|
|
}
|
|
|
|
if (level.m_fade_style == additive) {
|
|
client->ps.blend[0] = level.m_fade_color[0] * level.m_fade_alpha * alpha;
|
|
client->ps.blend[1] = level.m_fade_color[1] * level.m_fade_alpha * alpha;
|
|
client->ps.blend[2] = level.m_fade_color[2] * level.m_fade_alpha * alpha;
|
|
client->ps.blend[3] = level.m_fade_alpha * alpha;
|
|
client->ps.stats[STAT_ADDFADE] = 1;
|
|
} else {
|
|
client->ps.blend[0] = level.m_fade_color[0];
|
|
client->ps.blend[1] = level.m_fade_color[1];
|
|
client->ps.blend[2] = level.m_fade_color[2];
|
|
client->ps.blend[3] = level.m_fade_alpha * alpha;
|
|
client->ps.stats[STAT_ADDFADE] = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
P_DamageFeedback
|
|
|
|
Handles color blends and view kicks
|
|
===============
|
|
*/
|
|
|
|
void Player::DamageFeedback(void)
|
|
{
|
|
float realcount;
|
|
float count;
|
|
vec3_t vDir;
|
|
str painAnim;
|
|
int animnum;
|
|
|
|
// if we are dead, don't setup any feedback
|
|
if (IsDead()) {
|
|
damage_count = 0;
|
|
damage_blood = 0;
|
|
damage_alpha = 0;
|
|
VectorClear(damage_angles);
|
|
return;
|
|
}
|
|
|
|
if (damage_count) {
|
|
// decay damage_count over time
|
|
damage_count *= 0.8f;
|
|
damage_from *= 0.8f;
|
|
damage_angles *= 0.8f;
|
|
if (damage_count < 0.1f) {
|
|
damage_count = 0;
|
|
damage_from = Vector(0, 0, 0);
|
|
}
|
|
}
|
|
|
|
// total points of damage shot at the player this frame
|
|
if (!damage_blood) {
|
|
// didn't take any damage
|
|
return;
|
|
}
|
|
|
|
VectorNormalize2(damage_from, vDir);
|
|
|
|
damage_angles.x -=
|
|
DotProduct(vDir, orientation[0]) * damage_blood * g_viewkick_pitch->value * g_viewkick_dmmult->value;
|
|
damage_angles.x = Q_clamp_float(damage_angles.x, -30, 30);
|
|
|
|
damage_angles.y -=
|
|
DotProduct(vDir, orientation[1]) * damage_blood * g_viewkick_yaw->value * g_viewkick_dmmult->value;
|
|
damage_angles.y = Q_clamp_float(damage_angles.y, -30, 30);
|
|
|
|
damage_angles.z +=
|
|
DotProduct(vDir, orientation[2]) * damage_blood * g_viewkick_roll->value * g_viewkick_dmmult->value;
|
|
damage_angles.z = Q_clamp_float(damage_angles.y, -25, 25);
|
|
|
|
damage_count += damage_blood;
|
|
count = damage_blood;
|
|
realcount = count;
|
|
if (count < 10) {
|
|
// always make a visible effect
|
|
count = 10;
|
|
}
|
|
|
|
// the total alpha of the blend is always proportional to count
|
|
if (damage_alpha < 0) {
|
|
damage_alpha = 0;
|
|
}
|
|
|
|
damage_alpha += count * 0.001;
|
|
if (damage_alpha < 0.2f) {
|
|
damage_alpha = 0.2f;
|
|
}
|
|
if (damage_alpha > 0.6f) {
|
|
// don't go too saturated
|
|
damage_alpha = 0.6f;
|
|
}
|
|
|
|
// the color of the blend will vary based on how much was absorbed
|
|
// by different armors
|
|
damage_blend = vec_zero;
|
|
if (damage_blood) {
|
|
damage_blend += (damage_blood / realcount) * bcolor;
|
|
}
|
|
|
|
if (g_target_game >= target_game_e::TG_MOHTA) {
|
|
//
|
|
// Since 2.0: Try to find and play pain animation
|
|
//
|
|
if (getMoveType() == MOVETYPE_PORTABLE_TURRET) {
|
|
// use mg42 pain animation
|
|
painAnim = "mg42_tripod_";
|
|
} else {
|
|
Weapon *pWeap;
|
|
const char *itemName;
|
|
// try to find an animation
|
|
|
|
pWeap = GetActiveWeapon(WEAPON_MAIN);
|
|
if (pWeap) {
|
|
int weapon_class;
|
|
|
|
weapon_class = pWeap->GetWeaponClass();
|
|
if (weapon_class & WEAPON_CLASS_PISTOL) {
|
|
painAnim = "pistol_";
|
|
} else if (weapon_class & WEAPON_CLASS_RIFLE) {
|
|
painAnim = "rifle_";
|
|
} else if (weapon_class & WEAPON_CLASS_SMG) {
|
|
// get the animation name from the item name
|
|
itemName = pWeap->GetItemName();
|
|
|
|
if (!Q_stricmp(itemName, "MP40")) {
|
|
painAnim = "mp40_";
|
|
} else if (!Q_stricmp(itemName, "Sten Mark II")) {
|
|
painAnim = "sten_";
|
|
} else {
|
|
painAnim = "smg_";
|
|
}
|
|
} else if (weapon_class & WEAPON_CLASS_MG) {
|
|
itemName = pWeap->GetItemName();
|
|
|
|
if (!Q_stricmp(itemName, "StG 44")) {
|
|
painAnim = "mp44_";
|
|
} else {
|
|
painAnim = "mg_";
|
|
}
|
|
} else if (weapon_class & WEAPON_CLASS_GRENADE) {
|
|
itemName = pWeap->GetItemName();
|
|
|
|
// 2.30: use landmine animations
|
|
if (!Q_stricmp(itemName, "Minedetector")) {
|
|
painAnim = "minedetector_";
|
|
} else if (!Q_stricmp(itemName, "Minensuchgerat")) {
|
|
painAnim = "minedetectoraxis_";
|
|
} else if (!Q_stricmp(itemName, "LandmineAllies")) {
|
|
painAnim = "mine_";
|
|
} else if (!Q_stricmp(itemName, "LandmineAxis")) {
|
|
painAnim = "mine_";
|
|
} else if (!Q_stricmp(itemName, "LandmineAxis")) {
|
|
painAnim = "grenade_";
|
|
}
|
|
} else if (weapon_class & WEAPON_CLASS_HEAVY) {
|
|
itemName = pWeap->GetItemName();
|
|
|
|
if (!Q_stricmp(itemName, "Shotgun")) {
|
|
painAnim = "shotgun_";
|
|
} else {
|
|
// Defaults to bazooka
|
|
painAnim = "bazooka_";
|
|
}
|
|
} else {
|
|
itemName = pWeap->GetItemName();
|
|
|
|
if (!Q_stricmp(itemName, "Packed MG42 Turret")) {
|
|
painAnim = "mg42_";
|
|
} else {
|
|
// Default animation if not found
|
|
painAnim = "unarmed_";
|
|
}
|
|
}
|
|
} else {
|
|
painAnim = "unarmed_";
|
|
}
|
|
|
|
// use the animation based on the movement
|
|
if (m_iMovePosFlags & MPF_POSITION_CROUCHING) {
|
|
painAnim += "crouch_";
|
|
} else {
|
|
painAnim += "stand_";
|
|
}
|
|
}
|
|
|
|
painAnim += "hit_";
|
|
|
|
if (pain_dir == PAIN_REAR || pain_location == HITLOC_TORSO_MID || HITLOC_TORSO_LOWER) {
|
|
painAnim += "back";
|
|
} else {
|
|
switch (pain_location) {
|
|
case HITLOC_HEAD:
|
|
case HITLOC_HELMET:
|
|
case HITLOC_NECK:
|
|
painAnim += "head";
|
|
break;
|
|
case HITLOC_TORSO_UPPER:
|
|
case HITLOC_TORSO_MID:
|
|
painAnim += "uppertorso";
|
|
break;
|
|
case HITLOC_TORSO_LOWER:
|
|
case HITLOC_PELVIS:
|
|
painAnim += "lowertorso";
|
|
break;
|
|
case HITLOC_R_ARM_UPPER:
|
|
case HITLOC_R_ARM_LOWER:
|
|
case HITLOC_R_HAND:
|
|
painAnim += "rarm";
|
|
break;
|
|
case HITLOC_L_ARM_UPPER:
|
|
case HITLOC_L_ARM_LOWER:
|
|
case HITLOC_L_HAND:
|
|
painAnim += "larm";
|
|
break;
|
|
case HITLOC_R_LEG_UPPER:
|
|
case HITLOC_L_LEG_UPPER:
|
|
case HITLOC_R_LEG_LOWER:
|
|
case HITLOC_L_LEG_LOWER:
|
|
case HITLOC_R_FOOT:
|
|
case HITLOC_L_FOOT:
|
|
painAnim += "leg";
|
|
break;
|
|
default:
|
|
painAnim += "uppertorso";
|
|
break;
|
|
}
|
|
}
|
|
|
|
animnum = gi.Anim_NumForName(edict->tiki, painAnim.c_str());
|
|
if (animnum == -1) {
|
|
gi.DPrintf("WARNING: Could not find player pain animation '%s'\n", painAnim.c_str());
|
|
} else {
|
|
NewAnim(animnum, EV_Player_AnimLoop_Pain, ANIMSLOT_PAIN);
|
|
RestartAnimSlot(ANIMSLOT_PAIN);
|
|
m_sPainAnim = painAnim;
|
|
m_fPainBlend = 1.f;
|
|
animdone_Pain = false;
|
|
}
|
|
}
|
|
|
|
//
|
|
// clear totals
|
|
//
|
|
damage_blood = 0;
|
|
|
|
if (g_target_game >= target_game_e::TG_MOHTA) {
|
|
//
|
|
// Added in 2.0
|
|
// No more damage angles since MOHTA
|
|
//
|
|
|
|
if (IsSubclassOfPlayer()) {
|
|
damage_count = 0;
|
|
damage_blood = 0;
|
|
damage_alpha = 0;
|
|
damage_angles = vec_zero;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::GetPlayerView(Vector *pos, Vector *angle)
|
|
{
|
|
if (pos) {
|
|
*pos = origin;
|
|
pos->z += viewheight;
|
|
}
|
|
|
|
if (angle) {
|
|
*angle = Vector(client->ps.viewangles);
|
|
}
|
|
}
|
|
|
|
void Player::SetPlayerView(
|
|
Camera *camera, Vector position, float cameraoffset, Vector ang, Vector vel, float camerablend[4], float camerafov
|
|
)
|
|
{
|
|
VectorCopy(ang, client->ps.viewangles);
|
|
client->ps.viewheight = cameraoffset;
|
|
|
|
VectorCopy(position, client->ps.origin);
|
|
VectorCopy(vel, client->ps.velocity);
|
|
|
|
/*
|
|
client->ps.blend[ 0 ] = camerablend[ 0 ];
|
|
client->ps.blend[ 1 ] = camerablend[ 1 ];
|
|
client->ps.blend[ 2 ] = camerablend[ 2 ];
|
|
client->ps.blend[ 3 ] = camerablend[ 3 ];
|
|
*/
|
|
|
|
client->ps.fov = camerafov;
|
|
|
|
if (camera) {
|
|
if (camera->IsSubclassOfCamera()) {
|
|
VectorCopy(camera->angles, client->ps.camera_angles);
|
|
VectorCopy(camera->origin, client->ps.camera_origin);
|
|
|
|
Vector vOfs = camera->GetPositionOffset();
|
|
VectorCopy(vOfs, client->ps.camera_posofs);
|
|
|
|
client->ps.pm_flags |= PMF_CAMERA_VIEW;
|
|
|
|
if (camera->ShowQuakes()) {
|
|
client->ps.pm_flags |= PMF_DAMAGE_ANGLES;
|
|
} else {
|
|
client->ps.pm_flags &= ~PMF_DAMAGE_ANGLES;
|
|
}
|
|
|
|
//
|
|
// clear out the flags, but preserve the CF_CAMERA_CUT_BIT
|
|
//
|
|
client->ps.camera_flags = client->ps.camera_flags & CF_CAMERA_CUT_BIT;
|
|
} else {
|
|
Vector vVec;
|
|
|
|
if (camera->IsSubclassOfPlayer()) {
|
|
Vector vPos;
|
|
Player *pPlayer = (Player *)camera;
|
|
|
|
GetSpectateFollowOrientation(pPlayer, vPos, vVec);
|
|
|
|
VectorCopy(vVec, client->ps.camera_angles);
|
|
VectorCopy(vPos, client->ps.camera_origin);
|
|
|
|
SetViewAngles(vVec);
|
|
|
|
vPos[2] -= viewheight;
|
|
setOrigin(vPos);
|
|
|
|
vVec.setXYZ(0, 0, 0);
|
|
} else {
|
|
VectorCopy(ang, client->ps.camera_angles);
|
|
VectorCopy(position, client->ps.camera_angles);
|
|
|
|
vVec.setXYZ(0, 0, 0);
|
|
}
|
|
|
|
VectorCopy(vVec, client->ps.camera_posofs);
|
|
client->ps.pm_flags |= PMF_CAMERA_VIEW;
|
|
client->ps.camera_flags = client->ps.camera_flags & CF_CAMERA_CUT_BIT;
|
|
}
|
|
} else {
|
|
client->ps.pm_flags &= ~PMF_CAMERA_VIEW;
|
|
//
|
|
// make sure the third person camera is setup correctly.
|
|
//
|
|
|
|
if (getMoveType() != MOVETYPE_NOCLIP) {
|
|
qboolean do_cut;
|
|
int camera_type;
|
|
|
|
if (currentState_Torso) {
|
|
camera_type = currentState_Torso->getCameraType();
|
|
} else {
|
|
camera_type = CAMERA_BEHIND;
|
|
}
|
|
if (last_camera_type != camera_type) {
|
|
//
|
|
// clear out the flags, but preserve the CF_CAMERA_CUT_BIT
|
|
//
|
|
client->ps.camera_flags = client->ps.camera_flags & CF_CAMERA_CUT_BIT;
|
|
do_cut = qtrue;
|
|
switch (camera_type) {
|
|
case CAMERA_TOPDOWN:
|
|
client->ps.camera_flags |= CF_CAMERA_ANGLES_IGNORE_PITCH;
|
|
client->ps.camera_offset[PITCH] = -75;
|
|
client->ps.camera_flags |= CF_CAMERA_ANGLES_ALLOWOFFSET;
|
|
do_cut = qfalse;
|
|
break;
|
|
case CAMERA_FRONT:
|
|
client->ps.camera_flags |= CF_CAMERA_ANGLES_IGNORE_PITCH;
|
|
client->ps.camera_flags |= CF_CAMERA_ANGLES_ALLOWOFFSET;
|
|
client->ps.camera_offset[YAW] = 180;
|
|
client->ps.camera_offset[PITCH] = 0;
|
|
break;
|
|
case CAMERA_SIDE:
|
|
client->ps.camera_flags |= CF_CAMERA_ANGLES_IGNORE_PITCH;
|
|
client->ps.camera_flags |= CF_CAMERA_ANGLES_ALLOWOFFSET;
|
|
// randomly invert the YAW
|
|
if (G_Random(1) > 0.5f) {
|
|
client->ps.camera_offset[YAW] = -90;
|
|
} else {
|
|
client->ps.camera_offset[YAW] = 90;
|
|
}
|
|
client->ps.camera_offset[PITCH] = 0;
|
|
break;
|
|
case CAMERA_SIDE_LEFT:
|
|
client->ps.camera_flags |= CF_CAMERA_ANGLES_IGNORE_PITCH;
|
|
client->ps.camera_flags |= CF_CAMERA_ANGLES_ALLOWOFFSET;
|
|
client->ps.camera_offset[YAW] = 90;
|
|
client->ps.camera_offset[PITCH] = 0;
|
|
break;
|
|
case CAMERA_SIDE_RIGHT:
|
|
client->ps.camera_flags |= CF_CAMERA_ANGLES_IGNORE_PITCH;
|
|
client->ps.camera_flags |= CF_CAMERA_ANGLES_ALLOWOFFSET;
|
|
client->ps.camera_offset[YAW] = -90;
|
|
client->ps.camera_offset[PITCH] = 0;
|
|
break;
|
|
case CAMERA_BEHIND_FIXED:
|
|
do_cut = qfalse;
|
|
client->ps.camera_offset[YAW] = 0;
|
|
client->ps.camera_offset[PITCH] = 0;
|
|
client->ps.camera_flags |= CF_CAMERA_ANGLES_ALLOWOFFSET;
|
|
break;
|
|
case CAMERA_BEHIND_NOPITCH:
|
|
do_cut = qfalse;
|
|
client->ps.camera_flags |= CF_CAMERA_ANGLES_IGNORE_PITCH;
|
|
client->ps.camera_offset[YAW] = 0;
|
|
client->ps.camera_offset[PITCH] = 0;
|
|
break;
|
|
case CAMERA_BEHIND:
|
|
do_cut = qfalse;
|
|
client->ps.camera_offset[YAW] = 0;
|
|
client->ps.camera_offset[PITCH] = 0;
|
|
break;
|
|
default:
|
|
do_cut = qfalse;
|
|
client->ps.camera_offset[YAW] = 0;
|
|
client->ps.camera_offset[PITCH] = 0;
|
|
break;
|
|
}
|
|
last_camera_type = camera_type;
|
|
if (do_cut) {
|
|
CameraCut();
|
|
}
|
|
}
|
|
} else {
|
|
client->ps.camera_flags = client->ps.camera_flags & CF_CAMERA_CUT_BIT;
|
|
}
|
|
|
|
//
|
|
// these are explicitly not cleared so that when the client lerps it still has the last
|
|
// camera position for reference. Additionally this causes no extra hits to the network
|
|
// traffic.
|
|
//
|
|
//VectorClear( client->ps.camera_angles );
|
|
//VectorClear( client->ps.camera_origin );
|
|
}
|
|
|
|
#define EARTHQUAKE_SCREENSHAKE_PITCH 2
|
|
#define EARTHQUAKE_SCREENSHAKE_YAW 2
|
|
#define EARTHQUAKE_SCREENSHAKE_ROLL 3
|
|
|
|
if (level.earthquake_magnitude != 0.0f) {
|
|
client->ps.damage_angles[PITCH] = G_CRandom() * level.earthquake_magnitude * EARTHQUAKE_SCREENSHAKE_PITCH;
|
|
client->ps.damage_angles[YAW] = G_CRandom() * level.earthquake_magnitude * EARTHQUAKE_SCREENSHAKE_YAW;
|
|
client->ps.damage_angles[ROLL] = G_CRandom() * level.earthquake_magnitude * EARTHQUAKE_SCREENSHAKE_ROLL;
|
|
} else if (damage_count) {
|
|
client->ps.damage_angles[PITCH] = damage_angles[PITCH];
|
|
client->ps.damage_angles[YAW] = damage_angles[YAW];
|
|
client->ps.damage_angles[ROLL] = damage_angles[ROLL];
|
|
} else {
|
|
VectorClear(client->ps.damage_angles);
|
|
}
|
|
|
|
if (m_vViewVariation != vec_zero) {
|
|
for (int i = 0; i < 3; i++) {
|
|
if (m_vViewVariation[i] == 0.0f) {
|
|
continue;
|
|
}
|
|
|
|
client->ps.damage_angles[i] += G_CRandom() * m_vViewVariation[i];
|
|
|
|
m_vViewVariation[i] = m_vViewVariation[i] - m_vViewVariation[i] * level.frametime * 8.0f;
|
|
|
|
if (m_vViewVariation[i] < 0.01f) {
|
|
m_vViewVariation[i] = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::SetupView(void)
|
|
{
|
|
// if we currently are not in a camera or the camera we are looking through is automatic, evaluate our camera choices
|
|
|
|
if (actor_to_watch || actor_camera) {
|
|
Vector dir;
|
|
Vector watch_angles;
|
|
float dist = 0;
|
|
Vector focal_point;
|
|
Vector left;
|
|
trace_t trace;
|
|
qboolean delete_actor_camera = false;
|
|
Vector camera_mins;
|
|
Vector camera_maxs;
|
|
|
|
if (actor_to_watch) {
|
|
dir = actor_to_watch->origin - origin;
|
|
dist = dir.length();
|
|
}
|
|
|
|
// See if we still want to watch this actor
|
|
|
|
if (!actor_to_watch || dist > 150 || actor_to_watch->deadflag) {
|
|
delete_actor_camera = true;
|
|
} else {
|
|
// Create the camera if we don't have one yet
|
|
|
|
if (!actor_camera) {
|
|
actor_camera = new Camera();
|
|
|
|
if (G_Random() < .5) {
|
|
actor_camera_right = true;
|
|
starting_actor_camera_right = true;
|
|
} else {
|
|
actor_camera_right = false;
|
|
starting_actor_camera_right = false;
|
|
}
|
|
}
|
|
|
|
// Setup the new position of the actor camera
|
|
|
|
// Go a little above the view height
|
|
|
|
actor_camera->origin = origin;
|
|
actor_camera->origin[2] += DEFAULT_VIEWHEIGHT + 10;
|
|
|
|
// Find the focal point ( either the actor's watch offset or top of the bounding box)
|
|
|
|
if (actor_to_watch->watch_offset != vec_zero) {
|
|
MatrixTransformVector(actor_to_watch->watch_offset, actor_to_watch->orientation, focal_point);
|
|
focal_point += actor_to_watch->origin;
|
|
} else {
|
|
focal_point = actor_to_watch->origin;
|
|
focal_point[2] = actor_to_watch->maxs[2];
|
|
}
|
|
|
|
// Shift the camera back just a little
|
|
|
|
dir = focal_point - actor_camera->origin;
|
|
dir.normalize();
|
|
actor_camera->origin -= dir * 15;
|
|
|
|
// Shift the camera a little to the left or right
|
|
|
|
watch_angles = dir.toAngles();
|
|
watch_angles.AngleVectors(NULL, &left);
|
|
|
|
if (actor_camera_right) {
|
|
actor_camera->origin -= left * 15;
|
|
} else {
|
|
actor_camera->origin += left * 15;
|
|
}
|
|
|
|
// Make sure this camera position is ok
|
|
|
|
camera_mins = "-5 -5 -5";
|
|
camera_maxs = "5 5 5";
|
|
|
|
trace = G_Trace(
|
|
actor_camera->origin,
|
|
camera_mins,
|
|
camera_maxs,
|
|
actor_camera->origin,
|
|
actor_camera,
|
|
MASK_DEADSOLID,
|
|
false,
|
|
"SetupView"
|
|
);
|
|
|
|
if (trace.startsolid) {
|
|
// Try other side
|
|
|
|
if (actor_camera_right == starting_actor_camera_right) {
|
|
if (actor_camera_right) {
|
|
actor_camera->origin += left * 30;
|
|
} else {
|
|
actor_camera->origin -= left * 30;
|
|
}
|
|
|
|
actor_camera_right = !actor_camera_right;
|
|
|
|
trace = G_Trace(
|
|
actor_camera->origin,
|
|
camera_mins,
|
|
camera_maxs,
|
|
actor_camera->origin,
|
|
actor_camera,
|
|
MASK_DEADSOLID,
|
|
false,
|
|
"SetupView2"
|
|
);
|
|
|
|
if (trace.startsolid) {
|
|
// Both spots have failed stop doing actor camera
|
|
delete_actor_camera = true;
|
|
}
|
|
} else {
|
|
// Both spots have failed stop doing actor camera
|
|
delete_actor_camera = true;
|
|
}
|
|
}
|
|
|
|
if (!delete_actor_camera) {
|
|
// Set the camera's position
|
|
|
|
actor_camera->setOrigin(actor_camera->origin);
|
|
|
|
// Set the camera's angles
|
|
|
|
dir = focal_point - actor_camera->origin;
|
|
watch_angles = dir.toAngles();
|
|
actor_camera->setAngles(watch_angles);
|
|
|
|
// Set this as our camera
|
|
|
|
SetCamera(actor_camera, .5);
|
|
}
|
|
}
|
|
|
|
if (delete_actor_camera) {
|
|
// Get rid of this camera
|
|
|
|
actor_to_watch = NULL;
|
|
|
|
if (actor_camera) {
|
|
delete actor_camera;
|
|
actor_camera = NULL;
|
|
SetCamera(NULL, .5);
|
|
}
|
|
}
|
|
} else if ((level.automatic_cameras.NumObjects() > 0) && (!camera || camera->IsAutomatic())) {
|
|
int i;
|
|
float score, bestScore;
|
|
Camera *cam, *bestCamera;
|
|
|
|
bestScore = 999;
|
|
bestCamera = NULL;
|
|
for (i = 1; i <= level.automatic_cameras.NumObjects(); i++) {
|
|
cam = level.automatic_cameras.ObjectAt(i);
|
|
score = cam->CalculateScore(this, currentState_Torso->getName());
|
|
// if this is our current camera, scale down the score a bit to favor it.
|
|
if (cam == camera) {
|
|
score *= 0.9f;
|
|
}
|
|
|
|
if (score < bestScore) {
|
|
bestScore = score;
|
|
bestCamera = cam;
|
|
}
|
|
}
|
|
if (bestScore <= 1.0f) {
|
|
// we have a camera to switch to
|
|
if (bestCamera != camera) {
|
|
float time;
|
|
|
|
if (camera) {
|
|
camera->AutomaticStop(this);
|
|
}
|
|
time = bestCamera->AutomaticStart(this);
|
|
SetCamera(bestCamera, time);
|
|
}
|
|
} else {
|
|
// we don't have a camera to switch to
|
|
if (camera) {
|
|
float time;
|
|
|
|
time = camera->AutomaticStop(this);
|
|
SetCamera(NULL, time);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If there is no camera, use the player's view
|
|
if (!camera) {
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER && IsSpectator() && m_iPlayerSpectating != 0) {
|
|
gentity_t *ent = g_entities + m_iPlayerSpectating - 1;
|
|
|
|
if (ent->inuse && ent->entity && ent->entity->deadflag <= DEAD_DYING) {
|
|
Player *m_player = (Player *)ent->entity;
|
|
Vector vAngles;
|
|
|
|
m_player->GetPlayerView(NULL, &vAngles);
|
|
|
|
SetPlayerView(
|
|
(Camera *)m_player,
|
|
m_player->origin,
|
|
m_player->viewheight,
|
|
vAngles,
|
|
m_player->velocity,
|
|
blend,
|
|
m_player->fov
|
|
);
|
|
} else {
|
|
SetPlayerView(NULL, origin, viewheight, v_angle, velocity, blend, fov);
|
|
}
|
|
} else {
|
|
SetPlayerView(NULL, origin, viewheight, v_angle, velocity, blend, fov);
|
|
}
|
|
} else {
|
|
SetPlayerView(camera, origin, viewheight, v_angle, velocity, blend, camera->Fov());
|
|
}
|
|
}
|
|
|
|
Vector Player::GetAngleToTarget(Entity *ent, str tag, float yawclamp, float pitchclamp, Vector baseangles)
|
|
{
|
|
assert(ent);
|
|
|
|
if (ent) {
|
|
Vector delta, angs;
|
|
orientation_t tag_or;
|
|
|
|
int tagnum = gi.Tag_NumForName(edict->tiki, tag.c_str());
|
|
|
|
if (tagnum < 0) {
|
|
return Vector(0, 0, 0);
|
|
}
|
|
|
|
GetTagPositionAndOrientation(tagnum, &tag_or);
|
|
|
|
delta = ent->centroid - tag_or.origin;
|
|
delta.normalize();
|
|
|
|
angs = delta.toAngles();
|
|
|
|
AnglesSubtract(angs, baseangles, angs);
|
|
|
|
angs[PITCH] = AngleNormalize180(angs[PITCH]);
|
|
angs[YAW] = AngleNormalize180(angs[YAW]);
|
|
|
|
if (angs[PITCH] > pitchclamp) {
|
|
angs[PITCH] = pitchclamp;
|
|
} else if (angs[PITCH] < -pitchclamp) {
|
|
angs[PITCH] = -pitchclamp;
|
|
}
|
|
|
|
if (angs[YAW] > yawclamp) {
|
|
angs[YAW] = yawclamp;
|
|
} else if (angs[YAW] < -yawclamp) {
|
|
angs[YAW] = -yawclamp;
|
|
}
|
|
|
|
return angs;
|
|
} else {
|
|
return Vector(0, 0, 0);
|
|
}
|
|
}
|
|
|
|
void Player::DebugWeaponTags(int controller_tag, Weapon *weapon, str weapon_tagname)
|
|
{
|
|
int i;
|
|
orientation_t bone_or, tag_weapon_or, barrel_or, final_barrel_or;
|
|
|
|
GetTagPositionAndOrientation(edict->s.bone_tag[controller_tag], &bone_or);
|
|
//G_DrawCoordSystem( Vector( bone_or.origin ), Vector( bone_or.axis[0] ), Vector( bone_or.axis[1] ), Vector( bone_or.axis[2] ), 20 );
|
|
|
|
GetTagPositionAndOrientation(gi.Tag_NumForName(edict->tiki, weapon_tagname), &tag_weapon_or);
|
|
//G_DrawCoordSystem( Vector( tag_weapon_or.origin ), Vector( tag_weapon_or.axis[0] ), Vector( tag_weapon_or.axis[1] ), Vector( tag_weapon_or.axis[2] ), 40 );
|
|
|
|
weapon->GetRawTag("tag_barrel", &barrel_or);
|
|
VectorCopy(tag_weapon_or.origin, final_barrel_or.origin);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
VectorMA(final_barrel_or.origin, barrel_or.origin[i], tag_weapon_or.axis[i], final_barrel_or.origin);
|
|
}
|
|
|
|
MatrixMultiply(barrel_or.axis, tag_weapon_or.axis, final_barrel_or.axis);
|
|
//G_DrawCoordSystem( Vector( final_barrel_or.origin ), Vector( final_barrel_or.axis[0] ), Vector( final_barrel_or.axis[1] ), Vector( final_barrel_or.axis[2] ), 80 );
|
|
|
|
#if 0
|
|
if ( g_crosshair->integer )
|
|
{
|
|
trace_t trace;
|
|
Vector start,end,ang,dir,delta;
|
|
vec3_t mat[3];
|
|
|
|
AnglesToAxis( v_angle, mat );
|
|
|
|
dir = mat[0];
|
|
start = final_barrel_or.origin;
|
|
end = start + ( dir * MAX_MAP_BOUNDS );
|
|
|
|
G_DrawCoordSystem( start, Vector( mat[0] ), Vector( mat[1] ), Vector( mat[2] ), 80 );
|
|
|
|
trace = G_Trace( start, vec_zero, vec_zero, end, this, MASK_PROJECTILE|MASK_WATER, qfalse, "Crosshair" );
|
|
crosshair->setOrigin( trace.endpos );
|
|
|
|
delta = trace.endpos - start;
|
|
float length = delta.length();
|
|
float scale = g_crosshair_maxscale->value * length / MAX_MAP_BOUNDS;
|
|
|
|
if ( scale < 1 )
|
|
scale = 1;
|
|
|
|
crosshair->setScale( scale );
|
|
|
|
if ( trace.ent )
|
|
{
|
|
vectoangles( trace.plane.normal, ang );
|
|
}
|
|
else
|
|
{
|
|
vectoangles( dir, ang );
|
|
}
|
|
|
|
crosshair->setAngles( ang );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Player::AcquireTarget(void) {}
|
|
|
|
void Player::RemoveTarget(Entity *ent_to_remove) {}
|
|
|
|
void Player::AutoAim(void) {}
|
|
|
|
/*
|
|
===============
|
|
PlayerAngles
|
|
===============
|
|
*/
|
|
void Player::PlayerAngles(void)
|
|
{
|
|
if (getMoveType() == MOVETYPE_PORTABLE_TURRET) {
|
|
PortableTurret *portableTurret = static_cast<PortableTurret *>(m_pTurret.Pointer());
|
|
angles[0] = portableTurret->GetGroundPitch();
|
|
angles[1] = portableTurret->GetStartYaw();
|
|
}
|
|
|
|
PmoveAdjustAngleSettings(v_angle, angles, &client->ps, &edict->s);
|
|
|
|
SetViewAngles(v_angle);
|
|
setAngles(angles);
|
|
}
|
|
|
|
void Player::FinishMove(void)
|
|
{
|
|
//
|
|
// If the origin or velocity have changed since ClientThink(),
|
|
// update the pmove values. This will happen when the client
|
|
// is pushed by a bmodel or kicked by an explosion.
|
|
//
|
|
// If it wasn't updated here, the view position would lag a frame
|
|
// behind the body position when pushed -- "sinking into plats",
|
|
//
|
|
if (!(client->ps.pm_flags & PMF_FROZEN) && !(client->ps.pm_flags & PMF_NO_MOVE)) {
|
|
origin.copyTo(client->ps.origin);
|
|
velocity.copyTo(client->ps.velocity);
|
|
}
|
|
|
|
// This check is in mohaa but the animation will look bad
|
|
if (!(client->ps.pm_flags & PMF_FROZEN)) {
|
|
PlayerAngles();
|
|
AdjustAnimBlends();
|
|
}
|
|
|
|
// burn from lava, etc
|
|
WorldEffects();
|
|
|
|
// determine the view offsets
|
|
DamageFeedback();
|
|
CalcBlend();
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER && g_smoothClients->integer) {
|
|
VectorCopy(client->ps.velocity, edict->s.pos.trDelta);
|
|
edict->s.pos.trTime = client->ps.commandTime;
|
|
} else {
|
|
VectorClear(edict->s.pos.trDelta);
|
|
edict->s.pos.trTime = 0;
|
|
}
|
|
}
|
|
|
|
void Player::CopyStats(Player *player)
|
|
{
|
|
gentity_t *ent;
|
|
int i;
|
|
|
|
origin = player->origin;
|
|
SetViewAngles(player->GetViewAngles());
|
|
|
|
client->ps.bobCycle = player->client->ps.bobCycle;
|
|
|
|
client->ps.pm_flags |=
|
|
player->client->ps.pm_flags & (PMF_DUCKED | PMF_VIEW_DUCK_RUN | PMF_VIEW_JUMP_START | PMF_VIEW_PRONE);
|
|
|
|
memcpy(&client->ps.stats, &player->client->ps.stats, sizeof(client->ps.stats));
|
|
memcpy(&client->ps.activeItems, &player->client->ps.activeItems, sizeof(client->ps.activeItems));
|
|
memcpy(&client->ps.ammo_name_index, &player->client->ps.ammo_name_index, sizeof(client->ps.ammo_name_index));
|
|
memcpy(&client->ps.ammo_amount, &player->client->ps.ammo_amount, sizeof(client->ps.ammo_amount));
|
|
memcpy(&client->ps.max_ammo_amount, &player->client->ps.max_ammo_amount, sizeof(client->ps.max_ammo_amount));
|
|
|
|
VectorCopy(player->client->ps.origin, client->ps.origin);
|
|
VectorCopy(player->client->ps.velocity, client->ps.velocity);
|
|
|
|
client->ps.iViewModelAnim = player->client->ps.iViewModelAnim;
|
|
client->ps.iViewModelAnimChanged = player->client->ps.iViewModelAnimChanged;
|
|
|
|
client->ps.gravity = player->client->ps.gravity;
|
|
client->ps.speed = player->client->ps.speed;
|
|
|
|
// copy angles
|
|
memcpy(&client->ps.delta_angles, &player->client->ps.delta_angles, sizeof(client->ps.delta_angles));
|
|
|
|
memcpy(&client->ps.blend, &player->client->ps.blend, sizeof(client->ps.blend));
|
|
memcpy(&client->ps.damage_angles, &player->client->ps.damage_angles, sizeof(client->ps.damage_angles));
|
|
memcpy(&client->ps.viewangles, &player->client->ps.viewangles, sizeof(client->ps.delta_angles));
|
|
|
|
// copy camera stuff
|
|
//memcpy( &client->ps.camera_origin, &player->client->ps.camera_origin, sizeof( client->ps.camera_origin ) );
|
|
//memcpy( &client->ps.camera_angles, &player->client->ps.camera_angles, sizeof( client->ps.camera_angles ) );
|
|
//memcpy( &client->ps.camera_offset, &player->client->ps.camera_offset, sizeof( client->ps.camera_offset ) );
|
|
//memcpy( &client->ps.camera_posofs, &player->client->ps.camera_posofs, sizeof( client->ps.camera_posofs ) );
|
|
//client->ps.camera_time = player->client->ps.camera_time;
|
|
//client->ps.camera_flags = player->client->ps.camera_flags;
|
|
|
|
client->ps.fLeanAngle = player->client->ps.fLeanAngle;
|
|
client->ps.fov = player->client->ps.fov;
|
|
|
|
client->ps.viewheight = player->client->ps.viewheight;
|
|
client->ps.walking = player->client->ps.walking;
|
|
client->ps.groundPlane = player->client->ps.groundPlane;
|
|
client->ps.groundEntityNum = player->client->ps.groundEntityNum;
|
|
memcpy(&client->ps.groundTrace, &player->client->ps.groundTrace, sizeof(trace_t));
|
|
|
|
edict->s.eFlags &= ~EF_UNARMED;
|
|
edict->r.svFlags &= ~SVF_NOCLIENT;
|
|
edict->s.renderfx &= ~RF_DONTDRAW;
|
|
|
|
player->edict->r.svFlags |= SVF_NOTSINGLECLIENT;
|
|
player->edict->r.singleClient = client->ps.clientNum;
|
|
|
|
edict->r.svFlags |= SVF_SINGLECLIENT;
|
|
edict->r.singleClient = client->ps.clientNum;
|
|
|
|
client->ps.pm_flags |= PMF_FROZEN | PMF_NO_MOVE | PMF_NO_PREDICTION;
|
|
|
|
memcpy(&edict->s.frameInfo, &player->edict->s.frameInfo, sizeof(edict->s.frameInfo));
|
|
|
|
DetachAllChildren(NULL);
|
|
|
|
for (i = 0; i < MAX_MODEL_CHILDREN; i++) {
|
|
Entity *dest;
|
|
|
|
if (player->children[i] == ENTITYNUM_NONE) {
|
|
continue;
|
|
}
|
|
|
|
ent = g_entities + player->children[i];
|
|
|
|
if (!ent->inuse || !ent->entity) {
|
|
continue;
|
|
}
|
|
|
|
dest = new Entity;
|
|
|
|
CloneEntity(dest, ent->entity);
|
|
|
|
dest->edict->s.modelindex = ent->entity->edict->s.modelindex;
|
|
dest->edict->tiki = ent->entity->edict->tiki;
|
|
dest->edict->s.actionWeight = ent->entity->edict->s.actionWeight;
|
|
memcpy(&dest->edict->s.frameInfo, &ent->entity->edict->s.frameInfo, sizeof(dest->edict->s.frameInfo));
|
|
dest->CancelPendingEvents();
|
|
dest->attach(entnum, ent->entity->edict->s.tag_num);
|
|
|
|
dest->PostEvent(EV_DetachAllChildren, level.frametime);
|
|
}
|
|
}
|
|
|
|
void Player::UpdateStats(void)
|
|
{
|
|
int i, count;
|
|
Vector vObjectiveLocation;
|
|
float healthfrac;
|
|
float healfrac;
|
|
|
|
//
|
|
// Health
|
|
//
|
|
|
|
if (g_spectatefollow_firstperson->integer && IsSpectator() && m_iPlayerSpectating != 0) {
|
|
//
|
|
// Added in OPM
|
|
// First-person spectate
|
|
//
|
|
gentity_t *ent = g_entities + (m_iPlayerSpectating - 1);
|
|
|
|
if (ent->inuse && ent->entity && ent->entity->deadflag <= DEAD_DYING) {
|
|
CopyStats((Player *)ent->entity);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
client->ps.stats[STAT_TEAM] = TEAM_ALLIES;
|
|
client->ps.stats[STAT_KILLS] = 0;
|
|
client->ps.stats[STAT_DEATHS] = 0;
|
|
client->ps.stats[STAT_HIGHEST_SCORE] = 0;
|
|
client->ps.stats[STAT_ATTACKERCLIENT] = -1;
|
|
client->ps.stats[STAT_INFOCLIENT] = -1;
|
|
client->ps.stats[STAT_INFOCLIENT_HEALTH] = 0;
|
|
|
|
vObjectiveLocation = level.m_vObjectiveLocation;
|
|
} else {
|
|
client->ps.stats[STAT_TEAM] = dm_team;
|
|
|
|
if (g_gametype->integer >= GT_TEAM && current_team != NULL) {
|
|
client->ps.stats[STAT_KILLS] = current_team->m_teamwins;
|
|
client->ps.stats[STAT_DEATHS] = current_team->m_iDeaths;
|
|
} else {
|
|
client->ps.stats[STAT_KILLS] = num_kills;
|
|
client->ps.stats[STAT_DEATHS] = num_deaths;
|
|
}
|
|
|
|
if (g_gametype->integer < GT_TEAM) {
|
|
gentity_t *ent;
|
|
int i;
|
|
int bestKills = -9999;
|
|
|
|
// Get the best player
|
|
for (i = 0, ent = g_entities; i < game.maxclients; i++, ent++) {
|
|
if (!ent->inuse || !ent->client || !ent->entity) {
|
|
continue;
|
|
}
|
|
|
|
Player *p = (Player *)ent->entity;
|
|
if (p->GetNumKills() > bestKills) {
|
|
bestKills = p->GetNumKills();
|
|
}
|
|
}
|
|
|
|
client->ps.stats[STAT_HIGHEST_SCORE] = bestKills;
|
|
} else {
|
|
if (dmManager.GetTeamAxis()->m_teamwins > dmManager.GetTeamAllies()->m_teamwins) {
|
|
client->ps.stats[STAT_HIGHEST_SCORE] = dmManager.GetTeamAxis()->m_teamwins;
|
|
} else {
|
|
client->ps.stats[STAT_HIGHEST_SCORE] = dmManager.GetTeamAllies()->m_teamwins;
|
|
}
|
|
}
|
|
|
|
if (!pAttackerDistPointer) {
|
|
client->ps.stats[STAT_ATTACKERCLIENT] = -1;
|
|
} else if (fAttackerDispTime <= level.time && deadflag == DEAD_NO) {
|
|
pAttackerDistPointer = NULL;
|
|
client->ps.stats[STAT_ATTACKERCLIENT] = -1;
|
|
} else {
|
|
client->ps.stats[STAT_ATTACKERCLIENT] = pAttackerDistPointer->edict - g_entities;
|
|
}
|
|
|
|
client->ps.stats[STAT_INFOCLIENT] = -1;
|
|
client->ps.stats[STAT_INFOCLIENT_HEALTH] = 0;
|
|
|
|
if (IsSpectator() || g_gametype->integer >= GT_TEAM) {
|
|
if (m_iPlayerSpectating && IsSpectator()) {
|
|
gentity_t *ent = g_entities + (m_iPlayerSpectating - 1);
|
|
|
|
if (ent->inuse && ent->entity && deadflag < DEAD_DEAD) {
|
|
m_iInfoClient = ent - g_entities;
|
|
m_iInfoClientHealth = ent->entity->health;
|
|
m_fInfoClientTime = level.time;
|
|
|
|
float percent = ent->entity->health / ent->entity->max_health * 100.0f;
|
|
|
|
if (percent > 0.0f && percent < 1.0f) {
|
|
percent = 1.0f;
|
|
}
|
|
|
|
client->ps.stats[STAT_INFOCLIENT_HEALTH] = percent;
|
|
}
|
|
} else {
|
|
Vector vForward;
|
|
trace_t trace;
|
|
|
|
AngleVectors(m_vViewAng, vForward, NULL, NULL);
|
|
|
|
Vector vEnd = m_vViewPos + vForward * 2048.0f;
|
|
|
|
trace = G_Trace(m_vViewPos, vec_zero, vec_zero, vEnd, this, MASK_BEAM, qfalse, "infoclientcheck");
|
|
|
|
if (trace.ent && trace.ent->entity->IsSubclassOfPlayer() && !(trace.ent->r.svFlags & SVF_NOCLIENT)) {
|
|
Player *p = static_cast<Player *>(trace.ent->entity);
|
|
|
|
if (IsSpectator() || p->GetTeam() == GetTeam()) {
|
|
m_iInfoClient = trace.ent - g_entities;
|
|
m_iInfoClientHealth = p->health;
|
|
m_fInfoClientTime = level.time;
|
|
|
|
float percent = trace.ent->entity->health / trace.ent->entity->max_health * 100.0f;
|
|
|
|
if (percent > 0.0f && percent < 1.0f) {
|
|
percent = 1.0f;
|
|
}
|
|
|
|
client->ps.stats[STAT_INFOCLIENT_HEALTH] = percent;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_iInfoClient != -1) {
|
|
if (level.time <= m_fInfoClientTime + 1.5f) {
|
|
client->ps.stats[STAT_INFOCLIENT] = m_iInfoClient;
|
|
client->ps.stats[STAT_INFOCLIENT_HEALTH] = m_iInfoClientHealth;
|
|
} else {
|
|
m_iInfoClient = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_gametype->integer >= GT_TOW || level.m_bForceTeamObjectiveLocation) {
|
|
if (GetTeam() == TEAM_AXIS) {
|
|
vObjectiveLocation = level.m_vAxisObjectiveLocation;
|
|
} else if (GetTeam() == TEAM_ALLIES) {
|
|
vObjectiveLocation = level.m_vAlliedObjectiveLocation;
|
|
}
|
|
} else {
|
|
vObjectiveLocation = level.m_vObjectiveLocation;
|
|
}
|
|
|
|
if (g_protocol < protocol_e::PROTOCOL_MOHTA_MIN && vObjectiveLocation == vec_zero) {
|
|
//
|
|
// try to use the nearest teammate instead.
|
|
// the reason is that mohaa 1.11 and below doesn't have a radar
|
|
// for teammates
|
|
//
|
|
if (g_gametype->integer > GT_FFA && !IsDead() && !IsSpectator()) {
|
|
gentity_t *ent;
|
|
int i;
|
|
Player *p;
|
|
float fNearest = 9999.0f;
|
|
float fLength;
|
|
|
|
// match the compass direction to the nearest player
|
|
for (i = 0, ent = g_entities; i < game.maxclients; i++, ent++) {
|
|
if (!ent->inuse || !ent->client || !ent->entity || ent->entity == this) {
|
|
continue;
|
|
}
|
|
|
|
p = (Player *)ent->entity;
|
|
if (p->IsDead() || p->IsSpectator() || p->dm_team != dm_team) {
|
|
continue;
|
|
}
|
|
|
|
fLength = (p->centroid - centroid).length();
|
|
|
|
if (fLength < fNearest) {
|
|
fNearest = fLength;
|
|
vObjectiveLocation = p->centroid;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_pVehicle && !m_pTurret) {
|
|
client->ps.stats[STAT_VEHICLE_HEALTH] = m_pVehicle->health;
|
|
client->ps.stats[STAT_VEHICLE_MAX_HEALTH] = m_pVehicle->max_health;
|
|
}
|
|
|
|
//
|
|
// Health fraction
|
|
//
|
|
healthfrac = (health / max_health * 100.0f);
|
|
|
|
if (m_pVehicle && !m_pTurret) {
|
|
if (!m_pVehicle->isSubclassOf(FixedTurret)) {
|
|
healthfrac = (m_pVehicle->health / m_pVehicle->max_health * 100.f);
|
|
}
|
|
}
|
|
|
|
if (healthfrac < 1 && healthfrac > 0) {
|
|
healthfrac = 1;
|
|
}
|
|
if (healthfrac < 0) {
|
|
healthfrac = 0;
|
|
}
|
|
|
|
client->ps.stats[STAT_HEALTH] = healthfrac;
|
|
|
|
//
|
|
// Healing
|
|
//
|
|
if (m_fHealRate && (!m_pVehicle || m_pTurret || m_pVehicle->isSubclassOf(FixedTurret))) {
|
|
healfrac = (health + m_fHealRate) / max_health * 100.f;
|
|
} else {
|
|
healfrac = 0;
|
|
}
|
|
if (healfrac < 1 && healfrac > 0) {
|
|
healfrac = 1;
|
|
}
|
|
if (healfrac < 0) {
|
|
healfrac = 0;
|
|
}
|
|
|
|
client->ps.stats[STAT_NEXTHEALTH] = healfrac;
|
|
client->ps.stats[STAT_MAXHEALTH] = 100;
|
|
|
|
Weapon *activeweap = GetActiveWeapon(WEAPON_MAIN);
|
|
|
|
client->ps.stats[STAT_WEAPONS] = 0;
|
|
client->ps.stats[STAT_EQUIPPED_WEAPON] = 0;
|
|
client->ps.stats[STAT_AMMO] = 0;
|
|
client->ps.stats[STAT_MAXAMMO] = 0;
|
|
client->ps.stats[STAT_CLIPAMMO] = 0;
|
|
client->ps.stats[STAT_MAXCLIPAMMO] = 0;
|
|
|
|
client->ps.activeItems[ITEM_AMMO] = -1;
|
|
client->ps.activeItems[ITEM_WEAPON] = -1;
|
|
client->ps.activeItems[2] = -1;
|
|
client->ps.activeItems[3] = -1;
|
|
client->ps.activeItems[4] = -1;
|
|
client->ps.activeItems[5] = -1;
|
|
|
|
if (m_pTurret) {
|
|
client->ps.activeItems[ITEM_WEAPON] = m_pTurret->getIndex();
|
|
if (getMoveType() == MOVETYPE_PORTABLE_TURRET || getMoveType() == MOVETYPE_TURRET) {
|
|
// Use the turret's ammo
|
|
client->ps.stats[STAT_CLIPAMMO] = m_pTurret->ammo_in_clip[FIRE_PRIMARY];
|
|
client->ps.stats[STAT_MAXCLIPAMMO] = m_pTurret->ammo_clip_size[FIRE_PRIMARY];
|
|
}
|
|
} else if (activeweap) {
|
|
if (activeweap->m_bSecondaryAmmoInHud) {
|
|
client->ps.stats[STAT_AMMO] = AmmoCount(activeweap->GetAmmoType(FIRE_SECONDARY));
|
|
client->ps.stats[STAT_MAXAMMO] = MaxAmmoCount(activeweap->GetAmmoType(FIRE_SECONDARY));
|
|
client->ps.stats[STAT_SECONDARY_AMMO] = AmmoCount(activeweap->GetAmmoType(FIRE_PRIMARY));
|
|
} else {
|
|
client->ps.stats[STAT_AMMO] = AmmoCount(activeweap->GetAmmoType(FIRE_PRIMARY));
|
|
client->ps.stats[STAT_MAXAMMO] = MaxAmmoCount(activeweap->GetAmmoType(FIRE_PRIMARY));
|
|
}
|
|
|
|
client->ps.stats[STAT_CLIPAMMO] = activeweap->ClipAmmo(FIRE_PRIMARY);
|
|
client->ps.stats[STAT_MAXCLIPAMMO] = activeweap->GetClipSize(FIRE_PRIMARY);
|
|
|
|
client->ps.activeItems[ITEM_AMMO] = AmmoIndex(activeweap->GetAmmoType(FIRE_PRIMARY));
|
|
|
|
// grenade and rockets must match the number of ammo
|
|
if (client->ps.stats[STAT_MAXCLIPAMMO] == 1) {
|
|
client->ps.stats[STAT_MAXAMMO]++;
|
|
client->ps.stats[STAT_AMMO] += client->ps.stats[STAT_CLIPAMMO];
|
|
}
|
|
|
|
if (!activeweap->IsSubclassOfInventoryItem()) {
|
|
client->ps.stats[STAT_EQUIPPED_WEAPON] = activeweap->GetWeaponClass();
|
|
}
|
|
|
|
client->ps.activeItems[ITEM_WEAPON] = activeweap->getIndex();
|
|
} else if (m_pVehicle) {
|
|
Entity *pEnt = m_pVehicle->QueryTurretSlotEntity(0);
|
|
if (pEnt && pEnt->IsSubclassOfVehicleTurretGun()) {
|
|
VehicleTurretGun *vt = static_cast<VehicleTurretGun *>(pEnt);
|
|
|
|
client->ps.activeItems[ITEM_WEAPON] = vt->getIndex();
|
|
client->ps.stats[STAT_CLIPAMMO] = vt->ammo_in_clip[FIRE_PRIMARY];
|
|
client->ps.stats[STAT_MAXCLIPAMMO] = vt->ammo_clip_size[FIRE_PRIMARY];
|
|
client->ps.stats[STAT_SECONDARY_AMMO] = vt->GetWarmupFraction() * 100.f;
|
|
}
|
|
}
|
|
|
|
//
|
|
// set boss health
|
|
//
|
|
client->ps.stats[STAT_BOSSHEALTH] = bosshealth->value * 100.0f;
|
|
|
|
if (bosshealth->value * 100.0f > 0 && client->ps.stats[STAT_BOSSHEALTH] == 0) {
|
|
client->ps.stats[STAT_BOSSHEALTH] = 1;
|
|
}
|
|
|
|
// Set cinematic stuff
|
|
|
|
client->ps.stats[STAT_CINEMATIC] = 0;
|
|
|
|
if (level.cinematic) {
|
|
client->ps.stats[STAT_CINEMATIC] = (1 << 0);
|
|
}
|
|
|
|
if (actor_camera) {
|
|
client->ps.stats[STAT_CINEMATIC] += (1 << 1);
|
|
}
|
|
|
|
count = inventory.NumObjects();
|
|
|
|
int iItem = 0;
|
|
|
|
for (i = 1; i <= count; i++) {
|
|
int entnum = inventory.ObjectAt(i);
|
|
Weapon *weapon = (Weapon *)G_GetEntity(entnum);
|
|
int weapon_class;
|
|
|
|
if (weapon->IsSubclassOfWeapon()) {
|
|
if (weapon->IsSubclassOfInventoryItem()) {
|
|
if (iItem > 3) {
|
|
weapon->SetItemSlot(0);
|
|
} else {
|
|
client->ps.activeItems[iItem + 2] = weapon->getIndex();
|
|
weapon->SetItemSlot(WEAPON_CLASS_ITEM1 << iItem);
|
|
|
|
if (activeweap && weapon == activeweap) {
|
|
client->ps.stats[STAT_EQUIPPED_WEAPON] = WEAPON_CLASS_ITEM1 << iItem;
|
|
}
|
|
|
|
iItem++;
|
|
}
|
|
} else {
|
|
weapon_class = weapon->GetWeaponClass();
|
|
|
|
if (weapon_class & WEAPON_CLASS_GRENADE) {
|
|
if (weapon->HasAmmo(FIRE_PRIMARY)) {
|
|
client->ps.stats[STAT_WEAPONS] |= weapon_class;
|
|
}
|
|
} else {
|
|
client->ps.stats[STAT_WEAPONS] |= weapon_class;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Go through all the player's ammo and send over the names/amounts
|
|
memset(client->ps.ammo_amount, 0, sizeof(client->ps.ammo_amount));
|
|
memset(client->ps.ammo_name_index, 0, sizeof(client->ps.ammo_name_index));
|
|
memset(client->ps.max_ammo_amount, 0, sizeof(client->ps.max_ammo_amount));
|
|
|
|
count = ammo_inventory.NumObjects();
|
|
|
|
for (i = 1; i <= count; i++) {
|
|
Ammo *ammo = ammo_inventory.ObjectAt(i);
|
|
|
|
if (ammo) {
|
|
client->ps.ammo_amount[i - 1] = ammo->getAmount();
|
|
client->ps.max_ammo_amount[i - 1] = ammo->getMaxAmount();
|
|
client->ps.ammo_name_index[i - 1] = ammo->getIndex();
|
|
}
|
|
}
|
|
|
|
if (m_iInZoomMode == -1) {
|
|
client->ps.stats[STAT_INZOOM] = fov;
|
|
} else {
|
|
client->ps.stats[STAT_INZOOM] = 0;
|
|
}
|
|
|
|
client->ps.stats[STAT_CROSSHAIR] =
|
|
((!client->ps.stats[STAT_INZOOM] || client->ps.stats[STAT_INZOOM] > 30)
|
|
&& (activeweap && !activeweap->IsSubclassOfInventoryItem() && activeweap->GetUseCrosshair()))
|
|
|| m_pTurret || (m_pVehicle && m_pVehicle->IsSubclassOfVehicleTank());
|
|
|
|
client->ps.stats[STAT_COMPASSNORTH] = ANGLE2SHORT(world->m_fNorth);
|
|
|
|
if (VectorCompare(vObjectiveLocation, vec_zero)) {
|
|
client->ps.stats[STAT_OBJECTIVELEFT] = 1730;
|
|
client->ps.stats[STAT_OBJECTIVERIGHT] = 1870;
|
|
client->ps.stats[STAT_OBJECTIVECENTER] = 1800;
|
|
} else {
|
|
Vector vDelta;
|
|
float yaw;
|
|
float fOffset;
|
|
|
|
vDelta = vObjectiveLocation - centroid;
|
|
yaw = AngleSubtract(v_angle[1], vDelta.toYaw()) + 180.0f;
|
|
|
|
vDelta = yaw_left * 300.0f + yaw_forward * vDelta.length() + centroid - centroid;
|
|
fOffset = AngleSubtract(vDelta.toYaw(), v_angle[1]);
|
|
if (fOffset < 0.0f) {
|
|
fOffset = -fOffset;
|
|
}
|
|
|
|
fOffset = 53.0f - fOffset + 7.0f;
|
|
if (fOffset < 7.0f) {
|
|
fOffset = 7.0f;
|
|
}
|
|
|
|
client->ps.stats[STAT_OBJECTIVELEFT] = anglemod(yaw - fOffset) * 10.0f;
|
|
if (client->ps.stats[STAT_OBJECTIVELEFT] <= 0) {
|
|
client->ps.stats[STAT_OBJECTIVELEFT] = 1;
|
|
} else if (client->ps.stats[STAT_OBJECTIVELEFT] > 3599) {
|
|
client->ps.stats[STAT_OBJECTIVELEFT] = 3599;
|
|
}
|
|
|
|
client->ps.stats[STAT_OBJECTIVERIGHT] = anglemod(yaw + fOffset) * 10.0f;
|
|
if (client->ps.stats[STAT_OBJECTIVERIGHT] <= 0) {
|
|
client->ps.stats[STAT_OBJECTIVERIGHT] = 1;
|
|
} else if (client->ps.stats[STAT_OBJECTIVERIGHT] > 3599) {
|
|
client->ps.stats[STAT_OBJECTIVERIGHT] = 3599;
|
|
}
|
|
|
|
client->ps.stats[STAT_OBJECTIVECENTER] = anglemod(yaw) * 10.0f;
|
|
if (client->ps.stats[STAT_OBJECTIVECENTER] <= 0) {
|
|
client->ps.stats[STAT_OBJECTIVECENTER] = 1;
|
|
} else if (client->ps.stats[STAT_OBJECTIVECENTER] > 3599) {
|
|
client->ps.stats[STAT_OBJECTIVECENTER] = 3599;
|
|
}
|
|
}
|
|
|
|
client->ps.stats[STAT_DAMAGEDIR] = damage_yaw;
|
|
if (client->ps.stats[STAT_DAMAGEDIR] < 0) {
|
|
client->ps.stats[STAT_DAMAGEDIR] = 0;
|
|
} else if (client->ps.stats[STAT_DAMAGEDIR] > 3600) {
|
|
client->ps.stats[STAT_DAMAGEDIR] = 3600;
|
|
}
|
|
|
|
// Do letterbox
|
|
|
|
// Check for letterbox fully out
|
|
if ((level.m_letterbox_time <= 0) && (level.m_letterbox_dir == letterbox_in)) {
|
|
client->ps.stats[STAT_LETTERBOX] = level.m_letterbox_fraction * MAX_LETTERBOX_SIZE;
|
|
return;
|
|
} else if ((level.m_letterbox_time <= 0) && (level.m_letterbox_dir == letterbox_out)) {
|
|
client->ps.stats[STAT_LETTERBOX] = 0;
|
|
return;
|
|
}
|
|
|
|
float frac;
|
|
|
|
level.m_letterbox_time -= level.frametime;
|
|
|
|
frac = level.m_letterbox_time / level.m_letterbox_time_start;
|
|
|
|
if (frac > 1) {
|
|
frac = 1;
|
|
}
|
|
if (frac < 0) {
|
|
frac = 0;
|
|
}
|
|
|
|
if (level.m_letterbox_dir == letterbox_in) {
|
|
frac = 1.0f - frac;
|
|
}
|
|
|
|
client->ps.stats[STAT_LETTERBOX] = (frac * level.m_letterbox_fraction) * MAX_LETTERBOX_SIZE;
|
|
}
|
|
|
|
void Player::UpdateMusic(void)
|
|
{
|
|
if (music_forced) {
|
|
client->ps.current_music_mood = music_current_mood;
|
|
client->ps.fallback_music_mood = music_fallback_mood;
|
|
}
|
|
|
|
// Copy music volume and fade time to player state
|
|
client->ps.music_volume = music_current_volume;
|
|
client->ps.music_volume_fade_time = music_volume_fade_time;
|
|
}
|
|
|
|
void Player::SetReverb(int type, float level)
|
|
{
|
|
reverb_type = type;
|
|
reverb_level = level;
|
|
}
|
|
|
|
void Player::SetReverb(str type, float level)
|
|
{
|
|
reverb_type = EAXMode_NameToNum(type);
|
|
reverb_level = level;
|
|
}
|
|
|
|
void Player::SetReverb(Event *ev)
|
|
{
|
|
if (ev->NumArgs() < 2) {
|
|
return;
|
|
}
|
|
|
|
SetReverb(ev->GetInteger(1), ev->GetFloat(2));
|
|
}
|
|
|
|
void Player::UpdateReverb(void)
|
|
{
|
|
client->ps.reverb_type = reverb_type;
|
|
client->ps.reverb_level = reverb_level;
|
|
}
|
|
|
|
void Player::UpdateMisc(void)
|
|
{
|
|
//
|
|
// clear out the level exit flag
|
|
//
|
|
client->ps.pm_flags &= ~PMF_LEVELEXIT;
|
|
|
|
//
|
|
// see if our camera is the level exit camera
|
|
//
|
|
if (camera && camera->IsLevelExit()) {
|
|
client->ps.pm_flags |= PMF_LEVELEXIT;
|
|
} else if (level.near_exit) {
|
|
client->ps.pm_flags |= PMF_LEVELEXIT;
|
|
}
|
|
|
|
//
|
|
// do anything special for respawns
|
|
//
|
|
if (client->ps.pm_flags & PMF_RESPAWNED) {
|
|
//
|
|
// change music
|
|
//
|
|
if (music_current_mood != mood_success) {
|
|
ChangeMusic("success", "normal", false);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
EndFrame
|
|
|
|
Called for each player at the end of the server frame
|
|
and right after spawning
|
|
=================
|
|
*/
|
|
void Player::EndFrame(void)
|
|
{
|
|
FinishMove();
|
|
UpdateStats();
|
|
UpdateMusic();
|
|
UpdateReverb();
|
|
UpdateMisc();
|
|
|
|
if (!g_spectatefollow_firstperson->integer || !IsSpectator() || !m_iPlayerSpectating) {
|
|
SetupView();
|
|
} else {
|
|
gentity_t *ent = g_entities + m_iPlayerSpectating - 1;
|
|
|
|
if (!ent->inuse || !ent->entity || ent->entity->deadflag >= DEAD_DEAD) {
|
|
SetupView();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::GotKill(Event *ev)
|
|
{
|
|
/*
|
|
Entity *victim;
|
|
Entity *inflictor;
|
|
float damage;
|
|
int meansofdeath;
|
|
qboolean gibbed;
|
|
|
|
if ( deathmatch->integer )
|
|
{
|
|
return;
|
|
}
|
|
|
|
victim = ev->GetEntity( 1 );
|
|
damage = ev->GetInteger( 2 );
|
|
inflictor = ev->GetEntity( 3 );
|
|
meansofdeath = ev->GetInteger( 4 );
|
|
gibbed = ev->GetInteger( 5 );
|
|
*/
|
|
}
|
|
|
|
void Player::SetPowerupTimer(Event *ev)
|
|
{
|
|
Event *event;
|
|
|
|
poweruptimer = ev->GetInteger(1);
|
|
poweruptype = ev->GetInteger(2);
|
|
event = new Event(EV_Player_UpdatePowerupTimer);
|
|
PostEvent(event, 1);
|
|
}
|
|
|
|
void Player::UpdatePowerupTimer(Event *ev)
|
|
{
|
|
poweruptimer -= 1;
|
|
if (poweruptimer > 0) {
|
|
PostEvent(ev, 1);
|
|
} else {
|
|
poweruptype = 0;
|
|
}
|
|
}
|
|
|
|
void Player::ChangeMusic(const char *current, const char *fallback, qboolean force)
|
|
{
|
|
int current_mood_num;
|
|
int fallback_mood_num;
|
|
|
|
music_forced = force;
|
|
|
|
if (current) {
|
|
current_mood_num = MusicMood_NameToNum(current);
|
|
if (current_mood_num < 0) {
|
|
gi.DPrintf("current music mood %s not found", current);
|
|
} else {
|
|
music_current_mood = current_mood_num;
|
|
}
|
|
}
|
|
|
|
if (fallback) {
|
|
fallback_mood_num = MusicMood_NameToNum(fallback);
|
|
if (fallback_mood_num < 0) {
|
|
gi.DPrintf("fallback music mood %s not found", fallback);
|
|
fallback = NULL;
|
|
} else {
|
|
music_fallback_mood = fallback_mood_num;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::ChangeMusicVolume(float volume, float fade_time)
|
|
{
|
|
music_volume_fade_time = fade_time;
|
|
music_saved_volume = music_current_volume;
|
|
music_current_volume = volume;
|
|
}
|
|
|
|
void Player::RestoreMusicVolume(float fade_time)
|
|
{
|
|
music_volume_fade_time = fade_time;
|
|
music_current_volume = music_saved_volume;
|
|
music_saved_volume = -1.0;
|
|
}
|
|
|
|
void Player::addOrigin(Vector org)
|
|
{
|
|
setLocalOrigin(localorigin + org);
|
|
|
|
animspeed = org.x * (1.f / level.frametime);
|
|
airspeed = org.y * (1.f / level.frametime);
|
|
m_vPushVelocity.x = org.z * (1.f / level.frametime);
|
|
}
|
|
|
|
void Player::Jump(Event *ev)
|
|
{
|
|
float maxheight;
|
|
|
|
if (m_pTurret || m_pVehicle) {
|
|
// Don't jump when inside a vehicle or turret
|
|
return;
|
|
}
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
// Added in 2.0
|
|
// Don't jump when on top of another sentient
|
|
if (groundentity && groundentity->entity && groundentity->entity->IsSubclassOfSentient()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
maxheight = ev->GetFloat(1);
|
|
|
|
if (maxheight > 16) {
|
|
// v^2 = 2ad
|
|
velocity[2] += sqrt(2 * sv_gravity->integer * maxheight);
|
|
|
|
if (client->ps.groundEntityNum != ENTITYNUM_NONE) {
|
|
velocity += m_vPushVelocity;
|
|
}
|
|
|
|
// make sure the player leaves the ground
|
|
client->ps.walking = qfalse;
|
|
|
|
// Added in 2.0
|
|
m_bHasJumped = true;
|
|
}
|
|
}
|
|
|
|
void Player::JumpXY(Event *ev)
|
|
{
|
|
float forwardmove;
|
|
float sidemove;
|
|
float distance;
|
|
float time;
|
|
float speed;
|
|
|
|
if (m_pTurret || m_pVehicle) {
|
|
// Don't jump when inside a vehicle or turret
|
|
return;
|
|
}
|
|
|
|
forwardmove = ev->GetFloat(1);
|
|
sidemove = ev->GetFloat(2);
|
|
speed = ev->GetFloat(3);
|
|
|
|
velocity = yaw_forward * forwardmove - yaw_left * sidemove;
|
|
distance = velocity.length();
|
|
velocity *= speed / distance;
|
|
time = distance / speed;
|
|
velocity[2] = sv_gravity->integer * time * 0.5f;
|
|
|
|
if (client->ps.groundEntityNum != ENTITYNUM_NONE) {
|
|
velocity += G_GetEntity(client->ps.groundEntityNum)->velocity;
|
|
}
|
|
|
|
airspeed = distance;
|
|
|
|
// make sure the player leaves the ground
|
|
client->ps.walking = qfalse;
|
|
}
|
|
|
|
void Player::SetViewAngles(Vector newViewangles)
|
|
{
|
|
// set the delta angle
|
|
client->ps.delta_angles[0] = ANGLE2SHORT(newViewangles.x - client->cmd_angles[0]);
|
|
client->ps.delta_angles[1] = ANGLE2SHORT(newViewangles.y - client->cmd_angles[1]);
|
|
client->ps.delta_angles[2] = ANGLE2SHORT(newViewangles.z - client->cmd_angles[2]);
|
|
|
|
v_angle = newViewangles;
|
|
// Fixed in OPM
|
|
// Normalize angles to the range (-180, +180)
|
|
// so interpolation is done properly client-side
|
|
v_angle.EulerNormalize();
|
|
|
|
// get the pitch and roll from our leg angles
|
|
newViewangles.x = angles.x;
|
|
newViewangles.z = angles.z;
|
|
AnglesToMat(newViewangles, orientation);
|
|
yaw_forward = orientation[0];
|
|
yaw_left = orientation[1];
|
|
}
|
|
|
|
void Player::SetTargetViewAngles(Vector angles)
|
|
{
|
|
v_angle = angles;
|
|
}
|
|
|
|
void Player::DumpState(Event *ev)
|
|
{
|
|
gi.DPrintf(
|
|
"Legs: %s Torso: %s\n", currentState_Legs ? currentState_Legs->getName() : "NULL", currentState_Torso->getName()
|
|
);
|
|
}
|
|
|
|
void Player::ForceTorsoState(Event *ev)
|
|
{
|
|
State *ts = statemap_Torso->FindState(ev->GetString(1));
|
|
EvaluateState(ts);
|
|
}
|
|
|
|
void Player::ForceLegsState(Event *ev)
|
|
{
|
|
State *ls = statemap_Legs->FindState(ev->GetString(1));
|
|
EvaluateState(NULL, ls);
|
|
}
|
|
|
|
void Player::TouchedUseAnim(Entity *ent)
|
|
{
|
|
toucheduseanim = ent;
|
|
}
|
|
|
|
void Player::NextPainTime(Event *ev)
|
|
{
|
|
float time = ev->GetFloat(1);
|
|
|
|
nextpaintime = level.time + time;
|
|
|
|
if (time >= 0.0f) {
|
|
pain = 0.0f;
|
|
pain_type = MOD_NONE;
|
|
pain_location = HITLOC_MISS;
|
|
|
|
m_pLegsPainCond->clearCheck();
|
|
m_pTorsoPainCond->clearCheck();
|
|
}
|
|
}
|
|
|
|
void Player::EnterVehicle(Event *ev)
|
|
{
|
|
Entity *ent;
|
|
|
|
ent = ev->GetEntity(1);
|
|
if (ent && ent->IsSubclassOfVehicle()) {
|
|
flags |= FL_PARTIAL_IMMOBILE;
|
|
viewheight = STAND_EYE_HEIGHT;
|
|
velocity = vec_zero;
|
|
m_pVehicle = (Vehicle *)ent;
|
|
if (m_pVehicle->IsDrivable()) {
|
|
setMoveType(MOVETYPE_VEHICLE);
|
|
} else {
|
|
setMoveType(MOVETYPE_NOCLIP);
|
|
}
|
|
|
|
SafeHolster(true);
|
|
}
|
|
}
|
|
|
|
void Player::ExitVehicle(Event *ev)
|
|
{
|
|
flags &= ~FL_PARTIAL_IMMOBILE;
|
|
setMoveType(MOVETYPE_WALK);
|
|
m_pVehicle = NULL;
|
|
|
|
if (camera) {
|
|
SetCamera(NULL, 0.5f);
|
|
ZoomOff();
|
|
}
|
|
|
|
SafeHolster(false);
|
|
takedamage = DAMAGE_YES;
|
|
setSolidType(SOLID_BBOX);
|
|
}
|
|
|
|
void Player::EnterTurret(TurretGun *ent)
|
|
{
|
|
flags |= FL_PARTIAL_IMMOBILE;
|
|
viewheight = DEFAULT_VIEWHEIGHT;
|
|
velocity = vec_zero;
|
|
m_pTurret = ent;
|
|
|
|
if (ent->inheritsFrom(PortableTurret::classinfostatic())) {
|
|
// carryable turret
|
|
setMoveType(MOVETYPE_PORTABLE_TURRET);
|
|
StopPartAnimating(torso);
|
|
SetPartAnim("mg42tripod_aim_straight_straight");
|
|
} else {
|
|
// standard turret
|
|
setMoveType(MOVETYPE_TURRET);
|
|
}
|
|
|
|
SafeHolster(true);
|
|
}
|
|
|
|
void Player::EnterTurret(Event *ev)
|
|
{
|
|
TurretGun *ent = (TurretGun *)ev->GetEntity(1);
|
|
|
|
if (!ent) {
|
|
return;
|
|
}
|
|
|
|
if (!ent->inheritsFrom(TurretGun::classinfostatic())) {
|
|
return;
|
|
}
|
|
|
|
EnterTurret(ent);
|
|
}
|
|
|
|
void Player::ExitTurret(void)
|
|
{
|
|
if (m_pTurret->inheritsFrom(PortableTurret::classinfostatic())) {
|
|
StopPartAnimating(torso);
|
|
SetPartAnim("mg42tripod_aim_straight_straight");
|
|
}
|
|
|
|
flags &= ~FL_PARTIAL_IMMOBILE;
|
|
setMoveType(MOVETYPE_WALK);
|
|
m_pTurret = NULL;
|
|
|
|
SafeHolster(qfalse);
|
|
|
|
new_buttons = 0;
|
|
server_new_buttons = 0;
|
|
}
|
|
|
|
void Player::ExitTurret(Event *ev)
|
|
{
|
|
ExitTurret();
|
|
}
|
|
|
|
void Player::HolsterToggle(Event *ev)
|
|
{
|
|
if (deadflag) {
|
|
return;
|
|
}
|
|
|
|
if (WeaponsOut()) {
|
|
// fucking compiler bug
|
|
// it won't call the parent's override function
|
|
((Sentient *)this)->Holster(qtrue);
|
|
} else {
|
|
((Sentient *)this)->Holster(qfalse);
|
|
}
|
|
}
|
|
|
|
void Player::Holster(Event *ev)
|
|
{
|
|
SafeHolster(ev->GetBoolean(1));
|
|
}
|
|
|
|
void Player::WatchActor(Event *ev)
|
|
{
|
|
if (camera || currentState_Torso->getCameraType() != CAMERA_BEHIND) {
|
|
return;
|
|
}
|
|
|
|
actor_to_watch = (Actor *)ev->GetEntity(1);
|
|
}
|
|
|
|
void Player::StopWatchingActor(Event *ev)
|
|
{
|
|
Actor *old_actor;
|
|
|
|
old_actor = (Actor *)ev->GetEntity(1);
|
|
|
|
if (old_actor == actor_to_watch) {
|
|
actor_to_watch = NULL;
|
|
}
|
|
}
|
|
|
|
void Player::setAngles(Vector ang)
|
|
{
|
|
// set the angles normally
|
|
|
|
if (bindmaster) {
|
|
ang -= bindmaster->angles;
|
|
}
|
|
|
|
Entity::setAngles(ang);
|
|
}
|
|
|
|
painDirection_t Player::Pain_string_to_int(str pain)
|
|
{
|
|
if (!pain.icmp(pain, "Front")) {
|
|
return PAIN_FRONT;
|
|
} else if (!pain.icmp(pain, "Left")) {
|
|
return PAIN_LEFT;
|
|
} else if (!pain.icmp(pain, "Right")) {
|
|
return PAIN_RIGHT;
|
|
} else if (!pain.icmp(pain, "Rear")) {
|
|
return PAIN_REAR;
|
|
} else {
|
|
return PAIN_NONE;
|
|
}
|
|
}
|
|
|
|
void Player::ArchivePersistantData(Archiver& arc)
|
|
{
|
|
str model_name;
|
|
str name;
|
|
|
|
Sentient::ArchivePersistantData(arc);
|
|
|
|
model_name = g_playermodel->string;
|
|
|
|
arc.ArchiveString(&model_name);
|
|
|
|
if (arc.Loading()) {
|
|
// set the cvar
|
|
gi.cvar_set("g_playermodel", model_name.c_str());
|
|
|
|
setModel("models/player/" + model_name + ".tik");
|
|
}
|
|
|
|
if (arc.Saving()) {
|
|
if (holsteredWeapon) {
|
|
name = holsteredWeapon->getName();
|
|
} else {
|
|
name = "none";
|
|
}
|
|
}
|
|
|
|
arc.ArchiveString(&name);
|
|
if (arc.Loading() && name != "none") {
|
|
holsteredWeapon = (Weapon *)FindItem(name);
|
|
}
|
|
|
|
UpdateWeapons();
|
|
|
|
// Force a re-evaluation of the player's state
|
|
LoadStateTable();
|
|
}
|
|
|
|
void Player::VelocityModified(void) {}
|
|
|
|
int Player::GetKnockback(int original_knockback, qboolean blocked)
|
|
{
|
|
int new_knockback;
|
|
|
|
new_knockback = original_knockback - 50;
|
|
|
|
// See if we still have enough knockback to knock the player down
|
|
if (new_knockback >= 200 && take_pain) {
|
|
knockdown = true;
|
|
|
|
if (blocked) {
|
|
float damage;
|
|
|
|
damage = new_knockback / 50;
|
|
|
|
if (damage > 10) {
|
|
damage = 10;
|
|
}
|
|
|
|
Damage(world, world, damage, origin, vec_zero, vec_zero, 0, DAMAGE_NO_ARMOR, MOD_CRUSH);
|
|
}
|
|
}
|
|
|
|
// Make sure knockback is still at least 0
|
|
|
|
if (new_knockback < 0) {
|
|
new_knockback = 0;
|
|
}
|
|
|
|
return new_knockback;
|
|
}
|
|
|
|
void Player::ResetHaveItem(Event *ev)
|
|
{
|
|
str fullname;
|
|
ScriptVariable *var;
|
|
|
|
fullname = str("playeritem_") + ev->GetString(1);
|
|
|
|
var = game.vars->GetVariable(fullname.c_str());
|
|
|
|
if (var) {
|
|
var->setIntValue(0);
|
|
}
|
|
}
|
|
|
|
void Player::ReceivedItem(Item *item) {}
|
|
|
|
void Player::RemovedItem(Item *item) {}
|
|
|
|
void Player::AmmoAmountChanged(Ammo *ammo, int ammo_in_clip)
|
|
{
|
|
str fullname;
|
|
ScriptVariable *var;
|
|
|
|
//
|
|
// set our level variables
|
|
//
|
|
fullname = str("playerammo_") + ammo->getName();
|
|
|
|
var = level.vars->GetVariable(fullname.c_str());
|
|
if (!var) {
|
|
level.vars->SetVariable(fullname.c_str(), ammo->getAmount() + ammo_in_clip);
|
|
} else {
|
|
var->setIntValue(ammo->getAmount() + ammo_in_clip);
|
|
}
|
|
}
|
|
|
|
void Player::WaitForState(Event *ev)
|
|
{
|
|
waitForState = ev->GetString(1);
|
|
}
|
|
|
|
void Player::SetDamageMultiplier(Event *ev)
|
|
{
|
|
damage_multiplier = ev->GetFloat(1);
|
|
}
|
|
|
|
void Player::SetTakePain(Event *ev)
|
|
{
|
|
take_pain = ev->GetBoolean(1);
|
|
}
|
|
|
|
void Player::Loaded(void)
|
|
{
|
|
UpdateWeapons();
|
|
}
|
|
|
|
void Player::PlayerShowModel(Event *ev)
|
|
{
|
|
Entity::showModel();
|
|
UpdateWeapons();
|
|
}
|
|
|
|
void Player::showModel(void)
|
|
{
|
|
Entity::showModel();
|
|
UpdateWeapons();
|
|
}
|
|
|
|
Vector Player::EyePosition(void)
|
|
{
|
|
return m_vViewPos;
|
|
}
|
|
|
|
void Player::ModifyHeight(Event *ev)
|
|
{
|
|
str height = ev->GetString(1);
|
|
|
|
if (!height.icmp("stand")) {
|
|
viewheight = DEFAULT_VIEWHEIGHT;
|
|
maxs.z = 94.0f;
|
|
m_bHasJumped = false;
|
|
} else if (!height.icmp("jumpstart")) {
|
|
if (g_protocol < protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
viewheight = JUMP_START_VIEWHEIGHT;
|
|
}
|
|
maxs.z = 94.0f;
|
|
} else if (!height.icmp("duck")) {
|
|
viewheight = CROUCH_VIEWHEIGHT;
|
|
maxs.z = 54.0f;
|
|
} else if (!height.icmp("duckrun")) {
|
|
viewheight = CROUCH_RUN_VIEWHEIGHT;
|
|
maxs.z = 60.0f;
|
|
} else if (!height.icmp("prone")) {
|
|
//
|
|
// Added in OPM
|
|
// (prone)
|
|
viewheight = PRONE_VIEWHEIGHT;
|
|
maxs.z = 20.0f;
|
|
} else {
|
|
gi.Printf("Unknown modheight '%s' defaulting to stand\n", height.c_str());
|
|
viewheight = DEFAULT_VIEWHEIGHT;
|
|
maxs.z = 94.0f;
|
|
}
|
|
}
|
|
|
|
// Specify the view height of the player and the height of his bounding box
|
|
void Player::ModifyHeightFloat(Event *ev)
|
|
{
|
|
// params
|
|
int height;
|
|
float max_z;
|
|
|
|
height = ev->GetInteger(1);
|
|
max_z = ev->GetFloat(2);
|
|
|
|
viewheight = height;
|
|
|
|
if (max_z >= 94.0) {
|
|
max_z = 94.0;
|
|
} else if (max_z >= 74.0 && max_z < 94.0) {
|
|
max_z = 54.0;
|
|
} else if (max_z >= 30.0 && max_z < 54.0) {
|
|
max_z = 20.0;
|
|
} else if (max_z <= 20.0) {
|
|
max_z = 20.0;
|
|
}
|
|
|
|
maxs.z = max_z;
|
|
|
|
client->ps.pm_flags &= ~(PMF_DUCKED | PMF_VIEW_PRONE | PMF_VIEW_DUCK_RUN | PMF_VIEW_JUMP_START);
|
|
|
|
// FIXME...
|
|
/*
|
|
gi.MSG_SetClient(edict - g_entities);
|
|
|
|
gi.MSG_StartCGM(CGM_MODHEIGHTFLOAT);
|
|
gi.MSG_WriteLong(height);
|
|
gi.MSG_WriteFloat(max_z);
|
|
gi.MSG_EndCGM();
|
|
*/
|
|
}
|
|
|
|
void Player::SetMovePosFlags(Event *ev)
|
|
{
|
|
str sParm;
|
|
|
|
if (ev->NumArgs() <= 0) {
|
|
Com_Printf("moveposflags command without any parameters\n");
|
|
return;
|
|
}
|
|
|
|
sParm = ev->GetString(1);
|
|
|
|
if (!sParm.icmp("crouching")) {
|
|
m_iMovePosFlags = MPF_POSITION_CROUCHING;
|
|
} else if (!sParm.icmp("prone")) {
|
|
m_iMovePosFlags = MPF_POSITION_PRONE;
|
|
} else if (!sParm.icmp("offground")) {
|
|
m_iMovePosFlags = MPF_POSITION_OFFGROUND;
|
|
} else {
|
|
m_iMovePosFlags = MPF_POSITION_STANDING;
|
|
}
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
sParm = ev->GetString(2);
|
|
|
|
if (!sParm.icmp("walking") || !sParm.icmp("walking\"") // there is a mistake in WALK_FORWARD
|
|
) {
|
|
m_iMovePosFlags |= MPF_MOVEMENT_WALKING;
|
|
} else if (!sParm.icmp("running")) {
|
|
m_iMovePosFlags |= MPF_MOVEMENT_RUNNING;
|
|
} else if (!sParm.icmp("falling")) {
|
|
m_iMovePosFlags |= MPF_MOVEMENT_FALLING;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::GetPositionForScript(Event *ev)
|
|
{
|
|
if (m_iMovePosFlags & MPF_POSITION_CROUCHING) {
|
|
ev->AddConstString(STRING_CROUCHING);
|
|
} else if (m_iMovePosFlags & MPF_POSITION_PRONE) {
|
|
ev->AddConstString(STRING_PRONE);
|
|
} else if (m_iMovePosFlags & MPF_POSITION_OFFGROUND) {
|
|
ev->AddConstString(STRING_OFFGROUND);
|
|
} else {
|
|
ev->AddConstString(STRING_STANDING);
|
|
}
|
|
}
|
|
|
|
void Player::GetMovementForScript(Event *ev)
|
|
{
|
|
if (m_iMovePosFlags & MPF_MOVEMENT_WALKING) {
|
|
ev->AddConstString(STRING_WALKING);
|
|
} else if (m_iMovePosFlags & MPF_MOVEMENT_RUNNING) {
|
|
ev->AddConstString(STRING_RUNNING);
|
|
} else if (m_iMovePosFlags & MPF_MOVEMENT_FALLING) {
|
|
ev->AddConstString(STRING_FALLING);
|
|
} else {
|
|
ev->AddConstString(STRING_STANDING);
|
|
}
|
|
}
|
|
|
|
void Player::ToggleZoom(int iZoom)
|
|
{
|
|
if (iZoom && m_iInZoomMode == -1) {
|
|
SetFov(selectedfov);
|
|
m_iInZoomMode = 0;
|
|
} else {
|
|
SetFov(iZoom);
|
|
m_iInZoomMode = -1;
|
|
}
|
|
}
|
|
|
|
void Player::ZoomOff(void)
|
|
{
|
|
SetFov(selectedfov);
|
|
m_iInZoomMode = 0;
|
|
}
|
|
|
|
void Player::ZoomOffEvent(Event *ev)
|
|
{
|
|
ZoomOff();
|
|
}
|
|
|
|
qboolean Player::IsZoomed(void)
|
|
{
|
|
return m_iInZoomMode == -1;
|
|
}
|
|
|
|
void Player::SafeZoomed(Event *ev)
|
|
{
|
|
if (ev->GetInteger(1)) {
|
|
if (m_iInZoomMode > 0) {
|
|
SetFov(m_iInZoomMode);
|
|
m_iInZoomMode = -1;
|
|
}
|
|
} else {
|
|
if (m_iInZoomMode == -1) {
|
|
m_iInZoomMode = fov;
|
|
SetFov(selectedfov);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::AttachToLadder(Event *ev)
|
|
{
|
|
Vector vStart, vEnd, vOffset;
|
|
trace_t trace;
|
|
FuncLadder *pLadder;
|
|
|
|
if (deadflag) {
|
|
return;
|
|
}
|
|
|
|
AngleVectors(m_vViewAng, vOffset, NULL, NULL);
|
|
|
|
vStart = m_vViewPos - vOffset * 12.0f;
|
|
vEnd = m_vViewPos + vOffset * 128.0f;
|
|
|
|
trace = G_Trace(vStart, vec_zero, vec_zero, vEnd, this, MASK_LADDER, qfalse, "Player::AttachToLadder");
|
|
|
|
if (trace.fraction == 1.0f || !trace.ent || !trace.ent->entity || !trace.ent->entity->isSubclassOf(FuncLadder)) {
|
|
return;
|
|
}
|
|
|
|
pLadder = (FuncLadder *)trace.ent->entity;
|
|
m_pLadder = pLadder;
|
|
|
|
pLadder->PositionOnLadder(this);
|
|
|
|
SetViewAngles(Vector(v_angle[0], angles[1], v_angle[2]));
|
|
}
|
|
|
|
void Player::UnattachFromLadder(Event *ev)
|
|
{
|
|
m_pLadder = NULL;
|
|
}
|
|
|
|
void Player::TweakLadderPos(Event *ev)
|
|
{
|
|
FuncLadder *pLadder = (FuncLadder *)m_pLadder.Pointer();
|
|
|
|
if (pLadder) {
|
|
pLadder->AdjustPositionOnLadder(this);
|
|
}
|
|
}
|
|
|
|
void Player::EnsureOverLadder(Event *ev)
|
|
{
|
|
FuncLadder *pLadder = (FuncLadder *)m_pLadder.Pointer();
|
|
|
|
if (pLadder) {
|
|
pLadder->EnsureOverLadder(this);
|
|
}
|
|
}
|
|
|
|
void Player::EnsureForwardOffLadder(Event *ev)
|
|
{
|
|
FuncLadder *pLadder = (FuncLadder *)m_pLadder.Pointer();
|
|
|
|
if (pLadder) {
|
|
pLadder->EnsureForwardOffLadder(this);
|
|
}
|
|
}
|
|
|
|
void Player::EventForceLandmineMeasure(Event *ev)
|
|
{
|
|
MeasureLandmineDistances();
|
|
}
|
|
|
|
str Player::GetCurrentDMWeaponType() const
|
|
{
|
|
return m_sDmPrimary;
|
|
}
|
|
|
|
void Player::Score(Event *ev)
|
|
{
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
// Of course useless in single-player mode
|
|
return;
|
|
}
|
|
|
|
dmManager.Score(this);
|
|
}
|
|
|
|
// Was between 2.0 and 2.15
|
|
/*
|
|
nationality_t GetAlliedType(const char* name)
|
|
{
|
|
if (!Q_stricmpn(name, "american", 8)) {
|
|
return NA_AMERICAN;
|
|
} else if (!Q_stricmpn(name, "allied_russian", 14)) {
|
|
return NA_RUSSIAN;
|
|
} else if (!Q_stricmpn(name, "allied_british", 14)) {
|
|
return NA_BRITISH;
|
|
} else if (!Q_stricmpn(name, "allied", 6)) {
|
|
return NA_AMERICAN;
|
|
} else {
|
|
return NA_NONE;
|
|
}
|
|
}
|
|
*/
|
|
|
|
// Commented out in OPM. See the other comment below.
|
|
/*
|
|
nationality_t GetPlayerTeamType(const char *name)
|
|
{
|
|
if (!Q_stricmpn(name, "american", 8)) {
|
|
return NA_AMERICAN;
|
|
} else if (!Q_stricmpn(name, "allied_russian", 14)) {
|
|
return NA_RUSSIAN;
|
|
} else if (!Q_stricmpn(name, "allied_british", 14)) {
|
|
return NA_BRITISH;
|
|
} else if (!Q_stricmpn(name, "allied_sas", 10)) {
|
|
return NA_BRITISH;
|
|
} else if (!Q_stricmpn(name, "allied", 6)) {
|
|
return NA_AMERICAN;
|
|
} else if (!Q_stricmpn(name, "german", 6)) {
|
|
return NA_GERMAN;
|
|
} else if (!Q_stricmpn(name, "it", 2)) {
|
|
return NA_ITALIAN;
|
|
} else if (!Q_stricmpn(name, "sc", 2)) {
|
|
return NA_ITALIAN;
|
|
} else {
|
|
return NA_NONE;
|
|
}
|
|
}
|
|
*/
|
|
|
|
// Fixed in OPM.
|
|
// This fixes the issue where the player can equip weapons
|
|
// from the other team
|
|
nationality_t GetPlayerAxisTeamType(const char *name)
|
|
{
|
|
if (g_target_game < target_game_e::TG_MOHTA) {
|
|
// Only american and german are supported on older versions of the game
|
|
return NA_GERMAN;
|
|
}
|
|
|
|
if (!Q_stricmpn(name, "german", 6)) {
|
|
return NA_GERMAN;
|
|
}
|
|
|
|
if (g_target_game < target_game_e::TG_MOHTT) {
|
|
// Italian skins are supported only in mohaab
|
|
return NA_GERMAN;
|
|
}
|
|
|
|
if (!Q_stricmpn(name, "it", 2)) {
|
|
return NA_ITALIAN;
|
|
} else if (!Q_stricmpn(name, "sc", 2)) {
|
|
return NA_ITALIAN;
|
|
}
|
|
|
|
// fallback to german
|
|
return NA_GERMAN;
|
|
}
|
|
|
|
nationality_t GetPlayerAlliedTeamType(const char *name)
|
|
{
|
|
if (g_target_game < target_game_e::TG_MOHTA) {
|
|
// Only american and german are supported on older versions of the game
|
|
return NA_AMERICAN;
|
|
}
|
|
|
|
if (!Q_stricmpn(name, "american", 8)) {
|
|
return NA_AMERICAN;
|
|
} else if (!Q_stricmpn(name, "allied_russian", 14)) {
|
|
return NA_RUSSIAN;
|
|
} else if (!Q_stricmpn(name, "allied_british", 14)) {
|
|
return NA_BRITISH;
|
|
} else if (!Q_stricmpn(name, "allied_sas", 10)) {
|
|
return NA_BRITISH;
|
|
} else if (!Q_stricmpn(name, "allied", 6)) {
|
|
return NA_AMERICAN;
|
|
}
|
|
|
|
// fallback to american
|
|
return NA_AMERICAN;
|
|
}
|
|
|
|
void Player::InitDeathmatch(void)
|
|
{
|
|
fAttackerDispTime = 0.0f;
|
|
pAttackerDistPointer = nullptr;
|
|
m_iInfoClient = -1;
|
|
m_fWeapSelectTime = level.time - 9.0f;
|
|
|
|
if (!g_realismmode->integer) {
|
|
m_fDamageMultipliers[HITLOC_HEAD] = 2.0f;
|
|
m_fDamageMultipliers[HITLOC_HELMET] = 2.0f;
|
|
m_fDamageMultipliers[HITLOC_NECK] = 2.0f;
|
|
m_fDamageMultipliers[HITLOC_TORSO_UPPER] = 1.0f;
|
|
m_fDamageMultipliers[HITLOC_TORSO_MID] = 0.95f;
|
|
m_fDamageMultipliers[HITLOC_TORSO_LOWER] = 0.90f;
|
|
m_fDamageMultipliers[HITLOC_PELVIS] = 0.85f;
|
|
m_fDamageMultipliers[HITLOC_R_ARM_UPPER] = 0.80f;
|
|
m_fDamageMultipliers[HITLOC_L_ARM_UPPER] = 0.80f;
|
|
m_fDamageMultipliers[HITLOC_R_LEG_UPPER] = 0.80f;
|
|
m_fDamageMultipliers[HITLOC_L_LEG_UPPER] = 0.80f;
|
|
m_fDamageMultipliers[HITLOC_R_ARM_LOWER] = 0.60f;
|
|
m_fDamageMultipliers[HITLOC_L_ARM_LOWER] = 0.60f;
|
|
m_fDamageMultipliers[HITLOC_R_LEG_LOWER] = 0.60f;
|
|
m_fDamageMultipliers[HITLOC_L_LEG_LOWER] = 0.60f;
|
|
m_fDamageMultipliers[HITLOC_R_HAND] = 0.50f;
|
|
m_fDamageMultipliers[HITLOC_L_HAND] = 0.50f;
|
|
m_fDamageMultipliers[HITLOC_R_FOOT] = 0.50f;
|
|
m_fDamageMultipliers[HITLOC_L_FOOT] = 0.50f;
|
|
}
|
|
|
|
if (current_team) {
|
|
if (AllowTeamRespawn()) {
|
|
EndSpectator();
|
|
|
|
if (dmManager.GetMatchStartTime() > 0.0f && !dmManager.AllowRespawn() && g_allowjointime->value > 0.0f
|
|
&& (level.time - dmManager.GetMatchStartTime()) > g_allowjointime->value) {
|
|
m_bTempSpectator = true;
|
|
}
|
|
|
|
switch (g_gametype->integer) {
|
|
case GT_TEAM_ROUNDS:
|
|
case GT_OBJECTIVE:
|
|
case GT_TOW:
|
|
case GT_LIBERATION:
|
|
if (!m_bTempSpectator) {
|
|
BeginFight();
|
|
} else {
|
|
Spectator();
|
|
}
|
|
break;
|
|
default:
|
|
BeginFight();
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (client->pers.teamnum) {
|
|
SetTeam(client->pers.teamnum);
|
|
} else {
|
|
SetTeam(TEAM_SPECTATOR);
|
|
}
|
|
}
|
|
|
|
edict->s.eFlags &= ~(TEAM_ALLIES | TEAM_AXIS);
|
|
|
|
if (GetTeam() == TEAM_ALLIES) {
|
|
edict->s.eFlags |= TEAM_ALLIES;
|
|
} else if (GetTeam() == TEAM_AXIS) {
|
|
edict->s.eFlags |= TEAM_AXIS;
|
|
}
|
|
|
|
G_SetClientConfigString(edict);
|
|
|
|
if (g_gametype->integer >= GT_TEAM_ROUNDS) {
|
|
if (client->pers.round_kills) {
|
|
num_deaths = client->pers.round_kills;
|
|
client->pers.round_kills = 0;
|
|
}
|
|
}
|
|
|
|
ChooseSpawnPoint();
|
|
EquipWeapons();
|
|
|
|
if (current_team) {
|
|
current_team->m_bHasSpawnedPlayers = qtrue;
|
|
}
|
|
}
|
|
|
|
bool Player::QueryLandminesAllowed() const
|
|
{
|
|
const char *mapname;
|
|
|
|
if (g_target_game < target_game_e::TG_MOHTT) {
|
|
return false;
|
|
}
|
|
|
|
if (dmflags->integer & DF_WEAPON_NO_LANDMINE) {
|
|
return false;
|
|
}
|
|
|
|
if (dmflags->integer & DF_WEAPON_LANDMINE_ALWAYS) {
|
|
return true;
|
|
}
|
|
|
|
mapname = level.mapname.c_str();
|
|
|
|
if (!Q_stricmpn(mapname, "obj/obj_", 8u)) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmpn(mapname, "dm/mohdm", 8u)) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "DM/MP_Bahnhof_DM")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "obj/MP_Ardennes_TOW")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "DM/MP_Bazaar_DM")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "obj/MP_Berlin_TOW")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "DM/MP_Brest_DM")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "obj/MP_Druckkammern_TOW")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "DM/MP_Gewitter_DM")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "obj/MP_Flughafen_TOW")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "DM/MP_Holland_DM")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "DM/MP_Malta_DM")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "DM/MP_Stadt_DM")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "DM/MP_Unterseite_DM")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "DM/MP_Verschneit_DM")) {
|
|
return false;
|
|
}
|
|
if (!Q_stricmp(mapname, "lib/mp_ship_lib")) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Player::EnsurePlayerHasAllowedWeapons()
|
|
{
|
|
int i;
|
|
|
|
//if (client != (gclient_t*)-2190)
|
|
if (!client) {
|
|
return;
|
|
}
|
|
|
|
if (!client->pers.dm_primary[0]) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < 7; i++) {
|
|
if (!Q_stricmp(client->pers.dm_primary, "sniper")) {
|
|
if (!(dmflags->integer & DF_WEAPON_NO_SNIPER)) {
|
|
return;
|
|
}
|
|
|
|
Q_strncpyz(client->pers.dm_primary, "rifle", sizeof(client->pers.dm_primary));
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "rifle")) {
|
|
if (!(dmflags->integer & DF_WEAPON_NO_RIFLE)) {
|
|
return;
|
|
}
|
|
|
|
Q_strncpyz(client->pers.dm_primary, "smg", sizeof(client->pers.dm_primary));
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "smg")) {
|
|
if (!(dmflags->integer & DF_WEAPON_NO_RIFLE)) {
|
|
return;
|
|
}
|
|
|
|
Q_strncpyz(client->pers.dm_primary, "mg", sizeof(client->pers.dm_primary));
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "mg")) {
|
|
if (!(dmflags->integer & DF_WEAPON_NO_RIFLE)) {
|
|
return;
|
|
}
|
|
|
|
Q_strncpyz(client->pers.dm_primary, "shotgun", sizeof(client->pers.dm_primary));
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "shotgun")) {
|
|
if (!(dmflags->integer & DF_WEAPON_NO_RIFLE)) {
|
|
return;
|
|
}
|
|
|
|
Q_strncpyz(client->pers.dm_primary, "heavy", sizeof(client->pers.dm_primary));
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "heavy")) {
|
|
if (!(dmflags->integer & DF_WEAPON_NO_RIFLE)) {
|
|
return;
|
|
}
|
|
|
|
Q_strncpyz(client->pers.dm_primary, "landmine", sizeof(client->pers.dm_primary));
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "landmine")) {
|
|
if (QueryLandminesAllowed()) {
|
|
return;
|
|
}
|
|
|
|
Q_strncpyz(client->pers.dm_primary, "sniper", sizeof(client->pers.dm_primary));
|
|
}
|
|
}
|
|
|
|
gi.cvar_set("dmflags", va("%i", dmflags->integer & ~DF_WEAPON_NO_RIFLE));
|
|
Com_Printf("No valid weapons -- re-allowing the rifle\n");
|
|
Q_strncpyz(client->pers.dm_primary, "rifle", sizeof(client->pers.dm_primary));
|
|
}
|
|
|
|
void Player::EquipWeapons()
|
|
{
|
|
Event *event;
|
|
|
|
nationality_t nationality;
|
|
|
|
if (IsSpectator()) {
|
|
FreeInventory();
|
|
return;
|
|
}
|
|
|
|
// Fixed in OPM
|
|
// Old behavior was calling GetPlayerTeamType() regardless of the team
|
|
if (GetTeam() == TEAM_AXIS) {
|
|
nationality = GetPlayerAxisTeamType(client->pers.dm_playergermanmodel);
|
|
} else {
|
|
nationality = GetPlayerAlliedTeamType(client->pers.dm_playermodel);
|
|
}
|
|
|
|
event = new Event(EV_Sentient_UseItem);
|
|
|
|
if (!m_sDmPrimary.length()) {
|
|
// Set the primary weapon
|
|
m_sDmPrimary = client->pers.dm_primary;
|
|
}
|
|
|
|
EnsurePlayerHasAllowedWeapons();
|
|
|
|
if (!Q_stricmp(client->pers.dm_primary, "sniper") && !(dmflags->integer & DF_WEAPON_NO_SNIPER)) {
|
|
switch (nationality) {
|
|
case NA_BRITISH:
|
|
if (g_target_game < target_game_e::TG_MOHTT) {
|
|
giveItem("weapons/springfield.tik");
|
|
event->AddString("Springfield '03 Sniper");
|
|
} else {
|
|
giveItem("weapons/Uk_W_L42A1.tik");
|
|
event->AddString("Enfield L42A1");
|
|
}
|
|
break;
|
|
case NA_RUSSIAN:
|
|
if (g_target_game < target_game_e::TG_MOHTA || dmflags->integer & DF_OLD_SNIPER) {
|
|
// Old snipers are forced older versions of the game
|
|
giveItem("weapons/springfield.tik");
|
|
event->AddString("Springfield '03 Sniper");
|
|
} else {
|
|
giveItem("weapons/svt_rifle.tik");
|
|
event->AddString("SVT 40");
|
|
}
|
|
break;
|
|
case NA_GERMAN:
|
|
if (g_target_game < target_game_e::TG_MOHTA || dmflags->integer & DF_OLD_SNIPER) {
|
|
// Old snipers are forced older versions of the game
|
|
giveItem("weapons/kar98sniper.tik");
|
|
event->AddString("KAR98 - Sniper");
|
|
} else {
|
|
giveItem("weapons/g43.tik");
|
|
event->AddString("G 43");
|
|
}
|
|
break;
|
|
case NA_ITALIAN:
|
|
giveItem("weapons/kar98sniper.tik");
|
|
event->AddString("KAR98 - Sniper");
|
|
break;
|
|
case NA_AMERICAN:
|
|
default:
|
|
giveItem("weapons/springfield.tik");
|
|
event->AddString("Springfield '03 Sniper");
|
|
break;
|
|
}
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "smg") && !(dmflags->integer & DF_WEAPON_NO_SMG)) {
|
|
switch (nationality) {
|
|
case NA_BRITISH:
|
|
giveItem("weapons/sten.tik");
|
|
event->AddString("Sten Mark II");
|
|
break;
|
|
case NA_RUSSIAN:
|
|
giveItem("weapons/ppsh_smg.tik");
|
|
event->AddString("PPSH SMG");
|
|
break;
|
|
case NA_GERMAN:
|
|
giveItem("weapons/mp40.tik");
|
|
event->AddString("MP40");
|
|
break;
|
|
case NA_ITALIAN:
|
|
giveItem("weapons/it_w_moschetto.tik");
|
|
event->AddString("Moschetto");
|
|
break;
|
|
case NA_AMERICAN:
|
|
default:
|
|
giveItem("weapons/thompsonsmg.tik");
|
|
event->AddString("Thompson");
|
|
break;
|
|
}
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "mg") && !(dmflags->integer & DF_WEAPON_NO_MG)) {
|
|
switch (nationality) {
|
|
case NA_BRITISH:
|
|
if (g_target_game < target_game_e::TG_MOHTT) {
|
|
giveItem("weapons/bar.tik");
|
|
event->AddString("BAR");
|
|
break;
|
|
} else {
|
|
giveItem("weapons/Uk_W_Vickers.tik");
|
|
event->AddString("Vickers-Berthier");
|
|
}
|
|
break;
|
|
case NA_GERMAN:
|
|
giveItem("weapons/mp44.tik");
|
|
event->AddString("StG 44");
|
|
break;
|
|
case NA_ITALIAN:
|
|
giveItem("weapons/It_W_Breda.tik");
|
|
event->AddString("Breda");
|
|
break;
|
|
case NA_AMERICAN:
|
|
default:
|
|
giveItem("weapons/bar.tik");
|
|
event->AddString("BAR");
|
|
break;
|
|
}
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "heavy") && !(dmflags->integer & DF_WEAPON_NO_ROCKET)) {
|
|
switch (nationality) {
|
|
case NA_GERMAN:
|
|
case NA_ITALIAN:
|
|
giveItem("weapons/panzerschreck.tik");
|
|
event->AddString("Panzerschreck");
|
|
break;
|
|
case NA_BRITISH:
|
|
if (g_target_game < target_game_e::TG_MOHTT) {
|
|
giveItem("weapons/bazooka.tik");
|
|
event->AddString("Bazooka");
|
|
} else {
|
|
giveItem("weapons/Uk_W_Piat.tik");
|
|
event->AddString("PIAT");
|
|
}
|
|
break;
|
|
case NA_AMERICAN:
|
|
default:
|
|
giveItem("weapons/bazooka.tik");
|
|
event->AddString("Bazooka");
|
|
break;
|
|
}
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "shotgun") && !(dmflags->integer & DF_WEAPON_NO_SHOTGUN)) {
|
|
switch (nationality) {
|
|
case NA_BRITISH:
|
|
if (g_target_game < target_game_e::TG_MOHTT) {
|
|
giveItem("weapons/shotgun.tik");
|
|
event->AddString("Shotgun");
|
|
} else {
|
|
giveItem("weapons/DeLisle.tik");
|
|
event->AddString("DeLisle");
|
|
}
|
|
break;
|
|
case NA_GERMAN:
|
|
if (g_target_game < target_game_e::TG_MOHTA || dmflags->integer & DF_DISALLOW_KAR98_MORTAR) {
|
|
// Fallback to shotgun
|
|
// The shotgun is forced on older versions of the game
|
|
giveItem("weapons/shotgun.tik");
|
|
event->AddString("Shotgun");
|
|
} else {
|
|
giveItem("weapons/kar98_mortar.tik");
|
|
event->AddString("Gewehrgranate");
|
|
}
|
|
break;
|
|
case NA_AMERICAN:
|
|
default:
|
|
giveItem("weapons/shotgun.tik");
|
|
event->AddString("Shotgun");
|
|
break;
|
|
}
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "landmine") && QueryLandminesAllowed()) {
|
|
//gi.Cvar_Get("g_rifles_for_sweepers", "0", 0);
|
|
|
|
switch (nationality) {
|
|
case NA_BRITISH:
|
|
giveItem("weapons/US_W_Minedetector.tik");
|
|
event->AddString("Minedetector");
|
|
|
|
if (g_rifles_for_sweepers->integer) {
|
|
// Give a lite version of the rifle
|
|
giveItem("weapons/enfield_lite.tik");
|
|
} else {
|
|
// Just give some ammo for the pistol
|
|
GiveAmmo("pistol", 12);
|
|
}
|
|
break;
|
|
case NA_RUSSIAN:
|
|
giveItem("weapons/US_W_Minedetector.tik");
|
|
event->AddString("Minedetector");
|
|
|
|
if (g_rifles_for_sweepers->integer) {
|
|
// Give a lite version of the rifle
|
|
giveItem("weapons/Mosin_Nagant_Rifle_lite.tik");
|
|
} else {
|
|
// Just give some ammo for the pistol
|
|
GiveAmmo("pistol", 14);
|
|
}
|
|
break;
|
|
case NA_GERMAN:
|
|
giveItem("weapons/Gr_W_Minedetector.tik");
|
|
event->AddString("Minensuchgerat");
|
|
|
|
if (g_rifles_for_sweepers->integer) {
|
|
// Give a lite version of the rifle
|
|
giveItem("weapons/kar98_lite.tik");
|
|
} else {
|
|
// Just give some ammo for the pistol
|
|
GiveAmmo("pistol", 16);
|
|
}
|
|
break;
|
|
case NA_AMERICAN:
|
|
default:
|
|
giveItem("weapons/US_W_Minedetector.tik");
|
|
event->AddString("Minedetector");
|
|
|
|
if (g_rifles_for_sweepers->integer) {
|
|
// Give a lite version of the rifle
|
|
giveItem("weapons/m1_garand_lite.tik");
|
|
} else {
|
|
// Just give some ammo for the pistol
|
|
GiveAmmo("pistol", 14);
|
|
}
|
|
break;
|
|
}
|
|
} else if (!(dmflags->integer & DF_WEAPON_NO_RIFLE)) {
|
|
switch (nationality) {
|
|
case NA_BRITISH:
|
|
giveItem("weapons/enfield.tik");
|
|
event->AddString("Lee-Enfield");
|
|
break;
|
|
case NA_RUSSIAN:
|
|
giveItem("weapons/Mosin_Nagant_Rifle.tik");
|
|
event->AddString("Mosin Nagant Rifle");
|
|
break;
|
|
case NA_GERMAN:
|
|
giveItem("weapons/kar98.tik");
|
|
event->AddString("Mauser KAR 98K");
|
|
break;
|
|
case NA_ITALIAN:
|
|
giveItem("weapons/it_w_carcano.tik");
|
|
event->AddString("Carcano");
|
|
break;
|
|
case NA_AMERICAN:
|
|
default:
|
|
giveItem("weapons/m1_garand.tik");
|
|
event->AddString("M1 Garand");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Make the player switch to the weapon some time after spawning
|
|
PostEvent(event, 0.3f);
|
|
|
|
//
|
|
// Pistols and grenades
|
|
//
|
|
switch (nationality) {
|
|
case NA_BRITISH:
|
|
giveItem("weapons/mills_grenade.tik");
|
|
if (g_target_game >= target_game_e::TG_MOHTA) {
|
|
giveItem("weapons/M18_smoke_grenade.tik");
|
|
}
|
|
giveItem("weapons/Webley_Revolver.tik");
|
|
break;
|
|
case NA_RUSSIAN:
|
|
giveItem("weapons/Russian_F1_grenade.tik");
|
|
if (g_target_game >= target_game_e::TG_MOHTA) {
|
|
giveItem("weapons/RDG-1_Smoke_grenade.tik");
|
|
}
|
|
giveItem("weapons/Nagant_revolver.tik");
|
|
break;
|
|
case NA_GERMAN:
|
|
giveItem("weapons/steilhandgranate.tik");
|
|
if (g_target_game >= target_game_e::TG_MOHTA) {
|
|
giveItem("weapons/nebelhandgranate.tik");
|
|
}
|
|
giveItem("weapons/p38.tik");
|
|
break;
|
|
case NA_ITALIAN:
|
|
giveItem("weapons/it_w_bomba.tik");
|
|
if (g_target_game >= target_game_e::TG_MOHTA) {
|
|
giveItem("weapons/it_w_bombabreda.tik");
|
|
}
|
|
giveItem("weapons/it_w_beretta.tik");
|
|
break;
|
|
default:
|
|
giveItem("weapons/m2frag_grenade.tik");
|
|
if (g_target_game >= target_game_e::TG_MOHTA) {
|
|
giveItem("weapons/M18_smoke_grenade.tik");
|
|
}
|
|
giveItem("weapons/colt45.tik");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Player::EquipWeapons_ver8()
|
|
{
|
|
// spectators should not have weapons
|
|
if (IsSpectator()) {
|
|
FreeInventory();
|
|
} else {
|
|
Event *ev = new Event("use");
|
|
|
|
if (!Q_stricmp(client->pers.dm_primary, "rifle")) {
|
|
if (dm_team == TEAM_ALLIES) {
|
|
giveItem("models/weapons/m1_garand.tik");
|
|
ev->AddString("models/weapons/m1_garand.tik");
|
|
} else {
|
|
giveItem("models/weapons/kar98.tik");
|
|
ev->AddString("models/weapons/kar98.tik");
|
|
}
|
|
|
|
GiveAmmo("rifle", 100);
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "sniper")) {
|
|
if (dm_team == TEAM_ALLIES) {
|
|
giveItem("models/weapons/springfield.tik");
|
|
ev->AddString("models/weapons/springfield.tik");
|
|
} else {
|
|
giveItem("models/weapons/kar98sniper.tik");
|
|
ev->AddString("models/weapons/kar98sniper.tik");
|
|
}
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "smg")) {
|
|
if (dm_team == TEAM_ALLIES) {
|
|
giveItem("models/weapons/thompsonsmg.tik");
|
|
ev->AddString("models/weapons/thompsonsmg.tik");
|
|
} else {
|
|
giveItem("models/weapons/mp40.tik");
|
|
ev->AddString("models/weapons/mp40.tik");
|
|
}
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "mg")) {
|
|
if (dm_team == TEAM_ALLIES) {
|
|
giveItem("models/weapons/bar.tik");
|
|
ev->AddString("models/weapons/bar.tik");
|
|
} else {
|
|
giveItem("models/weapons/mp44.tik");
|
|
ev->AddString("models/weapons/mp44.tik");
|
|
}
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "heavy")) {
|
|
if (dm_team == TEAM_ALLIES) {
|
|
giveItem("models/weapons/bazooka.tik");
|
|
ev->AddString("models/weapons/bazooka.tik");
|
|
} else {
|
|
giveItem("models/weapons/panzerschreck.tik");
|
|
ev->AddString("models/weapons/panzerschreck.tik");
|
|
}
|
|
} else if (!Q_stricmp(client->pers.dm_primary, "shotgun")) {
|
|
giveItem("models/weapons/shotgun.tik");
|
|
ev->AddString("models/weapons/shotgun.tik");
|
|
}
|
|
|
|
PostEvent(ev, 0.3f);
|
|
|
|
if (dm_team == TEAM_ALLIES) {
|
|
giveItem("models/weapons/colt45.tik");
|
|
giveItem("models/weapons/m2frag_grenade.tik");
|
|
} else {
|
|
giveItem("models/weapons/p38.tik");
|
|
giveItem("models/weapons/steilhandgranate.tik");
|
|
}
|
|
|
|
giveItem("models/items/binoculars.tik");
|
|
}
|
|
}
|
|
|
|
void Player::Spectator(void)
|
|
{
|
|
if (!IsSpectator()) {
|
|
respawn_time = level.time + 1.0f;
|
|
}
|
|
|
|
RemoveFromVehiclesAndTurrets();
|
|
|
|
m_bSpectator = !m_bTempSpectator;
|
|
m_iPlayerSpectating = 0;
|
|
takedamage = DAMAGE_NO;
|
|
deadflag = DEAD_NO;
|
|
health = max_health;
|
|
|
|
client->ps.feetfalling = 0;
|
|
movecontrol = MOVECONTROL_USER;
|
|
client->ps.pm_flags |= PMF_SPECTATING;
|
|
|
|
EvaluateState(statemap_Torso->FindState("STAND"), statemap_Legs->FindState("STAND"));
|
|
|
|
setSolidType(SOLID_NOT);
|
|
setMoveType(MOVETYPE_NOCLIP);
|
|
|
|
FreeInventory();
|
|
|
|
hideModel();
|
|
|
|
SetPlayerSpectateRandom();
|
|
}
|
|
|
|
bool Player::IsValidSpectatePlayer(Player *pPlayer)
|
|
{
|
|
if (g_gametype->integer <= GT_FFA) {
|
|
return true;
|
|
}
|
|
|
|
if (GetTeam() <= TEAM_FREEFORALL) {
|
|
return true;
|
|
}
|
|
|
|
if (g_forceteamspectate->integer) {
|
|
if (!GetDM_Team()->NumLivePlayers()) {
|
|
return true;
|
|
}
|
|
|
|
if (pPlayer->GetTeam() == GetTeam()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Player::SetPlayerSpectate(bool bNext)
|
|
{
|
|
int i;
|
|
int dir;
|
|
int num;
|
|
gentity_t *ent;
|
|
Player *pPlayer;
|
|
|
|
if (bNext) {
|
|
dir = 1;
|
|
num = m_iPlayerSpectating;
|
|
} else {
|
|
dir = -1;
|
|
if (m_iPlayerSpectating) {
|
|
num = m_iPlayerSpectating - 2;
|
|
} else {
|
|
num = game.maxclients - 1;
|
|
}
|
|
}
|
|
|
|
for (i = num; i < game.maxclients && i >= 0; i += dir) {
|
|
ent = &g_entities[i];
|
|
if (!ent->inuse || !ent->entity) {
|
|
continue;
|
|
}
|
|
|
|
pPlayer = (Player *)ent->entity;
|
|
|
|
if (!pPlayer->IsDead() && !pPlayer->IsSpectator() && IsValidSpectatePlayer(pPlayer)) {
|
|
m_iPlayerSpectating = i + 1;
|
|
client->ps.camera_flags &= ~CF_CAMERA_CUT_BIT;
|
|
client->ps.camera_flags |= (client->ps.camera_flags & CF_CAMERA_CUT_BIT) ^ CF_CAMERA_CUT_BIT;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (m_iPlayerSpectating) {
|
|
m_iPlayerSpectating = 0;
|
|
SetPlayerSpectate(bNext);
|
|
}
|
|
}
|
|
|
|
void Player::SetPlayerSpectateRandom(void)
|
|
{
|
|
Player *pPlayer;
|
|
int i;
|
|
int numvalid;
|
|
int iRandom;
|
|
|
|
numvalid = 0;
|
|
|
|
for (i = 0; i < game.maxclients; i++) {
|
|
gentity_t *ent = &g_entities[i];
|
|
if (!ent->inuse || !ent->entity) {
|
|
continue;
|
|
}
|
|
|
|
pPlayer = static_cast<Player *>(ent->entity);
|
|
if (!pPlayer->IsDead() && !pPlayer->IsSpectator() && IsValidSpectatePlayer(pPlayer)) {
|
|
numvalid++;
|
|
}
|
|
}
|
|
|
|
if (!numvalid) {
|
|
// There is no valid player to spectate
|
|
|
|
// Added in OPM.
|
|
// Clear the player spectating value
|
|
m_iPlayerSpectating = 0;
|
|
return;
|
|
}
|
|
|
|
iRandom = (int)(random() * numvalid);
|
|
|
|
for (i = 0; i < game.maxclients; i++) {
|
|
gentity_t *ent = &g_entities[i];
|
|
if (!ent->inuse || !ent->entity) {
|
|
continue;
|
|
}
|
|
|
|
pPlayer = static_cast<Player *>(ent->entity);
|
|
if (!pPlayer->IsDead() && !pPlayer->IsSpectator() && IsValidSpectatePlayer(pPlayer)) {
|
|
if (!iRandom) {
|
|
m_iPlayerSpectating = i + 1;
|
|
|
|
client->ps.camera_flags &= ~CF_CAMERA_CUT_BIT;
|
|
client->ps.camera_flags |= (client->ps.camera_flags & CF_CAMERA_CUT_BIT) ^ CF_CAMERA_CUT_BIT;
|
|
break;
|
|
}
|
|
|
|
iRandom--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::GetSpectateFollowOrientation(Player *pPlayer, Vector& vPos, Vector& vAng)
|
|
{
|
|
Vector forward, right, up;
|
|
Vector vCamOfs;
|
|
Vector start;
|
|
trace_t trace;
|
|
|
|
if (!g_spectatefollow_firstperson->integer) {
|
|
// spectating a player
|
|
vAng = pPlayer->GetVAngles();
|
|
|
|
AngleVectors(vAng, forward, right, up);
|
|
|
|
vCamOfs = pPlayer->origin;
|
|
vCamOfs[2] += pPlayer->viewheight;
|
|
|
|
vCamOfs += forward * g_spectatefollow_forward->value;
|
|
vCamOfs += right * g_spectatefollow_right->value;
|
|
vCamOfs += up * g_spectatefollow_up->value;
|
|
|
|
if (pPlayer->client->ps.fLeanAngle != 0.0f) {
|
|
vCamOfs += pPlayer->client->ps.fLeanAngle * 0.65f * right;
|
|
}
|
|
|
|
start = pPlayer->origin;
|
|
start[2] += pPlayer->maxs[2] - 2.0;
|
|
|
|
Vector vMins = Vector(-2, -2, 2);
|
|
Vector vMaxs = Vector(2, 2, 2);
|
|
|
|
trace =
|
|
G_Trace(start, vMins, vMaxs, vCamOfs, pPlayer, MASK_SHOT, false, "Player::GetSpectateFollowOrientation");
|
|
|
|
vAng[0] += g_spectatefollow_pitch->value * trace.fraction;
|
|
vPos = trace.endpos;
|
|
} else {
|
|
vAng = pPlayer->angles;
|
|
vPos = pPlayer->origin;
|
|
}
|
|
}
|
|
|
|
void Player::Spectator(Event *ev)
|
|
{
|
|
client->pers.dm_primary[0] = 0;
|
|
SetTeam(TEAM_SPECTATOR);
|
|
}
|
|
|
|
void Player::Leave_DM_Team(Event *ev)
|
|
{
|
|
// Fixed in OPM
|
|
// FIXME: should it be permanently disabled ?
|
|
#if 0
|
|
if (current_team)
|
|
{
|
|
dmManager.LeaveTeam(this);
|
|
}
|
|
else
|
|
{
|
|
gi.centerprintf(edict, gi.LV_ConvertString("You are not on a team"));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Player::Join_DM_Team(Event *ev)
|
|
{
|
|
teamtype_t team;
|
|
str teamname;
|
|
const char *join_message;
|
|
Entity *ent;
|
|
|
|
if (ev->isSubclassOf(ConsoleEvent) && disable_team_change) {
|
|
// Added in OPM
|
|
return;
|
|
}
|
|
|
|
teamname = ev->GetString(1);
|
|
|
|
if (!teamname.icmp("allies")) {
|
|
team = TEAM_ALLIES;
|
|
} else if (!teamname.icmp("axis") || !teamname.icmp("german") || !teamname.icmp("nazi")) {
|
|
team = TEAM_AXIS;
|
|
} else {
|
|
team = TEAM_AXIS;
|
|
}
|
|
|
|
if (current_team && current_team->m_teamnumber == team) {
|
|
//
|
|
// don't switch if on same team
|
|
//
|
|
return;
|
|
}
|
|
|
|
if (deadflag && deadflag != DEAD_DEAD) {
|
|
// ignore team switching if the player is dying and not dead
|
|
return;
|
|
}
|
|
|
|
if (ev->isSubclassOf(ConsoleEvent) && !CheckCanSwitchTeam(team)) {
|
|
return;
|
|
}
|
|
|
|
m_fTeamSelectTime = level.time;
|
|
SetTeam(team);
|
|
// Make sure to remove player from turret
|
|
RemoveFromVehiclesAndTurrets();
|
|
|
|
//
|
|
// Since 2.0: Remove projectiles the player shot
|
|
//
|
|
for (ent = G_NextEntity(NULL); ent; ent = G_NextEntity(ent)) {
|
|
if (ent->IsSubclassOfProjectile() && ent->edict->r.ownerNum == edict->r.ownerNum) {
|
|
ent->PostEvent(EV_Remove, 0);
|
|
}
|
|
}
|
|
|
|
if (client->pers.dm_primary[0]) {
|
|
if (IsSpectator()) {
|
|
if (m_fSpawnTimeLeft) {
|
|
m_bWaitingForRespawn = true;
|
|
} else if (AllowTeamRespawn()) {
|
|
EndSpectator();
|
|
|
|
if (deadflag) {
|
|
deadflag = DEAD_DEAD;
|
|
}
|
|
|
|
PostEvent(EV_Player_Respawn, 0);
|
|
gi.centerprintf(edict, " ");
|
|
}
|
|
} else if (g_gametype->integer >= GT_TEAM) {
|
|
client->pers.dm_primary[0] = 0;
|
|
UserSelectWeapon(false);
|
|
Spectator();
|
|
} else {
|
|
PostEvent(EV_Player_Respawn, 0);
|
|
}
|
|
} else if (IsSpectator()) {
|
|
UserSelectWeapon(true);
|
|
}
|
|
|
|
if (g_gametype->integer >= GT_TEAM) {
|
|
//
|
|
// in team game modes, display a message to indicate
|
|
// a player joined a team
|
|
//
|
|
if (GetTeam() == TEAM_ALLIES) {
|
|
join_message = "has joined the Allies";
|
|
} else if (GetTeam() == TEAM_AXIS) {
|
|
join_message = "has joined the Axis";
|
|
} else {
|
|
return;
|
|
}
|
|
|
|
G_PrintToAllClients(va("%s %s\n", client->pers.netname, gi.LV_ConvertString(join_message)), 2);
|
|
}
|
|
}
|
|
|
|
void Player::Auto_Join_DM_Team(Event *ev)
|
|
{
|
|
Event *event = new Event(EV_Player_JoinDMTeam);
|
|
|
|
if (dmManager.GetAutoJoinTeam() == TEAM_AXIS) {
|
|
event->AddString("axis");
|
|
} else {
|
|
event->AddString("allies");
|
|
}
|
|
|
|
ProcessEvent(event);
|
|
}
|
|
|
|
teamtype_t Player::GetTeam() const
|
|
{
|
|
return dm_team;
|
|
}
|
|
|
|
void Player::SetTeam(teamtype_t team)
|
|
{
|
|
dmManager.JoinTeam(this, team);
|
|
|
|
if (dm_team == TEAM_SPECTATOR) {
|
|
Spectator();
|
|
}
|
|
}
|
|
|
|
void Player::SetDM_Team(DM_Team *team)
|
|
{
|
|
current_team = team;
|
|
|
|
// clear the player's team
|
|
edict->s.eFlags &= ~EF_ANY_TEAM;
|
|
|
|
if (team) {
|
|
dm_team = static_cast<teamtype_t>(team->getNumber());
|
|
if (dm_team == TEAM_ALLIES) {
|
|
edict->s.eFlags |= EF_ALLIES;
|
|
} else if (dm_team == TEAM_AXIS) {
|
|
edict->s.eFlags |= EF_AXIS;
|
|
}
|
|
} else {
|
|
dm_team = TEAM_NONE;
|
|
}
|
|
|
|
client->pers.teamnum = dm_team;
|
|
G_SetClientConfigString(edict);
|
|
|
|
if (m_fTeamSelectTime != level.time && (edict->s.eFlags & (EF_ANY_TEAM))) {
|
|
InitModel();
|
|
}
|
|
}
|
|
|
|
DM_Team *Player::GetDM_Team()
|
|
{
|
|
return current_team;
|
|
}
|
|
|
|
bool Player::IsSpectator(void)
|
|
{
|
|
return (m_bSpectator || m_bTempSpectator);
|
|
}
|
|
|
|
void Player::BeginFight(void)
|
|
{
|
|
m_bAllowFighting = true;
|
|
}
|
|
|
|
void Player::EndFight(void)
|
|
{
|
|
m_bAllowFighting = false;
|
|
}
|
|
|
|
void Player::WarpToPoint(Entity *spawnpoint)
|
|
{
|
|
if (spawnpoint) {
|
|
setOrigin(spawnpoint->origin + Vector(0, 0, 1));
|
|
setAngles(spawnpoint->angles);
|
|
SetViewAngles(angles);
|
|
client->ps.camera_flags &= ~CF_CAMERA_CUT_BIT;
|
|
client->ps.camera_flags |= (client->ps.camera_flags & CF_CAMERA_CUT_BIT) ^ CF_CAMERA_CUT_BIT;
|
|
}
|
|
}
|
|
|
|
void Player::UpdateStatus(const char *s)
|
|
{
|
|
gi.SendServerCommand(edict - g_entities, "status \"%s\"", s);
|
|
}
|
|
|
|
void Player::HUDPrint(const char *s)
|
|
{
|
|
gi.SendServerCommand(edict - g_entities, "hudprint \"%s\"\n", s);
|
|
}
|
|
|
|
void Player::GibEvent(Event *ev)
|
|
{
|
|
qboolean hidemodel;
|
|
|
|
hidemodel = !ev->GetInteger(1);
|
|
|
|
if (com_blood->integer) {
|
|
if (hidemodel) {
|
|
gibbed = true;
|
|
takedamage = DAMAGE_NO;
|
|
setSolidType(SOLID_NOT);
|
|
hideModel();
|
|
}
|
|
|
|
CreateGibs(this, health, 0.75f, 3);
|
|
}
|
|
}
|
|
|
|
void Player::ArmorDamage(Event *ev)
|
|
{
|
|
int mod = ev->GetInteger(9);
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
// players that are not allowed fighting mustn't take damage
|
|
if (!m_bAllowFighting && mod != MOD_TELEFRAG) {
|
|
return;
|
|
}
|
|
|
|
Player *attacker = (Player *)ev->GetEntity(1);
|
|
|
|
if (attacker && attacker->IsSubclassOfPlayer()) {
|
|
if (attacker != this) {
|
|
if (g_gametype->integer >= GT_TEAM && !g_teamdamage->integer) {
|
|
// check for team damage
|
|
if (attacker->GetDM_Team() == GetDM_Team() && mod != MOD_TELEFRAG) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
pAttackerDistPointer = attacker;
|
|
fAttackerDispTime = g_drawattackertime->value + level.time;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_iNumHitsTaken++;
|
|
|
|
Sentient::ArmorDamage(ev);
|
|
|
|
Event *event = new Event;
|
|
|
|
event->AddEntity(ev->GetEntity(1));
|
|
event->AddFloat(ev->GetFloat(2));
|
|
event->AddEntity(ev->GetEntity(3));
|
|
event->AddVector(ev->GetVector(4));
|
|
event->AddVector(ev->GetVector(5));
|
|
event->AddVector(ev->GetVector(6));
|
|
event->AddInteger(ev->GetInteger(7));
|
|
event->AddInteger(ev->GetInteger(8));
|
|
event->AddInteger(ev->GetInteger(9));
|
|
event->AddInteger(ev->GetInteger(10));
|
|
event->AddEntity(this);
|
|
|
|
scriptedEvents[SE_DAMAGE].Trigger(event);
|
|
}
|
|
|
|
void Player::Disconnect(void)
|
|
{
|
|
Event *ev = new Event;
|
|
|
|
ev->AddListener(this);
|
|
scriptedEvents[SE_DISCONNECTED].Trigger(ev);
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
dmManager.RemovePlayer(this);
|
|
}
|
|
}
|
|
|
|
void Player::CallVote(Event *ev)
|
|
{
|
|
str arg1;
|
|
str arg2;
|
|
int numVoters;
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
return;
|
|
}
|
|
|
|
if (!g_allowvote->integer) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("Voting not allowed here.")));
|
|
return;
|
|
}
|
|
|
|
if (level.m_voteTime != 0.0f) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("A vote is already in progress.")));
|
|
return;
|
|
}
|
|
|
|
if (votecount >= MAX_VOTE_COUNT) {
|
|
if (m_fLastVoteTime) {
|
|
while (m_fLastVoteTime < level.time && votecount > 0) {
|
|
m_fLastVoteTime += 60;
|
|
votecount--;
|
|
}
|
|
}
|
|
|
|
if (votecount >= MAX_VOTE_COUNT) {
|
|
HUDPrint(
|
|
va("%s %d %s.\n",
|
|
gi.LV_ConvertString("You cannot call another vote for"),
|
|
(unsigned int)(m_fLastVoteTime - level.time + 1),
|
|
gi.LV_ConvertString("seconds"))
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (IsSpectator() || IsDead()) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("You are not allowed to call a vote as a spectator.")));
|
|
return;
|
|
}
|
|
|
|
arg1 = ev->GetString(1);
|
|
if (ev->NumArgs() > 1) {
|
|
arg2 = ev->GetString(2);
|
|
}
|
|
|
|
if (!atoi(arg1.c_str())) {
|
|
if (strchr(arg1.c_str(), ';') || strchr(arg2.c_str(), ';')) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("Invalid vote string.")));
|
|
return;
|
|
}
|
|
|
|
if (Q_stricmp(arg1.c_str(), "restart") && Q_stricmp(arg1.c_str(), "nextmap") && Q_stricmp(arg1.c_str(), "map")
|
|
&& Q_stricmp(arg1.c_str(), "g_gametype") && Q_stricmp(arg1.c_str(), "kick")
|
|
&& Q_stricmp(arg1.c_str(), "clientkick") && Q_stricmp(arg1.c_str(), "fraglimit")) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("Invalid vote string.")));
|
|
HUDPrint(va(
|
|
"%s restart, nextmap, map <mapname>, g_gametype <n>, fraglimit <n>, timelimit <n>, kick <player>, and "
|
|
"clientkick <player #>.\n",
|
|
gi.LV_ConvertString("Vote commands are:")
|
|
));
|
|
|
|
return;
|
|
}
|
|
|
|
if (!Q_stricmp(arg1.c_str(), "kick")) {
|
|
//
|
|
// check for a valid player
|
|
//
|
|
gentity_t *ent;
|
|
int i;
|
|
|
|
for (i = 0; i < game.maxclients; i++) {
|
|
ent = &g_entities[i];
|
|
|
|
if (!ent->inuse || !ent->client || !ent->entity) {
|
|
continue;
|
|
}
|
|
|
|
if (!Q_stricmp(ent->client->pers.netname, arg2.c_str())) {
|
|
// Prevent the player from kicking himself out
|
|
if (ent->entity == this) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("You are not allowed to kick yourself.")));
|
|
return;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == game.maxclients) {
|
|
HUDPrint(va("%s %s\n", arg2.c_str(), gi.LV_ConvertString("is not a valid player name to kick.")));
|
|
return;
|
|
}
|
|
} else if (!Q_stricmp(arg1.c_str(), "map") && *sv_nextmap->string) {
|
|
level.m_voteString = va("%s %s; set next map \"%s\"", arg1.c_str(), arg2.c_str(), arg2.c_str());
|
|
} else {
|
|
level.m_voteString = va("%s %s", arg1.c_str(), arg2.c_str());
|
|
}
|
|
|
|
if (level.m_nextVoteTime) {
|
|
level.m_nextVoteTime = 0;
|
|
gi.SendConsoleCommand(va("%s", level.m_voteString.c_str()));
|
|
}
|
|
|
|
if (!Q_stricmp(arg1.c_str(), "g_gametype")) {
|
|
int gametypeNum;
|
|
|
|
// get the gametype number
|
|
gametypeNum = atoi(arg2.c_str());
|
|
if (gametypeNum <= GT_SINGLE_PLAYER || gametypeNum >= GT_MAX_GAME_TYPE) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("Invalid gametype for a vote.")));
|
|
return;
|
|
}
|
|
|
|
level.m_voteString = va("%s %i", arg1.c_str(), gametypeNum);
|
|
|
|
switch (gametypeNum) {
|
|
case GT_FFA:
|
|
level.m_voteName = "Game Type Free-For-All";
|
|
break;
|
|
case GT_TEAM:
|
|
level.m_voteName = "Game Type Match";
|
|
break;
|
|
case GT_TEAM_ROUNDS:
|
|
level.m_voteName = "Game Type Round-Based-Match";
|
|
break;
|
|
case GT_OBJECTIVE:
|
|
level.m_voteName = "Game Type Objective-Match";
|
|
break;
|
|
case GT_TOW:
|
|
level.m_voteName = "Game Type Tug of War";
|
|
break;
|
|
case GT_LIBERATION:
|
|
level.m_voteName = "Game Type Liberation";
|
|
break;
|
|
default:
|
|
HUDPrint(va("%s %s %d\n", gi.LV_ConvertString("Game Type"), arg1.c_str(), gametypeNum));
|
|
return;
|
|
}
|
|
} else if (!Q_stricmp(arg1.c_str(), "map")) {
|
|
if (*sv_nextmap->string) {
|
|
level.m_voteString = va("%s %s; set nextmap \"%s\"", arg1.c_str(), arg2.c_str(), sv_nextmap->string);
|
|
} else {
|
|
level.m_voteString = va("%s %s", arg1.c_str(), arg2.c_str());
|
|
}
|
|
|
|
level.m_voteName = va("Map %s", arg2.c_str());
|
|
} else {
|
|
level.m_voteString = va("%s %s", arg1.c_str(), arg2.c_str());
|
|
level.m_voteName = level.m_voteString;
|
|
}
|
|
} else {
|
|
int voteIndex;
|
|
int subListIndex;
|
|
str voteOptionCommand;
|
|
str voteOptionSubCommand;
|
|
str voteOptionName;
|
|
str voteOptionSubName;
|
|
voteoptiontype_t optionType;
|
|
|
|
union {
|
|
int optionInteger;
|
|
float optionFloat;
|
|
int optionClientNum;
|
|
};
|
|
|
|
gentity_t *ent;
|
|
|
|
char buffer[64];
|
|
|
|
voteIndex = atoi(arg1.c_str());
|
|
if (!level.GetVoteOptionMain(voteIndex, &voteOptionCommand, &optionType)) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("Invalid vote option.")));
|
|
return;
|
|
}
|
|
|
|
level.GetVoteOptionMainName(voteIndex, &voteOptionName);
|
|
|
|
switch (optionType) {
|
|
case VOTE_NO_CHOICES:
|
|
level.m_voteString = voteOptionCommand;
|
|
level.m_voteName = voteOptionName;
|
|
break;
|
|
case VOTE_OPTION_LIST:
|
|
subListIndex = atoi(arg2.c_str());
|
|
|
|
if (!level.GetVoteOptionSub(voteIndex, subListIndex, &voteOptionSubCommand)) {
|
|
HUDPrint(
|
|
va("%s %i %s \"%s\".\n",
|
|
gi.LV_ConvertString("Invalid vote choice"),
|
|
subListIndex,
|
|
gi.LV_ConvertString("for vote option"),
|
|
voteOptionName.c_str())
|
|
);
|
|
return;
|
|
}
|
|
|
|
level.m_voteString = va("%s %s", voteOptionCommand.c_str(), voteOptionSubCommand.c_str());
|
|
// get the sub-option name
|
|
level.GetVoteOptionSubName(voteIndex, subListIndex, &voteOptionSubName);
|
|
level.m_voteName =
|
|
va("%s %s", gi.LV_ConvertString(voteOptionName.c_str()), gi.LV_ConvertString(voteOptionSubName.c_str())
|
|
);
|
|
break;
|
|
case VOTE_OPTION_TEXT:
|
|
if (strchr(arg2.c_str(), ';')) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("Invalid vote text entered.")));
|
|
return;
|
|
}
|
|
|
|
level.m_voteString = va("%s %s", voteOptionCommand.c_str(), arg2.c_str());
|
|
level.m_voteName = va("%s %s", gi.LV_ConvertString(voteOptionName.c_str()), arg2.c_str());
|
|
break;
|
|
case VOTE_OPTION_INTEGER:
|
|
optionInteger = atoi(arg2.c_str());
|
|
Com_sprintf(buffer, sizeof(buffer), "%d", optionInteger);
|
|
|
|
if (Q_stricmp(buffer, arg2.c_str())) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("Invalid vote integer entered.")));
|
|
return;
|
|
}
|
|
|
|
level.m_voteString = va("%s %i", voteOptionCommand.c_str(), optionInteger);
|
|
level.m_voteName = va("%s %i", gi.LV_ConvertString(voteOptionName.c_str()), optionInteger);
|
|
break;
|
|
case VOTE_OPTION_FLOAT:
|
|
optionFloat = atof(arg2.c_str());
|
|
Com_sprintf(buffer, sizeof(buffer), "%f", optionFloat);
|
|
|
|
if (Q_stricmp(buffer, arg2.c_str())) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("Invalid vote float entered.")));
|
|
return;
|
|
}
|
|
|
|
level.m_voteString = va("%s %g", voteOptionCommand.c_str(), optionFloat);
|
|
level.m_voteName = va("%s %g", gi.LV_ConvertString(voteOptionName.c_str()), optionFloat);
|
|
break;
|
|
case VOTE_OPTION_CLIENT:
|
|
case VOTE_OPTION_CLIENT_NOT_SELF:
|
|
optionClientNum = atoi(arg2.c_str());
|
|
if (optionClientNum < 0 || optionClientNum >= game.maxclients) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("Invalid client number for a vote.")));
|
|
return;
|
|
}
|
|
|
|
ent = &g_entities[optionClientNum];
|
|
if (!ent->inuse || !ent->client || !ent->entity) {
|
|
HUDPrint(va("%s\n", gi.LV_ConvertString("Client selected for the vote is not connected.")));
|
|
return;
|
|
}
|
|
|
|
level.m_voteString = va("%s %i", voteOptionCommand.c_str(), optionClientNum);
|
|
level.m_voteName =
|
|
va("%s #%i: %s", gi.LV_ConvertString(voteOptionName.c_str()), optionClientNum, ent->client->pers.netname
|
|
);
|
|
break;
|
|
default:
|
|
level.GetVoteOptionMainName(voteIndex, &voteOptionName);
|
|
gi.Printf(
|
|
"ERROR: Vote option (\"%s\" \"%s\") with unknown vote option type\n",
|
|
voteOptionName.c_str(),
|
|
voteOptionCommand.c_str()
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
G_PrintToAllClients(va("%s %s.\n", client->pers.netname, gi.LV_ConvertString("called a vote")));
|
|
|
|
level.m_voteTime = (level.svsFloatTime - level.svsStartFloatTime) * 1000;
|
|
level.m_voteYes = 1;
|
|
level.m_voteNo = 0;
|
|
|
|
// Reset all player's vote
|
|
numVoters = 0;
|
|
|
|
for (int i = 0; i < game.maxclients; i++) {
|
|
gentity_t *ent = &g_entities[i];
|
|
|
|
if (!ent->client || !ent->inuse) {
|
|
continue;
|
|
}
|
|
|
|
Player *p = (Player *)ent->entity;
|
|
p->voted = false;
|
|
|
|
numVoters++;
|
|
}
|
|
|
|
level.m_numVoters = numVoters;
|
|
client->ps.voted = true;
|
|
voted = true;
|
|
votecount++;
|
|
|
|
m_fLastVoteTime = level.time + 60;
|
|
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
//
|
|
// clients below version 2.0 don't support vote cs
|
|
//
|
|
gi.setConfigstring(CS_VOTE_TIME, va("%i", level.m_voteTime));
|
|
gi.setConfigstring(CS_VOTE_STRING, level.m_voteName.c_str());
|
|
gi.setConfigstring(CS_VOTE_YES, va("%i", level.m_voteYes));
|
|
gi.setConfigstring(CS_VOTE_NO, va("%i", level.m_voteNo));
|
|
gi.setConfigstring(CS_VOTE_UNDECIDED, va("%i", level.m_numVoters - (level.m_voteYes + level.m_voteNo)));
|
|
}
|
|
}
|
|
|
|
void Player::Vote(Event *ev)
|
|
{
|
|
str arg1;
|
|
|
|
if (level.m_voteTime == 0.0f) {
|
|
HUDPrint(gi.LV_ConvertString("No vote in progress."));
|
|
return;
|
|
}
|
|
|
|
if (client->ps.voted) {
|
|
HUDPrint(gi.LV_ConvertString("Vote already cast."));
|
|
return;
|
|
}
|
|
|
|
if (ev->NumArgs() != 1) {
|
|
HUDPrint(va("%s: vote <1|0|y|n>", gi.LV_ConvertString("Usage")));
|
|
return;
|
|
}
|
|
|
|
HUDPrint(gi.LV_ConvertString("Vote cast."));
|
|
client->ps.voted = true;
|
|
|
|
arg1 = ev->GetString(1);
|
|
voted = (arg1[0] == 'y') || (arg1[0] == 'Y') || (arg1[0] == '1');
|
|
}
|
|
|
|
void Player::RetrieveVoteOptions(Event *ev)
|
|
{
|
|
if (m_fNextVoteOptionTime > level.time) {
|
|
gi.SendServerCommand(edict - g_entities, "vo0 \"\"\n");
|
|
gi.SendServerCommand(edict - g_entities, "vo2 \"\"\n");
|
|
} else {
|
|
m_fNextVoteOptionTime = level.time + 2.0;
|
|
level.SendVoteOptionsFile(edict);
|
|
}
|
|
}
|
|
|
|
void Player::EventPrimaryDMWeapon(Event *ev)
|
|
{
|
|
str dm_weapon = ev->GetString(1);
|
|
bool bIsBanned = false;
|
|
|
|
if (!dm_weapon.length()) {
|
|
// Added in OPM.
|
|
// Prevent the player from cheating by going into spectator
|
|
return;
|
|
}
|
|
|
|
if (!str::icmp(dm_weapon, "shotgun")) {
|
|
bIsBanned = (dmflags->integer & DF_WEAPON_NO_SHOTGUN);
|
|
} else if (!str::icmp(dm_weapon, "rifle")) {
|
|
bIsBanned = (dmflags->integer & DF_WEAPON_NO_RIFLE);
|
|
} else if (!str::icmp(dm_weapon, "sniper")) {
|
|
bIsBanned = (dmflags->integer & DF_WEAPON_NO_SNIPER);
|
|
} else if (!str::icmp(dm_weapon, "smg")) {
|
|
bIsBanned = (dmflags->integer & DF_WEAPON_NO_SMG);
|
|
} else if (!str::icmp(dm_weapon, "mg")) {
|
|
bIsBanned = (dmflags->integer & DF_WEAPON_NO_MG);
|
|
} else if (!str::icmp(dm_weapon, "heavy")) {
|
|
bIsBanned = (dmflags->integer & DF_WEAPON_NO_ROCKET);
|
|
} else if (!str::icmp(dm_weapon, "landmine")) {
|
|
bIsBanned = (dmflags->integer & DF_WEAPON_NO_LANDMINE) || !QueryLandminesAllowed();
|
|
} else if (!str::icmp(dm_weapon, "auto")) {
|
|
const char *primaryList[7];
|
|
size_t numPrimaries = 0;
|
|
|
|
//
|
|
// Added in OPM
|
|
// Choose a random allowed weapon
|
|
//
|
|
if (!(dmflags->integer & DF_WEAPON_NO_SHOTGUN)) {
|
|
primaryList[numPrimaries++] = "shotgun";
|
|
}
|
|
if (!(dmflags->integer & DF_WEAPON_NO_RIFLE)) {
|
|
primaryList[numPrimaries++] = "rifle";
|
|
}
|
|
if (!(dmflags->integer & DF_WEAPON_NO_SNIPER)) {
|
|
primaryList[numPrimaries++] = "sniper";
|
|
}
|
|
if (!(dmflags->integer & DF_WEAPON_NO_SMG)) {
|
|
primaryList[numPrimaries++] = "smg";
|
|
}
|
|
if (!(dmflags->integer & DF_WEAPON_NO_MG)) {
|
|
primaryList[numPrimaries++] = "mg";
|
|
}
|
|
if (!(dmflags->integer & DF_WEAPON_NO_ROCKET)) {
|
|
primaryList[numPrimaries++] = "heavy";
|
|
}
|
|
if (!(dmflags->integer & DF_WEAPON_NO_LANDMINE) && QueryLandminesAllowed()) {
|
|
primaryList[numPrimaries++] = "landmine";
|
|
}
|
|
|
|
if (numPrimaries) {
|
|
dm_weapon = primaryList[rand() % numPrimaries];
|
|
} else {
|
|
bIsBanned = qtrue;
|
|
}
|
|
}
|
|
|
|
if (bIsBanned) {
|
|
gi.SendServerCommand(
|
|
edict - g_entities, "print \"" HUD_MESSAGE_WHITE "%s\n\"", "That weapon is currently banned."
|
|
);
|
|
return;
|
|
}
|
|
|
|
Q_strncpyz(client->pers.dm_primary, dm_weapon.c_str(), sizeof(client->pers.dm_primary));
|
|
|
|
if (m_bSpectator) {
|
|
if (current_team && (current_team->m_teamnumber == TEAM_AXIS || current_team->m_teamnumber == TEAM_ALLIES)) {
|
|
if (m_fSpawnTimeLeft) {
|
|
m_bWaitingForRespawn = true;
|
|
} else if (AllowTeamRespawn()) {
|
|
EndSpectator();
|
|
|
|
if (deadflag) {
|
|
deadflag = DEAD_DEAD;
|
|
}
|
|
|
|
PostEvent(EV_Player_Respawn, 0);
|
|
|
|
gi.centerprintf(edict, "");
|
|
}
|
|
} else {
|
|
gi.SendServerCommand(edict - g_entities, "stufftext \"wait 250;pushmenu_teamselect\"");
|
|
}
|
|
} else {
|
|
gi.SendServerCommand(
|
|
edict - g_entities, "print \"" HUD_MESSAGE_WHITE "%s\n\"", "Will switch to new weapon next time you respawn"
|
|
);
|
|
}
|
|
}
|
|
|
|
void Player::DeadBody(Event *ev)
|
|
{
|
|
Body *body;
|
|
|
|
if (knockdown) {
|
|
return;
|
|
}
|
|
|
|
knockdown = true;
|
|
|
|
body = new Body;
|
|
body->setModel(model);
|
|
|
|
for (int i = 0; i < MAX_FRAMEINFOS; i++) {
|
|
body->edict->s.frameInfo[i] = edict->s.frameInfo[i];
|
|
}
|
|
|
|
body->edict->s.actionWeight = edict->s.actionWeight;
|
|
body->edict->s.scale = edict->s.scale;
|
|
|
|
body->setOrigin(origin);
|
|
body->setAngles(angles);
|
|
|
|
body->edict->s.eFlags &= ~(EF_AXIS | EF_ALLIES);
|
|
|
|
if (GetTeam() == TEAM_ALLIES) {
|
|
edict->s.eFlags |= EF_ALLIES;
|
|
} else if (GetTeam() == TEAM_AXIS) {
|
|
edict->s.eFlags |= EF_AXIS;
|
|
}
|
|
}
|
|
|
|
void Player::WonMatch(void)
|
|
{
|
|
num_won_matches++;
|
|
}
|
|
|
|
void Player::LostMatch(void)
|
|
{
|
|
num_lost_matches++;
|
|
}
|
|
|
|
void Player::ArmWithWeapons(Event *ev)
|
|
{
|
|
EquipWeapons();
|
|
}
|
|
|
|
void Player::EventGetCurrentDMWeaponType(Event *ev)
|
|
{
|
|
ev->AddString(GetCurrentDMWeaponType());
|
|
}
|
|
|
|
void Player::PhysicsOff(Event *ev)
|
|
{
|
|
if (g_target_game > TG_MOH || g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
// Added in 2.0
|
|
// Reset the state to STAND before disabling physics
|
|
EvaluateState(statemap_Torso->FindState("STAND"), statemap_Legs->FindState("STAND"));
|
|
}
|
|
|
|
flags |= FL_IMMOBILE;
|
|
}
|
|
|
|
void Player::PhysicsOn(Event *ev)
|
|
{
|
|
flags &= ~FL_IMMOBILE;
|
|
}
|
|
|
|
void Player::GetIsSpectator(Event *ev)
|
|
{
|
|
ev->AddInteger(IsSpectator());
|
|
}
|
|
|
|
void Player::EventSetInJail(Event *ev)
|
|
{
|
|
m_bIsInJail = ev->GetBoolean(1);
|
|
}
|
|
|
|
bool Player::IsInJail() const
|
|
{
|
|
return m_bIsInJail;
|
|
}
|
|
|
|
void Player::EventGetInJail(Event *ev)
|
|
{
|
|
ev->AddInteger(m_bIsInJail);
|
|
}
|
|
|
|
void Player::GetNationalityPrefix(Event *ev)
|
|
{
|
|
nationality_t nationality;
|
|
|
|
if (GetTeam() == TEAM_AXIS) {
|
|
nationality = GetPlayerAxisTeamType(client->pers.dm_playergermanmodel);
|
|
} else {
|
|
nationality = GetPlayerAlliedTeamType(client->pers.dm_playermodel);
|
|
}
|
|
|
|
switch (nationality) {
|
|
case NA_RUSSIAN:
|
|
ev->AddString("dfrru");
|
|
break;
|
|
case NA_ITALIAN:
|
|
ev->AddString("denit");
|
|
break;
|
|
case NA_BRITISH:
|
|
ev->AddString("dfruk");
|
|
break;
|
|
case NA_AMERICAN:
|
|
ev->AddString("dfr");
|
|
break;
|
|
|
|
case NA_NONE:
|
|
default:
|
|
ev->AddString("dfr");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Player::GetIsDisguised(Event *ev)
|
|
{
|
|
ev->AddInteger(m_bIsDisguised);
|
|
}
|
|
|
|
void Player::GetHasDisguise(Event *ev)
|
|
{
|
|
ev->AddInteger(m_bHasDisguise);
|
|
}
|
|
|
|
void Player::SetHasDisguise(Event *ev)
|
|
{
|
|
m_bHasDisguise = ev->GetBoolean(1);
|
|
}
|
|
|
|
void Player::SetObjectiveCount(Event *ev)
|
|
{
|
|
m_iObjectivesCompleted = ev->GetInteger(1);
|
|
m_iNumObjectives = ev->GetInteger(2);
|
|
}
|
|
|
|
void Player::Stats(Event *ev)
|
|
{
|
|
char entry[2048];
|
|
int i;
|
|
str szPreferredWeapon;
|
|
str szGunneryEvaluation;
|
|
int iNumHeadShots;
|
|
int iNumTorsoShots;
|
|
int iNumLeftLegShots;
|
|
int iNumRightLegShots;
|
|
int iNumGroinShots;
|
|
int iNumLeftArmShots;
|
|
int iNumRightArmShots;
|
|
int iNumShotsFired;
|
|
int iNumHits;
|
|
int iBestNumHits;
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
// Only works in singleplayer
|
|
return;
|
|
}
|
|
|
|
szPreferredWeapon = "none";
|
|
szGunneryEvaluation = "none";
|
|
iNumHeadShots = m_iNumHeadShots;
|
|
iNumTorsoShots = m_iNumTorsoShots;
|
|
iNumLeftLegShots = m_iNumLeftLegShots;
|
|
iNumRightLegShots = m_iNumRightLegShots;
|
|
iNumGroinShots = m_iNumGroinShots;
|
|
iNumLeftArmShots = m_iNumLeftArmShots;
|
|
iNumRightArmShots = m_iNumRightArmShots;
|
|
iNumShotsFired = m_iNumShotsFired;
|
|
iNumHits = m_iNumHits;
|
|
iBestNumHits = 0;
|
|
|
|
for (i = 1; i <= inventory.NumObjects(); i++) {
|
|
Entity *pEnt = G_GetEntity(inventory.ObjectAt(i));
|
|
if (pEnt->IsSubclassOfWeapon()) {
|
|
Weapon *pWeap = static_cast<Weapon *>(pEnt);
|
|
|
|
iNumHeadShots += pWeap->m_iNumHeadShots;
|
|
iNumTorsoShots += pWeap->m_iNumTorsoShots;
|
|
iNumLeftLegShots += pWeap->m_iNumLeftLegShots;
|
|
iNumRightLegShots += pWeap->m_iNumRightLegShots;
|
|
iNumGroinShots += pWeap->m_iNumGroinShots;
|
|
iNumLeftArmShots += pWeap->m_iNumLeftArmShots;
|
|
iNumRightArmShots += pWeap->m_iNumRightArmShots;
|
|
iNumShotsFired += pWeap->m_iNumShotsFired;
|
|
iNumHits += pWeap->m_iNumHits;
|
|
|
|
if (pWeap->m_iNumHits > iBestNumHits) {
|
|
szPreferredWeapon = pWeap->item_name;
|
|
iBestNumHits = pWeap->m_iNumHits;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_sPerferredWeaponOverride.length()) {
|
|
szPreferredWeapon = m_sPerferredWeaponOverride;
|
|
}
|
|
|
|
if (iNumHits) {
|
|
Com_sprintf(
|
|
entry,
|
|
sizeof(entry),
|
|
"%i %i %i %i %.1f \"%s\" %i %i %i \"%.1f\" \"%.1f\" \"%.1f\" \"%.1f\" \"%.1f\" \"%.1f\" \"%.1f\" \"%s\" %i "
|
|
"%i %i",
|
|
m_iNumObjectives,
|
|
m_iObjectivesCompleted,
|
|
iNumShotsFired,
|
|
iNumHits,
|
|
(iNumHits / iNumShotsFired * 100.f),
|
|
szPreferredWeapon.c_str(),
|
|
m_iNumHitsTaken,
|
|
m_iNumObjectsDestroyed,
|
|
m_iNumEnemiesKilled,
|
|
iNumHeadShots * 100.f / iNumHits,
|
|
iNumTorsoShots * 100.f / iNumHits,
|
|
iNumLeftLegShots * 100.f / iNumHits,
|
|
iNumRightLegShots * 100.f / iNumHits,
|
|
iNumGroinShots * 100.f / iNumHits,
|
|
iNumLeftArmShots * 100.f / iNumHits,
|
|
iNumRightArmShots * 100.f / iNumHits,
|
|
szGunneryEvaluation.c_str(),
|
|
g_gotmedal->integer,
|
|
g_success->integer,
|
|
g_failed->integer
|
|
);
|
|
} else {
|
|
Com_sprintf(
|
|
entry,
|
|
sizeof(entry),
|
|
"%i %i %i %i %i \"%s\" %i %i %i \"%i\" \"%i\" \"%i\" \"%i\" \"%i\" \"%i\" \"%i\" \"%s\" %i %i %i",
|
|
m_iNumObjectives,
|
|
m_iObjectivesCompleted,
|
|
iNumShotsFired,
|
|
0,
|
|
0,
|
|
szPreferredWeapon.c_str(),
|
|
m_iNumHitsTaken,
|
|
m_iNumObjectsDestroyed,
|
|
m_iNumEnemiesKilled,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
0,
|
|
szGunneryEvaluation.c_str(),
|
|
g_gotmedal->integer,
|
|
g_success->integer,
|
|
g_failed->integer
|
|
);
|
|
}
|
|
|
|
gi.SendServerCommand(edict - g_entities, "stats %s", entry);
|
|
}
|
|
|
|
void Player::EventStuffText(Event *ev)
|
|
{
|
|
if (level.spawning) {
|
|
Event *event = new Event(EV_Player_StuffText);
|
|
event->AddValue(ev->GetValue(1));
|
|
PostEvent(event, level.frametime, 0);
|
|
} else {
|
|
gi.SendServerCommand(edict - g_entities, "stufftext \"%s\"", ev->GetString(1).c_str());
|
|
}
|
|
}
|
|
|
|
void Player::EventSetVoiceType(Event *ev)
|
|
{
|
|
str sVoiceName = ev->GetString(1);
|
|
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
if (!sVoiceName.icmp("american")) {
|
|
m_voiceType = PVT_ALLIED_AMERICAN;
|
|
} else if (!sVoiceName.icmp("british")) {
|
|
m_voiceType = PVT_ALLIED_BRITISH;
|
|
} else if (!sVoiceName.icmp("russian")) {
|
|
m_voiceType = PVT_ALLIED_RUSSIAN;
|
|
} else if (!sVoiceName.icmp("german")) {
|
|
m_voiceType = PVT_AXIS_GERMAN;
|
|
} else if (!sVoiceName.icmp("italian")) {
|
|
m_voiceType = PVT_AXIS_ITALIAN;
|
|
} else {
|
|
m_voiceType = PVT_NONE_SET;
|
|
}
|
|
} else {
|
|
if (!sVoiceName.icmp("airborne")) {
|
|
m_voiceType = PVT_ALLIED_AIRBORNE;
|
|
} else if (!sVoiceName.icmp("manon")) {
|
|
m_voiceType = PVT_ALLIED_MANON;
|
|
} else if (!sVoiceName.icmp("SAS")) {
|
|
m_voiceType = PVT_ALLIED_SAS;
|
|
} else if (!sVoiceName.icmp("pilot")) {
|
|
m_voiceType = PVT_ALLIED_PILOT;
|
|
} else if (!sVoiceName.icmp("army")) {
|
|
m_voiceType = PVT_ALLIED_ARMY;
|
|
} else if (!sVoiceName.icmp("ranger")) {
|
|
m_voiceType = PVT_ALLIED_RANGER;
|
|
} else if (!sVoiceName.icmp("axis1")) {
|
|
m_voiceType = PVT_AXIS_AXIS1;
|
|
} else if (!sVoiceName.icmp("axis2")) {
|
|
m_voiceType = PVT_AXIS_AXIS2;
|
|
} else if (!sVoiceName.icmp("axis3")) {
|
|
m_voiceType = PVT_AXIS_AXIS3;
|
|
} else if (!sVoiceName.icmp("axis4")) {
|
|
m_voiceType = PVT_AXIS_AXIS4;
|
|
} else if (!sVoiceName.icmp("axis5")) {
|
|
m_voiceType = PVT_AXIS_AXIS5;
|
|
} else {
|
|
m_voiceType = PVT_NONE_SET;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::GetTeamDialogPrefix(str& outPrefix)
|
|
{
|
|
if (GetTeam() == TEAM_AXIS) {
|
|
outPrefix = "axis_";
|
|
} else {
|
|
outPrefix = "allied_";
|
|
}
|
|
|
|
if (g_protocol >= protocol_e::PROTOCOL_MOHTA_MIN) {
|
|
switch (m_voiceType) {
|
|
case PVT_ALLIED_AMERICAN:
|
|
outPrefix += "american_";
|
|
break;
|
|
case PVT_ALLIED_BRITISH:
|
|
outPrefix += "british_";
|
|
break;
|
|
case PVT_ALLIED_RUSSIAN:
|
|
outPrefix += "russian_";
|
|
break;
|
|
case PVT_AXIS_START:
|
|
outPrefix += "german_";
|
|
break;
|
|
case PVT_AXIS_ITALIAN:
|
|
outPrefix += "italian_";
|
|
break;
|
|
default:
|
|
if (GetTeam() == TEAM_AXIS) {
|
|
outPrefix += "german_";
|
|
} else {
|
|
outPrefix += "american_";
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
switch (m_voiceType) {
|
|
case PVT_ALLIED_AIRBORNE:
|
|
outPrefix += "airborne_";
|
|
break;
|
|
|
|
case PVT_ALLIED_MANON:
|
|
outPrefix += "manon_";
|
|
break;
|
|
|
|
case PVT_ALLIED_SAS:
|
|
outPrefix += "sas_";
|
|
break;
|
|
|
|
case PVT_ALLIED_PILOT:
|
|
outPrefix += "pilot_";
|
|
break;
|
|
|
|
case PVT_ALLIED_ARMY:
|
|
outPrefix += "army_";
|
|
break;
|
|
|
|
case PVT_ALLIED_RANGER:
|
|
outPrefix += "ranger_";
|
|
break;
|
|
|
|
case PVT_AXIS_AXIS1:
|
|
outPrefix += "axis1_";
|
|
break;
|
|
|
|
case PVT_AXIS_AXIS2:
|
|
outPrefix += "axis2_";
|
|
break;
|
|
|
|
case PVT_AXIS_AXIS3:
|
|
outPrefix += "axis3_";
|
|
break;
|
|
|
|
case PVT_AXIS_AXIS4:
|
|
outPrefix += "axis4_";
|
|
break;
|
|
|
|
case PVT_AXIS_AXIS5:
|
|
outPrefix += "axis5_";
|
|
break;
|
|
|
|
default:
|
|
if (dm_team != TEAM_AXIS) {
|
|
outPrefix += "army_";
|
|
} else {
|
|
outPrefix += "axis4_";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::PlayInstantMessageSound(const char *name)
|
|
{
|
|
str soundName;
|
|
|
|
if (g_protocol < PROTOCOL_MOHTA_MIN) {
|
|
gi.DPrintf2("Instant message sound isn't supported on protocol below version 15");
|
|
return;
|
|
}
|
|
|
|
GetTeamDialogPrefix(soundName);
|
|
|
|
soundName += name;
|
|
|
|
gi.MSG_SetClient(client->ps.clientNum);
|
|
gi.MSG_StartCGM(CGM_VOICE_CHAT);
|
|
gi.MSG_WriteCoord(m_vViewPos[0]);
|
|
gi.MSG_WriteCoord(m_vViewPos[1]);
|
|
gi.MSG_WriteCoord(m_vViewPos[2]);
|
|
gi.MSG_WriteBits(0, 1);
|
|
gi.MSG_WriteBits(edict - g_entities, 6);
|
|
gi.MSG_WriteString(soundName.c_str());
|
|
gi.MSG_EndCGM();
|
|
}
|
|
|
|
void Player::EventDMMessage(Event *ev)
|
|
{
|
|
int i;
|
|
//int iStringLength;
|
|
int iMode = 0;
|
|
str sToken;
|
|
char szPrintString[MAX_SAY_TEXT]; // it's MAX_STRING_CHARS in mohaa
|
|
size_t iStringLength;
|
|
const char *pTmpInstantMsg = "";
|
|
qboolean bInstaMessage = qfalse;
|
|
AliasListNode_t *pSoundAlias = NULL;
|
|
const char *pszAliasname = NULL;
|
|
str sAliasName;
|
|
str sRandomAlias;
|
|
gentity_t *ent;
|
|
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
return;
|
|
}
|
|
|
|
if (ev->NumArgs() <= 1) {
|
|
return;
|
|
}
|
|
|
|
sToken = ev->GetString(2);
|
|
|
|
// Check for taunts
|
|
if (sToken.length() == 3 && *sToken == '*' && sToken[1] > '0' && sToken[1] <= '9' && sToken[2] > '0'
|
|
&& sToken[2] <= '9') {
|
|
unsigned int n1, n2;
|
|
|
|
if (IsSpectator() || IsDead()) {
|
|
// spectators or death players can't talk
|
|
return;
|
|
}
|
|
|
|
if (edict->r.svFlags & SVF_NOCLIENT) {
|
|
// Changed in OPM
|
|
// Don't send a voice chat message if the entity is not sent to client
|
|
return;
|
|
}
|
|
|
|
if (!g_instamsg_allowed->integer) {
|
|
// Added in OPM
|
|
return;
|
|
}
|
|
|
|
if (g_instamsg_minDelay->integer > 0 && level.inttime < m_iInstantMessageTime + g_instamsg_minDelay->integer) {
|
|
// Added in OPM
|
|
return;
|
|
}
|
|
|
|
GetTeamDialogPrefix(sAliasName);
|
|
if (g_target_game >= target_game_e::TG_MOHTT && sToken[1] == '6') {
|
|
// Added in 2.30
|
|
// Liberation messages
|
|
sAliasName += va("lib%c", (sToken[2] + '0'));
|
|
} else {
|
|
sAliasName += va("%c%c", (sToken[1] + '0'), (sToken[2] + '0'));
|
|
}
|
|
|
|
sRandomAlias = GetRandomAlias(sAliasName, &pSoundAlias);
|
|
|
|
// find a random alias
|
|
if (sRandomAlias.length() > 0) {
|
|
pszAliasname = sRandomAlias.c_str();
|
|
}
|
|
|
|
if (!pszAliasname) {
|
|
pszAliasname = gi.GlobalAlias_FindRandom(sAliasName, &pSoundAlias);
|
|
}
|
|
|
|
if (!pszAliasname || !pSoundAlias) {
|
|
return;
|
|
}
|
|
|
|
n1 = sToken[1] - '1';
|
|
n2 = sToken[2] - '1';
|
|
|
|
if (g_protocol >= PROTOCOL_MOHTA_MIN) {
|
|
if (n1 >= ARRAY_LEN(pInstantMsgEng) || n2 >= ARRAY_LEN(pInstantMsgEng[0])) {
|
|
return;
|
|
}
|
|
|
|
pTmpInstantMsg = pInstantMsgEng[n1][n2];
|
|
} else {
|
|
if (n1 >= ARRAY_LEN(pInstantMsgEng_ver6) || n2 >= ARRAY_LEN(pInstantMsgEng_ver6[0])) {
|
|
return;
|
|
}
|
|
|
|
// fallback to old version
|
|
pTmpInstantMsg = pInstantMsgEng_ver6[n1][n2];
|
|
}
|
|
|
|
bInstaMessage = qtrue;
|
|
|
|
if (g_gametype->integer == GT_FFA) {
|
|
iMode = 0;
|
|
} else {
|
|
if (n1 == 4) {
|
|
iMode = 0;
|
|
} else {
|
|
iMode = -1;
|
|
}
|
|
}
|
|
} else {
|
|
if (!g_textmsg_allowed->integer) {
|
|
// Added in OPM
|
|
|
|
str errorString = gi.LV_ConvertString("Message Error");
|
|
str reasonString = gi.LV_ConvertString("Text chat is disabled on this server");
|
|
|
|
gi.SendServerCommand(
|
|
edict - g_entities,
|
|
"print \"" HUD_MESSAGE_CHAT_WHITE "%s: %s.\n\"",
|
|
errorString.c_str(),
|
|
reasonString.c_str()
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (g_textmsg_minDelay->integer > 0 && level.inttime < m_iTextChatTime + g_textmsg_minDelay->integer) {
|
|
// Added in OPM
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (bInstaMessage) {
|
|
if (g_voiceChatTime->value > 0) {
|
|
m_fTalkTime = g_voiceChatTime->value + level.time;
|
|
}
|
|
m_iInstantMessageTime = level.inttime;
|
|
} else {
|
|
iMode = ev->GetInteger(1);
|
|
if (g_textChatTime->value > 0) {
|
|
m_fTalkTime = g_textChatTime->value + level.time;
|
|
}
|
|
m_iTextChatTime = level.inttime;
|
|
}
|
|
|
|
Q_strncpyz(szPrintString, "print \"" HUD_MESSAGE_CHAT_WHITE, sizeof(szPrintString));
|
|
|
|
if (m_bSpectator) {
|
|
if (iMode <= 0) {
|
|
Q_strcat(szPrintString, sizeof(szPrintString), gi.CL_LV_ConvertString("(spectator)"));
|
|
Q_strcat(szPrintString, sizeof(szPrintString), " ");
|
|
} else if (iMode <= game.maxclients) {
|
|
ent = &g_entities[iMode - 1];
|
|
|
|
if (ent->inuse && ent->entity && !static_cast<Player *>(ent->entity)->IsSpectator()) {
|
|
str errorString = gi.LV_ConvertString("Message Error");
|
|
str reasonString =
|
|
gi.LV_ConvertString("Spectators are not allowed to send private messages to non-spectators");
|
|
|
|
gi.SendServerCommand(
|
|
edict - g_entities,
|
|
"print \"" HUD_MESSAGE_CHAT_WHITE "%s: %s.\n\"",
|
|
errorString.c_str(),
|
|
reasonString.c_str()
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
} else if (IsDead() || m_bTempSpectator) {
|
|
if (iMode <= 0) {
|
|
Q_strcat(szPrintString, sizeof(szPrintString), gi.CL_LV_ConvertString("(dead)"));
|
|
Q_strcat(szPrintString, sizeof(szPrintString), " ");
|
|
} else if (iMode <= game.maxclients) {
|
|
ent = &g_entities[iMode - 1];
|
|
|
|
if (ent->inuse && ent->entity && !static_cast<Player *>(ent->entity)->IsSpectator()) {
|
|
str errorString = gi.LV_ConvertString("Message Error");
|
|
str reasonString =
|
|
gi.LV_ConvertString("Dead players are not allowed to send private messages to active players");
|
|
|
|
gi.SendServerCommand(
|
|
edict - g_entities,
|
|
"print \"" HUD_MESSAGE_CHAT_WHITE "%s: %s.\n\"",
|
|
errorString.c_str(),
|
|
reasonString.c_str()
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
} else if (iMode < 0) {
|
|
Q_strcat(szPrintString, sizeof(szPrintString), gi.CL_LV_ConvertString("(team)"));
|
|
Q_strcat(szPrintString, sizeof(szPrintString), " ");
|
|
} else if (iMode > 0) {
|
|
Q_strcat(szPrintString, sizeof(szPrintString), gi.CL_LV_ConvertString("(private)"));
|
|
Q_strcat(szPrintString, sizeof(szPrintString), " ");
|
|
}
|
|
|
|
Q_strcat(szPrintString, sizeof(szPrintString), client->pers.netname);
|
|
|
|
if (bInstaMessage) {
|
|
Q_strcat(szPrintString, sizeof(szPrintString), ": ");
|
|
Q_strcat(szPrintString, sizeof(szPrintString), gi.LV_ConvertString(pTmpInstantMsg));
|
|
} else {
|
|
bool met_comment;
|
|
|
|
Q_strcat(szPrintString, sizeof(szPrintString), ":");
|
|
iStringLength = strlen(szPrintString);
|
|
|
|
for (i = 2; i <= ev->NumArgs(); i++) {
|
|
sToken = ev->GetString(i);
|
|
// Added in 2.40
|
|
// Special battle language tokens
|
|
// So players can easily tell their position, health, etc.
|
|
sToken = TranslateBattleLanguageTokens(sToken);
|
|
|
|
if (iStringLength + sToken.length() > (ARRAY_LEN(szPrintString) - 1)) {
|
|
break;
|
|
}
|
|
|
|
// Added in OPM.
|
|
// Checks for comments in string (as COM_Parse will parse them)
|
|
if (strstr(sToken, "/*")) {
|
|
met_comment = true;
|
|
}
|
|
|
|
if (strstr(sToken, "*/") && met_comment) {
|
|
// ignore messages containing comments
|
|
return;
|
|
}
|
|
|
|
Q_strcat(szPrintString, sizeof(szPrintString), " ");
|
|
Q_strcat(szPrintString, sizeof(szPrintString), gi.LV_ConvertString(sToken));
|
|
}
|
|
}
|
|
|
|
Q_strcat(szPrintString, sizeof(szPrintString), "\n");
|
|
|
|
// ignore names containing comments
|
|
if (strstr(client->pers.netname, "//")
|
|
|| (strstr(client->pers.netname, "/*") && strstr(client->pers.netname, "*/"))) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Added in OPM
|
|
//=============
|
|
// Print the dm message to console
|
|
sToken = "";
|
|
|
|
for (i = 2; i <= ev->NumArgs(); i++) {
|
|
if (i != 2) {
|
|
sToken += " ";
|
|
}
|
|
|
|
sToken += ev->GetString(i);
|
|
}
|
|
//=============
|
|
|
|
if (iMode == 0) {
|
|
//
|
|
// everyone
|
|
//
|
|
|
|
// Added in OPM
|
|
gi.Printf("%s (%zu) says to everyone: %s\n", client->pers.netname, edict - g_entities, sToken.c_str());
|
|
|
|
if (!IsSpectator() || g_spectate_allow_full_chat->integer) {
|
|
for (i = 0; i < game.maxclients; i++) {
|
|
ent = &g_entities[i];
|
|
|
|
if (!ent->inuse || !ent->entity) {
|
|
continue;
|
|
}
|
|
|
|
gi.SendServerCommand(i, "%s", szPrintString);
|
|
|
|
if (bInstaMessage) {
|
|
gi.MSG_SetClient(i);
|
|
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_VOICE_CHAT));
|
|
gi.MSG_WriteCoord(m_vViewPos[0]);
|
|
gi.MSG_WriteCoord(m_vViewPos[1]);
|
|
gi.MSG_WriteCoord(m_vViewPos[2]);
|
|
gi.MSG_WriteBits(qtrue, 1);
|
|
gi.MSG_WriteBits(edict - g_entities, 6);
|
|
gi.MSG_WriteString(sAliasName.c_str());
|
|
gi.MSG_EndCGM();
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// send a message to spectators
|
|
//
|
|
for (i = 0; i < game.maxclients; i++) {
|
|
ent = &g_entities[i];
|
|
|
|
if (!ent->inuse || !ent->entity) {
|
|
continue;
|
|
}
|
|
|
|
if (!static_cast<Player *>(ent->entity)->IsSpectator()) {
|
|
continue;
|
|
}
|
|
|
|
gi.SendServerCommand(i, "%s", szPrintString);
|
|
}
|
|
}
|
|
} else if (iMode < 0) {
|
|
//
|
|
// team message
|
|
//
|
|
|
|
// Added in OPM
|
|
gi.Printf("%s (%zu) says to team: %s\n", client->pers.netname, edict - g_entities, sToken.c_str());
|
|
|
|
if (IsSpectator()) {
|
|
for (i = 0; i < game.maxclients; i++) {
|
|
ent = &g_entities[i];
|
|
|
|
if (!ent->inuse || !ent->entity) {
|
|
continue;
|
|
}
|
|
|
|
if (!static_cast<Player *>(ent->entity)->IsSpectator()) {
|
|
continue;
|
|
}
|
|
|
|
gi.SendServerCommand(i, "%s", szPrintString);
|
|
}
|
|
} else {
|
|
for (i = 0; i < game.maxclients; i++) {
|
|
bool bSameTeam;
|
|
|
|
ent = &g_entities[i];
|
|
|
|
if (!ent->inuse || !ent->entity) {
|
|
continue;
|
|
}
|
|
|
|
bSameTeam = static_cast<Player*>(ent->entity)->GetTeam() == GetTeam();
|
|
if (bSameTeam) {
|
|
gi.SendServerCommand(i, "%s", szPrintString);
|
|
}
|
|
|
|
if (bInstaMessage) {
|
|
gi.MSG_SetClient(i);
|
|
gi.MSG_StartCGM(BG_MapCGMToProtocol(g_protocol, CGM_VOICE_CHAT));
|
|
gi.MSG_WriteCoord(m_vViewPos[0]);
|
|
gi.MSG_WriteCoord(m_vViewPos[1]);
|
|
gi.MSG_WriteCoord(m_vViewPos[2]);
|
|
gi.MSG_WriteBits(!bSameTeam, 1);
|
|
gi.MSG_WriteBits(edict - g_entities, 6);
|
|
gi.MSG_WriteString(sAliasName.c_str());
|
|
gi.MSG_EndCGM();
|
|
}
|
|
}
|
|
}
|
|
} else if (iMode <= game.maxclients) {
|
|
ent = &g_entities[iMode - 1];
|
|
|
|
if (!ent->inuse || !ent->entity) {
|
|
str errorString = gi.LV_ConvertString("Message Error");
|
|
str reasonString = gi.LV_ConvertString("is not a connected client");
|
|
|
|
gi.SendServerCommand(
|
|
edict - g_entities,
|
|
"print \"" HUD_MESSAGE_CHAT_WHITE "%s: %i %s.\n\"",
|
|
errorString.c_str(),
|
|
iMode,
|
|
reasonString.c_str()
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Added in OPM
|
|
gi.Printf("%s (%zu) says to client #%d: %s\n", client->pers.netname, edict - g_entities, iMode - 1, sToken.c_str());
|
|
|
|
gi.SendServerCommand(iMode - 1, "%s", szPrintString);
|
|
|
|
if (ent->entity != this) {
|
|
gi.SendServerCommand(
|
|
edict - g_entities,
|
|
"print \"" HUD_MESSAGE_CHAT_WHITE "%s %i:\n\"",
|
|
gi.LV_ConvertString("Message to player"),
|
|
iMode
|
|
);
|
|
gi.SendServerCommand(edict - g_entities, "%s", szPrintString);
|
|
}
|
|
} else {
|
|
str errorString = gi.LV_ConvertString("Message Error");
|
|
str reasonString = gi.LV_ConvertString("is a bad client number");
|
|
|
|
gi.SendServerCommand(
|
|
edict - g_entities,
|
|
"print \"" HUD_MESSAGE_CHAT_WHITE "%s: %i %s.\n\"",
|
|
errorString.c_str(),
|
|
iMode,
|
|
reasonString.c_str()
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
str Player::GetBattleLanguageCondition() const
|
|
{
|
|
int healthRatio;
|
|
|
|
if (health <= 0) {
|
|
return "dead";
|
|
}
|
|
|
|
if (health >= max_health) {
|
|
return "just peachy";
|
|
}
|
|
|
|
healthRatio = (health * 5.f) / max_health;
|
|
switch (healthRatio) {
|
|
case 0:
|
|
return "almost dead";
|
|
case 1:
|
|
return "severely wounded";
|
|
case 2:
|
|
return "wounded";
|
|
case 3:
|
|
return "slightly wounded";
|
|
case 4:
|
|
default:
|
|
return "pretty good";
|
|
}
|
|
}
|
|
|
|
str Player::GetBattleLanguageDirection() const
|
|
{
|
|
int dir = ((m_vViewAng.y - world->m_fNorth) + 22.5f + 360.f) / 45.f;
|
|
switch (dir % 8) {
|
|
case 0:
|
|
return "North";
|
|
case 1:
|
|
return "North West";
|
|
case 2:
|
|
return "West";
|
|
case 3:
|
|
return "South West";
|
|
case 4:
|
|
return "South";
|
|
case 5:
|
|
return "South East";
|
|
case 6:
|
|
return "East";
|
|
case 7:
|
|
return "North East";
|
|
default:
|
|
return "???";
|
|
}
|
|
}
|
|
|
|
str Player::GetBattleLanguageLocation() const
|
|
{
|
|
return gi.CL_LV_ConvertString(level.GetDMLocation(m_vViewPos).c_str());
|
|
}
|
|
|
|
str Player::GetBattleLanguageLocalFolks()
|
|
{
|
|
static char buf[256];
|
|
char *p;
|
|
char *curP;
|
|
size_t remaining;
|
|
size_t length;
|
|
Player *pPlayer;
|
|
Player *pFolk;
|
|
gentity_t *ent;
|
|
int i;
|
|
|
|
remaining = ARRAY_LEN(buf) - 1;
|
|
p = buf;
|
|
curP = NULL;
|
|
pFolk = NULL;
|
|
|
|
for (i = 0; i < game.maxclients; i++) {
|
|
ent = &g_entities[i];
|
|
if (!ent->inuse || !ent->entity) {
|
|
continue;
|
|
}
|
|
|
|
pPlayer = static_cast<Player *>(ent->entity);
|
|
if (pPlayer != this && pPlayer->GetTeam() == GetTeam() && CanSee(pPlayer, 360, 1600, false)) {
|
|
if (p != buf) {
|
|
if (remaining < 2) {
|
|
// No more space remaining
|
|
break;
|
|
}
|
|
|
|
Q_strncpyz(p, ", ", sizeof(buf) - (p - buf));
|
|
p += 2;
|
|
curP = p;
|
|
remaining -= 2;
|
|
}
|
|
|
|
length = strlen(client->pers.netname);
|
|
if (remaining < length) {
|
|
break;
|
|
}
|
|
|
|
Q_strncpyz(p, client->pers.netname, sizeof(buf) - (p - buf));
|
|
p += length;
|
|
remaining -= length;
|
|
pFolk = pPlayer;
|
|
}
|
|
}
|
|
|
|
if (curP && remaining >= 2) {
|
|
Q_strncpyz(curP, "and ", sizeof(buf) - (curP - buf));
|
|
Q_strncpyz(curP + strlen(curP), pFolk->client->pers.netname, sizeof(buf) - (curP + strlen(curP) - buf));
|
|
} else if (!pFolk) {
|
|
return "nobody";
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
str Player::GetBattleLanguageWeapon() const
|
|
{
|
|
return GetCurrentDMWeaponType().c_str();
|
|
}
|
|
|
|
str Player::GetBattleLanguageDistance() const
|
|
{
|
|
Vector vStart, vEnd;
|
|
Vector vForward;
|
|
trace_t trace;
|
|
float dist;
|
|
|
|
vStart = m_vViewPos;
|
|
AngleVectors(m_vViewAng, vForward, NULL, NULL);
|
|
|
|
vEnd = vStart + vForward * 10240;
|
|
|
|
trace = G_Trace(
|
|
vStart,
|
|
vec_zero,
|
|
vec_zero,
|
|
vEnd,
|
|
static_cast<const Entity *>(this),
|
|
MASK_BATTLELANGUAGE,
|
|
qfalse,
|
|
"Player::GetBattleLanguageDistance"
|
|
);
|
|
|
|
dist = (vStart - trace.endpos).length();
|
|
|
|
if (g_qunits_to_feet->integer) {
|
|
return GetBattleLanguageDistanceFeet(dist);
|
|
} else {
|
|
return GetBattleLanguageDistanceMeters(dist);
|
|
}
|
|
}
|
|
|
|
str Player::GetBattleLanguageDistanceMeters(float dist) const
|
|
{
|
|
int meters;
|
|
|
|
meters = (int)((dist + 26.f) / 52.f);
|
|
if (meters >= 5) {
|
|
if (meters < 21) {
|
|
meters = 5 * ((meters + 2) / 5);
|
|
} else if (meters < 101) {
|
|
meters = 10 * ((meters + 5) / 10);
|
|
} else {
|
|
meters = 25 * ((meters + 12) / 25);
|
|
}
|
|
}
|
|
|
|
return va("%d meters", meters);
|
|
}
|
|
|
|
str Player::GetBattleLanguageDistanceFeet(float dist) const
|
|
{
|
|
int ft;
|
|
|
|
ft = (int)((dist + 26.f) / 52.f);
|
|
if (ft >= 11) {
|
|
if (ft < 51) {
|
|
ft = 5 * ((ft + 2) / 5);
|
|
} else if (ft < 251) {
|
|
ft = 10 * ((ft + 5) / 10);
|
|
} else {
|
|
ft = 25 * ((ft + 12) / 25);
|
|
}
|
|
}
|
|
|
|
return va("%d feet", ft);
|
|
}
|
|
|
|
str Player::GetBattleLanguageTarget() const
|
|
{
|
|
Vector vStart, vEnd;
|
|
Vector vForward;
|
|
trace_t trace;
|
|
|
|
vStart = m_vViewPos;
|
|
AngleVectors(m_vViewAng, vForward, NULL, NULL);
|
|
|
|
vEnd = vStart + vForward * 10240;
|
|
|
|
trace = G_Trace(
|
|
vStart,
|
|
vec_zero,
|
|
vec_zero,
|
|
vEnd,
|
|
static_cast<const Entity *>(this),
|
|
MASK_BATTLELANGUAGE,
|
|
qfalse,
|
|
"Player::GetBattleLanguageDistance"
|
|
);
|
|
|
|
if (!trace.ent) {
|
|
return "something";
|
|
}
|
|
|
|
if (!trace.ent->entity || trace.ent->entity == world) {
|
|
return "something";
|
|
}
|
|
|
|
if (trace.ent->entity->IsSubclassOfPlayer()) {
|
|
Player *pPlayer = static_cast<Player *>(trace.ent->entity);
|
|
return pPlayer->client->pers.netname;
|
|
}
|
|
|
|
if (trace.ent->entity->IsSubclassOfSentient()) {
|
|
return "someone";
|
|
}
|
|
|
|
return "something";
|
|
}
|
|
|
|
str Player::TranslateBattleLanguageTokens(const char *string)
|
|
{
|
|
str token;
|
|
int type;
|
|
|
|
if (!g_chat_expansions->integer) {
|
|
return string;
|
|
}
|
|
|
|
if (!string) {
|
|
return str();
|
|
}
|
|
|
|
if (string[0] != '$') {
|
|
return string;
|
|
}
|
|
|
|
type = string[1];
|
|
if (!type || string[2]) {
|
|
return string;
|
|
}
|
|
|
|
switch (type) {
|
|
case 'a':
|
|
token = GetBattleLanguageTarget();
|
|
break;
|
|
case 'c':
|
|
token = GetBattleLanguageCondition();
|
|
break;
|
|
case 'd':
|
|
token = GetBattleLanguageDirection();
|
|
break;
|
|
case 'l':
|
|
token = GetBattleLanguageLocation();
|
|
break;
|
|
case 'n':
|
|
token = GetBattleLanguageLocalFolks();
|
|
break;
|
|
case 'r':
|
|
token = GetBattleLanguageDistance();
|
|
break;
|
|
case 'w':
|
|
token = GetBattleLanguageWeapon();
|
|
break;
|
|
default:
|
|
return string;
|
|
}
|
|
|
|
return gi.LV_ConvertString(token);
|
|
}
|
|
|
|
void Player::EventIPrint(Event *ev)
|
|
{
|
|
str sString = ev->GetString(1);
|
|
qboolean iBold = qfalse;
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
iBold = ev->GetInteger(2);
|
|
}
|
|
|
|
if (iBold) {
|
|
gi.SendServerCommand(edict - g_entities, "print \"" HUD_MESSAGE_WHITE "%s\n\"", gi.LV_ConvertString(sString.c_str()));
|
|
} else {
|
|
gi.SendServerCommand(edict - g_entities, "print \"" HUD_MESSAGE_YELLOW "%s\n\"", gi.LV_ConvertString(sString.c_str()));
|
|
}
|
|
}
|
|
|
|
void Player::SetViewangles(Event *ev)
|
|
{
|
|
SetViewAngles(ev->GetVector(1));
|
|
}
|
|
|
|
void Player::GetViewangles(Event *ev)
|
|
{
|
|
ev->AddVector(GetVAngles());
|
|
}
|
|
|
|
void Player::EventGetUseHeld(Event *ev)
|
|
{
|
|
ev->AddInteger((buttons & BUTTON_USE) ? true : false);
|
|
}
|
|
|
|
void Player::EventGetFireHeld(Event *ev)
|
|
{
|
|
ev->AddInteger(buttons & (BUTTON_ATTACKLEFT | BUTTON_ATTACKRIGHT) ? qtrue : qfalse);
|
|
}
|
|
|
|
void Player::EventGetPrimaryFireHeld(Event *ev)
|
|
{
|
|
ev->AddInteger(buttons & BUTTON_ATTACKLEFT ? true : false);
|
|
}
|
|
|
|
void Player::EventGetSecondaryFireHeld(Event *ev)
|
|
{
|
|
ev->AddInteger(buttons & BUTTON_ATTACKRIGHT ? true : false);
|
|
}
|
|
|
|
void Player::BeginTempSpectator(void)
|
|
{
|
|
m_bTempSpectator = true;
|
|
Spectator();
|
|
}
|
|
|
|
void Player::EndSpectator(void)
|
|
{
|
|
m_bSpectator = false;
|
|
m_bTempSpectator = false;
|
|
|
|
client->ps.pm_flags &= ~(PMF_SPECTATING | PMF_SPECTATE_FOLLOW);
|
|
}
|
|
|
|
void Player::EventGetReady(Event *ev)
|
|
{
|
|
ev->AddInteger(m_bReady);
|
|
}
|
|
|
|
void Player::EventSetReady(Event *ev)
|
|
{
|
|
if (m_bReady) {
|
|
return;
|
|
}
|
|
|
|
m_bReady = true;
|
|
gi.Printf("%s is ready\n", client->pers.netname);
|
|
}
|
|
|
|
void Player::EventSetNotReady(Event *ev)
|
|
{
|
|
if (!m_bReady) {
|
|
return;
|
|
}
|
|
|
|
m_bReady = false;
|
|
gi.Printf("%s is not ready\n", client->pers.netname);
|
|
}
|
|
|
|
void Player::EventGetDMTeam(Event *ev)
|
|
{
|
|
if (dm_team == TEAM_FREEFORALL) {
|
|
ev->AddConstString(STRING_FREEFORALL);
|
|
} else if (dm_team == TEAM_AXIS) {
|
|
ev->AddConstString(STRING_AXIS);
|
|
} else if (dm_team == TEAM_ALLIES) {
|
|
ev->AddConstString(STRING_ALLIES);
|
|
} else if (dm_team == TEAM_SPECTATOR) {
|
|
ev->AddConstString(STRING_SPECTATOR);
|
|
} else {
|
|
ScriptError("dmteam is invalid in single player");
|
|
}
|
|
}
|
|
|
|
void Player::EventGetNetName(Event *ev)
|
|
{
|
|
ev->AddString(client->pers.netname);
|
|
}
|
|
|
|
void Player::EventSetViewModelAnim(Event *ev)
|
|
{
|
|
str anim;
|
|
int force_restart = 0;
|
|
qboolean bfullanim = 0;
|
|
|
|
anim = ev->GetString(1);
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
force_restart = ev->GetInteger(2);
|
|
}
|
|
|
|
if (ev->NumArgs() > 2) {
|
|
bfullanim = ev->GetInteger(3);
|
|
}
|
|
|
|
ViewModelAnim(anim, force_restart, bfullanim);
|
|
}
|
|
|
|
void Player::FullHeal(Event *ev)
|
|
{
|
|
if (IsDead()) {
|
|
if (!ev->IsFromScript()) {
|
|
HUDPrint("TESTING: Cannot resurrect yourself with the fullheal.\n");
|
|
}
|
|
} else {
|
|
if (!ev->IsFromScript()) {
|
|
HUDPrint("TESTING: You used the fullheal.\n");
|
|
}
|
|
|
|
health = max_health;
|
|
}
|
|
}
|
|
|
|
void Player::RemoveFromVehiclesAndTurretsInternal(void)
|
|
{
|
|
if (m_pVehicle) {
|
|
Event *event;
|
|
|
|
m_pVehicle->flags &= ~FL_GODMODE;
|
|
|
|
event = new Event(EV_Use);
|
|
event->AddEntity(this);
|
|
m_pVehicle->ProcessEvent(event);
|
|
} else if (m_pTurret) {
|
|
m_pTurret->TurretUsed(this);
|
|
}
|
|
}
|
|
|
|
void Player::RemoveFromVehiclesAndTurrets(void)
|
|
{
|
|
Weapon *activeWeap = GetActiveWeapon(WEAPON_MAIN);
|
|
if (activeWeap && activeWeap->IsCarryableTurret()) {
|
|
CarryableTurret *pTurret = static_cast<CarryableTurret *>(activeWeap);
|
|
pTurret->DropTurret(NULL);
|
|
}
|
|
|
|
if (!m_pVehicle && !m_pTurret) {
|
|
return;
|
|
}
|
|
|
|
if (m_pVehicle && m_pVehicle->isLocked()) {
|
|
m_pVehicle->UnLock();
|
|
|
|
if (m_pTurret && m_pTurret->IsSubclassOfVehicleTurretGun()) {
|
|
VehicleTurretGun *turret = (VehicleTurretGun *)m_pTurret.Pointer();
|
|
|
|
if (turret->isLocked()) {
|
|
turret->UnLock();
|
|
RemoveFromVehiclesAndTurretsInternal();
|
|
turret->Lock();
|
|
} else {
|
|
RemoveFromVehiclesAndTurretsInternal();
|
|
}
|
|
} else {
|
|
RemoveFromVehiclesAndTurretsInternal();
|
|
}
|
|
|
|
// the vehicle might have been modified
|
|
if (m_pVehicle) {
|
|
m_pVehicle->Lock();
|
|
}
|
|
} else if (m_pTurret && m_pTurret->IsSubclassOfVehicleTurretGun()) {
|
|
VehicleTurretGun *turret = (VehicleTurretGun *)m_pTurret.Pointer();
|
|
|
|
if (turret->isLocked()) {
|
|
turret->UnLock();
|
|
RemoveFromVehiclesAndTurretsInternal();
|
|
|
|
// the turret might have been modified
|
|
if (m_pTurret) {
|
|
turret->Lock();
|
|
}
|
|
} else {
|
|
RemoveFromVehiclesAndTurretsInternal();
|
|
}
|
|
} else {
|
|
RemoveFromVehiclesAndTurretsInternal();
|
|
}
|
|
}
|
|
|
|
void Player::EventEnterIntermission(Event *ev)
|
|
{
|
|
if (!level.intermissiontime) {
|
|
return;
|
|
}
|
|
|
|
if (level.intermissiontype) {
|
|
G_DisplayScores(this);
|
|
|
|
if (level.intermissiontype == TRANS_MISSION_FAILED || IsDead()) {
|
|
gi.cvar_set("g_success", "0");
|
|
gi.cvar_set("g_failed", "1");
|
|
} else {
|
|
gi.cvar_set("g_success", "1");
|
|
gi.cvar_set("g_failed", "0");
|
|
}
|
|
} else {
|
|
G_HideScores(this);
|
|
}
|
|
}
|
|
|
|
bool Player::BlocksAIMovement()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void Player::EventSetPerferredWeapon(Event *ev)
|
|
{
|
|
m_sPerferredWeaponOverride = ev->GetString(1);
|
|
}
|
|
|
|
void Player::SetMouthAngle(Event *ev)
|
|
{
|
|
int tag_num;
|
|
float angle_percent;
|
|
Vector mouth_angles;
|
|
|
|
angle_percent = ev->GetFloat(1);
|
|
|
|
if (angle_percent < 0) {
|
|
angle_percent = 0;
|
|
}
|
|
|
|
if (angle_percent > 1) {
|
|
angle_percent = 1;
|
|
}
|
|
|
|
tag_num = gi.Tag_NumForName(edict->tiki, "tag_mouth");
|
|
|
|
if (tag_num != -1) {
|
|
SetControllerTag(MOUTH_TAG, tag_num);
|
|
|
|
mouth_angles = vec_zero;
|
|
mouth_angles[PITCH] = max_mouth_angle * angle_percent;
|
|
|
|
SetControllerAngles(MOUTH_TAG, mouth_angles);
|
|
}
|
|
}
|
|
|
|
int Player::GetMoveResult(void)
|
|
{
|
|
return moveresult;
|
|
}
|
|
|
|
qboolean Player::CheckCanSwitchTeam(teamtype_t team)
|
|
{
|
|
float startTime;
|
|
|
|
startTime = dmManager.GetMatchStartTime();
|
|
|
|
if (startTime >= 0.0f && (level.time - startTime) > 30.0
|
|
&& (level.time - m_fTeamSelectTime) < g_teamswitchdelay->integer) {
|
|
int seconds = g_teamswitchdelay->integer - (level.time - m_fTeamSelectTime);
|
|
|
|
gi.SendServerCommand(
|
|
edict - g_entities,
|
|
"print \"" HUD_MESSAGE_WHITE "%s %i %s\n\"",
|
|
gi.LV_ConvertString("Can not change teams again for another"),
|
|
seconds + 1,
|
|
gi.LV_ConvertString("seconds")
|
|
);
|
|
return qfalse;
|
|
}
|
|
|
|
// Added in OPM
|
|
// Check and prevent joining the team with the highest number of players
|
|
if (g_teambalance->integer && g_gametype->integer >= GT_TEAM && !dmManager.WaitingForPlayers()) {
|
|
DM_Team *pNewTeam = dmManager.GetTeam(team);
|
|
int i;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
DM_Team *pTeam = dmManager.GetTeam((teamtype_t)(TEAM_ALLIES + i));
|
|
int numTeamPlayers = pTeam->m_players.NumObjects();
|
|
|
|
if (pTeam->m_players.IndexOfObject(this)) {
|
|
// Don't count the current player
|
|
numTeamPlayers--;
|
|
}
|
|
|
|
if (pNewTeam->m_players.NumObjects() > numTeamPlayers) {
|
|
gi.SendServerCommand(
|
|
edict - g_entities,
|
|
"print \"" HUD_MESSAGE_WHITE "%s\n\"",
|
|
gi.LV_ConvertString(
|
|
"That team has enough players. Choose the team that has the lowest number of players."
|
|
)
|
|
);
|
|
return qfalse;
|
|
}
|
|
}
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
qboolean Player::ViewModelAnim(str anim, qboolean force_restart, qboolean bFullAnim)
|
|
{
|
|
Unregister(STRING_VIEWMODELANIM_DONE);
|
|
|
|
if (client == NULL) {
|
|
return true;
|
|
}
|
|
|
|
int viewModelAnim;
|
|
playerState_t *playerState = &client->ps;
|
|
Weapon *weapon;
|
|
|
|
if (!anim.length()) {
|
|
anim = "";
|
|
}
|
|
|
|
// Copy the item prefix and the anim name
|
|
weapon = GetActiveWeapon(WEAPON_MAIN);
|
|
|
|
if (!Q_stricmp(anim, "charge")) {
|
|
viewModelAnim = VM_ANIM_CHARGE;
|
|
} else if (!Q_stricmp(anim, "fire")) {
|
|
viewModelAnim = VM_ANIM_FIRE;
|
|
} else if (!Q_stricmp(anim, "fire_secondary")) {
|
|
viewModelAnim = VM_ANIM_FIRE_SECONDARY;
|
|
} else if (!Q_stricmp(anim, "rechamber")) {
|
|
viewModelAnim = VM_ANIM_RECHAMBER;
|
|
} else if (!Q_stricmp(anim, "reload")) {
|
|
viewModelAnim = VM_ANIM_RELOAD;
|
|
} else if (!Q_stricmp(anim, "reload_single")) {
|
|
viewModelAnim = VM_ANIM_RELOAD_SINGLE;
|
|
} else if (!Q_stricmp(anim, "reload_end")) {
|
|
viewModelAnim = VM_ANIM_RELOAD_END;
|
|
} else if (!Q_stricmp(anim, "pullout")) {
|
|
viewModelAnim = VM_ANIM_PULLOUT;
|
|
} else if (!Q_stricmp(anim, "putaway")) {
|
|
viewModelAnim = VM_ANIM_PUTAWAY;
|
|
} else if (!Q_stricmp(anim, "ladderstep")) {
|
|
viewModelAnim = VM_ANIM_LADDERSTEP;
|
|
} else {
|
|
if (!Q_stricmp(anim, "idle")) {
|
|
viewModelAnim = VM_ANIM_IDLE;
|
|
} else if (!Q_stricmp(anim, "idle0")) {
|
|
viewModelAnim = VM_ANIM_IDLE_0;
|
|
} else if (!Q_stricmp(anim, "idle1")) {
|
|
viewModelAnim = VM_ANIM_IDLE_1;
|
|
} else if (!Q_stricmp(anim, "idle2")) {
|
|
viewModelAnim = VM_ANIM_IDLE_2;
|
|
} else {
|
|
// Defaults to idle
|
|
viewModelAnim = VM_ANIM_IDLE;
|
|
}
|
|
|
|
//
|
|
// check the fire movement speed if the weapon has a max fire movement
|
|
//
|
|
if (weapon && weapon->m_fMaxFireMovement < 1) {
|
|
float length;
|
|
|
|
length = velocity.length();
|
|
if (length / sv_runspeed->value > ((weapon->m_fMaxFireMovement * weapon->m_fMovementSpeed) + 0.1f)) {
|
|
// Set the view model animation to disabled
|
|
viewModelAnim = VM_ANIM_DISABLED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!weapon) {
|
|
weapon = newActiveWeapon.weapon;
|
|
}
|
|
|
|
if (weapon) {
|
|
m_sVMAcurrent = GetItemPrefix(weapon->getName()) + str("_") + anim;
|
|
} else {
|
|
m_sVMAcurrent = "unarmed_" + anim;
|
|
}
|
|
|
|
m_sVMcurrent = anim;
|
|
|
|
if (viewModelAnim != playerState->iViewModelAnim || force_restart) {
|
|
playerState->iViewModelAnimChanged = (playerState->iViewModelAnimChanged + 1) & 3;
|
|
}
|
|
|
|
playerState->iViewModelAnim = viewModelAnim;
|
|
|
|
if (m_fpsTiki && gi.Anim_NumForName(m_fpsTiki, m_sVMAcurrent) < 0) {
|
|
//gi.DPrintf("WARNING: Invalid view model anim \"%s\"\n", m_sVMAcurrent.c_str());
|
|
}
|
|
|
|
animDoneVM = false;
|
|
|
|
m_fVMAtime = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Player::FindAlias(str& output, str name, AliasListNode_t **node)
|
|
{
|
|
const char *alias = gi.Alias_FindRandom(edict->tiki, name, node);
|
|
|
|
if (alias == NULL) {
|
|
alias = gi.GlobalAlias_FindRandom(name, node);
|
|
}
|
|
|
|
if (alias != NULL) {
|
|
output = alias;
|
|
}
|
|
}
|
|
|
|
bool Player::HasVotedYes() const
|
|
{
|
|
return voted;
|
|
}
|
|
|
|
bool Player::HasVotedNo() const
|
|
{
|
|
return !voted;
|
|
}
|
|
|
|
void Player::TickSprint()
|
|
{
|
|
float timeHeld;
|
|
|
|
if (last_ucmd.buttons & BUTTON_RUN && last_ucmd.forwardmove) {
|
|
timeHeld = 0;
|
|
|
|
if (!m_fLastSprintTime) {
|
|
m_fLastSprintTime = level.time;
|
|
}
|
|
} else {
|
|
timeHeld = 0;
|
|
m_fLastSprintTime = 0;
|
|
}
|
|
|
|
if (last_ucmd.rightmove) {
|
|
m_fLastSprintTime = timeHeld;
|
|
}
|
|
if (last_ucmd.upmove) {
|
|
m_fLastSprintTime = timeHeld;
|
|
}
|
|
}
|
|
|
|
float Player::GetRunSpeed() const
|
|
{
|
|
float sprintTime;
|
|
float sprintMult;
|
|
|
|
sprintTime = sv_sprinttime->value;
|
|
sprintMult = sv_sprintmult->value;
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
sprintTime = sv_sprinttime_dm->value;
|
|
sprintMult = sv_sprintmult_dm->value;
|
|
}
|
|
|
|
if (sv_sprinton->integer == 1 && m_fLastSprintTime && (level.time - m_fLastSprintTime) > sprintTime) {
|
|
return sv_runspeed->value * sprintMult;
|
|
} else {
|
|
return sv_runspeed->value;
|
|
}
|
|
}
|
|
|
|
void Player::FireWeapon(int number, firemode_t mode)
|
|
{
|
|
if (m_pVehicle || m_pTurret) {
|
|
return;
|
|
}
|
|
|
|
if (G_GetWeaponCommand(last_ucmd.buttons)) {
|
|
// Added in OPM
|
|
// If there is a weapon command (like DROP), then just don't fire
|
|
// this prevent tricky behaviors, like silent firing
|
|
return;
|
|
}
|
|
|
|
Sentient::FireWeapon(number, mode);
|
|
|
|
if (g_gametype->integer != GT_SINGLE_PLAYER) {
|
|
//
|
|
// Make sure to remove the player's invulnerability
|
|
//
|
|
CancelInvulnerable();
|
|
}
|
|
}
|
|
|
|
void Player::SetInvulnerable()
|
|
{
|
|
if (IsInvulnerable()) {
|
|
return;
|
|
}
|
|
|
|
if (!sv_invulnerabletime->integer) {
|
|
return;
|
|
}
|
|
|
|
if (gi.Cvar_Get("g_invulnoverride", "0", 0)->integer == 1) {
|
|
return;
|
|
}
|
|
|
|
if (IsDead()) {
|
|
return;
|
|
}
|
|
|
|
if (IsSpectator() || GetTeam() == TEAM_SPECTATOR) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// The player can now be invulnerable
|
|
//
|
|
takedamage = DAMAGE_NO;
|
|
m_iInvulnerableTimeRemaining = sv_invulnerabletime->integer;
|
|
m_fLastInvulnerableTime = level.time;
|
|
m_fInvulnerableTimeElapsed = level.time;
|
|
|
|
TickInvulnerable();
|
|
}
|
|
|
|
void Player::TickInvulnerable()
|
|
{
|
|
if (m_iInvulnerableTimeRemaining >= 0 && level.time >= m_fInvulnerableTimeElapsed) {
|
|
if (m_iInvulnerableTimeRemaining) {
|
|
m_fInvulnerableTimeElapsed = m_fInvulnerableTimeElapsed + 1.f;
|
|
} else {
|
|
SetVulnerable();
|
|
m_fInvulnerableTimeElapsed = 0;
|
|
}
|
|
|
|
m_iInvulnerableTimeRemaining--;
|
|
}
|
|
}
|
|
|
|
void Player::SetVulnerable()
|
|
{
|
|
if (IsInvulnerable()) {
|
|
takedamage = DAMAGE_AIM;
|
|
m_fLastInvulnerableTime = 0;
|
|
}
|
|
}
|
|
|
|
bool Player::IsInvulnerable()
|
|
{
|
|
return m_fLastInvulnerableTime != 0;
|
|
}
|
|
|
|
void Player::CancelInvulnerable()
|
|
{
|
|
if (IsInvulnerable()) {
|
|
SetVulnerable();
|
|
m_iInvulnerableTimeRemaining = -1;
|
|
gi.centerprintf(edict, " ");
|
|
}
|
|
}
|
|
|
|
void Player::InitInvulnerable()
|
|
{
|
|
m_fLastInvulnerableTime = 0;
|
|
m_iInvulnerableTimeRemaining = -1;
|
|
}
|
|
|
|
void Player::TickTeamSpawn()
|
|
{
|
|
int timeLeft;
|
|
|
|
if (!IsSpectator() && !IsDead() || (GetTeam() == TEAM_SPECTATOR || !client->pers.dm_primary[0])) {
|
|
return;
|
|
}
|
|
|
|
timeLeft = dmManager.GetTeamSpawnTimeLeft();
|
|
if (timeLeft == -1) {
|
|
// Can spawn
|
|
m_fSpawnTimeLeft = 0;
|
|
return;
|
|
}
|
|
|
|
if (timeLeft == m_fSpawnTimeLeft) {
|
|
// Still waiting
|
|
return;
|
|
}
|
|
|
|
if (m_bShouldRespawn) {
|
|
// The player can spawn
|
|
m_fSpawnTimeLeft = 0;
|
|
return;
|
|
}
|
|
|
|
m_fSpawnTimeLeft = timeLeft;
|
|
if (timeLeft) {
|
|
if (AllowTeamRespawn()) {
|
|
const char *string;
|
|
|
|
if (timeLeft == 1) {
|
|
string = va("Next respawn in 1 second");
|
|
} else {
|
|
string = va("Next respawn in %d seconds", timeLeft);
|
|
}
|
|
|
|
gi.centerprintf(edict, string);
|
|
}
|
|
} else if (m_bWaitingForRespawn && AllowTeamRespawn()) {
|
|
m_bWaitingForRespawn = false;
|
|
m_bDeathSpectator = false;
|
|
EndSpectator();
|
|
|
|
PostEvent(EV_Player_Respawn, 0);
|
|
} else {
|
|
m_bShouldRespawn = true;
|
|
}
|
|
}
|
|
|
|
bool Player::ShouldForceSpectatorOnDeath() const
|
|
{
|
|
return dmManager.GetTeamSpawnTimeLeft() > 0;
|
|
}
|
|
|
|
bool Player::HasVehicle() const
|
|
{
|
|
return m_pVehicle != NULL;
|
|
}
|
|
|
|
void Player::setContentsSolid()
|
|
{
|
|
edict->r.contents = CONTENTS_BODY;
|
|
}
|
|
|
|
void Player::UserSelectWeapon(bool bWait)
|
|
{
|
|
nationality_t nationality;
|
|
char buf[256];
|
|
|
|
if (g_protocol < PROTOCOL_MOHTA_MIN) {
|
|
//
|
|
// nationality was first introduced in 2.0
|
|
//
|
|
if (bWait) {
|
|
gi.SendServerCommand(edict - g_entities, "stufftext \"wait 250;pushmenu_weaponselect\"");
|
|
} else {
|
|
gi.SendServerCommand(edict - g_entities, "stufftext \"pushmenu_weaponselect\"");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (GetTeam() == TEAM_AXIS) {
|
|
nationality = GetPlayerAxisTeamType(client->pers.dm_playergermanmodel);
|
|
} else {
|
|
nationality = GetPlayerAlliedTeamType(client->pers.dm_playermodel);
|
|
}
|
|
|
|
if (bWait) {
|
|
Q_strncpyz(buf, "stufftext \"wait 250;pushmenu ", sizeof(buf));
|
|
} else {
|
|
Q_strncpyz(buf, "stufftext \"pushmenu ", sizeof(buf));
|
|
}
|
|
|
|
if (dmflags->integer & DF_WEAPON_NO_RIFLE && dmflags->integer & DF_WEAPON_NO_SNIPER
|
|
&& dmflags->integer & DF_WEAPON_NO_SMG && dmflags->integer & DF_WEAPON_NO_MG
|
|
&& dmflags->integer & DF_WEAPON_NO_ROCKET && dmflags->integer & DF_WEAPON_NO_SHOTGUN
|
|
&& dmflags->integer & DF_WEAPON_NO_LANDMINE && !QueryLandminesAllowed()) {
|
|
gi.cvar_set("dmflags", va("%i", dmflags->integer & ~DF_WEAPON_NO_RIFLE));
|
|
Com_Printf("No valid weapons -- re-allowing the rifle\n");
|
|
Q_strncpyz(client->pers.dm_primary, "rifle", sizeof(client->pers.dm_primary));
|
|
}
|
|
|
|
switch (nationality) {
|
|
case NA_BRITISH:
|
|
Q_strcat(buf, sizeof(buf), "SelectPrimaryWeapon_british\"");
|
|
break;
|
|
case NA_RUSSIAN:
|
|
Q_strcat(buf, sizeof(buf), "SelectPrimaryWeapon_russian\"");
|
|
break;
|
|
case NA_GERMAN:
|
|
Q_strcat(buf, sizeof(buf), "SelectPrimaryWeapon_german\"");
|
|
break;
|
|
case NA_ITALIAN:
|
|
Q_strcat(buf, sizeof(buf), "SelectPrimaryWeapon_italian\"");
|
|
break;
|
|
default:
|
|
Q_strcat(buf, sizeof(buf), "SelectPrimaryWeapon\"");
|
|
break;
|
|
}
|
|
|
|
gi.SendServerCommand(edict - g_entities, buf);
|
|
}
|
|
|
|
void Player::PickWeaponEvent(Event *ev)
|
|
{
|
|
if (g_gametype->integer == GT_SINGLE_PLAYER) {
|
|
return;
|
|
}
|
|
|
|
UserSelectWeapon(false);
|
|
}
|
|
|
|
bool Player::AllowTeamRespawn() const
|
|
{
|
|
if (m_bSpectator && !m_bDeathSpectator
|
|
&& (!dmManager.AllowTeamRespawn(TEAM_ALLIES) || !dmManager.AllowTeamRespawn(TEAM_AXIS))) {
|
|
return false;
|
|
}
|
|
|
|
if (GetTeam() > TEAM_AXIS || GetTeam() < TEAM_ALLIES) {
|
|
return false;
|
|
}
|
|
|
|
return dmManager.AllowTeamRespawn(GetTeam());
|
|
}
|
|
|
|
void Player::EventUseWeaponClass(Event *ev)
|
|
{
|
|
if (m_pTurret || level.playerfrozen) {
|
|
return;
|
|
}
|
|
|
|
Sentient::EventUseWeaponClass(ev);
|
|
}
|
|
|
|
void Player::EventAddKills(Event *ev)
|
|
{
|
|
SafePtr<DM_Team> pTeam = GetDM_Team();
|
|
if (pTeam) {
|
|
// Add kills to the team
|
|
pTeam->AddKills(this, ev->GetInteger(1));
|
|
}
|
|
}
|
|
|
|
bool Player::CanKnockback(float minHealth) const
|
|
{
|
|
if (m_pTurret || m_pVehicle) {
|
|
return minHealth >= health;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
void Player::EventKillAxis(Event *ev)
|
|
{
|
|
float radius = 0;
|
|
|
|
if (ev->NumArgs() >= 1) {
|
|
radius = ev->GetFloat(1);
|
|
}
|
|
|
|
for (Sentient *pSent = level.m_HeadSentient[TEAM_GERMAN]; pSent; pSent = pSent->m_NextSentient) {
|
|
if (radius > 0) {
|
|
Vector delta = pSent->origin - origin;
|
|
if (radius < delta.length()) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
pSent->Damage(this, this, pSent->max_health + 25, origin, vec_zero, vec_zero, 0, 0, MOD_NONE);
|
|
}
|
|
}
|
|
|
|
void Player::EventGetTurret(Event *ev)
|
|
{
|
|
ev->AddEntity(m_pTurret);
|
|
}
|
|
|
|
void Player::EventGetVehicle(Event *ev)
|
|
{
|
|
ev->AddEntity(m_pVehicle);
|
|
}
|
|
|
|
bool Player::IsReady(void) const
|
|
{
|
|
return m_bReady && !IsDead();
|
|
}
|
|
|
|
void Player::Spawned(void)
|
|
{
|
|
Event *ev = new Event;
|
|
|
|
ev->AddEntity(this);
|
|
scriptedEvents[SE_SPAWN].Trigger(ev);
|
|
}
|
|
|
|
void Player::AddKills(int num)
|
|
{
|
|
num_kills += num;
|
|
}
|
|
|
|
void Player::AddDeaths(int num)
|
|
{
|
|
num_deaths += num;
|
|
}
|
|
|
|
////////////////////////////
|
|
//
|
|
// Added in OPM
|
|
//
|
|
////////////////////////////
|
|
|
|
qboolean Player::canUse()
|
|
{
|
|
int touch[MAX_GENTITIES];
|
|
int num = getUseableEntities(touch, MAX_GENTITIES);
|
|
|
|
return num ? true : false;
|
|
}
|
|
|
|
qboolean Player::canUse(Entity *entity, bool requiresLookAt)
|
|
{
|
|
gentity_t *hit;
|
|
int touch[MAX_GENTITIES];
|
|
int num;
|
|
int i;
|
|
|
|
num = getUseableEntities(touch, MAX_GENTITIES, requiresLookAt);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
hit = &g_entities[touch[i]];
|
|
|
|
if (!hit->inuse || hit->entity == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (hit->entity == entity) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int Player::getUseableEntities(int *touch, int maxcount, bool requiresLookAt)
|
|
{
|
|
Vector end;
|
|
Vector start;
|
|
trace_t trace;
|
|
Vector offset;
|
|
Vector max;
|
|
Vector min;
|
|
|
|
if ((g_gametype->integer != GT_SINGLE_PLAYER && IsSpectator()) || IsDead()) {
|
|
return 0;
|
|
}
|
|
|
|
if (m_pTurret) {
|
|
*touch = m_pTurret->entnum;
|
|
return 1;
|
|
}
|
|
|
|
if (m_pTurret) {
|
|
return 0;
|
|
}
|
|
|
|
AngleVectors(client->ps.viewangles, offset, NULL, NULL);
|
|
|
|
start = origin;
|
|
start.z += client->ps.viewheight;
|
|
|
|
if (requiresLookAt) {
|
|
min = Vector(-4.f, -4.f, -4.f);
|
|
max = Vector(4.f, 4.f, 4.f);
|
|
|
|
end[0] = start[0] + (offset[0] * 64.f);
|
|
end[1] = start[1] + (offset[1] * 64.f);
|
|
|
|
if (v_angle[0] <= 0.0f) {
|
|
end[2] = start[2] + (offset[2] * 40.f);
|
|
} else {
|
|
end[2] = start[2] + (offset[2] * 88.f);
|
|
}
|
|
|
|
trace = G_Trace(start, min, max, end, this, MASK_USE, false, "Player::getUseableEntity");
|
|
|
|
offset = trace.endpos;
|
|
|
|
min = offset - Vector(16.f, 16.f, 16.f);
|
|
max = offset + Vector(16.f, 16.f, 16.f);
|
|
} else {
|
|
min = start - Vector(31.f, 31.f, 31.f);
|
|
max = start + Vector(31.f, 31.f, 31.f);
|
|
}
|
|
|
|
return gi.AreaEntities(min, max, touch, maxcount);
|
|
}
|
|
|
|
void Player::Postthink(void)
|
|
{
|
|
if (bindmaster) {
|
|
SetViewAngles(GetViewAngles() + Vector(0, bindmaster->avelocity[YAW] * level.frametime, 0));
|
|
}
|
|
}
|
|
|
|
void Player::AdminRights(Event *ev)
|
|
{
|
|
// FIXME: Admin manager ?
|
|
ev->AddInteger(0);
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
void Player::IsAdmin(Event *ev)
|
|
{
|
|
// FIXME: Admin manager ?
|
|
ev->AddInteger(0);
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
void Player::BindWeap(Event *ev)
|
|
{
|
|
Entity *ent = ev->GetEntity(1);
|
|
Listener *scriptOwner;
|
|
|
|
//
|
|
// FIXME: deprecate and use something else instead
|
|
// like implement this in the Item class directly
|
|
// this is dangerous to use especially if the weapon
|
|
// is a VehicleTurretGun, could easily mess up the camera
|
|
|
|
if (ent) {
|
|
scriptOwner = ent->GetScriptOwner();
|
|
|
|
if (scriptOwner != this) {
|
|
ent->SetScriptOwner(this);
|
|
} else {
|
|
ent->SetScriptOwner(NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Player::AddDeaths(Event *ev)
|
|
{
|
|
AddDeaths(ev->GetInteger(1));
|
|
}
|
|
|
|
void Player::Dive(Event *ev)
|
|
{
|
|
float height, airborne_duration, speed;
|
|
|
|
Vector forwardvector = orientation[0];
|
|
|
|
height = ev->GetFloat(1);
|
|
|
|
if (ev->NumArgs() < 2 || ev->IsNilAt(2)) {
|
|
airborne_duration = 1;
|
|
} else {
|
|
airborne_duration = ev->GetFloat(2);
|
|
}
|
|
|
|
speed = height * airborne_duration;
|
|
|
|
velocity[0] += height * forwardvector[0] * (speed / 16);
|
|
velocity[1] += height * forwardvector[1] * (speed / 16);
|
|
velocity[2] += height * speed / 6.80f;
|
|
}
|
|
|
|
void Player::EventSetTeam(Event *ev)
|
|
{
|
|
str team_name;
|
|
teamtype_t teamType;
|
|
|
|
team_name = ev->GetString(1);
|
|
if (!team_name.length()) {
|
|
ScriptError("Invalid team name !");
|
|
return;
|
|
}
|
|
|
|
if (Q_stricmp(team_name, "none") == 0) {
|
|
teamType = TEAM_NONE;
|
|
} else if (Q_stricmp(team_name, "spectator") == 0) {
|
|
teamType = TEAM_SPECTATOR;
|
|
} else if (Q_stricmp(team_name, "freeforall") == 0) {
|
|
teamType = TEAM_FREEFORALL;
|
|
} else if (Q_stricmp(team_name, "allies") == 0) {
|
|
teamType = TEAM_ALLIES;
|
|
} else if (Q_stricmp(team_name, "axis") == 0) {
|
|
teamType = TEAM_AXIS;
|
|
} else {
|
|
ScriptError("Unknown team name \"%s\"\n", team_name.c_str());
|
|
return;
|
|
}
|
|
|
|
SetTeam(teamType);
|
|
|
|
gi.DPrintf("Player::SetTeam : Player is now on team \"%s\"\n", team_name.c_str());
|
|
}
|
|
|
|
void Player::EventGetViewModelAnim(Event *ev)
|
|
{
|
|
ev->AddString(m_sVMcurrent);
|
|
}
|
|
|
|
void Player::EventGetViewModelAnimFinished(Event *ev)
|
|
{
|
|
ev->AddInteger(animDoneVM);
|
|
}
|
|
|
|
void Player::EventGetViewModelAnimValid(Event *ev)
|
|
{
|
|
str anim_name = ev->GetString(1);
|
|
str fullanim;
|
|
bool bFullAnim = false;
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
bFullAnim = ev->GetBoolean(2);
|
|
}
|
|
|
|
if (!bFullAnim) {
|
|
// Copy the item prefix and the anim name
|
|
Item *item = GetActiveWeapon(WEAPON_MAIN);
|
|
|
|
if (!item) {
|
|
item = (Item *)newActiveWeapon.weapon.Pointer();
|
|
}
|
|
|
|
if (item) {
|
|
fullanim = GetItemPrefix(item->getName()) + str("_") + anim_name;
|
|
} else {
|
|
fullanim = "unarmed_" + anim_name;
|
|
}
|
|
} else {
|
|
fullanim = anim_name;
|
|
}
|
|
|
|
if (!m_fpsTiki || gi.Anim_NumForName(m_fpsTiki, fullanim.c_str()) < 0) {
|
|
ev->AddInteger(0);
|
|
} else {
|
|
ev->AddInteger(1);
|
|
}
|
|
}
|
|
|
|
void Player::FreezeControls(Event *ev)
|
|
{
|
|
m_bFrozen = ev->GetBoolean(1);
|
|
}
|
|
|
|
void Player::GetConnState(Event *ev)
|
|
{
|
|
// Assume CS_ACTIVE
|
|
ev->AddInteger(4);
|
|
|
|
gi.DPrintf(
|
|
"getconnstate is deprecated and will always return 4 (CS_ACTIVE).\nThe player is created only when the client "
|
|
"begins (CS_ACTIVE state).\n"
|
|
);
|
|
}
|
|
|
|
void Player::GetDamageMultiplier(Event *ev)
|
|
{
|
|
ev->AddFloat(damage_multiplier);
|
|
}
|
|
|
|
void Player::GetDeaths(Event *ev)
|
|
{
|
|
ev->AddInteger(num_deaths);
|
|
}
|
|
|
|
void Player::GetKillHandler(Event *ev)
|
|
{
|
|
if (m_killedLabel.IsSet()) {
|
|
m_killedLabel.GetScriptValue(&ev->GetValue());
|
|
} else {
|
|
ev->AddNil();
|
|
}
|
|
}
|
|
|
|
void Player::GetKills(Event *ev)
|
|
{
|
|
ev->AddInteger(num_kills);
|
|
}
|
|
|
|
void Player::GetMoveSpeedScale(Event *ev)
|
|
{
|
|
ev->AddFloat(speed_multiplier[0]);
|
|
}
|
|
|
|
void Player::GetLegsState(Event *ev)
|
|
{
|
|
const char *name;
|
|
|
|
if (currentState_Legs != NULL) {
|
|
name = currentState_Legs->getName();
|
|
} else {
|
|
name = "none";
|
|
}
|
|
|
|
ev->AddString(name);
|
|
}
|
|
|
|
void Player::GetStateFile(Event *ev)
|
|
{
|
|
int clientNum = G_GetClientNumber(this);
|
|
|
|
if (m_sStateFile.length()) {
|
|
ev->AddString(m_sStateFile);
|
|
} else {
|
|
ev->AddString(g_statefile->string);
|
|
}
|
|
}
|
|
|
|
void Player::GetTorsoState(Event *ev)
|
|
{
|
|
const char *name;
|
|
|
|
if (currentState_Torso != NULL) {
|
|
name = currentState_Torso->getName();
|
|
} else {
|
|
name = "none";
|
|
}
|
|
|
|
ev->AddString(name);
|
|
}
|
|
|
|
void Player::HideEntity(Event *ev)
|
|
{
|
|
// FIXME: todo
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
void Player::ShowEntity(Event *ev)
|
|
{
|
|
// FIXME: REDO
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
void Player::Inventory(Event *ev)
|
|
{
|
|
Entity *ent = NULL;
|
|
ScriptVariable *ref = new ScriptVariable, *array = new ScriptVariable;
|
|
int i = 0;
|
|
|
|
ref->setRefValue(array);
|
|
|
|
for (i = 0; i < inventory.NumObjects(); i++) {
|
|
ent = G_GetEntity(inventory[i]);
|
|
|
|
if (ent == NULL) {
|
|
continue;
|
|
}
|
|
|
|
ScriptVariable *index = new ScriptVariable, *value = new ScriptVariable;
|
|
|
|
index->setIntValue(i + 1);
|
|
value->setListenerValue((Listener *)ent);
|
|
|
|
ref->setArrayAt(*index, *value);
|
|
}
|
|
|
|
ev->AddValue(*array);
|
|
}
|
|
|
|
void Player::InventorySet(Event *ev)
|
|
{
|
|
ScriptVariable array;
|
|
ScriptVariable *value;
|
|
Entity *ent;
|
|
int arraysize;
|
|
|
|
if (ev->IsNilAt(1)) {
|
|
// Just clear the inventory
|
|
inventory.ClearObjectList();
|
|
return;
|
|
}
|
|
|
|
// Retrieve the array
|
|
array = ev->GetValue(1);
|
|
|
|
// Cast the array
|
|
array.CastConstArrayValue();
|
|
arraysize = array.arraysize();
|
|
|
|
// Detach all active weapons and free the inventory
|
|
|
|
if (inventory.NumObjects() > 0) {
|
|
inventory.FreeObjectList();
|
|
}
|
|
|
|
if (arraysize < 1) {
|
|
return;
|
|
}
|
|
|
|
// Allocate an inventory
|
|
for (int i = 1; i <= arraysize; i++) {
|
|
// Retrieve the value from the array
|
|
value = array[i];
|
|
|
|
// Get the entity from the value
|
|
ent = (Entity *)value->entityValue();
|
|
|
|
if (ent == NULL || !ent->edict->inuse) {
|
|
continue;
|
|
}
|
|
|
|
// Add the entity to the inventory
|
|
inventory.AddObject(ent->entnum);
|
|
}
|
|
|
|
// Clear the variable
|
|
array.Clear();
|
|
}
|
|
|
|
void Player::LeanLeftHeld(Event *ev)
|
|
{
|
|
Player *player = NULL;
|
|
int buttonheld = 0;
|
|
|
|
player = (Player *)this;
|
|
|
|
buttonheld = !!(player->buttons & BUTTON_LEAN_LEFT);
|
|
|
|
ev->AddInteger(buttonheld);
|
|
}
|
|
|
|
void Player::LeanRightHeld(Event *ev)
|
|
{
|
|
Player *player = NULL;
|
|
int buttonheld = 0;
|
|
|
|
player = (Player *)this;
|
|
|
|
buttonheld = !!(player->buttons & BUTTON_LEAN_RIGHT);
|
|
|
|
ev->AddInteger(buttonheld);
|
|
}
|
|
|
|
void Player::PlayLocalSound(Event *ev)
|
|
{
|
|
str soundName = ev->GetString(1);
|
|
qboolean loop = false;
|
|
float time;
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
loop = ev->GetBoolean(2);
|
|
}
|
|
|
|
if (ev->NumArgs() > 2) {
|
|
time = ev->GetFloat(3);
|
|
} else {
|
|
time = 0.0f;
|
|
}
|
|
|
|
AliasListNode_t *alias = NULL;
|
|
|
|
const char *found = gi.GlobalAlias_FindRandom(soundName, &alias);
|
|
|
|
if (found == NULL) {
|
|
gi.DPrintf("ERROR: Player::PlayLocalSound: %s needs to be aliased - Please fix.\n", soundName.c_str());
|
|
return;
|
|
}
|
|
|
|
#ifdef OPM_FEATURES
|
|
gi.MSG_SetClient(client->ps.clientNum);
|
|
|
|
gi.MSG_StartCGM(CGM_PLAYLOCALSOUND);
|
|
gi.MSG_WriteString(found);
|
|
gi.MSG_WriteBits(!!loop, 1);
|
|
gi.MSG_WriteFloat(time);
|
|
gi.MSG_WriteFloat(alias->volume);
|
|
gi.MSG_EndCGM();
|
|
|
|
return;
|
|
#endif
|
|
|
|
if (loop) {
|
|
edict->s.loopSound = gi.soundindex(found, alias->streamed);
|
|
edict->s.loopSoundVolume = 1.0f;
|
|
edict->s.loopSoundMinDist = 0;
|
|
edict->s.loopSoundMaxDist = 96;
|
|
edict->s.loopSoundPitch = 1.0f;
|
|
edict->s.loopSoundFlags = 1; // local sound
|
|
} else {
|
|
gi.Sound(&edict->s.origin, entnum, CHAN_LOCAL, found, 1.0f, 0, 1.0f, 96, alias->streamed);
|
|
}
|
|
}
|
|
|
|
void Player::RunHeld(Event *ev)
|
|
{
|
|
Player *player = NULL;
|
|
int buttonheld = 0;
|
|
|
|
player = (Player *)this;
|
|
|
|
buttonheld = !!(player->buttons & BUTTON_RUN);
|
|
|
|
ev->AddInteger(buttonheld);
|
|
}
|
|
|
|
void Player::SecFireHeld(Event *ev)
|
|
{
|
|
Player *player = NULL;
|
|
int buttonheld = 0;
|
|
|
|
player = (Player *)this;
|
|
|
|
buttonheld = !!(player->buttons & BUTTON_ATTACKRIGHT);
|
|
|
|
ev->AddInteger(buttonheld);
|
|
}
|
|
|
|
void Player::SetAnimSpeed(Event *ev)
|
|
{
|
|
float speed;
|
|
Player *player = (Player *)this;
|
|
|
|
speed = ev->GetFloat(1);
|
|
|
|
if (speed < 0.0f) {
|
|
speed = 0.0f;
|
|
}
|
|
|
|
UNIMPLEMENTED();
|
|
}
|
|
|
|
void Player::SetKillHandler(Event *ev)
|
|
{
|
|
if (ev->IsNilAt(1) || (ev->IsStringAt(1) && !ev->GetString(1).icmp("none"))) {
|
|
m_killedLabel.Clear();
|
|
} else {
|
|
m_killedLabel.SetScript(ev->GetValue(1));
|
|
}
|
|
}
|
|
|
|
void Player::SetSpeed(Event *ev)
|
|
{
|
|
float speed;
|
|
Player *player = (Player *)this;
|
|
int clientNum = G_GetClientNumber(this);
|
|
int index = 0;
|
|
|
|
speed = ev->GetFloat(1);
|
|
|
|
if (speed < 0.0f) {
|
|
speed = 0.0f;
|
|
}
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
index = ev->GetInteger(2);
|
|
|
|
/* Reserve a space for moveSpeedScale */
|
|
if (index < 1 || index > MAX_SPEED_MULTIPLIERS) {
|
|
gi.Printf("Player::SetSpeed : invalid index %d. Index must be between 1-%d\n", index, speed_multiplier);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
speed_multiplier[index] = speed;
|
|
}
|
|
|
|
void Player::SetStateFile(Event *ev)
|
|
{
|
|
int clientNum = G_GetClientNumber(this);
|
|
qboolean bRemove = false;
|
|
str string;
|
|
|
|
if (ev->NumArgs() <= 0) {
|
|
bRemove = true;
|
|
} else {
|
|
string = ev->GetString(1);
|
|
|
|
if (!string) {
|
|
bRemove = true;
|
|
}
|
|
}
|
|
|
|
if (bRemove) {
|
|
m_sStateFile = "";
|
|
} else {
|
|
m_sStateFile = string;
|
|
}
|
|
}
|
|
|
|
void Player::StopLocalSound(Event *ev)
|
|
{
|
|
str soundName = ev->GetString(1);
|
|
float time;
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
time = ev->GetFloat(2);
|
|
} else {
|
|
time = 0.0f;
|
|
}
|
|
|
|
AliasListNode_t *alias = NULL;
|
|
const char *found = gi.GlobalAlias_FindRandom(soundName, &alias);
|
|
|
|
if (found == NULL) {
|
|
gi.DPrintf("ERROR: Player::StopLocalSound: %s needs to be aliased - Please fix.\n", soundName.c_str());
|
|
return;
|
|
}
|
|
|
|
edict->s.loopSound = 0;
|
|
gi.StopSound(entnum, CHAN_LOCAL);
|
|
}
|
|
|
|
void Player::Userinfo(Event *ev)
|
|
{
|
|
if (!client) {
|
|
ScriptError("Entity is probably not of player type - userinfo\n");
|
|
return;
|
|
}
|
|
|
|
ev->AddString(client->pers.userinfo);
|
|
}
|
|
|
|
int Player::GetNumKills(void) const
|
|
{
|
|
return num_kills;
|
|
}
|
|
|
|
int Player::GetNumDeaths(void) const
|
|
{
|
|
return num_deaths;
|
|
}
|
|
|
|
#ifdef OPM_FEATURES
|
|
|
|
void Player::EventEarthquake(Event *ev)
|
|
{
|
|
float duration = ev->GetFloat(1);
|
|
float magnitude = ev->GetFloat(2);
|
|
qboolean no_rampup = ev->GetBoolean(3);
|
|
qboolean no_rampdown = ev->GetBoolean(4);
|
|
|
|
// full realistic, smooth earthquake
|
|
if (ev->NumArgs() > 4) {
|
|
Vector location = ev->GetVector(5);
|
|
float radius = 1.0f;
|
|
|
|
if (ev->NumArgs() > 5) {
|
|
radius = ev->GetFloat(6);
|
|
}
|
|
|
|
gi.SendServerCommand(
|
|
edict - g_entities,
|
|
"eq %f %f %d %d %f %f %f %f",
|
|
duration,
|
|
magnitude,
|
|
no_rampup,
|
|
no_rampdown,
|
|
location[0],
|
|
location[1],
|
|
location[2],
|
|
radius
|
|
);
|
|
} else {
|
|
gi.SendServerCommand(edict - g_entities, "eq %f %f %d %d", duration, magnitude, no_rampup, no_rampdown);
|
|
}
|
|
}
|
|
|
|
void Player::SetClientFlag(Event *ev)
|
|
{
|
|
str name = ev->GetString(1);
|
|
|
|
gi.SendServerCommand(client->ps.clientNum, "cf %s", name.c_str());
|
|
}
|
|
|
|
void Player::SetEntityShader(Event *ev)
|
|
{
|
|
Entity *entity = ev->GetEntity(1);
|
|
str shadername = ev->GetString(2);
|
|
qboolean fReset = false;
|
|
|
|
if (entity == NULL) {
|
|
ScriptError("Invalid entity !");
|
|
}
|
|
|
|
if (!shadername.length()) {
|
|
shadername = "default";
|
|
fReset = true;
|
|
}
|
|
|
|
gi.SendServerCommand(edict - g_entities, "setshader %d %s %d", entity->entnum, shadername.c_str(), fReset);
|
|
}
|
|
|
|
void Player::SetLocalSoundRate(Event *ev)
|
|
{
|
|
str name = ev->GetString(1);
|
|
float rate = ev->GetFloat(2);
|
|
float time;
|
|
|
|
if (ev->NumArgs() > 2) {
|
|
time = ev->GetFloat(3);
|
|
} else {
|
|
time = 0.0f;
|
|
}
|
|
|
|
AliasListNode_t *alias = NULL;
|
|
const char *found = gi.GlobalAlias_FindRandom(name, &alias);
|
|
|
|
if (found == NULL) {
|
|
gi.DPrintf("ERROR: Player::SetLocalSoundRate: %s needs to be aliased - Please fix.\n", name.c_str());
|
|
return;
|
|
}
|
|
|
|
gi.MSG_SetClient(client->ps.clientNum);
|
|
|
|
// FIXME...
|
|
/*
|
|
gi.MSG_StartCGM( CGM_SETLOCALSOUNDRATE );
|
|
gi.MSG_WriteString( found );
|
|
gi.MSG_WriteFloat( rate );
|
|
gi.MSG_WriteFloat( time );
|
|
gi.MSG_EndCGM();
|
|
*/
|
|
}
|
|
|
|
void Player::SetVMASpeed(Event *ev)
|
|
{
|
|
str name = ev->GetString(1);
|
|
float speed = ev->GetFloat(2);
|
|
|
|
if (!client || !sv_specialgame->integer) {
|
|
return;
|
|
}
|
|
|
|
vma_t *vma = &vmalist[name];
|
|
|
|
if (speed < 0.0f) {
|
|
speed = 0.0f;
|
|
}
|
|
|
|
vma->name = name;
|
|
vma->speed = speed;
|
|
|
|
// FIXME...
|
|
/*
|
|
gi.MSG_SetClient( edict - g_entities );
|
|
|
|
gi.MSG_StartCGM( CGM_SETVMASPEED );
|
|
gi.MSG_WriteString( name );
|
|
gi.MSG_WriteFloat( speed );
|
|
gi.MSG_EndCGM();
|
|
*/
|
|
}
|
|
|
|
void Player::VisionGetNaked(Event *ev)
|
|
{
|
|
// return the global vision
|
|
if (!m_sVision.length()) {
|
|
ev->AddString(vision_current);
|
|
} else {
|
|
ev->AddString(m_sVision);
|
|
}
|
|
}
|
|
|
|
void Player::VisionSetBlur(Event *ev)
|
|
{
|
|
float blur_level = ev->GetFloat(1);
|
|
float fade_time;
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
fade_time = ev->GetFloat(2);
|
|
} else {
|
|
fade_time = 0.0f;
|
|
}
|
|
|
|
gi.SendServerCommand(edict - g_entities, "vsb %f %f", blur_level, fade_time);
|
|
}
|
|
|
|
void Player::VisionSetNaked(Event *ev)
|
|
{
|
|
str vision = ev->GetString(1);
|
|
float fade_time;
|
|
float phase;
|
|
|
|
if (ev->NumArgs() > 1) {
|
|
fade_time = ev->GetFloat(2);
|
|
} else {
|
|
fade_time = 0.0f;
|
|
}
|
|
|
|
if (ev->NumArgs() > 2) {
|
|
phase = ev->GetFloat(3);
|
|
} else {
|
|
phase = 0.0f;
|
|
}
|
|
|
|
if (!vision.length()) {
|
|
vision = vision_current;
|
|
}
|
|
|
|
if (vision.length() >= MAX_STRING_TOKENS) {
|
|
ScriptError("vision_name exceeds the maximum vision name limit (256) !\n");
|
|
}
|
|
|
|
m_sVision = vision;
|
|
|
|
gi.SendServerCommand(edict - g_entities, "vsn %s %f %f", vision.c_str(), fade_time, phase);
|
|
}
|
|
#endif
|