1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966 |
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Copyright 2016 RWS Inc, All Rights Reserved
- //
- // This program is free software; you can redistribute it and/or modify
- // it under the terms of version 2 of the GNU General Public License as published by
- // the Free Software Foundation
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License along
- // with this program; if not, write to the Free Software Foundation, Inc.,
- // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- //
- // character.cpp
- // Project: Postal
- //
- // This module implements the CCharacter class which is the class of generic
- // character functionality for game characters.
- //
- // History:
- //
- // 03/03/97 BRH,JMI Started this generic character object to reduce the
- // amount of redundant code.
- //
- // 03/04/97 JMI Changed calling convention and naming for velocities and
- // position functions and added a Deluxe updater.
- //
- // 03/04/97 JMI The m_sprite.m_sInFlags are now only initialized in
- // Startup() (instead of in Render()). If it was in Render(),
- // it overwrites any other initializations that take place on
- // these bit flags (such as XRay).
- //
- // 03/04/97 JMI UpdateVelocities() now makes sure the delta value is cor-
- // rect even when m_dVel is capped or trimmed.
- // Drags are now added (was subtracting them).
- // Commented out code to restore old velocities (as if accel
- // did not take place) when no new position was obtained.
- //
- // 03/04/97 JMI Added support for messages.
- //
- // 03/05/97 JMI Converted all rspMod360() calls to new calling convention.
- // Added fire response in OnBurnMsg().
- // Added UpdateFirePosition().
- //
- // 03/05/97 JMI Added PrepareWeapon() and ShootWeapon() that can handle
- // any current CThing weapon (i.e., cannot handle bullets).
- //
- // 03/05/97 BRH Added code to WhileShot, WhileBurning, WhileBlownUp,
- // WhileDying for default functionality for most of the
- // victims and enemy guys.
- //
- // 03/05/97 JMI Now OnDead() draws current animation frame into the back-
- // ground.
- //
- // 03/05/97 JMI Added handler for suicide message.
- // WhileBlownUp() now stops all horizontal movement when the
- // character encounters the 'no walk' attrib.
- // Added UpdateVelocity() inline in attempt to unify the way
- // velocity is updated. Works for two of three cases, haven't
- // tried third for fear of hosedness.
- // Currently vertical tolerance is not considered in
- // ValidatePosition().
- // Made external horizontal drag in OnExplosionMsg() negative.
- //
- // 03/06/97 JMI Re-enabled vertical tolerance utilizing new m_bAboveTerrain
- // boolean.
- //
- // 03/06/97 JMI Increased horizontal surface drag.
- // Now Render() uses the combined attributes for the layer.
- //
- // 03/06/97 JMI No longer relies on m_pWeapon to store weapon ptr. But,
- // if a derived class sets m_pWeapon to point to a weapon,
- // Render() will still update its transform.
- // This should be removed as soon as no one uses m_pWeapon.
- //
- // 03/06/97 JMI Now, if EditRect() does not have enough info to create
- // a rectangle, it tries a couple ways.
- //
- // 03/06/97 JMI Now ShootWeapon() gets the rigid body transform from the
- // current animation instead of from the child's sprite.
- //
- // 03/06/97 JMI Now allows a character to fall off of things but not onto
- // unless the height difference is within tolerance.
- //
- // 03/06/97 JMI Added a GetAttributes() function.
- // Now uses GetAttributes() instead of manually doing it.
- //
- // 03/12/97 JMI Actually, mistakenly, last time I put GetAttributes() in
- // WhileBlowingUp() instead of in DeluxeUpdatePosition().
- // Now DeluxeUpdatePosition() utilizes GetAttributes().
- // DeluxeUpdatePosition() now takes the duration in seconds
- // as a parameter instead of calculating it itself (and over-
- // writing m_lPrevTime).
- //
- // 03/13/97 JMI GetAttributes() was combining ONLY the layer bits. What
- // I really wanted it to do was combine all but the height
- // bits. Now it uses ~ATTRIBUTE_HEIGHT_MASK to combined the
- // bits.
- //
- // 03/13/97 JMI Load now takes a version number.
- //
- // 03/17/97 JMI Now most of CCharacter's functionality is implemented in
- // its base class, CThing3d.
- //
- // 03/21/97 JMI Removed m_pWeapon.
- //
- // 03/21/97 JMI ShootWeapon() now sets the rotation of the weapon beforing
- // detaching it.
- //
- // 03/21/97 JMI ShootWeapon() now returns a ptr to the weapon.
- //
- // 04/02/97 JMI OnDead() now destroys any weapon the character still has.
- // The idea is that, if the weapon was just rendered as a
- // child object onto the background, it should no longer exist
- // in CThing form. Also, undeleted child weapons will clutter
- // memory until the realm they're in is destroyed.
- // Removed m_pCurrentAnim.
- //
- // 04/02/97 JMI PrepareWeapon() now, also, returns a ptr to the weapon.
- //
- // 04/15/97 BRH We were noticing that the guys no longer turned dark after
- // being burnt. I added the change to m_sBrightness in
- // WhileBurning() so that he turns dark when dead.
- //
- // 04/23/97 JMI Added IsPathClear(), a rather deluxe function.
- //
- // 04/24/97 JMI #if 0'd out the debug portion of IsPathClear() which
- // displayed the check path as red or green line depending on
- // whether or not the path was entirely clear.
- //
- // 04/25/97 JMI Broke blood pool creation out of MakeBloody() and into
- // MakeBloodPool().
- //
- // 04/28/97 BRH Changed PrepareWeapon to use the new fake class ID's to
- // identify weapons that don't have an object associated with
- // them like guns. Now this function is generic enough that
- // the CDude doesn't have to override its own version of
- // PrepareWeapon.
- //
- // 04/28/97 JMI Changed ShootWeapon() to work for CShotGunID, CPistolID,
- // CMachineGunID, and CFireballID.
- // Also, added FireBullets() to simplify firing of bullets.
- //
- // 04/29/97 JMI Added case to ShootWeapon() to handle mines.
- //
- // 04/29/97 JMI Changed references to child weapon's m_sprite to
- // GetSprite() calls and took out special cases for
- // CFireballID and CMineID.
- //
- // 05/02/97 JMI FireBullets() now returns type bool indicating whether or
- // not someone/thing was hit by the bullets.
- //
- // 05/02/97 JMI Added timer explicitly for CCharacter::While/On* functions.
- // Utilized the timer in WhileBurning() to make sure dude dies
- // within a certain amount of burning and to make the dude
- // darken while he burns instead of just at the end.
- //
- // 05/13/97 JMI Now MakeBloody() creates chunks based on the passed damage.
- //
- // 05/14/97 JMI Now Render() uses PositionChild() to position the weapon.
- //
- // 05/16/97 JMI Added directionality to blood.
- //
- // 05/22/97 JMI Added bullet casings and shells.
- // Also, made all particle effects (Blood, Casings, and
- // Shells) obey g_GameSettings.m_sParticleEffects.
- //
- // 05/23/97 JMI Lowered velocity of shells and casings generated by Fire-
- // Bullets().
- //
- // 05/26/97 JMI Lowered vertical velocity of shells and casing generated
- // by FireBullets().
- //
- // 05/26/97 BRH Added CAssaultWeapon which is just the shot gun allowed
- // to rapid fire.
- //
- // 05/29/97 JMI Removed references to m_pRealm->m_pAttribMap which no longer
- // exists.
- //
- // 05/30/97 JMI Changed FireBullets() to only play the sample once (through
- // FireDeluxe(...) ).
- //
- // 06/02/97 JMI Added an WhileOnLadder().
- //
- // 06/02/97 JMI Removed ladder stuff.
- //
- // 06/05/97 JMI Changed m_sHitPoints to m_stockpile.m_sHitPoints to
- // accommodate new m_stockpile in base class, CThing3d (which
- // used to contain the m_sHitPoints).
- //
- // 06/08/97 BRH Added IlluminateTarget() function to check for targets within
- // a cone in the direction you specify. This will be used by the
- // CDude for targeting feedback. He will use it to place a
- // target sprite on the target he is aiming at.
- //
- // 06/10/97 JMI Removed CSmash::Misc from the default bits to collide with
- // when using FireBullets().
- //
- // 06/11/97 BRH Added passing of Shooter ID down into the Shot message and
- // to the other weapons in ShootWeapon(). Also added the
- // bullets damage chart to give tunability to different
- // weapons and target vs shooter differences.
- //
- // 06/11/97 JMI Added Preload() for loading assets used during play.
- //
- // 06/11/97 BRH Added the ID of the killer and used that to report to
- // the score module at time of death.
- //
- // 06/12/97 JMI 3D weapons are now initially (in PrepareWeapon() ) set to
- // CWeapon::State_Hide.
- //
- // 06/13/97 JMI Added WhileHoldingWeapon().
- //
- // 06/15/97 JMI Now OnShotMsg() checks to make sure we're receiving damage
- // before calling MakeBloody() (if someone's wearing a kevlar
- // vest, they may receive the impact forces of the bullet but
- // little or no damage).
- //
- // 06/17/97 JMI Converted all occurrences of rand() to GetRand() and
- // srand() to SeedRand().
- //
- // 06/24/97 JMI Now intializes animthing's m_msg's priority to 0 on in
- // MakeBloodPool() so we don't end up with randomly
- // prioritized messages.
- //
- // 06/30/97 JMI Now uses CRealm's new GetRealmWidth() and *Height()
- // for dimensions of realm's X/Z plane.
- //
- // 07/01/97 JMI Now passes rigid body transform to PositionChild().
- //
- // 07/09/97 JMI Now uses m_pRealm->Make2dResPath() to get the fullpath
- // for 2D image components.
- //
- // 07/09/97 JMI Changed Preload() to take a pointer to the calling realm
- // as a parameter.
- //
- // 07/21/97 JMI Now has an Update() which updates the weapon's position.
- // Also, added ValidateWeaponPosition().
- //
- // 07/21/97 JMI Now ValidateWeaponPosition() checks to make sure the weapon
- // state is not State_Hide before checking the position.
- //
- // 07/27/97 JMI No longer calls CRealm::Make2dResPath() on res names
- // given to CAnimThing as it already does that
- // automatically.
- // Also, now appropriately places blood splats on outside of
- // character's body based on opposite angle of impact.
- //
- // 07/30/97 JMI Now copies necessary ammo from ccharacter's stockpile to
- // the ammo, if required.
- //
- // 08/02/97 BRH Added virtual OnHelpMsg function.
- //
- // 08/07/97 JMI Added ShootWeapon()/FireBullets() support for the double
- // barrel.
- //
- // 08/07/97 JMI Now blood splat lands on ground (at terrrain height)
- // instead of at the character's Y.
- // Changed RAND_SWAY to an inline that considers the scene
- // scale.
- //
- // 08/07/97 JMI In last version did not put the blood pool on the ground
- // while animating.
- //
- // 08/08/97 JMI Now this class plays the flamer sound and handles its
- // loopage.
- //
- // 08/08/97 JMI Upgraded PrepareWeapon(), ShootWeapon(), and FireBullets()
- // to handle AutoRifle, Uzi, and SmallPistol.
- //
- // 08/09/97 JMI Now MakeBloody() tries to keep shots low for characters
- // that are in the writhing state.
- //
- // 08/09/97 JMI Converted a use of TransformPtsToRealm() to GetLinkPoint().
- //
- // 08/11/97 JMI Changed DoubleBarrel sound to g_smidDeathWadLaunch (was the
- // ol' wimpy g_smidShotGun).
- //
- // 08/13/97 JMI Temporarily shows particle effects regardless of user
- // setting in multiplayer mode.
- //
- // 08/17/97 JMI Now FireBullets() will never shoot writhers.
- //
- // 08/17/97 JMI Changed m_pthingParent to m_idParent.
- //
- // 08/18/97 JMI Now applies no randomization to CChunks and instead allows
- // CChunk::Setup() to apply its own randomization based on
- // sway values passed to it. Also, CChunk::Construct() will
- // now fail if particles are disabled so we don't need to
- // check for that anymore.
- //
- // 08/18/97 JMI FireBullets() now includes the random variation in angle
- // in the bullet message (before it used the UNswayed angle).
- //
- // 08/18/97 JMI Changed OnDead() to call DeadRender3D() (which used to be
- // known/called as just another Render() overload).
- //
- // 08/24/97 JMI Adjusted shotgun pellets to be 8 when shot by a dude into
- // a non dude. Also, adjusted machine gun bullets to be 15
- // when shot by a dude into a non dude.
- //
- // 08/28/97 BRH Added a virtual put me down message handler.
- //
- ////////////////////////////////////////////////////////////////////////////////
- #define CHARACTER_CPP
- #include "RSPiX.h"
- #include <math.h>
- #include "character.h"
- #include "reality.h"
- #include "AnimThing.h"
- #include "fireball.h"
- #include "mine.h"
- #include "fire.h"
- #include "chunk.h"
- #include "score.h"
- #include "deathWad.h"
- ////////////////////////////////////////////////////////////////////////////////
- // Macros/types/etc.
- ////////////////////////////////////////////////////////////////////////////////
- #define MAX_FORE_VEL 80.0
- #define MAX_BACK_VEL -60.0
- #define MAX_STEPUP_THRESHOLD 10.0
- // Determines the number of elements in the passed array at compile time.
- #define NUM_ELEMENTS(a) (sizeof(a) / sizeof(a[0]) )
- // Random amount the fire angle can adjust for bullets.
- #define FIRE_ANGLE_Y_SWAY 15
- #define FIRE_ANGLE_Z_SWAY 10
- #define MAX_BULLET_RANGE 400
- #define MAX_SHELL_RANGE 200
- #define NUM_SHOTS_PER_SHELL 7
- #define FIREBALL_LIVE_MS 500
- #define MAX_TIME_WITHOUT_FLAMAGE 500
- // Random amount the blood splat can adjust.
- #define BLOOD_SPLAT_SWAY 10
- // This is the distance in realm units from the characters' centers
- // to the outsides of their bodies. This is used to adjust things
- // like blood splats around the outside of their torsos.
- #define TORSO_RADIUS 5
- #define BLOOD_SPLAT_RES_NAME "BloodFront.aan"
- #define BLOOD_POOL_RES_NAME "BloodPool.aan"
- // Light level for burnt character.
- #define BURNT_BRIGHTNESS -40 // -128 to 127.
- // Amount of time for character to burn.
- #define BURN_DURATION 2500 // In ms.
- // Sets a value pointed to if ptr is not NULL.
- #define SET(pval, val) ((pval != NULL) ? *pval = val : val)
- // Maximum chunks that can be generated from one MakeBloody().
- #define MAX_CHUNKS 10
- // Amount chunks can vary from the direction of damage.
- #define CHUNKS_DAMAGE_DIR_SWAY 135
- #define SHOOTER_IS_DUDE 0x0008
- #define TARGET_IS_DUDE 0x0004
- #define WEAPON_IS_PISTOL 0x0000
- #define WEAPON_IS_MACHINEGUN 0x0001
- #define WEAPON_IS_SHOTGUN 0x0002
- #define WEAPON_IS_ASSAULT 0x0003
- ////////////////////////////////////////////////////////////////////////////////
- // Variables/data
- ////////////////////////////////////////////////////////////////////////////////
- static short ms_asBulletDamageChart[16] =
- {
- // Damage Shooter Target Weapon
- 10, // 0000 NonDude NonDude Pistol
- 10, // 0001 NonDude NonDude MachineGun
- 10, // 0010 NonDude NonDude ShotGun
- 10, // 0011 NonDude NonDude AssaultWeapon
- 10, // 0100 NonDude Dude Pistol
- 10, // 0101 NonDude Dude MachineGun
- 10, // 0110 NonDude Dude ShotGun
- 7, // 0111 NonDude Dude AssaultWeapon
- 10, // 1000 Dude NonDude Pistol
- 15, // 1001 Dude NonDude MachineGun
- 8, // 1010 Dude NonDude ShotGun
- 10, // 1011 Dude NonDude AssaultWeapon
- 10, // 1100 Dude Dude Pistol
- 10, // 1101 Dude Dude MachineGun
- 10, // 1110 Dude Dude ShotGun
- 10, // 1111 Dude Dude AssaultWeapon
- };
- ////////////////////////////////////////////////////////////////////////////////
- // Gets a random value that is +/- lRange with sway scaled by the current
- // scene scaling.
- ////////////////////////////////////////////////////////////////////////////////
- inline
- long GetRandSway( // Returns sway value.
- long lRange, // In: Total range of output value.
- double dScale) // In: Scaling.
- {
- // There's two approaches possible here:
- // 1) Scale lRange by current scene scale.
- // 2) Scale result by current scene scale.
- // I think I like the first option better because it allows for more
- // granularity in the result. But, since that is variable on a per level
- // basis, I'm not sure it's worth worrying about.
- // Going with option 1.
- lRange *= dScale;
- return (GetRand() % lRange) - lRange / 2;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Load object (should call base class version!)
- ////////////////////////////////////////////////////////////////////////////////
- short CCharacter::Load( // Returns 0 if successfull, non-zero otherwise
- RFile* pFile, // In: File to load from
- bool bEditMode, // In: True for edit mode, false otherwise
- short sFileCount, // In: File count (unique per file, never 0)
- ULONG ulFileVersion) // In: Version of file format to load.
- {
- // Call the CThing base class load to get the instance ID
- short sResult = CThing3d::Load(pFile, bEditMode, sFileCount, ulFileVersion);
- if (sResult == 0)
- {
- switch (ulFileVersion)
- {
- default:
- case 3:
- pFile->Read(&m_eWeaponType);
- break;
- case 2: // Versions 1 and 2, when CCharacter was not descended from
- case 1: // CThing3d, loaded the weapon type amidst all the other data.
- // Load object data
- // **FUDGE.
- m_eWeaponType = CThing::CRocketID;
- break;
- }
- // Make sure there were no file errors or format errors . . .
- if (!pFile->Error() && sResult == 0)
- {
- // Success.
- }
- else
- {
- sResult = -1;
- TRACE("CCharacter::Load(): Error reading from file!\n");
- }
- }
- return sResult;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Save object (should call base class version!)
- ////////////////////////////////////////////////////////////////////////////////
- short CCharacter::Save( // Returns 0 if successfull, non-zero otherwise
- RFile* pFile, // In: File to save to
- short sFileCount) // In: File count (unique per file, never 0)
- {
- // Call the base class save to save the u16InstanceID
- short sResult = CThing3d::Save(pFile, sFileCount);
- if (sResult == 0)
- {
- // Save object data
- pFile->Write(&m_eWeaponType);
- sResult = pFile->Error();
- }
- return sResult;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Update object.
- // (virtual).
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::Update(void) // Returns nothing.
- {
- if (m_u16IdWeapon != CIdBank::IdNil)
- {
- CWeapon* pweapon;
- if (m_pRealm->m_idbank.GetThingByID((CThing**)&pweapon, m_u16IdWeapon) == 0)
- {
- RTransform* ptransWeapon = m_panimCur->m_ptransRigid->GetAtTime(m_lAnimTime);
- // Position weapon.
- PositionChild(
- pweapon->GetSprite(), // In: Child sprite to position.
- ptransWeapon, // In: Transform specifying position.
- &(pweapon->m_dX), // Out: New position of child.
- &(pweapon->m_dY), // Out: New position of child.
- &(pweapon->m_dZ) ); // Out: New position of child.
- }
- }
- // If we have a weapon sound play instance . . .
- if (m_siLastWeaponPlayInstance)
- {
- // If time has expired . . .
- if (m_pRealm->m_time.GetGameTime() > m_lStopLoopingWeaponSoundTime)
- {
- // Stop looping the sound.
- StopLoopingSample(m_siLastWeaponPlayInstance);
- // Forget about it.
- m_siLastWeaponPlayInstance = 0;
- }
- }
- // Call base class.
- CThing3d::Update();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Render object
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::Render(void)
- {
- // Call base class.
- CThing3d::Render();
- }
- //---------------------------------------------------------------------------
- // Useful generic character state-specific functionality.
- //---------------------------------------------------------------------------
- ////////////////////////////////////////////////////////////////////////////////
- // Implements basic one-time functionality for each time State_Shot is
- // entered.
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::OnShot(void)
- {
- CThing3d::OnShot();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Implements basic functionality while shot and returns true
- // until the state is completed.
- ////////////////////////////////////////////////////////////////////////////////
- bool CCharacter::WhileShot(void) // Returns true until state is complete
- {
- return CThing3d::WhileShot();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Implements basic functionality while being blown up and returns true
- // until the state is completed.
- ////////////////////////////////////////////////////////////////////////////////
- bool CCharacter::WhileBlownUp(void) // Returns true until state is complete.
- {
- return CThing3d::WhileBlownUp();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Implements basic functionality while being on fire and returns true
- // until the state is completed.
- ////////////////////////////////////////////////////////////////////////////////
- bool CCharacter::WhileBurning(void) // Returns true until state is complete.
- {
- bool bStatePersists = true; // Assume not done.
- // Get time from last call in seconds. Should this be passed in so we don't
- // update m_lPrevTime???
- long lCurTime = m_pRealm->m_time.GetGameTime();
- double dSeconds = double(lCurTime - m_lPrevTime) / 1000.0;
- m_lPrevTime = lCurTime;
- // Make character run around while on fire, varying in direction.
- m_dAcc = 150;
- m_dRot = rspMod360(m_dRot + (GetRand() % 30) - 15);
- DeluxeUpdatePosVel(dSeconds);
- // If the fire still exists . . .
- CFire* pfire;
- if (m_pRealm->m_idbank.GetThingByID((CThing**)&pfire, m_u16IdFire) == 0)
- {
- // If the fire is still burning . . .
- if (pfire->IsBurning() != FALSE)
- {
- // Brightness is the ratio of the amount of time expired to the
- // total time multiplied by the destination brightness.
- long lTimeExpired = MIN(lCurTime + BURN_DURATION - m_lCharacterTimer, (long)BURN_DURATION);
- m_sBrightness = lTimeExpired * BURNT_BRIGHTNESS / BURN_DURATION;
- // If time has expired . . .
- if (lTimeExpired >= BURN_DURATION)
- {
- bStatePersists = false;
- }
- }
- else
- {
- // We're done.
- bStatePersists = false;
- }
- }
- else
- {
- // We're done.
- bStatePersists = false;
- }
- // If we're done . . .
- if (bStatePersists == false)
- {
- // Let's make sure we're dead.
- m_stockpile.m_sHitPoints = 0;
- }
- return bStatePersists;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Implements basic functionality while dying and returns true
- // until the state is completed.
- ////////////////////////////////////////////////////////////////////////////////
- bool CCharacter::WhileDying(void) // Returns true until state is complete.
- {
- bool bStatePersists = true; // Assume not done.
- // When he finishes his current animation (assuming dying anim) then he is
- // officially dead.
- if (m_lAnimTime > m_panimCur->m_psops->TotalTime())
- bStatePersists = false;
- return bStatePersists;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Implements basic functionality while holding and preparing to release
- // a weapon. Shows the weapon when the event hits 1 and releases the
- // weapon via ShootWeapon() when the event hits 2.
- ////////////////////////////////////////////////////////////////////////////////
- bool CCharacter::WhileHoldingWeapon( // Returns true when weapon is released.
- U32 u32BitsInclude, // In: Collision bits to pass to ShootWeapon
- U32 u32BitsDontcare, // In: Collision bits to pass to ShootWeapon
- U32 u32BitsExclude) // In: Collision bits to pass to ShootWeapon
- {
- bool bReleased = false; // Assume not released.
- U8 u8Event = *( (U8*)(m_panimCur->m_pevent->GetAtTime(m_lAnimTime) ) );
- // Check for show point in animation . . .
- if (u8Event > 0)
- {
- CWeapon* pweapon;
- if (m_pRealm->m_idbank.GetThingByID((CThing**)&pweapon, m_u16IdWeapon) == 0)
- {
- // If hidden . . .
- if (pweapon->m_eState == CWeapon::State_Hide)
- {
- // Show.
- pweapon->m_eState = CWeapon::State_Idle;
- }
- // Check for release point . . .
- else if (u8Event > 1)
- {
- // Release.
- ShootWeapon(u32BitsInclude, u32BitsDontcare, u32BitsExclude);
-
- bReleased = true;
- }
- }
- else
- {
- // Check for release point . . .
- if (u8Event > 1)
- {
- // Release.
- ShootWeapon(u32BitsInclude, u32BitsDontcare, u32BitsExclude);
-
- bReleased = true;
- }
- }
- }
- return bReleased;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Implements basic one-time functionality for each time State_Dead is
- // entered.
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::OnDead(void)
- {
- CHood* phood = m_pRealm->m_phood;
- // Render current dead frame into background to stay.
- m_pRealm->m_scene.DeadRender3D(
- phood->m_pimBackground, // Destination image.
- &m_sprite, // Tree of 3D sprites to render.
- phood); // Dst clip rect.
-
- // If we just rendered a child weapon into the background . . .
- CThing* pthing;
- if (m_pRealm->m_idbank.GetThingByID(&pthing, m_u16IdWeapon) == 0)
- {
- // It has a permanent place in the background and, therefore, is no
- // longer needed.
- // "Shoot" it (release it).
- ShootWeapon();
- // Delete it!
- GameMessage msg;
- msg.msg_ObjectDelete.eType = typeObjectDelete;
- msg.msg_ObjectDelete.sPriority = 0;
- SendThingMessage(&msg, pthing);
- }
- // Register death with the score module
- ScoreRegisterKill(m_pRealm, m_u16InstanceId, m_u16KillerId);
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Implements basic functionality while being run over and returns true
- // until the state is completed.
- ////////////////////////////////////////////////////////////////////////////////
- bool CCharacter::WhileRunOver(void) // Returns true until state is complete.
- {
- return CThing3d::WhileRunOver();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Implements one-time functionality for when a weapon is destroyed while
- // we were moving it (i.e., before we let go or ShootWeapon()'ed it).
- // This can occur when a weapon, while traveling along our rigid body,
- // enters terrain.
- ////////////////////////////////////////////////////////////////////////////////
- // virtual.
- void CCharacter::OnWeaponDestroyed(void)
- {
- // Each higher object should implement this as needed for that type of object
- // or not at all. Please call this base though, in case we ever add something
- // here.
- }
- //---------------------------------------------------------------------------
- // Useful generic character functionality.
- //---------------------------------------------------------------------------
- ////////////////////////////////////////////////////////////////////////////////
- // Process the specified message. For most messages, this function
- // will call the equivalent On* function.
- // (virtual).
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::ProcessMessage( // Returns nothing.
- GameMessage* pmsg) // Message to process.
- {
- // Call base class.
- CThing3d::ProcessMessage(pmsg);
- // Process character specific messages.
- switch (pmsg->msg_Generic.eType)
- {
- case typeDrawBlood:
- OnDrawBloodMsg(&(pmsg->msg_DrawBlood) );
- break;
- case typeSuicide:
- OnSuicideMsg(&(pmsg->msg_Suicide) );
- break;
-
- default:
- // Should this complain when it doesn't know a message type?
- break;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Handles a msg_Shot.
- // (virtual).
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::OnShotMsg( // Returns nothing.
- Shot_Message* pshotmsg) // In: Message to handle.
- {
- ASSERT(pshotmsg->u16ShooterID != 0xebeb);
- m_u16KillerId = pshotmsg->u16ShooterID;
- CThing3d::OnShotMsg(pshotmsg);
- // If we're receiving any damage from the shot . . .
- if (pshotmsg->sDamage > 0)
- {
- MakeBloody(
- pshotmsg->sDamage,
- pshotmsg->sAngle,
- CHUNKS_DAMAGE_DIR_SWAY);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Handles an Explosion_Message.
- // (virtual).
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::OnExplosionMsg( // Returns nothing.
- Explosion_Message* pexplosionmsg) // In: Message to handle.
- {
- ASSERT(pexplosionmsg->u16ShooterID != 0xebeb);
- m_u16KillerId = pexplosionmsg->u16ShooterID;
- MakeBloody(
- pexplosionmsg->sDamage,
- 180, // Slightly more efficient than 0 (speeds up mod op).
- 360);
- CThing3d::OnExplosionMsg(pexplosionmsg);
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Handles a Burn_Message.
- // (virtual).
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::OnBurnMsg( // Returns nothing.
- Burn_Message* pburnmsg) // In: Message to handle.
- {
- ASSERT(pburnmsg->u16ShooterID != 0xebeb);
- m_u16KillerId = pburnmsg->u16ShooterID;
- CThing3d::OnBurnMsg(pburnmsg);
- // If we are not yet in the burn state . . .
- if (m_state != State_Burning)
- {
- if (StatsAreAllowed) Stat_Burns++;
- // End of burn, if we don't die of 'natural' (being on fire) causes first.
- m_lCharacterTimer = m_pRealm->m_time.GetGameTime() + BURN_DURATION;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Handles an ObjectDelete_Message.
- // (virtual).
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::OnDeleteMsg( // Returns nothing.
- ObjectDelete_Message* pdeletemsg) // In: Message to handle.
- {
- CThing3d::OnDeleteMsg(pdeletemsg);
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Handles a DrawBlood_Message.
- // (virtual).
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::OnDrawBloodMsg( // Returns nothing.
- DrawBlood_Message* pdrawbloodmsg) // In: Message to handle.
- {
- BloodToBackground(pdrawbloodmsg->s2dX, pdrawbloodmsg->s2dY);
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Handles a Suicide_Message.
- // (virtual).
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::OnSuicideMsg( // Returns nothing.
- Suicide_Message* psuicidemsg) // In: Message to handle.
- {
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Handles a Help_Message
- // (virtual)
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::OnHelpMsg( // Returns nothing
- Help_Message* phelpmsg)
- {
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Handles a PutMeDown_Message
- // (virtual)
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::OnPutMeDownMsg( // Returns nothing
- PutMeDown_Message* pputmedownmsg)
- {
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Creates blood splat and pool animations.
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::MakeBloody(
- short sDamage, // In: Damage to base carnage on.
- short sDamageAngle, // In: Angle in which (NOT from which) damage was
- // applied.
- short sSwayRange) // In: Random amount chunks can sway from the
- // sDamageAngle (If 360, there'll be no noticeable
- // damage direction for the chunks).
- {
- double dHitY = m_dY + GetRandSway(BLOOD_SPLAT_SWAY, m_pRealm->m_phood->m_dScale3d);
- double dHitX;
- double dHitZ;
- // If writhing on the ground . . .
- if (m_state == State_Writhing)
- {
- // Keep the shot low and on our X/Z execution point.
- dHitX = m_smash.m_sphere.sphere.X;
- dHitZ = m_smash.m_sphere.sphere.Z;
- }
- else
- {
- // X/Z position depends on angle of shot (it is opposite).
- // That is, the wound appears on a portion of the dude facing the damage
- // source (where the bullet came from, the epicenter of the explosion, etc.)
- // giving us a little more feedback and realism.
- short sDeflectionAngle = rspMod360(sDamageAngle + 180);
- dHitX = m_dX + COSQ[sDeflectionAngle] * TORSO_RADIUS;
- dHitZ = m_dZ - SINQ[sDeflectionAngle] * TORSO_RADIUS;
- // Put it up about half way up character's body.
- dHitY += m_sprite.m_sRadius;
- }
- // Create blood animation.
- CAnimThing* pat = new CAnimThing(m_pRealm);
- if (pat != NULL)
- {
- strcpy(pat->m_szResName, BLOOD_SPLAT_RES_NAME);
- // Start it up:
- // No looping.
- pat->m_sLoop = FALSE;
- // No notification necessary.
- // NOTE: sAngle is currently not utilized.
- pat->Setup(dHitX, dHitY, dHitZ + 1);
- }
- // Create some chunks.
- short sNumChunks = MIN(sDamage / 2, MAX_CHUNKS);
- short i;
- for (i = 0; i < sNumChunks; i++)
- {
- // Create blood particles . . .
- CChunk* pchunk = NULL; // Initialized for safety.
- // Note that this will fail if particles are disabled.
- if (Construct(CChunkID, m_pRealm, (CThing**)&pchunk) == 0)
- {
- pchunk->Setup(
- dHitX, // Source position.
- dHitY, // Source position.
- dHitZ, // Source position.
- sDamageAngle, // Angle of velocity.
- sSwayRange, // Angle sway.
- 40, // Velocity (X/Z plane).
- 80, // Velocity (X/Z plane) sway.
- 50, // Velocity (Vertical).
- 100, // Velocity (Vertical) sway.
- CChunk::Blood); // Type of chunk.
- }
- }
- // Let's go ahead and create the pool at the same time.
- // If it gets bigger it might look like the above anim
- // created the pool which, of course, is the desired
- // effect.
- MakeBloodPool();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Creates blood pool animation.
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::MakeBloodPool(void)
- {
- CAnimThing* pat = new CAnimThing(m_pRealm);
- if (pat != NULL)
- {
- strcpy(pat->m_szResName, BLOOD_POOL_RES_NAME);
- // Start it up:
- // No looping.
- pat->m_sLoop = FALSE;
- // Need notification to tell us when to put the animation
- // in the background.
- pat->m_msg.msg_DrawBlood.eType = typeDrawBlood;
- pat->m_msg.msg_DrawBlood.sPriority = 0;
- pat->m_u16IdSendMsg = m_u16InstanceId;
- double dHitX = m_dX + GetRandSway(BLOOD_SPLAT_SWAY, m_pRealm->m_phood->m_dScale3d);
- double dHitZ = m_dZ + GetRandSway(BLOOD_SPLAT_SWAY, m_pRealm->m_phood->m_dScale3d);
- // Make sure blood lands on the ground (terrain).
- double dTerrainH = m_pRealm->GetHeight(dHitX, dHitZ);
- Map3Dto2D(dHitX, dTerrainH, dHitZ,
- &(pat->m_msg.msg_DrawBlood.s2dX),
- &(pat->m_msg.msg_DrawBlood.s2dY) );
- // NOTE: sAngle is currently not utilized.
- pat->Setup(dHitX, dTerrainH, dHitZ);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Draws last frame of blood pool into background.
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::BloodToBackground(
- short sAnimX2d, // Position of animation in 2d.
- short sAnimY2d) // Position of animation in 2d.
- {
- // Draw last frame of pool directly into background.
- CAnimThing::ChannelAA* paachannel;
- if (rspGetResource(&g_resmgrGame, m_pRealm->Make2dResPath(BLOOD_POOL_RES_NAME), &paachannel) == 0)
- {
- // Get last frame.
- CAlphaAnim* paa = paachannel->GetItem(paachannel->NumItems() - 1);
- ASSERT(paa != NULL);
-
- short sX = sAnimX2d + paa->m_sX;
- short sY = sAnimY2d + paa->m_sY;
-
- // Note this does not handle alpha case yet.
- if (paa->m_pimAlphaArray != NULL)
- {
- RRect rcClip(0, 0,
- m_pRealm->m_phood->m_pimBackground->m_sWidth,
- m_pRealm->m_phood->m_pimBackground->m_sHeight);
-
- // Do the Alpha blit.
- rspGeneralAlphaBlit(
- m_pRealm->m_phood->m_pmaTransparency, // Levels multialpha table.
- paa->m_pimAlphaArray, // Alpha mask.
- &(paa->m_imColor), // Src.
- m_pRealm->m_phood->m_pimBackground, // Dst.
- sX, // 2D Dst coord.
- sY, // 2D Dst coord.
- rcClip); // Dst.
- }
- else
- {
- // Regular transparency blit.
- rspBlit(
- &(paa->m_imColor), // Src.
- m_pRealm->m_phood->m_pimBackground, // Dst.
- sX, // 2D Dst coord.
- sY, // 2D Dst coord.
- NULL); // Dst.
- }
-
- rspReleaseResource(&g_resmgrGame, &paachannel);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Prepare current weapon (ammo).
- // This should be done when the character starts its shoot animation.
- // (virtual).
- ////////////////////////////////////////////////////////////////////////////////
- CWeapon* CCharacter::PrepareWeapon(void) // Returns the weapon ptr or NULL.
- {
- CWeapon* pweapon = NULL;
- switch (m_eWeaponType)
- {
- case CPistolID:
- case CMachineGunID:
- case CShotGunID:
- case CAssaultWeaponID:
- case CDoubleBarrelID:
- case CUziID:
- case CAutoRifleID:
- case CSmallPistolID:
- break;
- default:
- if (ConstructWithID(m_eWeaponType, m_pRealm, (CThing**) &pweapon) == 0)
- {
- // Set its parent.
- pweapon->m_idParent = GetInstanceID();
- // Set it up.
- pweapon->Setup(0, 0, 0);
- pweapon->m_dRot = m_dRot;
- // Set its initial state to hidden.
- pweapon->m_eState = CWeapon::State_Hide;
- // Let the scene know to render the weapon as a child of this.
- m_sprite.AddChild(pweapon->GetSprite() );
- // Store ID.
- m_u16IdWeapon = pweapon->GetInstanceID();
- }
- else
- {
- TRACE("PrepareWeapon(): Failed to construct new %s.\n",
- ms_aClassInfo[m_eWeaponType].pszClassName);
- }
- break;
- }
- return pweapon;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Shoot current weapon.
- // This should be done when the character releases the weapon it's
- // shooting.
- // (virtual).
- ////////////////////////////////////////////////////////////////////////////////
- CWeapon* CCharacter::ShootWeapon( // Returns the weapon ptr or NULL
- CSmash::Bits bitsInclude, // Bits to use for bullet collision (enemies can specify different bits)
- CSmash::Bits bitsDontcare, // Bits to use for bullet colllsion
- CSmash::Bits bitsExclude) // Bits to use for bullet collision
- {
- CWeapon* pweapon = NULL;
- // Detatch the weapon.
- ASSERT(m_panimCur != NULL);
-
- // Get weapon's position relative to this character.
- double dWeaponRelX, dWeaponRelY, dWeaponRelZ;
- GetLinkPoint(
- m_panimCur->m_ptransRigid->GetAtTime(m_lAnimTime),
- &dWeaponRelX,
- &dWeaponRelY,
- &dWeaponRelZ);
- RP3d pt3WeaponRel = { static_cast<float>(dWeaponRelX), static_cast<float>(dWeaponRelY), static_cast<float>(dWeaponRelZ), 1 };
- switch (m_eWeaponType)
- {
- case CPistolID:
- case CMachineGunID:
- case CUziID:
- case CAutoRifleID:
- case CSmallPistolID:
- FireBullets(&pt3WeaponRel, 1, MAX_BULLET_RANGE, g_smidBulletFire, bitsInclude, bitsDontcare, bitsExclude);
- break;
-
- case CShotGunID:
- FireBullets(&pt3WeaponRel, NUM_SHOTS_PER_SHELL, MAX_SHELL_RANGE, g_smidShotgun, bitsInclude, bitsDontcare, bitsExclude);
- break;
- case CAssaultWeaponID:
- FireBullets(&pt3WeaponRel, NUM_SHOTS_PER_SHELL, MAX_SHELL_RANGE, g_smidSprayCannon, bitsInclude, bitsDontcare, bitsExclude);
- break;
- case CDoubleBarrelID:
- FireBullets(&pt3WeaponRel, 2 * NUM_SHOTS_PER_SHELL, MAX_SHELL_RANGE, g_smidDeathWadLaunch, bitsInclude, bitsDontcare, bitsExclude);
- // Double it.
- PlaySample(g_smidShotgun, SampleMaster::Weapon);
- break;
- case CFirestreamID:
- // If there's no sound going on already . . .
- if (m_siLastWeaponPlayInstance == 0)
- {
- PlaySample( // Returns nothing.
- // Does not fail.
- g_smidFlameThrower3, // In: Identifier of sample you want played.
- SampleMaster::Destruction, // In: Sound Volume Category for user adjustment
- 255, // In: Initial Sound Volume (0 - 255)
- &m_siLastWeaponPlayInstance, // Out: Handle for adjusting sound volume
- NULL, // Out: Sample duration in ms, if not NULL.
- 250, // In: Where to loop back to in milliseconds.
- // -1 indicates no looping (unless m_sLoop is
- // explicitly set).
- 750, // In: Where to loop back from in milliseconds.
- // In: If less than 1, the end + lLoopEndTime is used.
- false); // In: Call ReleaseAndPurge rather than Release after playing
- }
- m_lStopLoopingWeaponSoundTime = m_pRealm->m_time.GetGameTime() + MAX_TIME_WITHOUT_FLAMAGE;
- // Intentional fall through.
- default:
- {
- ASSERT(m_u16IdWeapon != CIdBank::IdNil);
- if (m_pRealm->m_idbank.GetThingByID((CThing**)&pweapon, m_u16IdWeapon) == 0)
- {
- // Set weapon position to character's position offset by rigid body's realm offset.
- pweapon->m_dX = m_dX + pt3WeaponRel.x;
- pweapon->m_dY = m_dY + pt3WeaponRel.y;
- pweapon->m_dZ = m_dZ + pt3WeaponRel.z;
- // I guess we usually want to launch in the direction we are _currently_ facing and
- // not the direction we were in when we prepared the weapon.
- pweapon->m_dRot = m_dRot;
- // Set the collision bits for the weapon
- pweapon->SetCollideBits(bitsInclude, bitsDontcare, bitsExclude);
- // Set Instance ID of the shooter
- pweapon->m_u16ShooterID = m_u16InstanceId;
- // Set the weapon in motion by changing its state.
- pweapon->m_eState = CWeapon::State_Fire;
-
- // Detach parent pointer
- pweapon->m_idParent = CIdBank::IdNil;
- // Detatch weapon's sprite
- CSprite* pspriteWeapon = pweapon->GetSprite();
- if (pspriteWeapon)
- {
- // Some weapons (one weapon, the flamer) are never parented.
- if (pspriteWeapon->m_psprParent)
- {
- m_sprite.RemoveChild(pspriteWeapon);
- }
- }
- // Done with weapon.
- m_u16IdWeapon = CIdBank::IdNil;
-
- // Specific behavior by type.
- switch (pweapon->GetClassID() )
- {
- case CDeathWadID:
- CDeathWad* pdw = (CDeathWad*)pweapon;
- // Let it take what it needs.
- pdw->FeedWad(&m_stockpile);
- break;
- }
- }
- break;
- }
- }
- // No longer exists.
- m_u16IdWeapon = CIdBank::IdNil;
- return pweapon;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Validate weapon position. If invalid, the weapon is destroyed and
- // the notification function, OnWeaponDestroyed() is called.
- ////////////////////////////////////////////////////////////////////////////////
- bool CCharacter::ValidateWeaponPosition(void) // Returns true, if weapon is in a valid position.
- // Returns false, if weapon destroyed because it
- // it is not in a valid position.
- {
- bool bValid = true; // Assume valid.
- if (m_u16IdWeapon != CIdBank::IdNil)
- {
- switch (m_eWeaponType)
- {
- case CGrenadeID:
- case CFirebombID:
- case CNapalmID:
- case CRocketID:
- case CHeatseekerID:
- {
- CWeapon* pweapon;
- if (m_pRealm->m_idbank.GetThingByID((CThing**)&pweapon, m_u16IdWeapon) == 0)
- {
- // If this weapon is not hidden . . .
- if (pweapon->m_eState != CWeapon::State_Hide)
- {
- RTransform* ptransWeapon = m_panimCur->m_ptransRigid->GetAtTime(m_lAnimTime);
- // Get the position to check for terrain obstacles.
- // Remember this is an offset from our hotspot (origin).
- double dX, dY, dZ;
- GetLinkPoint(ptransWeapon, &dX, &dY, &dZ);
- // Check position to make sure it's not inside terrain.
- short sTerrainH = m_pRealm->GetHeight(m_dX + dX, m_dZ + dZ);
- if (sTerrainH > m_dY + dY)
- {
- // Send the object a delete message.
- GameMessage msg;
- msg.msg_ObjectDelete.eType = typeObjectDelete;
- msg.msg_ObjectDelete.sPriority = 0;
- SendThingMessage(&msg, pweapon);
- // Clear our instance ID.
- m_u16IdWeapon = CIdBank::IdNil;
- // Call the notify function.
- OnWeaponDestroyed();
- bValid = false;
- }
- }
- }
- else
- {
- // Clear our instance ID.
- m_u16IdWeapon = CIdBank::IdNil;
- }
- break;
- }
- default:
- break;
- }
- }
- return bValid;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Fire some bullets.
- ////////////////////////////////////////////////////////////////////////////////
- bool CCharacter::FireBullets( // Returns true, if we hit someone/thing.
- RP3d* ppt3d, // In: Launch pt in Postal units.
- short sNumShots, // In: Number of shots to fire.
- short sRange, // In: Bullet range.
- SampleMasterID smidAmmo, // In: Ammo noise.
- CSmash::Bits bitsInclude,/*=0*/ // In: Optional bits we can hit
- CSmash::Bits bitsDontcare, // In: Optional bits not involved in collision
- CSmash::Bits bitsExclude) // In: Optional bits excluded from collision
- {
- bool bHit = false; // Assume no hit.
- if (bitsInclude == 0)
- {
- bitsInclude = CSmash::Character | CSmash::Barrel | CSmash::Mine;
- }
- // Never shoot dead people or writhers.
- bitsExclude |= (CSmash::Dead | CSmash::AlmostDead);
- const bool isPlayer = (m_id == CDudeID); // !!! FIXME: make sure it's not a remote multiplayer dude.
- if (isPlayer)
- {
- if (StatsAreAllowed)
- {
- Stat_BulletsFired += sNumShots;
- if (Stat_BulletsFired >= 1000000)
- UnlockAchievement(ACHIEVEMENT_FIRE_1000000_BULLETS);
- }
- }
- short i;
- for (i = 0; i < sNumShots; i ++)
- {
- short sX, sY, sZ;
- short sFireAngle = m_dRot + GetRandSway(FIRE_ANGLE_Y_SWAY, m_pRealm->m_phood->m_dScale3d);
- CThing* pthingTarget;
- bool bResult = m_bullets.FireDeluxe(
- sFireAngle, // In: Angle of launch in degrees (on X/Z plane).
- GetRandSway(FIRE_ANGLE_Z_SWAY, m_pRealm->m_phood->m_dScale3d), // In: Angle of launch in degrees (on X/Y plane).
- m_dX + ppt3d->x, // In: Launch position.
- m_dY + ppt3d->y, // In: Launch position.
- m_dZ + ppt3d->z, // In: Launch position.
- sRange, // In: Maximum distance.
- m_pRealm, // In: Realm in which to fire.
- bitsInclude, // In: Mask of CSmash masks that this bullet can hit.
- bitsDontcare, // In: Mask of CSmash masks that this bullet does not care to hit.
- bitsExclude, // In: Mask of CSmash masks that this bullet cannot hit.
- 20, // In: Maximum angle with terrain that can cause
- // a ricochet (on X/Z plane).
- 2, // In: The maximum number of ricochets.
- &sX, // Out: Terrain hit position (i.e., where the bullet
- // would stop if no CThing collisions occurred).
- &sY, // Out: Terrain hit position (i.e., where the bullet
- // would stop if no CThing collisions occurred).
- &sZ, // Out: Terrain hit position (i.e., where the bullet
- // would stop if no CThing collisions occurred).
- &pthingTarget, // Out: Ptr to thing hit or NULL.
- true, // In: Draw a tracer at random point along path.
- (i == 0) ? smidAmmo : g_smidNil) // In: Use ammo sample.
-
- ;
- //!! FIXME: this should simply miss if we shoot ourself. Cheap hack for dude shooting himself in twinstick mode.
- if (pthingTarget != this && bResult)
- {
- // Create a message.
- // Note that right here we can do the tuning on the amount of bullet
- // damage based on m_eWeaponType and the pthingTarget->m_id == CDudeID and
- // this->m_id == CDudeID.
- GameMessage msg;
- msg.msg_Shot.eType = typeShot;
- msg.msg_Shot.sPriority = 0;
- msg.msg_Shot.sAngle = sFireAngle;
- msg.msg_Shot.u16ShooterID = m_u16InstanceId;
- short sIndex = 0;
- // Figure out how much damage the bullet should give
- switch (m_eWeaponType)
- {
- case CSmallPistolID:
- case CPistolID:
- sIndex = WEAPON_IS_PISTOL;
- break;
- case CUziID:
- case CAutoRifleID:
- case CMachineGunID:
- sIndex = WEAPON_IS_MACHINEGUN;
- break;
- case CShotGunID:
- sIndex = WEAPON_IS_SHOTGUN;
- break;
- case CAssaultWeaponID:
- sIndex = WEAPON_IS_ASSAULT;
- break;
- case CDoubleBarrelID:
- sIndex = WEAPON_IS_SHOTGUN;
- // There's additional damage via other means.
- break;
- }
- // See if the shooter is a CDude
- if (m_id == CDudeID)
- sIndex |= SHOOTER_IS_DUDE;
- // See if the target is a CDude
- if (pthingTarget->GetClassID() == CDudeID)
- sIndex |= TARGET_IS_DUDE;
- // Based on these parameters, look up the damage in the damage chart.
- msg.msg_Shot.sDamage = ms_asBulletDamageChart[sIndex];
- // Send it the message.
- SendThingMessage(&msg, pthingTarget);
- if (m_eWeaponType == CDoubleBarrelID)
- {
- // This is gonna hurt.
- msg.msg_Explosion.eType = typeExplosion;
- msg.msg_Explosion.sPriority = 0;
- // Spread this out.
- msg.msg_Explosion.sDamage = ms_asBulletDamageChart[sIndex] / sNumShots + 1;
- // Just in front of where bullet hit.
- short sAngle = rspMod360(m_dRot);
- msg.msg_Explosion.sX = (short) pthingTarget->GetX() - COSQ[sAngle] * 10;
- msg.msg_Explosion.sY = (short) pthingTarget->GetY();
- msg.msg_Explosion.sZ = (short) pthingTarget->GetZ() + SINQ[sAngle] * 10;
- msg.msg_Explosion.sVelocity = 100;
- msg.msg_Explosion.u16ShooterID = GetInstanceID();
- SendThingMessage(&msg, pthingTarget);
- }
- // Note we hit something.
- bHit = true;
- if ((isPlayer) && (StatsAreAllowed))
- {
- Stat_BulletsHit++;
- if (Stat_BulletsHit >= 100000)
- UnlockAchievement(ACHIEVEMENT_HIT_100000_TARGETS);
- }
- }
- else
- {
- if ((isPlayer) && (StatsAreAllowed)) Stat_BulletsMissed++;
- }
- }
- // Create shells/casings . . .
- CChunk* pchunk = NULL; // Initialized for safety.
- // Note that this will fail if particles are disabled.
- if (Construct(CChunkID, m_pRealm, (CThing**)&pchunk) == 0)
- {
- pchunk->Setup(
- m_dX + ppt3d->x, // Source position.
- m_dY + ppt3d->y, // Source position.
- m_dZ + ppt3d->z, // Source position.
- m_dRot + 90, // Angle of velocity.
- 0, // Angle sway.
- 30, // Velocity (X/Z plane).
- 20, // Velocity sway.
- 37, // Velocity (Vertical).
- 25, // Velocity (Vertical) sway.
- (sNumShots > 1) ? CChunk::Shell : CChunk::BulletCasing); // In: Type of chunk.
- }
- return bHit;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Determine if a path is clear of items identified by the specified
- // smash bits and terrain.
- ////////////////////////////////////////////////////////////////////////////////
- bool CCharacter::IsPathClear( // Returns true, if the entire path is clear.
- // Returns false, if only a portion of the path is clear.
- // (see *psX, *psY, *psZ, *ppthing).
- short sX, // In: Starting X.
- short sY, // In: Starting Y.
- short sZ, // In: Starting Z.
- short sRotY, // In: Rotation around y axis (direction on X/Z plane).
- short sCrawlRate, // In: Rate at which to scan ('crawl') path in pixels per
- // iteration.
- // NOTE: We scan terrain using GetFloorAttributes()
- // so small values of sCrawl are not necessary.
- // NOTE: We could change this to a speed in pixels per second
- // where we'd assume a certain frame rate.
- short sRangeXZ, // In: Range on X/Z plane.
- short sRadius, // In: Radius of path traverser.
- short sVerticalTolerance, // In: Max traverser can step up.
- CSmash::Bits bitsInclude, // In: Mask of CSmash bits that would terminate path.
- CSmash::Bits bitsDontCare, // In: Mask of CSmash bits that would not affect path.
- CSmash::Bits bitsExclude, // In: Mask of CSmash bits that cannot affect path.
- short* psX, // Out: Point of intercept, if any, on path.
- short* psY, // Out: Point of intercept, if any, on path.
- short* psZ, // Out: Point of intercept, if any, on path.
- CThing** ppthing, // Out: Thing that intercepted us or NULL, if none.
- CSmash* psmashExclude/*= NULL*/) // In: Optional CSmash to exclude or NULL, if none.
- {
- bool bEntirelyClear = false; // Assume entire path is not clear.
- /////////// Determine length of travel if no CThings hit ///////////////////
- // Get most efficient increments that won't miss any attributes.
- // For the rates we use trig with a hypotenuse of 1 which will give
- // us a rate <= 1.0 and then multiply by the the crawl for
- // a reasonable increase in the speed of this alg.
-
- // sAngle must be between 0 and 359.
- sRotY = rspMod360(sRotY);
- float fRateX = COSQ[sRotY] * sCrawlRate;
- float fRateZ = -SINQ[sRotY] * sCrawlRate;
- float fRateY = 0.0; // If we ever want vertical movement . . .
- // Set initial position to first point to check (NEVER checks original position).
- float fPosX = sX + fRateX;
- float fPosY = sY + fRateY;
- float fPosZ = sZ + fRateZ;
- // Determine amount traveled per iteration on X/Z plane just once.
- float fIterDistXZ = sqrt(ABS2(fRateX, fRateZ) );
- float fTotalDistXZ = 0.0F;
- // Store extents.
- short sMaxX = m_pRealm->GetRealmWidth();
- short sMaxY = 512; // Robustness: Guard against infinite loop
- // in the remote possibility that we shoot
- // straight up (currently one can only shoot
- // horizontally).
- short sMaxZ = m_pRealm->GetRealmHeight();
- short sMinX = 0;
- short sMinY = -512;
- short sMinZ = 0;
- short sCurH;
- U16 u16Attribute;
- // Scan while in realm.
- while (
- fPosX > sMinX
- && fPosY > sMinY
- && fPosZ > sMinZ
- && fPosX < sMaxX
- && fPosY < sMaxY
- && fPosZ < sMaxZ
- && fTotalDistXZ < sRangeXZ)
- {
- GetFloorAttributes((short)fPosX, (short)fPosZ, &u16Attribute, &sCurH);
- // If too big a height difference or completely not walkable . . .
- if ( (u16Attribute & REALM_ATTR_NOT_WALKABLE)
- || (sCurH - fPosY > sVerticalTolerance) )
- {
- break;
- }
- // Update position.
- fPosX += fRateX;
- fPosY = sCurH;
- fPosZ += fRateZ;
- // Update distance travelled on X/Z plane.
- fTotalDistXZ += fIterDistXZ;
- }
- // Check 3D line segments outlining path.
- R3DLine line1, line2;
- if (fRateX > 0.0F)
- {
- line1.X1 = sX - sRadius;
- line1.X2 = fPosX - sRadius;
- line2.X1 = sX - sRadius;
- line2.X2 = fPosX - sRadius;
- }
- else
- {
- line1.X1 = sX + sRadius;
- line1.X2 = fPosX + sRadius;
- line2.X1 = sX + sRadius;
- line2.X2 = fPosX + sRadius;
- }
- if (fRateY > 0.0F)
- {
- line1.Y1 = sY - sRadius;
- line1.Y2 = fPosY - sRadius;
- line2.Y1 = sY - sRadius;
- line2.Y2 = fPosY - sRadius;
- }
- else
- {
- line1.Y1 = sY + sRadius;
- line1.Y2 = fPosY + sRadius;
- line2.Y1 = sY + sRadius;
- line2.Y2 = fPosY + sRadius;
- }
- if (fRateZ > 0.0F)
- {
- line1.Z1 = sZ - sRadius;
- line1.Z2 = fPosZ - sRadius;
- line2.Z1 = sZ - sRadius;
- line2.Z2 = fPosZ - sRadius;
- }
- else
- {
- line1.Z1 = sZ + sRadius;
- line1.Z2 = fPosZ + sRadius;
- line2.Z1 = sZ + sRadius;
- line2.Z2 = fPosZ + sRadius;
- }
- CSmash* psmashClosest = NULL;
- // Determine if anything with specified smash description was hit on along each edge . . .
- CSmash* psmash1;
- if (m_pRealm->m_smashatorium.QuickCheckClosest(
- &line1,
- bitsInclude,
- bitsDontCare,
- bitsExclude,
- &psmash1,
- psmashExclude) == false)
- {
- psmash1 = NULL;
- }
- CSmash* psmash2;
- if (m_pRealm->m_smashatorium.QuickCheckClosest(
- &line2,
- bitsInclude,
- bitsDontCare,
- bitsExclude,
- &psmash2,
- psmashExclude) == false)
- {
- psmash2 = NULL;
- }
- // If two smashes found . . .
- if (psmash1 != NULL && psmash2 != NULL && psmash1 != psmash2)
- {
- // Determine closer on X/Z plane.
- if ( ABS2(psmash1->m_sphere.sphere.X, psmash1->m_sphere.sphere.Y)
- < ABS2(psmash2->m_sphere.sphere.X, psmash2->m_sphere.sphere.Y) )
- {
- psmashClosest = psmash1;
- }
- else
- {
- psmashClosest = psmash2;
- }
- }
- else
- {
- // Whichever is not NULL.
- if (psmash1 != NULL)
- {
- psmashClosest = psmash1;
- }
- else
- {
- psmashClosest = psmash2;
- }
- }
- // If anything hit . . .
- if (psmashClosest != NULL)
- {
- // Set *ppthing to thing hit.
- *ppthing = psmashClosest->m_pThing;
- // Set end pt.
- *psX = psmashClosest->m_sphere.sphere.X;
- *psY = psmashClosest->m_sphere.sphere.Y;
- *psZ = psmashClosest->m_sphere.sphere.Z;
- }
- else
- {
- // Clear thing ptr.
- *ppthing = NULL;
- // Set end pt.
- *psX = fPosX;
- *psY = fPosY;
- *psZ = fPosZ;
- // If we made it the whole way . . .
- if (fTotalDistXZ >= sRangeXZ)
- {
- bEntirelyClear = true;
- }
- }
- #if 0
- // FEEDBACK.
- // Create a line sprite.
- CSpriteLine2d* psl2d = new CSpriteLine2d;
- if (psl2d != NULL)
- {
- Map3Dto2D(
- sX,
- sY,
- sZ,
- &(psl2d->m_sX2),
- &(psl2d->m_sY2) );
- Map3Dto2D(
- *psX,
- *psY,
- *psZ,
- &(psl2d->m_sX2End),
- &(psl2d->m_sY2End) );
- psl2d->m_sPriority = sZ;
- psl2d->m_sLayer = CRealm::GetLayerViaAttrib(m_pRealm->GetLayer(sX, sZ));
- psl2d->m_u8Color = (bEntirelyClear == false) ? 249 : 250;
- // Destroy when done.
- psl2d->m_sInFlags = CSprite::InDeleteOnRender;
- // Put 'er there.
- m_pRealm->m_scene.UpdateSprite(psl2d);
- }
- #endif
- return bEntirelyClear;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Light up target if there is one within a cone in the given aiming direction.
- ////////////////////////////////////////////////////////////////////////////////
- bool CCharacter::IlluminateTarget( // Returns true if there is a target
- short sX, // In: Starting x position
- short sY, // In: Starting y position
- short sZ, // In: Starting z position
- short sRotY, // In: Aiming direction (rotation around y axis)
- short sRangeXZ, // In: Range on X/Z plane
- short sRadius, // In: Radius of path traverser.
- CSmash::Bits bitsInclude, // In: Mask of CSmash bits that would count as a hit
- CSmash::Bits bitsDontCare, // In: Mask of CSmash bits that would not affect path
- CSmash::Bits bitsExclude, // In: Mask of CSmash bits that cannot affect path
- CThing** hThing, // Out: Handle to thing that is the Target or NULL if none
- CSmash* psmashExclude) // In: Optional CSmash to exclude or NULL, if none.
- {
- bool bTargetFound = false;
- // sAngle must be between 0 and 359.
- sRotY = rspMod360(sRotY);
- float fRateX = COSQ[sRotY] * sRangeXZ;
- float fRateZ = -SINQ[sRotY] * sRangeXZ;
- float fRateY = 0.0; // If we ever want vertical movement . . .
- // Set initial position to first point to check (NEVER checks original position).
- float fPosX = sX + fRateX;
- float fPosY = sY + fRateY;
- float fPosZ = sZ + fRateZ;
- // Check 3D line segments outlining path.
- R3DLine line1, line2;
- if (fRateX > 0.0F)
- {
- line1.X1 = sX - sRadius;
- line1.X2 = fPosX - sRadius;
- line2.X1 = sX - sRadius;
- line2.X2 = fPosX - sRadius;
- }
- else
- {
- line1.X1 = sX + sRadius;
- line1.X2 = fPosX + sRadius;
- line2.X1 = sX + sRadius;
- line2.X2 = fPosX + sRadius;
- }
- if (fRateY > 0.0F)
- {
- line1.Y1 = sY - sRadius;
- line1.Y2 = fPosY - sRadius;
- line2.Y1 = sY - sRadius;
- line2.Y2 = fPosY - sRadius;
- }
- else
- {
- line1.Y1 = sY + sRadius;
- line1.Y2 = fPosY + sRadius;
- line2.Y1 = sY + sRadius;
- line2.Y2 = fPosY + sRadius;
- }
- if (fRateZ > 0.0F)
- {
- line1.Z1 = sZ - sRadius;
- line1.Z2 = fPosZ - sRadius;
- line2.Z1 = sZ - sRadius;
- line2.Z2 = fPosZ - sRadius;
- }
- else
- {
- line1.Z1 = sZ + sRadius;
- line1.Z2 = fPosZ + sRadius;
- line2.Z1 = sZ + sRadius;
- line2.Z2 = fPosZ + sRadius;
- }
- CSmash* psmashClosest = NULL;
- // Determine if anything with specified smash description was hit on along each edge . . .
- CSmash* psmash1;
- if (m_pRealm->m_smashatorium.QuickCheckClosest(
- &line1,
- bitsInclude,
- bitsDontCare,
- bitsExclude,
- &psmash1,
- psmashExclude) == false)
- {
- psmash1 = NULL;
- }
- CSmash* psmash2;
- if (m_pRealm->m_smashatorium.QuickCheckClosest(
- &line2,
- bitsInclude,
- bitsDontCare,
- bitsExclude,
- &psmash2,
- psmashExclude) == false)
- {
- psmash2 = NULL;
- }
- // If two smashes found . . .
- if (psmash1 != NULL && psmash2 != NULL && psmash1 != psmash2)
- {
- // Determine closer on X/Z plane.
- if ( ABS2(psmash1->m_sphere.sphere.X, psmash1->m_sphere.sphere.Y)
- < ABS2(psmash2->m_sphere.sphere.X, psmash2->m_sphere.sphere.Y) )
- {
- psmashClosest = psmash1;
- }
- else
- {
- psmashClosest = psmash2;
- }
- }
- else
- {
- // Whichever is not NULL.
- if (psmash1 != NULL)
- {
- psmashClosest = psmash1;
- }
- else
- {
- psmashClosest = psmash2;
- }
- }
- // If anything hit . . .
- if (psmashClosest != NULL)
- {
- // Set *ppthing to thing hit.
- *hThing = psmashClosest->m_pThing;
- bTargetFound = true;
- // Set end pt.
- // *psX = psmashClosest->m_sphere.sphere.X;
- // *psY = psmashClosest->m_sphere.sphere.Y;
- // *psZ = psmashClosest->m_sphere.sphere.Z;
- }
- else
- {
- // Clear thing ptr.
- *hThing = NULL;
- // Set end pt.
- // *psX = fPosX;
- // *psY = fPosY;
- // *psZ = fPosZ;
- }
- #if 0
- // FEEDBACK.
- // Create a line sprite.
- CSpriteLine2d* psl2d = new CSpriteLine2d;
- if (psl2d != NULL)
- {
- Map3Dto2D(
- sX,
- sY,
- sZ,
- &(psl2d->m_sX2),
- &(psl2d->m_sY2) );
- Map3Dto2D(
- *psX,
- *psY,
- *psZ,
- &(psl2d->m_sX2End),
- &(psl2d->m_sY2End) );
- psl2d->m_sPriority = sZ;
- psl2d->m_sLayer = CRealm::GetLayerViaAttrib(m_pRealm->GetLayer(sX, sZ));
- psl2d->m_u8Color = (bEntirelyClear == false) ? 249 : 250;
- // Destroy when done.
- psl2d->m_sInFlags = CSprite::InDeleteOnRender;
- // Put 'er there.
- m_pRealm->m_scene.UpdateSprite(psl2d);
- }
- #endif
- return bTargetFound;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Preload - cache the anims that may be used.
- // (static).
- ////////////////////////////////////////////////////////////////////////////////
- short CCharacter::Preload(
- CRealm* prealm) // In: Calling realm.
- {
- CAnimThing::ChannelAA* paaCache;
- short sResult = 0;
- if (rspGetResource(&g_resmgrGame, prealm->Make2dResPath(BLOOD_SPLAT_RES_NAME), &paaCache) == 0)
- rspReleaseResource(&g_resmgrGame, &paaCache);
- else
- sResult |= 1;
- if (rspGetResource(&g_resmgrGame, prealm->Make2dResPath(BLOOD_POOL_RES_NAME), &paaCache) == 0)
- rspReleaseResource(&g_resmgrGame, &paaCache);
- else
- sResult |= 1;
- // Tell samplemaster to cache (preload) these samples.
- CacheSample(g_smidBulletFire);
- CacheSample(g_smidShotgun);
- CBulletFest::Preload(prealm);
- return sResult;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Called by destructor to clean up.
- ////////////////////////////////////////////////////////////////////////////////
- void CCharacter::Kill(void)
- {
- // If we have a weapon sound play instance . . .
- if (m_siLastWeaponPlayInstance)
- {
- // Stop looping the sound.
- StopLoopingSample(m_siLastWeaponPlayInstance);
- // Forget about it.
- m_siLastWeaponPlayInstance = 0;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // EOF
- ////////////////////////////////////////////////////////////////////////////////
|