3184 lines
70 KiB
C++
3184 lines
70 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Logfile:: /Quake 2 Engine/Sin/code/game/sentient.cpp $
|
|
// $Revision:: 189 $
|
|
// $Author:: Jimdose $
|
|
// $Date:: 11/17/98 6:56p $
|
|
//
|
|
// Copyright (C) 1997 by Ritual Entertainment, Inc.
|
|
// All rights reserved.
|
|
//
|
|
// This source is may not be distributed and/or modified without
|
|
// expressly written permission by Ritual Entertainment, Inc.
|
|
//
|
|
// $Log:: /Quake 2 Engine/Sin/code/game/sentient.cpp $
|
|
//
|
|
// 189 11/17/98 6:56p Jimdose
|
|
// made EventGiveHealth clear the damage skin on the player
|
|
//
|
|
// 188 11/16/98 9:05p Markd
|
|
// added meansofdeath to pain
|
|
//
|
|
// 187 11/16/98 7:46p Markd
|
|
// Hooked up takeItem to an event, for some reason this was never done?
|
|
//
|
|
// 186 11/14/98 8:07p Jimdose
|
|
// added DF_NO_DROP_WEAPONS
|
|
//
|
|
// 185 11/13/98 10:20p Aldie
|
|
// Added event take item
|
|
//
|
|
// 184 11/13/98 2:36a Aldie
|
|
// Fixed typo for MOD_SPEARGUN and gibbing. Also removed pain for MUTANT_DRAIN
|
|
//
|
|
// 183 11/12/98 11:29p Aldie
|
|
// No blood for mutant drain
|
|
//
|
|
// 182 11/09/98 6:22p Markd
|
|
// Added dropweaponnow event
|
|
//
|
|
// 181 11/08/98 10:49p Jimdose
|
|
// sentientFrozen wasn't being archived
|
|
//
|
|
// 180 11/08/98 8:30p Aldie
|
|
// Added a couple of strings to archive for sentients
|
|
//
|
|
// 179 10/27/98 6:20a Aldie
|
|
// Fixed drop inventory items bug
|
|
//
|
|
// 178 10/27/98 3:50a Jimdose
|
|
// made armordamage not do Ion tesselation when FL_NOION is set
|
|
//
|
|
// 177 10/26/98 9:53p Markd
|
|
// Moved GotKill around, added gibfest sound
|
|
//
|
|
// 176 10/26/98 8:12p Aldie
|
|
// Fixed some weapon bugs with detaching from owner when removing
|
|
//
|
|
// 175 10/26/98 2:21a Aldie
|
|
// Added MOD_HELIGUN to DoGib
|
|
//
|
|
// 174 10/25/98 10:15p Aldie
|
|
// Fix dropweapon code (again)
|
|
//
|
|
// 173 10/25/98 4:37a Aldie
|
|
// Put an assertion to help us find a bug with giving items
|
|
//
|
|
// 172 10/25/98 1:51a Aldie
|
|
// Fixed an inventory bug ( free inventory ) and lowered percentage of gun
|
|
// getting shot out of your hands
|
|
//
|
|
// 171 10/24/98 11:47p Jimdose
|
|
// fixed RestorePersistantData so that setowner works after G_CallSpawn
|
|
//
|
|
// 170 10/24/98 6:38p Markd
|
|
// Added gibbed to GotKill, also added GotKill to gibbed section of ArmorDamage
|
|
//
|
|
// 169 10/24/98 4:34p Aldie
|
|
// Added a warning message if we could not find the item in
|
|
// "EventGiveTargetname" - hopefully this will help find the problem.
|
|
//
|
|
// 168 10/24/98 2:08p Aldie
|
|
// Fixed crash bug with floating inventories
|
|
//
|
|
// 167 10/24/98 12:42a Markd
|
|
// changed origins to worldorigins where appropriate
|
|
//
|
|
// 166 10/23/98 9:58p Aldie
|
|
// Fixed dropweapon bug
|
|
//
|
|
// 165 10/23/98 7:11p Aldie
|
|
// Damage tweak for singleplayer
|
|
//
|
|
// 164 10/23/98 2:37a Aldie
|
|
// Added MOD_VEHICLE
|
|
//
|
|
// 163 10/22/98 11:11p Markd
|
|
// Put item variables into Add and Remove to keep track of inventory
|
|
//
|
|
// 162 10/22/98 10:22p Markd
|
|
// Put in support for game and level item script variables
|
|
//
|
|
// 161 10/22/98 9:29p Aldie
|
|
// Added support for death gib animations
|
|
//
|
|
// 160 10/22/98 4:58p Aldie
|
|
// Only do rocket jump in deathmatch
|
|
//
|
|
// 159 10/22/98 2:38a Aldie
|
|
// Added setdropweapon command for sentients
|
|
//
|
|
// 158 10/22/98 1:40a Aldie
|
|
// Don't put blood on ents
|
|
//
|
|
// 157 10/21/98 8:41p Markd
|
|
// Fixed currentWeapon bug with WeaponUse
|
|
//
|
|
// 156 10/21/98 7:50p Aldie
|
|
// Fixed a function declaration
|
|
//
|
|
// 155 10/20/98 11:23p Jimdose
|
|
// made weapons switch properly if you pickup a weapon while reloading
|
|
// made drop weapon and take weapon use the best weapon when switching
|
|
//
|
|
// 154 10/20/98 8:05p Aldie
|
|
// Added a DoGib function to ease readability
|
|
//
|
|
// 153 10/20/98 1:18a Markd
|
|
// Made fists and adrenaline a gibbable act
|
|
//
|
|
// 152 10/19/98 12:07a Jimdose
|
|
// made all code use fast checks for inheritance (no text lookups when
|
|
// possible)
|
|
// isSubclassOf no longer requires ::_classinfo()
|
|
//
|
|
// 151 10/18/98 6:43p Markd
|
|
// Allowed other actors to have their weapons knocked from their hands
|
|
//
|
|
// 150 10/17/98 8:14p Jimdose
|
|
// Changed G_CallSpawn2 to G_CallSpawn
|
|
//
|
|
// 149 10/17/98 5:15p Markd
|
|
// rotate centroid by orientation when making force field
|
|
//
|
|
// 148 10/16/98 3:10a Aldie
|
|
// Removed global dmg multipliers
|
|
//
|
|
// 147 10/15/98 3:37p Markd
|
|
// Added forcefield support
|
|
//
|
|
// 146 10/15/98 3:11p Aldie
|
|
// Damage skill multiplier ( for testing purposes )
|
|
//
|
|
// 145 10/14/98 1:19a Jimdose
|
|
// Got cross-level persistant info working
|
|
//
|
|
// 144 10/12/98 3:49p Aldie
|
|
// Init a couple vars
|
|
//
|
|
// 143 10/11/98 5:34p Aldie
|
|
// Gib from MUTANTHANDS
|
|
//
|
|
// 142 10/11/98 12:03a Aldie
|
|
// Mutant mode takes less dmg
|
|
//
|
|
// 141 10/10/98 5:58p Aldie
|
|
// More quantumdestab fixes
|
|
//
|
|
// 140 10/09/98 5:23p Aldie
|
|
// Teamplay
|
|
//
|
|
// 139 10/08/98 12:02a Jimdose
|
|
// Bloodsplats are now a separate class
|
|
//
|
|
// 138 10/05/98 10:18p Aldie
|
|
// Covnverted over to new silencer methods
|
|
//
|
|
// 137 10/04/98 10:28p Aldie
|
|
// Added multiple weapon changes. Damage, flashes, quantum stuff
|
|
//
|
|
// 136 10/03/98 7:27p Markd
|
|
// Added pos tio get gun orientation
|
|
//
|
|
// 135 10/03/98 5:13p Jimdose
|
|
// changed the way Unarchive reads the event pointer
|
|
//
|
|
// 134 10/01/98 8:00p Aldie
|
|
// Consolidated cprintf
|
|
//
|
|
// 133 9/27/98 4:36p Aldie
|
|
// Modified armor damage and clipped velocity.z for bulletweapons in
|
|
// armordamage
|
|
//
|
|
// 132 9/26/98 4:45p Aldie
|
|
// Added mutant mode
|
|
//
|
|
// 131 9/25/98 4:37p Markd
|
|
// Made two sentient functions not inline, so that it would work with vc 6.0
|
|
//
|
|
// 130 9/23/98 12:37p Markd
|
|
// added tesselate sound when ionized
|
|
//
|
|
// 129 9/22/98 3:21p Markd
|
|
// put in parentmode lockout for blood and gibs
|
|
//
|
|
// 128 9/22/98 12:14p Aldie
|
|
// Added sentientFrozen
|
|
//
|
|
// 127 9/21/98 6:05p Markd
|
|
// added archive and unarchive functions
|
|
//
|
|
// 126 9/20/98 9:08p Aldie
|
|
// Fixed a particle bug with RandomParticles
|
|
//
|
|
// 125 9/20/98 7:11p Aldie
|
|
// Added flags to particles
|
|
//
|
|
// 124 9/18/98 9:55p Markd
|
|
// Added noshadow event
|
|
//
|
|
// 123 9/18/98 8:40p Aldie
|
|
// Ion tesselate effect
|
|
//
|
|
// 122 9/16/98 8:58p Aldie
|
|
// Added ability to do a hold down weapon charge
|
|
//
|
|
// 121 9/12/98 11:08p Aldie
|
|
// Fixed armor damage bug
|
|
//
|
|
// 120 9/11/98 2:50p Aldie
|
|
// Added releasefiring functionality
|
|
//
|
|
// 119 9/09/98 6:49p Markd
|
|
// Check to see if weapon is droppable before dropping it
|
|
//
|
|
// 118 9/05/98 12:12p Aldie
|
|
// Tweaked armor damage that does 50% damage to player and 50% damage to armor
|
|
// in deathmatch. Also inited poweruptype and poweruptimer
|
|
//
|
|
// 117 9/02/98 11:08a Markd
|
|
// Put in setModel into Sentient so that weapon could be properly detached and
|
|
// re-attached again.
|
|
//
|
|
// 116 9/01/98 7:46p Aldie
|
|
// Changed blood around so dead bodies bleed
|
|
//
|
|
// 115 8/31/98 7:16p Markd
|
|
// Fixed player animation naming convention
|
|
//
|
|
// 114 8/31/98 5:45p Aldie
|
|
// Powerup stuff
|
|
//
|
|
// 113 8/31/98 1:55p Markd
|
|
// weapons no longer double trigger animations
|
|
//
|
|
// 112 8/29/98 9:46p Jimdose
|
|
// Added call info to G_Trace
|
|
//
|
|
// 111 8/29/98 7:23p Aldie
|
|
// Made a givetargetname function for giving monsters targeted items.
|
|
//
|
|
// 110 8/29/98 2:53p Aldie
|
|
// Updated printing of location based damage
|
|
//
|
|
// 109 8/27/98 9:03p Jimdose
|
|
// made grav a reference
|
|
//
|
|
// 108 8/27/98 6:43p Markd
|
|
// re-added StartAnimating calls in TempAnim and SetANim
|
|
//
|
|
// 107 8/27/98 6:00p Aldie
|
|
// Drop inventory items if the actors are gibbed.
|
|
//
|
|
// 106 8/27/98 5:29p Markd
|
|
// Took out start animating calls
|
|
//
|
|
// 105 8/27/98 2:30p Aldie
|
|
// Added DAMAGE_NO_SKILL flag
|
|
//
|
|
// 104 8/24/98 6:52p Jimdose
|
|
// Fixed inverted gravity axis
|
|
//
|
|
// 103 8/22/98 9:35p Jimdose
|
|
// Added support for alternate gravity axis
|
|
//
|
|
// 102 8/21/98 7:38p Markd
|
|
// Fixed climbing code
|
|
//
|
|
// 101 8/19/98 8:50p Aldie
|
|
// Show damage locations and use random particles for blood.
|
|
//
|
|
// 100 8/17/98 2:58p Aldie
|
|
// Rearranged blood and spark particles
|
|
//
|
|
// 99 8/15/98 4:07p Aldie
|
|
// Made NextWeapon and PreviousWeapon obey the AutoChange function. So the
|
|
// sniperrifle will not be auto used.
|
|
//
|
|
// 98 8/14/98 5:48p Aldie
|
|
// Moved more of blood splat stuff here.
|
|
//
|
|
// 97 8/14/98 5:39p Aldie
|
|
// Moved spray blood to entity
|
|
//
|
|
// 96 8/13/98 8:10p Aldie
|
|
// Added sparks and blood to armordamage
|
|
//
|
|
// 95 8/12/98 4:20p Aldie
|
|
// Fixed icons not showing up correctly
|
|
//
|
|
// 94 8/06/98 10:53p Aldie
|
|
// Added weapon tweaks and kickback. Also modified blast radius damage and
|
|
// rocket jumping.
|
|
//
|
|
// 93 8/04/98 6:14p Aldie
|
|
// Updated armor damage stuff
|
|
//
|
|
// 92 8/04/98 5:10p Markd
|
|
// Took out "Monster" clause in ArmorDamage
|
|
//
|
|
// 91 7/31/98 8:09p Jimdose
|
|
// Script commands now include flags to indicate cheats and console commands
|
|
//
|
|
// 90 7/29/98 2:32p Aldie
|
|
// Changed health to a float
|
|
//
|
|
// 89 7/26/98 4:15a Aldie
|
|
// Check for in vehicle so the player can kill himself
|
|
//
|
|
// 88 7/26/98 2:59a Markd
|
|
// guys don't get their guns knocked out of their hands for now
|
|
//
|
|
// 87 7/26/98 2:54a Markd
|
|
// changed player damage multipliers
|
|
//
|
|
// 86 7/26/98 12:46a Aldie
|
|
// Trying to fix stuck dead anims
|
|
//
|
|
// 85 7/25/98 3:57p Markd
|
|
// Added EV_GotKill
|
|
//
|
|
// 84 7/25/98 2:11a Jimdose
|
|
// Added SentientList
|
|
//
|
|
// 83 7/24/98 9:58p Aldie
|
|
// Gib stuff
|
|
//
|
|
// 82 7/24/98 6:19p Aldie
|
|
// Check for inventory of type
|
|
//
|
|
// 81 7/24/98 3:49p Aldie
|
|
// Made sentients check for uselessness in the future
|
|
//
|
|
// 80 7/23/98 6:17p Aldie
|
|
// Updated damage system and fixed some damage related bugs. Also put tracers
|
|
// back to the way they were, and added gib event to funcscriptmodels
|
|
//
|
|
// 79 7/23/98 1:51p Aldie
|
|
// Print damage to attacker and client
|
|
//
|
|
// 78 7/23/98 12:17p Aldie
|
|
// Print damage reports to victims.
|
|
//
|
|
// 77 7/21/98 9:09p Aldie
|
|
// Process commands on given weapons too. Seems to work ok.
|
|
//
|
|
// 76 7/21/98 1:10p Aldie
|
|
// Added meansofdeath to obituaries
|
|
//
|
|
// 75 7/20/98 7:42p Aldie
|
|
// Set owner of given items before processing their commands.
|
|
//
|
|
// 74 7/20/98 6:54p Markd
|
|
// Added damage support based on skill level
|
|
//
|
|
// 73 7/20/98 5:40p Aldie
|
|
// When giving items to sentients process the commands immediately.
|
|
//
|
|
// 72 7/19/98 10:34p Aldie
|
|
// Added DAMAGE_NO_ARMOR functionality
|
|
//
|
|
// 71 7/19/98 7:38p Markd
|
|
// Added Shield support for 1/4 damage
|
|
//
|
|
// 70 7/19/98 7:35p Markd
|
|
// Fixed ForceWeaponChange so that it doesn't squash newWeapon
|
|
//
|
|
// 69 7/19/98 5:41p Markd
|
|
// Added ForceWeaponChange, added takeItem
|
|
//
|
|
// 68 7/18/98 7:37p Aldie
|
|
// Updated showdamage printing.
|
|
//
|
|
// 67 7/18/98 5:08p Aldie
|
|
// Added showdamage
|
|
//
|
|
// 66 7/17/98 4:01p Markd
|
|
// Added CanChangeWeapons
|
|
//
|
|
// 65 7/14/98 6:57p Aldie
|
|
// Updated health commands
|
|
//
|
|
// 64 7/13/98 5:00p Aldie
|
|
// Added dead player bodies with gibbing
|
|
//
|
|
// 63 7/11/98 8:22p Jimdose
|
|
// When dropping a weapon, if the weapon can't be dropped, it's deleted if the
|
|
// player is dying. This fixes the crash when the player dies while using the
|
|
// fists.
|
|
//
|
|
// 62 7/11/98 5:55p Aldie
|
|
// Undid when we send icons to the player for picking stuff up.
|
|
//
|
|
// 61 7/10/98 11:11p Jimdose
|
|
// characters no longer switch weapons after dropping them when killed
|
|
//
|
|
// 60 7/10/98 12:05a Aldie
|
|
// Fixed icon sending if you don't pickup a weapon
|
|
//
|
|
// 59 7/01/98 7:03p Aldie
|
|
// Floating inventory mods for empty inventories
|
|
//
|
|
// 58 6/30/98 6:48p Aldie
|
|
// Gib a dead body, and don't display items that have 0 amount.
|
|
//
|
|
// 57 6/25/98 8:47p Markd
|
|
// Added keyed items for Triggers, Rewrote Item class, rewrote every pickup
|
|
// method
|
|
//
|
|
// 56 6/25/98 12:42p Aldie
|
|
// Updated some inventory and weapon swtiching stuff
|
|
//
|
|
// 55 6/24/98 4:44p Aldie
|
|
// Made it so you can only view one inventory at a time
|
|
//
|
|
// 54 6/24/98 1:37p Aldie
|
|
// Implementation of inventory system and picking stuff up
|
|
//
|
|
// 53 6/21/98 5:54p Jimdose
|
|
// Moved call to GetBoneGroupName from the constructor to SetCurrentWeapon
|
|
// since the model isn't set yet when the constructor is called.
|
|
//
|
|
// 52 6/20/98 7:49p Markd
|
|
// Added location to pain and killed events, also added gun_bone_group_name
|
|
// variable
|
|
//
|
|
// 51 6/20/98 2:20p Aldie
|
|
// Removed sone printfs
|
|
//
|
|
// 50 6/19/98 9:28p Jimdose
|
|
// Added GetGunOrientation
|
|
//
|
|
// 49 6/19/98 6:36p Aldie
|
|
// Put some more inventory managment in
|
|
//
|
|
// 48 6/18/98 9:25p Aldie
|
|
// Started inventory system
|
|
//
|
|
// 47 6/18/98 2:00p Markd
|
|
// rewrote tesselation code
|
|
//
|
|
// 46 6/17/98 1:20a Jimdose
|
|
// Changed Attack to FireWeapon
|
|
//
|
|
// 45 6/15/98 9:09p Aldie
|
|
// Added a generic give and an updater for silenced weapons, when you pick it
|
|
// up
|
|
//
|
|
// 44 6/15/98 3:36p Aldie
|
|
// Updated armor damage calculations
|
|
//
|
|
// 43 6/10/98 2:10p Aldie
|
|
// Updated damage function.
|
|
//
|
|
// 42 6/09/98 4:22p Jimdose
|
|
// AnimLoop went into an infinite loop when the anim didn't exist
|
|
//
|
|
// 41 6/08/98 9:04p Aldie
|
|
// Updated checks for weapon changing when no ammo available
|
|
//
|
|
// 40 6/08/98 8:35p Markd
|
|
// put in code to put away weapon when readytouse becomes false
|
|
//
|
|
// 39 6/08/98 8:19p Markd
|
|
// Put in ReadyToUse call when switching weapons
|
|
//
|
|
// 38 6/08/98 11:34a Aldie
|
|
// Updated damage based location checks
|
|
//
|
|
// 37 6/05/98 6:25p Aldie
|
|
// Added give and take armor as well as ArmorDamage calculations based on
|
|
// location hit
|
|
//
|
|
// 36 5/27/98 12:59a Markd
|
|
// Put in shadows flag
|
|
//
|
|
// 35 5/26/98 4:46p Aldie
|
|
// added take functions
|
|
//
|
|
// 34 5/24/98 8:46p Jimdose
|
|
// Made a lot of functions more str-friendly.
|
|
// Got rid of a lot of char * based strings
|
|
// Cleaned up get spawn arg functions and sound functions
|
|
// sound functions now use consistant syntax
|
|
//
|
|
// 33 5/24/98 4:48p Jimdose
|
|
// Made char *'s const
|
|
//
|
|
// 32 5/20/98 6:38p Jimdose
|
|
// Made anim functions use const char *
|
|
//
|
|
// 31 5/08/98 2:57p Markd
|
|
// Added weapon pickup sound
|
|
//
|
|
// 30 5/03/98 4:37p Jimdose
|
|
// Changed Vector class
|
|
//
|
|
// 29 4/25/98 5:09p Markd
|
|
// Added up and down support for angles
|
|
//
|
|
// 28 4/07/98 6:47p Jimdose
|
|
// Fixed weapon switching bugs
|
|
//
|
|
// 27 4/06/98 5:46p Jimdose
|
|
// Added "angles" spawn values
|
|
//
|
|
// 26 4/05/98 2:57a Jimdose
|
|
// Made checks for anim and classname case insensitive
|
|
//
|
|
// 25 3/30/98 2:37p Jimdose
|
|
// Added Ammo
|
|
// Added weapon dropping
|
|
//
|
|
// 24 3/29/98 10:01p Jimdose
|
|
// Added animation control functions
|
|
//
|
|
// 23 3/27/98 5:37p Jimdose
|
|
// Added FreeInventory for when players respawn
|
|
//
|
|
// 22 3/24/98 4:57p Jimdose
|
|
// Changed usage of GetToken to GetString so that script variables can be used
|
|
//
|
|
// 21 3/23/98 1:31p Jimdose
|
|
// Revamped event and command system
|
|
//
|
|
// 20 3/18/98 7:23p Jimdose
|
|
// Added code for handling readying and putting away weapons
|
|
//
|
|
// 19 3/02/98 8:49p Jimdose
|
|
// Changed the classid parameter of CLASS_DECLARATION to a quoted string so
|
|
// that you could have a NULL classid.
|
|
//
|
|
// 18 3/02/98 5:38p Jimdose
|
|
// Added destructor so that all held weapons are released from being owned.
|
|
// Temporariliy disabled type checking on Weapons.
|
|
//
|
|
// 17 2/19/98 2:36p Jimdose
|
|
// Added weapons back in
|
|
//
|
|
// 16 2/17/98 6:59p Jimdose
|
|
// no longer pass script into interpretCommand
|
|
//
|
|
// 15 2/03/98 10:51a Jimdose
|
|
// Updated to work with Quake 2 engine
|
|
// Moved initialization to constructor and removed Init function
|
|
//
|
|
// 13 12/15/97 4:39p Aldie
|
|
// Added support for gunoffset in def files.
|
|
//
|
|
// 12 12/15/97 1:37a Jimdose
|
|
// Added assertions in nextWeapon and previousWeapon to help track down a bug.
|
|
// While changing weapons, I got a NULL pointer when I retrieved a entity
|
|
// number from the inventory. Weapons in the inventory should never be freed
|
|
// during the game without being removed from the inventory first.
|
|
//
|
|
// 11 12/11/97 3:36p Markd
|
|
// Moved ClearInventory List to before Init call to super class
|
|
//
|
|
// 10 12/09/97 7:41p Markd
|
|
// Added NumWeapons and WeaponNumber functions
|
|
//
|
|
// 9 11/20/97 9:02p Jimdose
|
|
// Changed Container class to use entity # instead of void *
|
|
//
|
|
// 8 11/18/97 5:27p Markd
|
|
// Changed "ammo" command and also fixed some weapon checking
|
|
//
|
|
// 7 11/15/97 6:54p Markd
|
|
// Added InterpretCommand, Added "fire", "ammo" and "weapon" command
|
|
//
|
|
// 6 10/28/97 6:05p Jimdose
|
|
// Sentients now pick up nails from nail weapons.
|
|
//
|
|
// 5 10/27/97 3:29p Jimdose
|
|
// Removed dependency on quakedef.h
|
|
//
|
|
// 4 10/24/97 2:58p Jimdose
|
|
// Fixed bug where sentient was bound to weapon instead of weapon bound to
|
|
// sentient.
|
|
//
|
|
// 3 10/01/97 2:04p Jimdose
|
|
// Weapons are now bound to their owners when they are picked up.
|
|
//
|
|
// 2 9/26/97 6:47p Jimdose
|
|
// Added standard Ritual headers
|
|
//
|
|
// DESCRIPTION:
|
|
// Base class of entity that can carry other entities, and use weapons.
|
|
//
|
|
|
|
#include "g_local.h"
|
|
#include "entity.h"
|
|
#include "sentient.h"
|
|
#include "weapon.h"
|
|
#include "scriptmaster.h"
|
|
#include "ammo.h"
|
|
#include "armor.h"
|
|
#include "misc.h"
|
|
#include "inventoryitem.h"
|
|
#include "player.h"
|
|
#include "actor.h"
|
|
|
|
CLASS_DECLARATION( Entity, Sentient, NULL );
|
|
|
|
Event EV_Sentient_Attack( "fire" );
|
|
Event EV_Sentient_ReleaseAttack( "releasefire" );
|
|
Event EV_Sentient_GiveWeapon( "weapon" );
|
|
Event EV_Sentient_GiveAmmo( "ammo" );
|
|
Event EV_Sentient_GiveArmor( "armor" );
|
|
Event EV_Sentient_GiveItem( "item" );
|
|
Event EV_Sentient_GiveTargetname( "give" );
|
|
Event EV_Sentient_GiveInventoryItem( "invitem" );
|
|
Event EV_Sentient_GiveHealth( "health", EV_CHEAT );
|
|
Event EV_Sentient_TakeWeapon( "take_weapon" );
|
|
Event EV_Sentient_TakeAmmo( "take_ammo" );
|
|
Event EV_Sentient_TakeArmor( "take_armor" );
|
|
Event EV_Sentient_TakeItem( "take_item" );
|
|
Event EV_Sentient_WeaponPutAway( "weapon_putaway" );
|
|
Event EV_Sentient_WeaponReady( "weapon_ready" );
|
|
Event EV_Sentient_WeaponDoneFiring( "weapon_donefiring" );
|
|
Event EV_Sentient_AnimLoop( "animloop" );
|
|
Event EV_Sentient_UselessCheck( "useless_check" );
|
|
Event EV_Sentient_TurnOffShadow( "noshadow" );
|
|
Event EV_Sentient_Freeze( "freeze" );
|
|
Event EV_Sentient_UnFreeze( "unfreeze" );
|
|
Event EV_Sentient_ImpactDamage( "impact_damage" );
|
|
Event EV_Sentient_WeaponUse( "weaponuse", EV_CONSOLE );
|
|
Event EV_Sentient_SetDropWeapon( "dropweapon" );
|
|
Event EV_Sentient_DropWeaponNow( "dropweaponnow" );
|
|
|
|
ResponseDef Sentient::Responses[] =
|
|
{
|
|
{ &EV_Sentient_Attack, ( Response )Sentient::FireWeapon },
|
|
{ &EV_Sentient_ReleaseAttack, ( Response )Sentient::ReleaseFireWeapon },
|
|
{ &EV_Sentient_GiveWeapon, ( Response )Sentient::EventGiveWeapon },
|
|
{ &EV_Sentient_TakeWeapon, ( Response )Sentient::EventTakeWeapon },
|
|
{ &EV_Sentient_GiveAmmo, ( Response )Sentient::EventGiveAmmo },
|
|
{ &EV_Sentient_TakeAmmo, ( Response )Sentient::EventTakeAmmo },
|
|
{ &EV_Sentient_GiveArmor, ( Response )Sentient::EventGiveArmor },
|
|
{ &EV_Sentient_TakeArmor, ( Response )Sentient::EventTakeArmor },
|
|
{ &EV_Sentient_GiveItem, ( Response )Sentient::EventGiveItem },
|
|
{ &EV_Sentient_GiveInventoryItem,( Response )Sentient::EventGiveInventoryItem },
|
|
{ &EV_Sentient_GiveHealth, ( Response )Sentient::EventGiveHealth },
|
|
{ &EV_Sentient_GiveTargetname, ( Response )Sentient::EventGiveTargetname },
|
|
{ &EV_Sentient_WeaponPutAway, ( Response )Sentient::WeaponPutAway },
|
|
{ &EV_Sentient_WeaponReady, ( Response )Sentient::WeaponReady },
|
|
{ &EV_Sentient_WeaponDoneFiring, ( Response )Sentient::WeaponDoneFiring },
|
|
{ &EV_Sentient_AnimLoop, ( Response )Sentient::AnimLoop },
|
|
{ &EV_Damage, ( Response )Sentient::ArmorDamage },
|
|
{ &EV_Touch, ( Response )Sentient::SearchBody },
|
|
{ &EV_Sentient_UselessCheck, ( Response )Sentient::UselessCheck },
|
|
{ &EV_Sentient_TurnOffShadow, ( Response )Sentient::TurnOffShadow },
|
|
{ &EV_Sentient_Freeze, ( Response )Sentient::Freeze },
|
|
{ &EV_Sentient_UnFreeze, ( Response )Sentient::UnFreeze },
|
|
{ &EV_Sentient_ImpactDamage, ( Response )Sentient::ImpactDamage },
|
|
{ &EV_Sentient_WeaponUse, ( Response )Sentient::WeaponUse },
|
|
{ &EV_Sentient_SetDropWeapon, ( Response )Sentient::SetDropWeapon },
|
|
{ &EV_Sentient_DropWeaponNow, ( Response )Sentient::DropWeaponNowEvent },
|
|
{ &EV_Sentient_TakeItem, ( Response )Sentient::EventTakeItem },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
Container<Sentient *> SentientList;
|
|
|
|
Sentient::Sentient()
|
|
{
|
|
Vector defangles;
|
|
|
|
SentientList.AddObject( ( Sentient * )this );
|
|
|
|
inventory.ClearObjectList();
|
|
currentWeapon = NULL;
|
|
currentItem = NULL;
|
|
newWeapon = NULL;
|
|
animOverride = false;
|
|
dropweapon = true;
|
|
tempAnimEvent = NULL;
|
|
|
|
eyeposition = "0 0 64";
|
|
gunoffset = "0 0 56";
|
|
|
|
firedown = false;
|
|
firedowntime = 0;
|
|
|
|
// angles
|
|
defangles = Vector( 0, G_GetFloatArg( "angle", 0 ), 0 );
|
|
if (defangles.y == -1)
|
|
{
|
|
defangles = Vector( -90, 0, 0 );
|
|
}
|
|
else if (defangles.y == -2)
|
|
{
|
|
defangles = Vector( 90, 0, 0 );
|
|
}
|
|
angles = G_GetVectorArg( "angles", defangles );
|
|
setAngles( angles );
|
|
stopanimating_tillchange = false;
|
|
|
|
poweruptype = 0;
|
|
poweruptimer = 0;
|
|
sentientFrozen = false;
|
|
|
|
// HACK FIXME
|
|
//
|
|
// temporary shadow flag
|
|
//
|
|
edict->s.renderfx |= RF_XFLIP;
|
|
}
|
|
|
|
Sentient::~Sentient()
|
|
{
|
|
SentientList.RemoveObject( ( Sentient * )this );
|
|
FreeInventory();
|
|
}
|
|
|
|
Vector Sentient::EyePosition
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
Vector pos;
|
|
|
|
pos = worldorigin;
|
|
pos[ gravity_axis[ gravaxis ].x ] += eyeposition[ 0 ];
|
|
pos[ gravity_axis[ gravaxis ].y ] += eyeposition[ 1 ] * gravity_axis[ gravaxis ].sign;
|
|
pos[ gravity_axis[ gravaxis ].z ] += eyeposition[ 2 ] * gravity_axis[ gravaxis ].sign;
|
|
|
|
return pos;
|
|
}
|
|
|
|
Vector Sentient::GunPosition
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
Vector pos;
|
|
|
|
pos = worldorigin;
|
|
pos[ gravity_axis[ gravaxis ].x ] += gunoffset[ 0 ];
|
|
pos[ gravity_axis[ gravaxis ].y ] += gunoffset[ 1 ] * gravity_axis[ gravaxis ].sign;
|
|
pos[ gravity_axis[ gravaxis ].z ] += gunoffset[ 2 ] * gravity_axis[ gravaxis ].sign;
|
|
|
|
return pos;
|
|
}
|
|
|
|
inline void Sentient::GetGunOrientation
|
|
(
|
|
Vector pos,
|
|
Vector *forward,
|
|
Vector *right,
|
|
Vector *up
|
|
)
|
|
|
|
{
|
|
const gravityaxis_t &grav = gravity_axis[ gravaxis ];
|
|
|
|
if ( forward )
|
|
{
|
|
( *forward )[ grav.x ] = orientation[ 0 ][ 0 ];
|
|
( *forward )[ grav.y ] = orientation[ 0 ][ 1 ] * grav.sign;
|
|
( *forward )[ grav.z ] = orientation[ 0 ][ 2 ] * grav.sign;
|
|
}
|
|
|
|
if ( right )
|
|
{
|
|
( *right )[ grav.x ] = -orientation[ 1 ][ 0 ];
|
|
( *right )[ grav.y ] = - orientation[ 1 ][ 1 ] * grav.sign;
|
|
( *right )[ grav.z ] = - orientation[ 1 ][ 2 ] * grav.sign;
|
|
}
|
|
|
|
if ( up )
|
|
{
|
|
( *up )[ grav.x ] = orientation[ 2 ][ 0 ];
|
|
( *up )[ grav.y ] = orientation[ 2 ][ 1 ] * grav.sign;
|
|
( *up )[ grav.z ] = orientation[ 2 ][ 2 ] * grav.sign;
|
|
}
|
|
}
|
|
|
|
void Sentient::GetMuzzlePositionAndDirection
|
|
(
|
|
Vector *pos,
|
|
Vector *dir
|
|
)
|
|
|
|
{
|
|
*pos = Vector( "0 0 0" );
|
|
*dir = Vector( "0 0 0" );
|
|
|
|
if ( currentWeapon )
|
|
currentWeapon->GetMuzzlePosition( pos, dir );
|
|
}
|
|
|
|
void Sentient::EventGiveHealth
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int group_num;
|
|
|
|
health = ev->GetFloat( 1 );
|
|
|
|
if ( isClient() )
|
|
{
|
|
for( group_num = 0; group_num < edict->s.numgroups ; group_num++ )
|
|
{
|
|
edict->s.groups[ group_num ] &= ~MDL_GROUP_SKINOFFSET_BIT0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sentient::FireWeapon
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
if ( ( currentWeapon ) && currentWeapon->ReadyToFire() && currentWeapon->HasAmmo() )
|
|
{
|
|
currentWeapon->Fire();
|
|
}
|
|
}
|
|
|
|
void Sentient::ReleaseFireWeapon
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
// Only need to check if currentWeapon because FireWeapon checks for
|
|
// the weapon ready and if it has ammo.
|
|
if ( currentWeapon )
|
|
{
|
|
float fireholdtime = ev->GetFloat( 1 );
|
|
currentWeapon->ReleaseFire( fireholdtime );
|
|
}
|
|
}
|
|
|
|
void Sentient::AddItem
|
|
(
|
|
Item *object
|
|
)
|
|
|
|
{
|
|
inventory.AddObject( object->entnum );
|
|
|
|
if ( object->isSubclassOf( InventoryItem ) )
|
|
{
|
|
currentItem = (InventoryItem *)object;
|
|
}
|
|
//
|
|
// set our global game variables if client
|
|
//
|
|
if ( isClient() )
|
|
{
|
|
str fullname;
|
|
ScriptVariable * var;
|
|
|
|
fullname = str( "playeritem_" ) + object->getClassname();
|
|
var = gameVars.GetVariable( fullname.c_str() );
|
|
if ( !var )
|
|
{
|
|
gameVars.SetVariable( fullname.c_str(), 1 );
|
|
}
|
|
else
|
|
{
|
|
int amount;
|
|
|
|
amount = var->intValue() + 1;
|
|
var->setIntValue( amount );
|
|
}
|
|
|
|
var = levelVars.GetVariable( fullname.c_str() );
|
|
if ( !var )
|
|
{
|
|
levelVars.SetVariable( fullname.c_str(), 1 );
|
|
}
|
|
else
|
|
{
|
|
int amount;
|
|
|
|
amount = var->intValue() + 1;
|
|
var->setIntValue( amount );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sentient::RemoveItem
|
|
(
|
|
Item *object
|
|
)
|
|
|
|
{
|
|
Weapon *weapon;
|
|
|
|
inventory.RemoveObject( object->entnum );
|
|
if ( currentWeapon == object )
|
|
{
|
|
currentWeapon = NULL;
|
|
if ( newWeapon == object )
|
|
{
|
|
newWeapon = NULL;
|
|
weapon = BestWeapon();
|
|
}
|
|
else
|
|
{
|
|
weapon = newWeapon;
|
|
}
|
|
|
|
SetCurrentWeapon( weapon );
|
|
}
|
|
|
|
//
|
|
// set our global game variables if client
|
|
//
|
|
if ( isClient() )
|
|
{
|
|
str fullname;
|
|
ScriptVariable * var;
|
|
|
|
fullname = str( "playeritem_" ) + object->getClassname();
|
|
|
|
var = levelVars.GetVariable( fullname.c_str() );
|
|
if ( var )
|
|
{
|
|
int amount;
|
|
|
|
amount = var->intValue() - 1;
|
|
if ( amount < 0 )
|
|
amount = 0;
|
|
var->setIntValue( amount );
|
|
}
|
|
}
|
|
|
|
if ( currentItem == (InventoryItem *) object )
|
|
{
|
|
currentItem = NULL;
|
|
// Try to set the the current item
|
|
|
|
currentItem = (InventoryItem *)NextItem( NULL );
|
|
if (!currentItem)
|
|
{
|
|
currentItem = (InventoryItem *)PrevItem( NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
Item *Sentient::FindItem
|
|
(
|
|
const char *itemname
|
|
)
|
|
|
|
{
|
|
int num;
|
|
int i;
|
|
Item *item;
|
|
|
|
num = inventory.NumObjects();
|
|
for( i = 1; i <= num; i++ )
|
|
{
|
|
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
if ( !Q_stricmp( item->getClassname(), itemname ) )
|
|
{
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void Sentient::FreeInventory
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int num;
|
|
int i;
|
|
Item *item;
|
|
|
|
if ( currentWeapon )
|
|
currentWeapon->DetachFromOwner();
|
|
|
|
SetCurrentWeapon( NULL );
|
|
newWeapon = NULL;
|
|
|
|
num = inventory.NumObjects();
|
|
for( i = num; i > 0; i-- )
|
|
{
|
|
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
delete item;
|
|
}
|
|
|
|
inventory.ClearObjectList();
|
|
currentItem = NULL;
|
|
}
|
|
|
|
void Sentient::FreeInventoryOfType
|
|
(
|
|
const char *type
|
|
)
|
|
|
|
{
|
|
int num;
|
|
int i;
|
|
Item *item;
|
|
|
|
num = inventory.NumObjects();
|
|
for( i = num; i > 0; i-- )
|
|
{
|
|
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
if ( checkInheritance( type, item->getClassname() ) )
|
|
{
|
|
inventory.RemoveObject( item->entnum );
|
|
|
|
// If the current item is about to be deleted, then look for
|
|
// another item to be current
|
|
if ( currentItem == item )
|
|
{
|
|
currentItem = NULL;
|
|
// Try to set the the current item
|
|
|
|
currentItem = (InventoryItem *)NextItem( NULL );
|
|
if (!currentItem)
|
|
{
|
|
currentItem = (InventoryItem *)PrevItem( NULL );
|
|
}
|
|
}
|
|
delete item;
|
|
}
|
|
}
|
|
}
|
|
|
|
qboolean Sentient::HasItem
|
|
(
|
|
const char *itemname
|
|
)
|
|
|
|
{
|
|
return ( FindItem( itemname ) != NULL );
|
|
}
|
|
|
|
int Sentient::NumWeapons
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int num;
|
|
int i;
|
|
Item *item;
|
|
int numweaps;
|
|
|
|
numweaps = 0;
|
|
|
|
num = inventory.NumObjects();
|
|
for( i = 1; i <= num; i++ )
|
|
{
|
|
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
if ( checkInheritance( &Weapon::ClassInfo, item->getClassname() ) )
|
|
{
|
|
numweaps++;
|
|
}
|
|
}
|
|
|
|
return numweaps;
|
|
}
|
|
|
|
Weapon * Sentient::WeaponNumber
|
|
(
|
|
int weaponnum
|
|
)
|
|
|
|
{
|
|
int num;
|
|
int i;
|
|
Item *item;
|
|
|
|
// make it so that 0 is our first weapon rather than 1
|
|
weaponnum++;
|
|
num = inventory.NumObjects();
|
|
for( i = 1; i <= num; i++ )
|
|
{
|
|
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
if ( item->isSubclassOf( Weapon ) )
|
|
{
|
|
weaponnum--;
|
|
if ( !weaponnum )
|
|
{
|
|
return ( Weapon * )item;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void Sentient::ChangeWeapon
|
|
(
|
|
Weapon *weapon
|
|
)
|
|
|
|
{
|
|
if ( weapon == currentWeapon )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( currentWeapon )
|
|
{
|
|
if ( !weapon->ReadyToUse() )
|
|
{
|
|
return;
|
|
}
|
|
newWeapon = weapon;
|
|
if ( !currentWeapon->ReadyToChange() )
|
|
{
|
|
return;
|
|
}
|
|
currentWeapon->PutAway();
|
|
}
|
|
else
|
|
{
|
|
SetCurrentWeapon( weapon );
|
|
}
|
|
}
|
|
|
|
void Sentient::ForceChangeWeapon
|
|
(
|
|
Weapon *weapon
|
|
)
|
|
|
|
{
|
|
if ( newWeapon == weapon )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( weapon == currentWeapon )
|
|
{
|
|
return;
|
|
}
|
|
|
|
newWeapon = NULL;
|
|
|
|
if ( currentWeapon )
|
|
{
|
|
currentWeapon->DetachFromOwner();
|
|
}
|
|
SetCurrentWeapon( weapon );
|
|
}
|
|
|
|
qboolean Sentient::CanChangeWeapons
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return !(
|
|
( newWeapon != NULL ) ||
|
|
( currentWeapon && !currentWeapon->ReadyToChange() ) ||
|
|
( flags & FL_MUTANT )
|
|
);
|
|
}
|
|
|
|
void Sentient::SetCurrentWeapon
|
|
(
|
|
Weapon *weapon
|
|
)
|
|
|
|
{
|
|
const char *name;
|
|
|
|
newWeapon = NULL;
|
|
currentWeapon = weapon;
|
|
if ( currentWeapon )
|
|
{
|
|
currentWeapon->ReadyWeapon();
|
|
|
|
// get the name of the group that the gun bone belongs to
|
|
name = gi.GetBoneGroupName( edict->s.modelindex, "gun" );
|
|
if ( name )
|
|
{
|
|
gun_bone_group_name = name;
|
|
}
|
|
else
|
|
{
|
|
gun_bone_group_name = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
Weapon *Sentient::CurrentWeapon
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return currentWeapon;
|
|
}
|
|
|
|
Weapon *Sentient::BestWeapon
|
|
(
|
|
Weapon *ignore
|
|
)
|
|
|
|
{
|
|
Item *next;
|
|
int n;
|
|
int j;
|
|
int bestrank;
|
|
Weapon *bestweapon;
|
|
|
|
n = inventory.NumObjects();
|
|
|
|
// Search forewards until we find a weapon
|
|
bestweapon = NULL;
|
|
bestrank = -999999;
|
|
for( j = 1; j <= n; j++ )
|
|
{
|
|
next = ( Item * )G_GetEntity( inventory.ObjectAt( j ) );
|
|
|
|
assert( next );
|
|
|
|
if ( ( next != ignore ) && next->isSubclassOf( Weapon ) && ( ( ( Weapon * )next )->Rank() > bestrank ) &&
|
|
( ( ( Weapon * )next )->HasAmmo() ) )
|
|
{
|
|
bestweapon = ( Weapon * )next;
|
|
bestrank = bestweapon->Rank();
|
|
}
|
|
}
|
|
|
|
return bestweapon;
|
|
}
|
|
|
|
Weapon *Sentient::NextWeapon
|
|
(
|
|
Weapon *weapon
|
|
)
|
|
|
|
{
|
|
Item *item;
|
|
int i;
|
|
int n;
|
|
int weaponorder;
|
|
Weapon *choice;
|
|
int choiceorder;
|
|
Weapon *bestchoice;
|
|
int bestorder;
|
|
Weapon *worstchoice;
|
|
int worstorder;
|
|
|
|
if ( !inventory.ObjectInList( weapon->entnum ) )
|
|
{
|
|
error( "NextWeapon", "Weapon not in list" );
|
|
}
|
|
|
|
weaponorder = weapon->Order();
|
|
bestchoice = weapon;
|
|
bestorder = 65535;
|
|
worstchoice = weapon;
|
|
worstorder = weaponorder;
|
|
|
|
n = inventory.NumObjects();
|
|
for( i = 1; i <= n; i++ )
|
|
{
|
|
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
|
|
assert( item );
|
|
|
|
if ( item->isSubclassOf( Weapon ) )
|
|
{
|
|
choice = ( Weapon * )item;
|
|
if ( !choice->HasAmmo() || !choice->AutoChange() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
choiceorder = choice->Order();
|
|
if ( ( choiceorder > weaponorder ) && ( choiceorder < bestorder ) )
|
|
{
|
|
bestorder = choiceorder;
|
|
bestchoice = choice;
|
|
}
|
|
|
|
if ( choiceorder < worstorder )
|
|
{
|
|
worstorder = choiceorder;
|
|
worstchoice = choice;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bestchoice == weapon )
|
|
{
|
|
return worstchoice;
|
|
}
|
|
|
|
return bestchoice;
|
|
}
|
|
|
|
Weapon *Sentient::PreviousWeapon
|
|
(
|
|
Weapon *weapon
|
|
)
|
|
|
|
{
|
|
Item *item;
|
|
int i;
|
|
int n;
|
|
int weaponorder;
|
|
Weapon *choice;
|
|
int choiceorder;
|
|
Weapon *bestchoice;
|
|
int bestorder;
|
|
Weapon *worstchoice;
|
|
int worstorder;
|
|
|
|
if ( !inventory.ObjectInList( weapon->entnum ) )
|
|
{
|
|
error( "PreviousWeapon", "Weapon not in list" );
|
|
}
|
|
|
|
weaponorder = weapon->Order();
|
|
bestchoice = weapon;
|
|
bestorder = -65535;
|
|
worstchoice = weapon;
|
|
worstorder = weaponorder;
|
|
|
|
n = inventory.NumObjects();
|
|
for( i = 1; i <= n; i++ )
|
|
{
|
|
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
|
|
assert( item );
|
|
|
|
if ( item->isSubclassOf( Weapon ) )
|
|
{
|
|
choice = ( Weapon * )item;
|
|
if ( !choice->HasAmmo() || !choice->AutoChange() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
choiceorder = choice->Order();
|
|
if ( ( choiceorder < weaponorder ) && ( choiceorder > bestorder ) )
|
|
{
|
|
bestorder = choiceorder;
|
|
bestchoice = choice;
|
|
}
|
|
|
|
if ( choiceorder > worstorder )
|
|
{
|
|
worstorder = choiceorder;
|
|
worstchoice = choice;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bestchoice == weapon )
|
|
{
|
|
return worstchoice;
|
|
}
|
|
|
|
return bestchoice;
|
|
}
|
|
|
|
qboolean Sentient::WeaponReady
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
Weapon *best;
|
|
|
|
if ( !currentWeapon )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!currentWeapon->ReadyToUse() )
|
|
{
|
|
best = BestWeapon();
|
|
if ( best )
|
|
{
|
|
ChangeWeapon( best );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if ( !currentWeapon->ReadyToFire() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !currentWeapon->HasAmmo() )
|
|
{
|
|
best = BestWeapon();
|
|
if ( best )
|
|
{
|
|
ChangeWeapon( best );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Sentient::DropWeapon
|
|
(
|
|
Weapon *weapon
|
|
)
|
|
|
|
{
|
|
if ( weapon == currentWeapon )
|
|
{
|
|
if ( deadflag )
|
|
{
|
|
SetCurrentWeapon( NULL );
|
|
}
|
|
else
|
|
{
|
|
// Choose the best weapon that's not the current
|
|
SetCurrentWeapon( BestWeapon( currentWeapon ) );
|
|
}
|
|
}
|
|
|
|
// check if the sentient can drop the weapon
|
|
if ( dropweapon )
|
|
{
|
|
// This one checks if the weapon is droppable
|
|
if ( !weapon->Drop() && deadflag )
|
|
{
|
|
// can't drop it, probably because there's no worldmodel
|
|
// since we're dead, just delete the weapon.
|
|
delete weapon;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delete weapon;
|
|
}
|
|
}
|
|
|
|
void Sentient::DropCurrentWeapon
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
if ( currentWeapon )
|
|
DropWeapon( currentWeapon );
|
|
}
|
|
|
|
void Sentient::takeWeapon
|
|
(
|
|
const char *weaponname
|
|
)
|
|
{
|
|
Weapon *weapon;
|
|
|
|
assert( weaponname );
|
|
|
|
weapon = ( Weapon * )FindItem( weaponname );
|
|
if ( !weapon )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( weapon == currentWeapon )
|
|
{
|
|
SetCurrentWeapon( BestWeapon( weapon ) );
|
|
}
|
|
|
|
weapon->DetachFromOwner();
|
|
weapon->PostEvent( EV_Remove, 0 );
|
|
}
|
|
|
|
void Sentient::EventGiveTargetname
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
str name;
|
|
Item *item=NULL;
|
|
edict_t *from;
|
|
|
|
name = ev->GetString( 1 );
|
|
|
|
for ( from = &g_edicts[ 1 ]; from < &g_edicts[ globals.num_edicts ] ; from++ )
|
|
{
|
|
if ( !from->inuse )
|
|
continue;
|
|
|
|
if ( from->entity->isSubclassOf( Item ) )
|
|
{
|
|
item = ( Item * ) from->entity;
|
|
if ( item->itemname == name )
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Item should be found
|
|
assert( item );
|
|
|
|
if ( item )
|
|
{
|
|
item->SetOwner( this );
|
|
item->ProcessPendingEvents();
|
|
AddItem( item );
|
|
}
|
|
else
|
|
{
|
|
ev->Error( "Could not give item %s to %s.\n", name, targetname );
|
|
}
|
|
}
|
|
|
|
Item *Sentient::giveItem
|
|
(
|
|
const char * itemname,
|
|
int amount,
|
|
int icon_index
|
|
)
|
|
|
|
{
|
|
ClassDef *cls;
|
|
Item *item;
|
|
int index;
|
|
|
|
item = ( Item * )FindItem( itemname );
|
|
|
|
if ( item )
|
|
{
|
|
item->Add( amount );
|
|
}
|
|
else
|
|
{
|
|
cls = getClass( itemname );
|
|
if ( !cls )
|
|
{
|
|
gi.dprintf( "No item named '%s'\n", itemname );
|
|
return NULL;
|
|
}
|
|
|
|
item = ( Item * )cls->newInstance();
|
|
|
|
item->SetOwner( this );
|
|
|
|
item->ProcessPendingEvents();
|
|
|
|
item->Set( amount );
|
|
AddItem( item );
|
|
}
|
|
|
|
// Send icon to the player
|
|
if ( this->isSubclassOf( Player ) )
|
|
{
|
|
if ( icon_index >= 0 )
|
|
index = icon_index;
|
|
else
|
|
index = item->Icon();
|
|
|
|
if ( index >= 0 )
|
|
{
|
|
gi.WriteByte( svc_console_command );
|
|
gi.WriteString( va( "pl %d %d", index, amount ) );
|
|
gi.unicast( edict, NULL );
|
|
}
|
|
}
|
|
return item;
|
|
}
|
|
|
|
void Sentient::takeItem
|
|
(
|
|
const char *itemname,
|
|
int amount
|
|
)
|
|
{
|
|
Item *item;
|
|
|
|
item = ( Item * )FindItem( itemname );
|
|
|
|
if ( item )
|
|
{
|
|
item->Remove( amount );
|
|
if ( !item->Amount() )
|
|
item->ProcessEvent( EV_Remove );
|
|
}
|
|
}
|
|
|
|
void Sentient::EventTakeItem
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int amount;
|
|
|
|
if ( ev->NumArgs() > 1 )
|
|
amount = ev->GetInteger( 2 );
|
|
else
|
|
amount = 1;
|
|
takeItem( ev->GetString( 1 ), amount );
|
|
}
|
|
|
|
|
|
Weapon *Sentient::giveWeapon
|
|
(
|
|
const char *weaponname
|
|
)
|
|
|
|
{
|
|
Weapon *weapon;
|
|
|
|
assert( weaponname );
|
|
|
|
if ( !checkInheritance( &Weapon::ClassInfo, weaponname ) )
|
|
{
|
|
gi.dprintf( "A %s is not usable as a weapon\n", weaponname );
|
|
return NULL;
|
|
}
|
|
|
|
weapon = ( Weapon * )giveItem( weaponname, 1 );
|
|
|
|
if ( !currentWeapon || ( weapon->Rank() > currentWeapon->Rank() ) )
|
|
{
|
|
ChangeWeapon( weapon );
|
|
}
|
|
|
|
return weapon;
|
|
}
|
|
|
|
Weapon *Sentient::useWeapon
|
|
(
|
|
const char *weaponname
|
|
)
|
|
|
|
{
|
|
Weapon *weapon;
|
|
|
|
assert( weaponname );
|
|
|
|
weapon = ( Weapon * )FindItem( weaponname );
|
|
if ( weapon )
|
|
{
|
|
ChangeWeapon( weapon );
|
|
}
|
|
|
|
return currentWeapon;
|
|
}
|
|
|
|
void Sentient::EventGiveWeapon
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
ChangeWeapon( giveWeapon( ev->GetString( 1 ) ) );
|
|
}
|
|
|
|
|
|
void Sentient::EventTakeWeapon
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
takeWeapon( ev->GetString( 1 ) );
|
|
}
|
|
|
|
void Sentient::EventTakeAmmo
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int amount;
|
|
const char *type;
|
|
Ammo *ammo;
|
|
Weapon *best;
|
|
|
|
type = ev->GetString( 1 );
|
|
amount = ev->GetInteger( 2 );
|
|
|
|
ammo = ( Ammo * )FindItem( type );
|
|
if ( ammo )
|
|
{
|
|
ammo->Remove( amount );
|
|
}
|
|
|
|
if ( currentWeapon && !currentWeapon->HasAmmo() )
|
|
{
|
|
best = BestWeapon();
|
|
if ( best )
|
|
{
|
|
ChangeWeapon( best );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sentient::EventGiveAmmo
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int amount;
|
|
const char *type;
|
|
|
|
type = ev->GetString( 1 );
|
|
amount = ev->GetInteger( 2 );
|
|
|
|
if ( !checkInheritance( &Ammo::ClassInfo, type ) )
|
|
{
|
|
gi.dprintf( "%s is not usable as ammo\n", type );
|
|
return;
|
|
}
|
|
|
|
giveItem( type, amount );
|
|
|
|
if ( currentWeapon && !currentWeapon->HasAmmo() )
|
|
{
|
|
Weapon *best;
|
|
best = BestWeapon();
|
|
if ( best )
|
|
{
|
|
ChangeWeapon( best );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sentient::EventTakeArmor
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int amount;
|
|
const char *type;
|
|
Armor *armor;
|
|
|
|
type = ev->GetString( 1 );
|
|
amount = ev->GetInteger( 2 );
|
|
|
|
armor = ( Armor * )FindItem( type );
|
|
if ( armor )
|
|
{
|
|
armor->Remove( amount );
|
|
}
|
|
}
|
|
|
|
void Sentient::EventGiveArmor
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int amount;
|
|
const char *type;
|
|
Armor *armor;
|
|
|
|
type = ev->GetString( 1 );
|
|
amount = ev->GetInteger( 2 );
|
|
|
|
if ( !checkInheritance( &Armor::ClassInfo, type ) )
|
|
{
|
|
gi.dprintf( "%s is not usable as armor\n", type );
|
|
return;
|
|
}
|
|
|
|
armor = ( Armor * )FindItem( type );
|
|
|
|
if ( armor )
|
|
{
|
|
armor->Set( amount );
|
|
}
|
|
else
|
|
{
|
|
giveItem( type, amount );
|
|
}
|
|
}
|
|
|
|
void Sentient::EventGiveItem
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
const char *type;
|
|
float amount;
|
|
|
|
type = ev->GetString( 1 );
|
|
amount = ev->GetInteger( 2 );
|
|
|
|
giveItem( type, amount );
|
|
}
|
|
|
|
void Sentient::EventGiveInventoryItem
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
const char *type;
|
|
int amount;
|
|
|
|
type = ev->GetString( 1 );
|
|
amount = ev->GetInteger( 2 );
|
|
|
|
if ( !checkInheritance( &InventoryItem::ClassInfo, type ) )
|
|
{
|
|
gi.dprintf( "item '%s' is not a InventoryItem\n", type );
|
|
return;
|
|
}
|
|
|
|
giveItem( type, amount );
|
|
}
|
|
|
|
void Sentient::WeaponPutAway
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
SetCurrentWeapon( newWeapon );
|
|
}
|
|
|
|
void Sentient::WeaponReady
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
if ( newWeapon )
|
|
{
|
|
ChangeWeapon( newWeapon );
|
|
}
|
|
}
|
|
|
|
void Sentient::WeaponDoneFiring
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Weapon *best;
|
|
|
|
if ( newWeapon )
|
|
{
|
|
ChangeWeapon( newWeapon );
|
|
}
|
|
else if ( !currentWeapon || !currentWeapon->HasAmmo() || !currentWeapon->ReadyToUse() )
|
|
{
|
|
best = BestWeapon();
|
|
if ( best )
|
|
{
|
|
ChangeWeapon( best );
|
|
}
|
|
}
|
|
}
|
|
|
|
EXPORT_FROM_DLL void Sentient::AnimLoop
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Event *t;
|
|
|
|
animOverride = false;
|
|
|
|
if ( deadflag )
|
|
{
|
|
StopAnimating();
|
|
}
|
|
else if ( animating )
|
|
{
|
|
RandomAnimate( currentAnim.c_str(), NULL );
|
|
}
|
|
else
|
|
{
|
|
stopanimating_tillchange = true;
|
|
}
|
|
|
|
if ( tempAnimEvent )
|
|
{
|
|
t = tempAnimEvent;
|
|
tempAnimEvent = NULL;
|
|
ProcessEvent( t );
|
|
}
|
|
}
|
|
|
|
EXPORT_FROM_DLL void Sentient::SetAnim
|
|
(
|
|
const char *anim
|
|
)
|
|
|
|
{
|
|
assert( anim );
|
|
|
|
assert ( !deadflag );
|
|
|
|
if ( str( anim ) == currentAnim )
|
|
{
|
|
if (!animating && !stopanimating_tillchange)
|
|
StartAnimating();
|
|
return;
|
|
}
|
|
|
|
stopanimating_tillchange = false;
|
|
currentAnim = str( anim );
|
|
|
|
if ( animOverride )
|
|
{
|
|
return;
|
|
}
|
|
|
|
RandomAnimate( currentAnim.c_str(), EV_Sentient_AnimLoop );
|
|
|
|
if (!animating)
|
|
StartAnimating();
|
|
}
|
|
|
|
EXPORT_FROM_DLL void Sentient::TempAnim
|
|
(
|
|
const char *anim,
|
|
Event *event
|
|
)
|
|
|
|
{
|
|
assert ( !deadflag );
|
|
|
|
assert( anim );
|
|
|
|
animOverride = true;
|
|
|
|
tempAnimEvent = event;
|
|
|
|
RandomAnimate( anim, EV_Sentient_AnimLoop );
|
|
}
|
|
|
|
void Sentient::TempAnim
|
|
(
|
|
const char *anim,
|
|
Event &event
|
|
)
|
|
|
|
{
|
|
Event *ev;
|
|
|
|
ev = new Event( event );
|
|
TempAnim( anim, ev );
|
|
}
|
|
|
|
void Sentient::PrintDamageLocationToAttacker
|
|
(
|
|
edict_s *attacker,
|
|
const char *victim_name,
|
|
const char *location
|
|
)
|
|
|
|
{
|
|
const char *expanded_location;
|
|
|
|
expanded_location = ExpandLocation( location );
|
|
|
|
if ( expanded_location )
|
|
gi.cprintf ( attacker, PRINT_MEDIUM, "You hit %s in the %s.\n", victim_name, expanded_location );
|
|
}
|
|
|
|
void Sentient::PrintDamageLocationToVictim
|
|
(
|
|
edict_s *victim,
|
|
const char *location
|
|
)
|
|
|
|
{
|
|
const char *expanded_location;
|
|
|
|
expanded_location = ExpandLocation( location );
|
|
|
|
if ( expanded_location )
|
|
gi.cprintf ( victim, PRINT_MEDIUM, "%s damage\n", expanded_location );
|
|
}
|
|
|
|
qboolean Sentient::DoGib
|
|
(
|
|
int meansofdeath,
|
|
Entity *inflictor
|
|
)
|
|
|
|
{
|
|
if ( !( flags & FL_DIE_GIBS ) || parentmode->value )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (
|
|
( meansofdeath == MOD_TELEFRAG ) ||
|
|
( meansofdeath == MOD_MUTANTHANDS ) ||
|
|
( meansofdeath == MOD_HELIGUN ) ||
|
|
( meansofdeath == MOD_LAVA ) ||
|
|
( ( meansofdeath == MOD_FISTS ) && ( inflictor->flags & FL_ADRENALINE ) ) ||
|
|
( ( meansofdeath == MOD_SHOTGUN ) && ( G_Random() < 0.3 ) )
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( health > -75 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Impact and Crush < -75 health
|
|
if ( ( meansofdeath == MOD_IMPACT ) ||
|
|
( meansofdeath == MOD_CRUSH ) ||
|
|
( meansofdeath == MOD_VEHICLE )
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Any projectiles except spear
|
|
if ( inflictor->isSubclassOf( Projectile ) )
|
|
{
|
|
if ( meansofdeath != MOD_SPEARGUN )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Shotgun only 10% of the time
|
|
if ( meansofdeath == MOD_SHOTGUN )
|
|
{
|
|
if ( G_Random() > 0.5 )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Sentient::ArmorDamage
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Armor *armor=0;
|
|
Entity *inflictor;
|
|
Entity *attacker;
|
|
float damage;
|
|
float armor_damage=0;
|
|
Vector momentum;
|
|
Vector position;
|
|
Vector normal;
|
|
Vector direction;
|
|
Event *event;
|
|
int dflags;
|
|
int meansofdeath;
|
|
int knockback;
|
|
float damage_mult;
|
|
int groupnum;
|
|
int trinum;
|
|
const char *location="";
|
|
float unmodified_damage=0;
|
|
|
|
if ( ( takedamage == DAMAGE_NO ) || ( movetype == MOVETYPE_NOCLIP ) )
|
|
{
|
|
if ( isClient () )
|
|
{
|
|
Player *player;
|
|
|
|
player = ( Player * )this;
|
|
if ( !player->GetVehicle() )
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
damage = ev->GetFloat ( 1 );
|
|
inflictor = ev->GetEntity ( 2 );
|
|
attacker = ev->GetEntity ( 3 );
|
|
position = ev->GetVector ( 4 );
|
|
direction = ev->GetVector ( 5 );
|
|
normal = ev->GetVector ( 6 );
|
|
knockback = ev->GetInteger( 7 );
|
|
dflags = ev->GetInteger( 8 );
|
|
meansofdeath = ev->GetInteger( 9 );
|
|
groupnum = ev->GetInteger( 10 );
|
|
trinum = ev->GetInteger( 11 );
|
|
damage_mult = ev->GetFloat ( 12 );
|
|
|
|
// Forcefields make objects invulnerable
|
|
if ( flags & FL_FORCEFIELD )
|
|
{
|
|
float alpha;
|
|
float radius;
|
|
Vector delta;
|
|
Vector org;
|
|
Entity *forcefield;
|
|
//
|
|
// spawn forcefield
|
|
//
|
|
forcefield = new Entity;
|
|
|
|
delta = centroid - worldorigin;
|
|
radius = delta.length();
|
|
|
|
//
|
|
// transform the centroid around by the current orientation
|
|
//
|
|
MatrixTransformVector( delta.vec3(), orientation, org.vec3() );
|
|
org += worldorigin;
|
|
|
|
forcefield->setModel( "sphere2.def" );
|
|
forcefield->setOrigin( org );
|
|
forcefield->worldorigin.copyTo(forcefield->edict->s.old_origin);
|
|
forcefield->setMoveType( MOVETYPE_NONE );
|
|
forcefield->setSolidType( SOLID_NOT );
|
|
forcefield->edict->s.scale = radius / 16;
|
|
alpha = ( damage * damage_mult ) / 100;
|
|
if ( alpha > 1 )
|
|
alpha = 1;
|
|
if ( alpha < 0.15f )
|
|
alpha = 0.15f;
|
|
forcefield->edict->s.alpha = alpha;
|
|
forcefield->edict->s.renderfx |= RF_TRANSLUCENT;
|
|
forcefield->PostEvent( EV_Remove, 0.1f );
|
|
return;
|
|
}
|
|
|
|
// if enabled you can't hurt teammates (but you can hurt yourself)
|
|
// knockback still occurs
|
|
if ( ( attacker != this ) && ( DM_FLAG( DF_MODELTEAMS | DF_SKINTEAMS ) ) )
|
|
{
|
|
if ( OnSameTeam ( this, attacker ) )
|
|
{
|
|
// check for friendly fire
|
|
if ( DM_FLAG( DF_FRIENDLY_FIRE ) )
|
|
meansofdeath = MOD_FRIENDLY_FIRE;
|
|
else
|
|
damage = 0;
|
|
}
|
|
}
|
|
|
|
if (groupnum == -1)
|
|
location = "all";
|
|
else
|
|
location = gi.Group_NumToName( edict->s.modelindex, groupnum );
|
|
|
|
// Adjust damage for skill level
|
|
if ( isClient() && !deathmatch->value && !(dflags & DAMAGE_NO_SKILL))
|
|
{
|
|
// Clamp the damage multiplier
|
|
if ( damage_mult > 2 )
|
|
damage_mult = 2;
|
|
|
|
switch( ( int )skill->value )
|
|
{
|
|
case 0:
|
|
damage *= 0.2f;
|
|
break;
|
|
case 1:
|
|
damage *= 0.4f;
|
|
break;
|
|
case 2:
|
|
damage *= 0.6f;
|
|
break;
|
|
default:
|
|
damage *= 0.8f;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Shields reduce damage by 75%
|
|
if ( flags & FL_SHIELDS )
|
|
{
|
|
damage *= 0.25f;
|
|
}
|
|
else if ( flags & FL_MUTANT )
|
|
{
|
|
damage *= 0.33f;
|
|
}
|
|
|
|
// If in deathmatch, client takes a minimum of 33% damage from source
|
|
if ( deathmatch->value )
|
|
{
|
|
damage *= 0.66f;
|
|
unmodified_damage = damage * 0.33f;
|
|
}
|
|
|
|
// check to see if we have armor in this location to reduce the damage
|
|
if (location[0] && !(dflags & DAMAGE_NO_ARMOR) )
|
|
{
|
|
if ( !strncmp( location,"all",3 ) )
|
|
{
|
|
char *armortypes[] = {"RiotHelmet", "FlakJacket", "FlakPants", NULL};
|
|
int i = 0;
|
|
float reduced_damage = 0;
|
|
|
|
// Go through all the armor types
|
|
while (armortypes[i])
|
|
{
|
|
float partial_damage = damage / 3;
|
|
float odam = partial_damage;
|
|
|
|
armor = ( Armor * )FindItem( armortypes[i] );
|
|
|
|
// Check for armor in this spot
|
|
if ( armor && ( armor->Amount() > 0 ) )
|
|
{
|
|
// Reduce the parital damage by the amount of armor
|
|
partial_damage -= armor->Amount();
|
|
|
|
if ( partial_damage < 0 )
|
|
partial_damage = 0;
|
|
|
|
// Remove the amount absorbed by the armor from the armor.
|
|
armor->Remove( odam - partial_damage );
|
|
|
|
// Keep track of the damage done to the armor for testing purposes
|
|
armor_damage += (odam - partial_damage );
|
|
}
|
|
// Keep track of damage done after armor has been applied
|
|
reduced_damage += partial_damage;
|
|
i++;
|
|
}
|
|
// This is the damage after all armor is done
|
|
damage = reduced_damage;
|
|
}
|
|
// else check for specific location based
|
|
else if ( !strncmp( location,"head",4 ) )
|
|
armor = ( Armor * )FindItem( "RiotHelmet" );
|
|
else if ( !strncmp(location,"torso",4 ) )
|
|
armor = ( Armor * )FindItem( "FlakJacket" );
|
|
// else if ( !strncmp( location,"arm",3 ) )
|
|
// armor = ( Armor * )FindItem( "FlakJacket" );
|
|
else if ( !strncmp(location,"leg",3 ) )
|
|
armor = ( Armor * )FindItem( "FlakPants" );
|
|
|
|
// If armor is there, remove the appropriate amount
|
|
if ( armor && ( armor->Amount() > 0 ) && !(dflags & DAMAGE_NO_ARMOR) )
|
|
{
|
|
float odam = damage;
|
|
damage -= armor->Amount();
|
|
|
|
if ( damage < 0 )
|
|
damage = 0;
|
|
|
|
armor->Remove( odam-damage );
|
|
armor_damage += (odam - damage );
|
|
}
|
|
}
|
|
|
|
damage += unmodified_damage;
|
|
|
|
// Damage multiplier
|
|
damage *= damage_mult;
|
|
|
|
// Damage skins if armor didn't absorb all damage
|
|
if ( ( damage > 0 ) && ( groupnum != -1 ) && ( flags & FL_BLOOD ) )
|
|
if ( !edict->s.groups[ groupnum ] )
|
|
edict->s.groups[ groupnum ]++;
|
|
|
|
// Show location based damage.
|
|
if ( sv_showdamagelocation->value && deathmatch->value )
|
|
{
|
|
// Send message to attacker
|
|
if ( attacker->isClient() )
|
|
{
|
|
if ( this->client )
|
|
{
|
|
PrintDamageLocationToAttacker( attacker->edict, this->client->pers.netname, location );
|
|
}
|
|
}
|
|
|
|
// Send message to victim
|
|
if ( this->isClient() && attacker != this )
|
|
{
|
|
PrintDamageLocationToVictim(this->edict, location);
|
|
}
|
|
}
|
|
|
|
// Shows detailed damage messages
|
|
if ( sv_showdamage->value )
|
|
{
|
|
// Send message to attacker
|
|
if ( attacker->isClient() )
|
|
{
|
|
if ( this->client )
|
|
{
|
|
gi.cprintf ( attacker->edict, PRINT_HIGH, "TARG:%s ARMOR_DAM:%0.2f TOT_DAM:%0.2f LOC:%s\n",
|
|
this->client->pers.netname, armor_damage, damage, location );
|
|
}
|
|
else
|
|
{
|
|
gi.cprintf ( attacker->edict, PRINT_HIGH, "TARG:%s ARMOR_DAM:%0.2f TOT_DAM:%0.2f LOC:%s\n",
|
|
getClassname(), armor_damage, damage, location );
|
|
}
|
|
}
|
|
|
|
// Send message to victim
|
|
if ( this->isClient() && attacker != this )
|
|
{
|
|
gi.cprintf ( this->edict, PRINT_HIGH, "TARG:%s ARMOR_DAM:%0.2f TOT_DAM:%0.2f LOC:%s\n",
|
|
this->client->pers.netname, armor_damage, damage, location );
|
|
}
|
|
}
|
|
|
|
|
|
// Blood and sparks effects
|
|
if (
|
|
( damage > 0 ) &&
|
|
( meansofdeath != MOD_DROWN ) &&
|
|
( meansofdeath != MOD_MUTANT_DRAIN )
|
|
)
|
|
{
|
|
// Blood particles
|
|
if ( flags & FL_BLOOD )
|
|
{
|
|
Particles( position, normal, __min( 150,damage ), 127, PARTICLE_RANDOM );
|
|
|
|
// Blood splat
|
|
if ( dflags & DAMAGE_BULLET )
|
|
SprayBlood( position, direction, damage );
|
|
}
|
|
// Sparks from metal
|
|
else if ( flags & FL_SPARKS )
|
|
{
|
|
SpawnSparks (position, normal, __min(damage, 75) );
|
|
}
|
|
}
|
|
else if ( armor_damage > 0 )
|
|
{
|
|
// Sparks off armor
|
|
Particles( position, normal, __min(armor_damage, 75), 122, 0 );
|
|
}
|
|
|
|
// Gib if we are dead and get hit by a rocket or shotgun
|
|
if ( deadflag == DEAD_DEAD )
|
|
{
|
|
health -= damage;
|
|
if ( DoGib( meansofdeath, inflictor ) )
|
|
{
|
|
Event *gibEv;
|
|
|
|
gibEv = new Event( EV_Gib );
|
|
gibEv->AddInteger( 0 );
|
|
ProcessEvent( gibEv );
|
|
}
|
|
return;
|
|
}
|
|
else if ( deadflag )
|
|
{
|
|
health -= damage;
|
|
return;
|
|
}
|
|
|
|
// Do the kick
|
|
if (!(dflags & DAMAGE_NO_KNOCKBACK))
|
|
{
|
|
if ((knockback) &&
|
|
(movetype != MOVETYPE_NONE) &&
|
|
(movetype != MOVETYPE_BOUNCE) &&
|
|
(movetype != MOVETYPE_PUSH) &&
|
|
(movetype != MOVETYPE_STOP))
|
|
{
|
|
float m;
|
|
|
|
if (mass < 50)
|
|
m = 50;
|
|
else
|
|
m = mass;
|
|
|
|
direction.normalize();
|
|
if ( isClient() && ( attacker == this ) && deathmatch->value )
|
|
momentum = direction * ( 1700.0f * ( float )knockback / m ); // the rocket jump hack...
|
|
else
|
|
momentum = direction * ( 500.0f * ( float )knockback / m );
|
|
|
|
if ( dflags & DAMAGE_BULLET )
|
|
{
|
|
// Clip the z velocity for bullet weapons
|
|
if ( momentum.z > 75)
|
|
momentum.z = 75;
|
|
}
|
|
velocity += momentum;
|
|
}
|
|
}
|
|
|
|
// check for godmode or invincibility
|
|
if ( flags & FL_GODMODE )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// do the damage
|
|
health -= damage;
|
|
|
|
// He's dead jim, send out a bunch of events
|
|
if ( health <= 0 )
|
|
{
|
|
if ( ( meansofdeath == MOD_ION ) && !( flags & FL_NOION ) )
|
|
{
|
|
flags |= FL_DIE_TESSELATE;
|
|
RandomGlobalSound( "snd_tesselate" );
|
|
}
|
|
|
|
event = new Event( EV_Killed );
|
|
event->AddEntity( attacker );
|
|
event->AddInteger( damage );
|
|
event->AddEntity( inflictor );
|
|
event->AddString( location );
|
|
event->AddInteger( meansofdeath );
|
|
ProcessEvent( event );
|
|
return;
|
|
}
|
|
|
|
if ( ( damage > 0 ) && ( meansofdeath == MOD_ION ) && !( flags & FL_NOION ) )
|
|
{
|
|
// Do the ion particlizer effect
|
|
TesselateModel
|
|
(
|
|
this,
|
|
1,
|
|
1000,
|
|
direction,
|
|
damage,
|
|
1.0f,
|
|
0,
|
|
vec3_origin,
|
|
TESSELATE_EXPANDANDSHRINK,
|
|
126
|
|
);
|
|
}
|
|
|
|
// Tesselate from damage
|
|
if (flags & FL_TESSELATE)
|
|
{
|
|
TesselateModel
|
|
(
|
|
this,
|
|
tess_min_size,
|
|
tess_max_size,
|
|
direction,
|
|
damage,
|
|
1.0f,
|
|
tess_thickness,
|
|
vec3_origin
|
|
);
|
|
}
|
|
|
|
// Darken if we are hurt
|
|
if (flags & FL_DARKEN)
|
|
{
|
|
edict->s.renderfx |= RF_LIGHTOFFSET;
|
|
if ( max_health )
|
|
{
|
|
edict->s.lightofs = - ( 40.0f * ( (float)(max_health - health) / (float)max_health ) );
|
|
}
|
|
else
|
|
{
|
|
edict->s.lightofs -= damage;
|
|
}
|
|
if ( edict->s.lightofs < -127 )
|
|
edict->s.lightofs = -127;
|
|
if ( edict->s.lightofs > 127 )
|
|
edict->s.lightofs = 127;
|
|
}
|
|
|
|
// Drop your weapon if you get hit in the hands
|
|
if ( location == gun_bone_group_name && ( ( damage > 50 ) || ( G_Random( 1 ) > 0.97f ) ) )
|
|
{
|
|
// don't drop weapons when in deathmatch and DF_NO_DROP_WEAPONS is set
|
|
if ( currentWeapon && currentWeapon->IsDroppable() && ( !deathmatch->value || !DM_FLAG( DF_NO_DROP_WEAPONS ) ) )
|
|
{
|
|
DropCurrentWeapon();
|
|
WeaponKnockedFromHands();
|
|
}
|
|
}
|
|
|
|
if ( meansofdeath == MOD_MUTANT_DRAIN )
|
|
return;
|
|
|
|
// Send pain event
|
|
event = new Event( EV_Pain );
|
|
event->AddFloat( damage );
|
|
event->AddEntity( attacker );
|
|
event->AddString( location );
|
|
event->AddInteger( meansofdeath );
|
|
ProcessEvent( event );
|
|
}
|
|
|
|
void Sentient::UpdateSilencedWeapons
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
// If the current weapon is silencable, then change it.
|
|
if ( currentWeapon && currentWeapon->IsSilenced() )
|
|
{
|
|
currentWeapon->ProcessEvent(EV_Weapon_PutAwayAndRaise);
|
|
}
|
|
}
|
|
|
|
int Sentient::NumInventoryItems
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
|
|
return inventory.NumObjects();
|
|
|
|
}
|
|
|
|
Item *Sentient::NextItem
|
|
(
|
|
Item *item
|
|
)
|
|
|
|
{
|
|
Item *next_item;
|
|
int i;
|
|
int n;
|
|
qboolean item_found = false;
|
|
|
|
if ( !item )
|
|
{
|
|
item_found = true;
|
|
}
|
|
else if ( !inventory.ObjectInList( item->entnum ) )
|
|
{
|
|
error( "NextItem", "Item not in list" );
|
|
}
|
|
|
|
n = inventory.NumObjects();
|
|
|
|
for( i = 1; i <= n; i++ )
|
|
{
|
|
next_item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
assert( next_item );
|
|
|
|
if ( next_item->isSubclassOf( InventoryItem ) && item_found )
|
|
return next_item;
|
|
|
|
if ( next_item == item )
|
|
item_found = true;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
Item *Sentient::PrevItem
|
|
(
|
|
Item *item
|
|
)
|
|
|
|
{
|
|
Item *prev_item;
|
|
int i;
|
|
int n;
|
|
qboolean item_found = false;
|
|
|
|
if ( !item )
|
|
{
|
|
item_found = true;
|
|
}
|
|
else if ( !inventory.ObjectInList( item->entnum ) )
|
|
{
|
|
error( "NextItem", "Item not in list" );
|
|
}
|
|
|
|
n = inventory.NumObjects();
|
|
|
|
for( i = n; i >= 1; i-- )
|
|
{
|
|
prev_item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
assert( prev_item );
|
|
|
|
if ( prev_item->isSubclassOf( InventoryItem ) && item_found)
|
|
return prev_item;
|
|
|
|
if ( prev_item == item )
|
|
item_found = true;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// This will search a dead body and create a floating inventory for that player
|
|
// which will be displayed on the client as a list of icons. If the user presses
|
|
// use, then he will pick up the items in that floating inventory, and the body
|
|
// will be faded out.
|
|
void Sentient::SearchBody
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
int i,n;
|
|
Item *item;
|
|
Entity *other;
|
|
Player *player;
|
|
Event *event;
|
|
qboolean inventory_created = false;
|
|
|
|
other = ev->GetEntity( 1 );
|
|
assert( other );
|
|
|
|
// Only players can touch this
|
|
if ( !other->isSubclassOf( Player ) )
|
|
return;
|
|
|
|
// Can't touch yourself
|
|
if ( other == this )
|
|
return;
|
|
|
|
// Make sure the body is dead
|
|
if ( !deadflag )
|
|
return;
|
|
|
|
n = inventory.NumObjects();
|
|
|
|
// No items, get out of here
|
|
if ( n == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
player = ( Player * )other;
|
|
|
|
// Check to see if this inventory is already being displayed for
|
|
// this body.
|
|
if ( player->GetFloatingOwner() == this )
|
|
{
|
|
// Still touching the body, so extend the clear time
|
|
player->CancelEventsOfType( EV_Player_ClearFloatingInventory );
|
|
event = new Event( EV_Player_ClearFloatingInventory );
|
|
player->PostEvent( event, 0.4f );
|
|
return;
|
|
}
|
|
else if ( player->GetFloatingOwner() )
|
|
{
|
|
// Currently looking at an inventory, so don't bother with this one.
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Make sure the inventory is cleared out before making a new one.
|
|
// Could have some left over items in there from other bodies
|
|
player->ProcessEvent( EV_Player_ClearFloatingInventory );
|
|
}
|
|
|
|
// Create the inventory
|
|
for( i = 1; i <= n; i++ )
|
|
{
|
|
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
assert( item );
|
|
if ( item->Amount() )
|
|
{
|
|
player->AddItemToFloatingInventory( item );
|
|
inventory_created = true;
|
|
}
|
|
}
|
|
|
|
if (!inventory_created)
|
|
return;
|
|
|
|
// Set the current owner of the inventory
|
|
player->SetFloatingOwner( this );
|
|
|
|
// Send the inventory to the client
|
|
player->SendFloatingInventory();
|
|
|
|
// Clear the inventory in the future
|
|
event = new Event( EV_Player_ClearFloatingInventory );
|
|
player->PostEvent( event, 0.3f );
|
|
}
|
|
|
|
void Sentient::UselessCheck( Event *ev )
|
|
{
|
|
Item *item;
|
|
int n;
|
|
int i;
|
|
Event *event;
|
|
|
|
// Check to see if we have any inventory and
|
|
// remove if we don't
|
|
|
|
n = inventory.NumObjects();
|
|
for ( i = 1; i <= n; i++ )
|
|
{
|
|
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
assert( item );
|
|
if ( item->Amount() )
|
|
{
|
|
PostEvent ( EV_Sentient_UselessCheck, 1.0f + G_Random() );
|
|
return;
|
|
}
|
|
}
|
|
|
|
event = new Event( "remove_useless" );
|
|
ProcessEvent( event );
|
|
}
|
|
|
|
qboolean Sentient::HasInventoryOfType
|
|
(
|
|
const char *type
|
|
)
|
|
|
|
{
|
|
int num;
|
|
int i;
|
|
Item *item;
|
|
|
|
num = inventory.NumObjects();
|
|
for( i = num; i > 0; i-- )
|
|
{
|
|
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
if ( checkInheritance( type, item->getClassname() ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Sentient::DropInventoryItems
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int num;
|
|
int i;
|
|
Item *item;
|
|
|
|
// Drop any inventory items
|
|
num = inventory.NumObjects();
|
|
for( i = num; i >= 1; i-- )
|
|
{
|
|
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
if ( item->isSubclassOf( InventoryItem ) )
|
|
{
|
|
item->Drop();
|
|
}
|
|
}
|
|
}
|
|
|
|
qboolean Sentient::PowerupActive
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
if ( poweruptype && this->client )
|
|
{
|
|
gi.cprintf( edict, PRINT_HIGH, "You are already using a powerup\n" );
|
|
}
|
|
|
|
return poweruptype;
|
|
}
|
|
|
|
void Sentient::SprayBlood
|
|
(
|
|
Vector src,
|
|
Vector dir,
|
|
float damage
|
|
)
|
|
|
|
{
|
|
trace_t trace;
|
|
Vector norm;
|
|
Vector end;
|
|
Vector ang;
|
|
float dist;
|
|
float scale;
|
|
BloodSplat *splat;
|
|
|
|
dir.normalize();
|
|
end = src + dir * 2048;
|
|
|
|
trace = G_Trace( src, vec_zero, vec_zero, end, this, MASK_SOLIDNONFENCE, "Sentient::SprayBlood" );
|
|
|
|
if ( HitSky( &trace ) || ( trace.ent->solid != SOLID_BSP ) || ( trace.ent->s.number != 0 ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
dist = ( Vector( trace.endpos ) - src ).length();
|
|
scale = damage / ( dist * 0.3 );
|
|
if ( scale > 0.6 )
|
|
{
|
|
scale = 0.6;
|
|
}
|
|
|
|
if ( scale < 0.02 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
norm = trace.plane.normal;
|
|
norm.x = -norm.x;
|
|
norm.y = -norm.y;
|
|
ang = norm.toAngles();
|
|
ang.z = G_Random( 360 );
|
|
end = trace.endpos + Vector( trace.plane.normal ) * 0.2;
|
|
|
|
splat = new BloodSplat( end, ang, scale );
|
|
}
|
|
|
|
EXPORT_FROM_DLL void Sentient::setModel
|
|
(
|
|
const char *mdl
|
|
)
|
|
|
|
{
|
|
if ( currentWeapon )
|
|
{
|
|
// rebind the current gun
|
|
currentWeapon->DetachFromOwner();
|
|
Entity::setModel( mdl );
|
|
currentWeapon->AttachToOwner();
|
|
}
|
|
else
|
|
{
|
|
Entity::setModel( mdl );
|
|
}
|
|
}
|
|
|
|
void Sentient::TurnOffShadow
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
// HACK FIXME
|
|
//
|
|
// temporary shadow flag
|
|
//
|
|
edict->s.renderfx &= ~RF_XFLIP;
|
|
}
|
|
|
|
EXPORT_FROM_DLL void Sentient::Archive
|
|
(
|
|
Archiver &arc
|
|
)
|
|
{
|
|
int i;
|
|
int num;
|
|
|
|
Entity::Archive( arc );
|
|
|
|
num = inventory.NumObjects();
|
|
arc.WriteInteger( num );
|
|
for( i = 1; i <= num; i++ )
|
|
{
|
|
arc.WriteInteger( inventory.ObjectAt( i ) );
|
|
}
|
|
arc.WriteObjectPointer( currentWeapon );
|
|
arc.WriteObjectPointer( currentItem );
|
|
arc.WriteObjectPointer( newWeapon );
|
|
arc.WriteString( currentAnim );
|
|
arc.WriteBoolean( animOverride );
|
|
arc.WriteEvent( *tempAnimEvent );
|
|
arc.WriteString( gun_bone_group_name );
|
|
arc.WriteBoolean( stopanimating_tillchange );
|
|
arc.WriteInteger( poweruptype );
|
|
arc.WriteInteger( poweruptimer );
|
|
arc.WriteBoolean( sentientFrozen );
|
|
arc.WriteBoolean( dropweapon );
|
|
arc.WriteVector( gunoffset );
|
|
arc.WriteVector( eyeposition );
|
|
arc.WriteFloat( firedowntime );
|
|
arc.WriteBoolean( firedown );
|
|
arc.WriteString( saveskin );
|
|
arc.WriteString( savemodel );
|
|
}
|
|
|
|
EXPORT_FROM_DLL void Sentient::Unarchive
|
|
(
|
|
Archiver &arc
|
|
)
|
|
{
|
|
int i;
|
|
int num;
|
|
int temp;
|
|
|
|
Entity::Unarchive( arc );
|
|
|
|
inventory.FreeObjectList();
|
|
num = arc.ReadInteger();
|
|
for( i = 1; i <= num; i++ )
|
|
{
|
|
temp = arc.ReadInteger();
|
|
inventory.AddObject( temp );
|
|
}
|
|
|
|
arc.ReadObjectPointer( ( Class ** )¤tWeapon );
|
|
arc.ReadObjectPointer( ( Class ** )¤tItem );
|
|
arc.ReadObjectPointer( ( Class ** )&newWeapon );
|
|
arc.ReadString( ¤tAnim );
|
|
arc.ReadBoolean( &animOverride );
|
|
//FIXME
|
|
// make a ReadEventPointer function
|
|
tempAnimEvent = new Event( arc.ReadEvent() );
|
|
arc.ReadString( &gun_bone_group_name );
|
|
arc.ReadBoolean( &stopanimating_tillchange );
|
|
arc.ReadInteger( &poweruptype );
|
|
arc.ReadInteger( &poweruptimer );
|
|
arc.ReadBoolean( &sentientFrozen );
|
|
arc.ReadBoolean( &dropweapon );
|
|
arc.ReadVector( &gunoffset );
|
|
arc.ReadVector( &eyeposition );
|
|
arc.ReadFloat( &firedowntime );
|
|
arc.ReadBoolean( &firedown );
|
|
arc.ReadString( &saveskin );
|
|
arc.ReadString( &savemodel );
|
|
}
|
|
|
|
void Sentient::WritePersistantData
|
|
(
|
|
SpawnArgGroup &group
|
|
)
|
|
|
|
{
|
|
int i;
|
|
int num;
|
|
Entity *ent;
|
|
|
|
G_SetSpawnArg( "classname", getClassname() );
|
|
if ( currentWeapon )
|
|
{
|
|
G_SetSpawnArg( "current_weapon", currentWeapon->getClassname() );
|
|
}
|
|
if ( currentItem )
|
|
{
|
|
G_SetSpawnArg( "current_item", currentItem->getClassname() );
|
|
}
|
|
|
|
G_SetIntArg( "health", health );
|
|
G_SetIntArg( "max_health", max_health );
|
|
G_SetIntArg( "savegame", 1 );
|
|
|
|
group.AddArgs();
|
|
|
|
num = inventory.NumObjects();
|
|
for( i = 1; i <= num; i++ )
|
|
{
|
|
ent = G_GetEntity( inventory.ObjectAt( i ) );
|
|
if ( ent && ent->isSubclassOf( Item ) )
|
|
{
|
|
G_InitSpawnArguments();
|
|
( ( Item * )ent )->CreateSpawnArgs();
|
|
G_SetSpawnArg( "classname", ent->getClassname() );
|
|
G_SetIntArg( "savegame", 1 );
|
|
group.AddArgs();
|
|
}
|
|
}
|
|
|
|
G_InitSpawnArguments();
|
|
}
|
|
|
|
void Sentient::RestorePersistantData
|
|
(
|
|
SpawnArgGroup &group
|
|
)
|
|
|
|
{
|
|
str weap;
|
|
str item;
|
|
Entity *ent;
|
|
int i;
|
|
int num;
|
|
Item *it;
|
|
|
|
num = group.NumInGroup();
|
|
for( i = 2; i <= num; i++ )
|
|
{
|
|
group.RestoreArgs( i );
|
|
|
|
game.force_entnum = false;
|
|
ent = G_CallSpawn();
|
|
if ( !ent->isSubclassOf( Item ) )
|
|
{
|
|
error( "RestorePersistantData", "Loaded non-Item object of class %s\n", ent->getClassname() );
|
|
}
|
|
|
|
// G_CallSpawn clears all the spawnargs
|
|
group.RestoreArgs( i );
|
|
|
|
it = ( Item * )ent;
|
|
it->SetOwner( this );
|
|
it->ProcessPendingEvents();
|
|
AddItem( it );
|
|
}
|
|
|
|
group.RestoreArgs( 1 );
|
|
|
|
weap = G_GetStringArg( "current_weapon" );
|
|
item = G_GetStringArg( "current_item" );
|
|
|
|
health = G_GetIntArg( "health", 100 );
|
|
max_health = G_GetIntArg( "max_health", 100 );
|
|
|
|
if ( weap.length() )
|
|
{
|
|
it = FindItem( weap.c_str() );
|
|
assert( it );
|
|
if ( it )
|
|
{
|
|
if ( !it->isSubclassOf( Weapon ) )
|
|
{
|
|
error( "RestorePersistantData", "Tried to set non-weapon of type %s as current weapon\n", it->getClassname() );
|
|
}
|
|
SetCurrentWeapon( ( Weapon * )it );
|
|
}
|
|
}
|
|
|
|
if ( item.length() )
|
|
{
|
|
it = FindItem( item.c_str() );
|
|
assert( it );
|
|
if ( it )
|
|
{
|
|
if ( !it->isSubclassOf( InventoryItem ) )
|
|
{
|
|
error( "RestorePersistantData", "Tried to set non-inventory item of type %s as current item\n", it->getClassname() );
|
|
}
|
|
currentItem = ( InventoryItem * )it;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_FROM_DLL void Sentient::Freeze
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
sentientFrozen = true;
|
|
}
|
|
|
|
EXPORT_FROM_DLL void Sentient::UnFreeze
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
sentientFrozen = false;
|
|
}
|
|
|
|
void Sentient::ImpactDamage
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
float damg;
|
|
|
|
if ( deathmatch->value )
|
|
{
|
|
damg = velocity.length();
|
|
damg /= 20.0f;
|
|
}
|
|
else
|
|
{
|
|
damg = 100;
|
|
}
|
|
|
|
setMoveType( MOVETYPE_STEP );
|
|
Damage(world, enemy, damg,
|
|
worldorigin, vec_zero, vec_zero, 0,
|
|
0, MOD_IMPACT,
|
|
-1, -1, 1.0f );
|
|
}
|
|
|
|
void Sentient::WeaponUse
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
Event *event;
|
|
|
|
if ( !currentWeapon )
|
|
return;
|
|
|
|
currentWeapon->ForceState( WEAPON_READY );
|
|
|
|
event = new Event( EV_Weapon_SecondaryUse );
|
|
event->AddEntity( this );
|
|
currentWeapon->ProcessEvent( event );
|
|
}
|
|
|
|
void Sentient::DoubleArmor
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int i,n;
|
|
|
|
n = inventory.NumObjects();
|
|
|
|
for( i = 1; i <= n; i++ )
|
|
{
|
|
Item *item;
|
|
item = ( Item * )G_GetEntity( inventory.ObjectAt( i ) );
|
|
|
|
if ( item->isSubclassOf( Armor ) )
|
|
item->Set( item->Amount() * 2 );
|
|
}
|
|
}
|
|
|
|
void Sentient::WeaponKnockedFromHands
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
str realname;
|
|
|
|
realname = GetRandomAlias( "snd_lostweapon" );
|
|
if ( realname.length() > 1 )
|
|
{
|
|
sound( realname, 1, CHAN_VOICE );
|
|
}
|
|
else
|
|
{
|
|
RandomSound( "snd_pain", 1, CHAN_VOICE );
|
|
}
|
|
}
|
|
|
|
void Sentient::SetDropWeapon
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
dropweapon = ev->GetInteger( 1 );
|
|
}
|
|
|
|
void Sentient::DropWeaponNowEvent
|
|
(
|
|
Event *ev
|
|
)
|
|
|
|
{
|
|
DropCurrentWeapon();
|
|
}
|