#include "precompiled.h" #pragma hdrstop #include "prey_local.h" const idEventDef EV_Hand_DoRemove( "DoHandRemove", NULL ); const idEventDef EV_Hand_DoAttach( "DoHandAttach", "e" ); const idEventDef EV_Hand_Remove( "RemoveHand", NULL ); const idEventDef EV_Hand_Ready( "" ); const idEventDef EV_Hand_Lowered( "" ); const idEventDef EV_Hand_Raise( "" ); CLASS_DECLARATION( hhAnimatedEntity, hhHand ) EVENT ( EV_Hand_DoRemove, hhHand::Event_DoHandRemove ) EVENT ( EV_Hand_DoAttach, hhHand::Event_DoHandAttach ) EVENT ( EV_Hand_Remove, hhHand::Event_RemoveHand ) EVENT ( EV_Hand_Ready, hhHand::Event_Ready ) EVENT ( EV_Hand_Lowered, hhHand::Event_Lowered ) EVENT ( EV_Hand_Raise, hhHand::Event_Raise ) END_CLASS /* ============ hhHand::~hhHand ============ */ hhHand::~hhHand() { bool shouldWarn = (gameLocal.GameState() != GAMESTATE_SHUTDOWN && !gameLocal.isClient); //rww - don't warn about this on client, since snapshot entities are free to be removed //at any time. // Check if the hand deleted is still in the hands list if ( gameLocal.hands.Find( this ) ) { if ( shouldWarn ) { gameLocal.Warning( "The hand (%p/%s) was deleted before being removed", this, spawnArgs.GetString( "classname" ) ); } gameLocal.hands.Remove( this ); } if ( owner != NULL ) { if ( shouldWarn ) { gameLocal.Warning( "A hand (%p) was deleted while having an owner", this ); } } StopSound( SND_CHANNEL_ANY ); } /* ============ hhHand::Spawn ============ */ void hhHand::Spawn( void ) { gameLocal.hands.Append( idEntityPtr (this) ); owner = NULL; previousHand = NULL; renderEntity.weaponDepthHack = true; animEvent = NULL; priority = spawnArgs.GetInt("priority"); status = HS_UNKNOWN; idealState = HS_READY; animBlendFrames = 4; // needs blending animDoneTime = 0; attached = false; attachTime = -1; spawnArgs.GetBool( "replace_previous", "0", replacePrevious ); spawnArgs.GetBool( "lower_weapon", "1", lowerWeapon ); spawnArgs.GetBool( "aside_weapon", "1", asideWeapon ); handedness = spawnArgs.GetInt( "handedness", "2" ); physicsObj.SetSelf( this ); physicsObj.SetClipModel( NULL, 1.0f ); physicsObj.SetOrigin( GetOrigin() ); physicsObj.SetAxis( GetAxis() ); SetPhysics( &physicsObj ); // Lower overrides aside if ( lowerWeapon && asideWeapon ) { asideWeapon = false; } Hide(); } void hhHand::Save(idSaveGame *savefile) const { //savefile->WriteObject( owner ); owner.Save(savefile); savefile->WriteInt( priority ); savefile->WriteObject( previousHand ); savefile->WriteBool( lowerWeapon ); savefile->WriteBool( asideWeapon ); savefile->WriteBool( attached ); savefile->WriteBool( replacePrevious ); savefile->WriteInt( attachTime ); savefile->WriteInt( handedness ); savefile->WriteInt( animDoneTime ); savefile->WriteInt( animBlendFrames ); savefile->WriteInt( status ); savefile->WriteInt( idealState ); savefile->WriteEventDef(animEvent); savefile->WriteStaticObject( physicsObj ); } void hhHand::Restore( idRestoreGame *savefile ) { //savefile->ReadObject( reinterpret_cast(owner) ); owner.Restore(savefile); savefile->ReadInt( priority ); savefile->ReadObject( reinterpret_cast(previousHand) ); savefile->ReadBool( lowerWeapon ); savefile->ReadBool( asideWeapon ); savefile->ReadBool( attached ); savefile->ReadBool( replacePrevious ); savefile->ReadInt( attachTime ); savefile->ReadInt( handedness ); savefile->ReadInt( animDoneTime ); savefile->ReadInt( animBlendFrames ); savefile->ReadInt( status ); savefile->ReadInt( idealState ); savefile->ReadEventDef(animEvent); savefile->ReadStaticObject( physicsObj ); RestorePhysics( &physicsObj ); } //rww - network code void hhHand::WriteToSnapshot( idBitMsgDelta &msg ) const { //physicsObj.WriteToSnapshot(msg); //rww - trying to keep this all local WriteBindToSnapshot(msg); msg.WriteBits(owner.GetSpawnId(), 32); if (previousHand) { msg.WriteBits(gameLocal.GetSpawnId(previousHand), 32); } else { msg.WriteBits(0, 32); } msg.WriteBits(attached, 1); msg.WriteBits(replacePrevious, 1); //msg.WriteBits(attachTime, 32); //msg.WriteBits(animDoneTime, 32); //msg.WriteBits(animBlendFrames, 32); assert(idealState < (1<<4)); msg.WriteBits(idealState, 4); assert(status < (1<<4)); msg.WriteBits(status, 4); msg.WriteBits(IsHidden(), 1); assert(gameLocal.hands.Num() < (1<<4)); msg.WriteBits(gameLocal.hands.Num(), 4); int i = 0; while (i < gameLocal.hands.Num()) { hhHand *hand = gameLocal.hands[i].GetEntity(); if (hand) { msg.WriteBits(gameLocal.GetSpawnId(hand), 32); } else { msg.WriteBits(0, 32); } i++; } msg.WriteBits(renderEntity.allowSurfaceInViewID, GENTITYNUM_BITS); idAnimatedEntity::WriteToSnapshot(msg); } void hhHand::ReadFromSnapshot( const idBitMsgDelta &msg ) { //physicsObj.ReadFromSnapshot(msg); //rww - trying to keep this all local ReadBindFromSnapshot(msg); if (owner.SetSpawnId(msg.ReadBits(32))) { physicsObj.SetSelfOwner(owner.GetEntity()); if (owner.IsValid() && owner.GetEntity()) { DoHandAttach(owner.GetEntity()); } else { owner = NULL; } } //Show(); idEntityPtr newPrevHand; int prevHandSpawnId = msg.ReadBits(32); if (newPrevHand.SetSpawnId(prevHandSpawnId)) { previousHand = newPrevHand.GetEntity(); } else { previousHand = NULL; } attached = !!msg.ReadBits(1); replacePrevious = !!msg.ReadBits(1); //attachTime = msg.ReadBits(32); //animDoneTime = msg.ReadBits(32); //animBlendFrames = msg.ReadBits(32); idealState = msg.ReadBits(4); int nextStatus = msg.ReadBits(4); if (status != nextStatus) { if (nextStatus == HS_LOWERING || nextStatus == HS_LOWERED) { PutAway(); } else if (nextStatus == HS_RAISING) { Raise(); } else if (nextStatus == HS_READY) { //PlayAnim( -1, "idle" ); Ready(); } status = nextStatus; } bool hidden = !!msg.ReadBits(1); if (hidden != IsHidden()) { if (hidden) { Hide(); } else { Show(); } } int numHands = msg.ReadBits(4); gameLocal.hands.SetNum(numHands); int i = 0; while (i < numHands) { int handSpawnId = msg.ReadBits(32); gameLocal.hands[i].SetSpawnId( handSpawnId ); i++; } renderEntity.allowSurfaceInViewID = msg.ReadBits(GENTITYNUM_BITS); idAnimatedEntity::ReadFromSnapshot(msg); /* if (msg.HasChanged()) { Present(); } */ } void hhHand::ClientPredictionThink( void ) { BecomeActive(TH_THINK|TH_ANIMATE); UpdateVisuals(); idAnimatedEntity::ClientPredictionThink(); } bool hhHand::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) { switch (event) { case EVENT_REMOVEHAND: { RemoveHand(); return true; } default: { return hhAnimatedEntity::ClientReceiveEvent(event, time, msg); } } } /* ============ hhHand::Event_Ready ============ */ void hhHand::Event_Ready() { Show(); Ready(); } /* =========== hhHand::Event_Lowered =========== */ void hhHand::Event_Lowered() { status = HS_LOWERED; Hide(); } /* ============ hhHand::Event_Raise ============ */ void hhHand::Event_Raise() { Raise(); } /* ============ hhHand::Ready ============ */ void hhHand::Ready() { switch ( idealState ) { case HS_READY: CycleAnim(-1, "idle", 500); status = HS_READY; break; case HS_LOWERED: PutAway(); break; default: gameLocal.Warning( "hhHand::Ready: Unsupported ideal state: %d", idealState ); break; } } /* ============ hhHand::Raise ============ */ void hhHand::Raise() { Show(); if ( IsRaising() ) { // If already raising, return return; } status = HS_RAISING; PlayAnim( -1, "raise", &EV_Hand_Ready ); } /* ============ hhHand::PutAway ============ */ void hhHand::PutAway() { if ( IsLowering() ) { // already being put away, so don't play more than one put away animations return; } status = HS_LOWERING; PlayAnim( -1, "putaway", &EV_Hand_Lowered ); } /* =============== hhHand::CycleAnim =============== */ void hhHand::CycleAnim( int channel, const char *animname, int blendTime ) { if ( channel < 0 ) { channel = GetChannelForAnim( animname ); } int anim = GetAnimator()->GetAnim( animname ); if ( anim ) { GetAnimator()->CycleAnim( channel, anim, gameLocal.time, blendTime ); } } /* =============== hhHand::PlayAnim =============== */ void hhHand::PlayAnim( int channel, const char *animname, const idEventDef *event ) { int anim; // HUMANHEAD nla if ( animEvent ) { CancelEvents( animEvent ); } animEvent = event; if ( channel < 0 ) { channel = GetChannelForAnim( animname ); } // HUMANHEAD END anim = GetAnimator()->GetAnim( animname ); if ( anim ) { GetAnimator()->PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) ); animDoneTime = GetAnimator()->CurrentAnim( channel )->GetEndTime(); } else { // This is a valid case, some hands (mounted gun) don't animate GetAnimator()->Clear( channel, gameLocal.time, FRAME2MS( animBlendFrames ) ); animDoneTime = 0; } animBlendFrames = 4; // HUMANHEAD nla if ( animEvent ) { PostEventMS( animEvent, animDoneTime - gameLocal.time ); } // HUMANHEAD END } /* ============ hhHand::AttachHand Returns false if the hand can not be attached ============ */ bool hhHand::AttachHand( hhPlayer *player, bool attachNow ) { assert(player); if ( !CheckHandAttach( player ) ) { return( false ); } int loweringFinished = LowerHandOrWeapon( player ); SetOwner( player ); Bind( player, true ); if ( loweringFinished >= 0 ) { player->handNext = this; // If the attachment should wait, and we don't want to force it to happen now if ( loweringFinished - gameLocal.time > 0 && !attachNow ) { PostEventMS( &EV_Hand_DoAttach, loweringFinished - gameLocal.time, player ); } else { Event_DoHandAttach( player ); } attachTime = loweringFinished; } return( true ); } /* ============ hhHand::LowerHandOrWeapon Used when the hand adds itself. returns the time when the lowering will be done will be -1 if the hand has already been attached. ============ */ int hhHand::LowerHandOrWeapon( hhPlayer *player ) { int animDone = gameLocal.time; int animDone2; // If there is a 'next' hand, we can just replace it, as it is already playing the down anim for the hand if ( player->handNext.IsValid() ) { animDone = player->handNext->GetAttachTime(); gameLocal.hands.Remove( player->handNext.GetEntity() ); // Clear the old hand of any pending events, and then remove it player->handNext->SetOwner( NULL ); player->handNext->CancelEvents( NULL ); player->handNext->ProcessEvent( &EV_Remove ); player->handNext = NULL; } // Else, If there is just a hand, play the down anim on it or replace it else if ( player->hand.IsValid() ) { //! Work out this logic more fully if ( replacePrevious ) { ReplaceHand( player ); animDone = -1; } // If it isn't on it's way down, move it down if ( !player->hand->IsLowered() ) { player->hand->PutAway(); animDone = player->hand->GetAnimDoneTime(); } } animDone2 = HandleWeapon( player, this, animDone - gameLocal.time ); if ( ( animDone >= 0 ) && ( animDone2 > animDone ) ) { animDone = animDone2; } // If there is a weapon play the down anim on it /* if ( player->hhweapon ) { if ( lowerWeapon && !player->hhweapon->IsLowered() ) { player->hhweapon->PutAway(); animDone2 = player->hhweapon->GetAnimDoneTime(); if ( animDone2 > animDone ) { animDone = animDone2; } } else if ( asideWeapon && !player->hhweapon->IsAside() ) { player->hhweapon->PutAside(); // When aside, we want to have it play right away, so dont' change animDone } // gameLocal.Printf("Scheduled down for %.2f\n", animDone); } */ return( animDone ); } /* ============== RaiseHandOrWeapon Assumes the hand is all the way down ============== */ void hhHand::RaiseHandOrWeapon( hhPlayer *player ) { hhHand *theHand = NULL; int raiseDelay = 0; // Get the next hand theHand = this->GetPreviousHand( player ); raiseDelay = HandleWeapon( player, theHand ); if ( theHand && ( !theHand->IsRaising() || !theHand->IsReady() ) ) { theHand->PostEventMS( &EV_Hand_Raise, raiseDelay - gameLocal.time ); } } /* ================ hhHand::Reraise Raises the hand again. ================ */ void hhHand::Reraise( ) { hhPlayer *thePlayer = NULL; int raiseDelay = 0; CancelEvents( &EV_Remove ); CancelEvents( &EV_Hand_Raise ); if ( owner.IsValid() && owner.GetEntity() && owner->IsType( hhPlayer::Type ) ) { thePlayer = ( hhPlayer * ) owner.GetEntity(); } else { gameLocal.Printf( "hhHand::Reraise: Warning tried to reraise when not on an hhPlayer (%p)\n", owner ); return; } raiseDelay = HandleWeapon( thePlayer, this ); if ( !IsRaising() || !IsReady() ) { PostEventMS( &EV_Hand_Raise, raiseDelay - gameLocal.time ); } } /* =========== hhHand::GetPreviousHand =========== */ hhHand *hhHand::GetPreviousHand( hhPlayer *player ) { hhHand *prevHand = NULL; // If there is no player, just give them our previous hand if ( player == NULL ) { return( previousHand ); } // If we are the next hand, up the current hand if ( player->handNext == this ) { prevHand = player->hand.GetEntity(); } // Else If we are the current hand else if ( player->hand == this ) { // If we have a previous hand, raise it if it isn't already up or being raised if ( previousHand ) { prevHand = previousHand; } } // Else we are neither, throw a warning. How are we here? else { gameLocal.Warning( "ERROR GetPreviousHand: ERROR: Tried to get a previous hand when we (%p) are not on the player (%p/%p)", this, player->handNext, player->hand ); } return( prevHand ); } /* ============ hhHand::HandleWeapon Set the weapon in the proper place for the hand Assumes the hand in question will be raised. Returns time (MS) to wait until the hand can be raised. Only in the case of the weapon being lowered does it really matter/ > 0 ============ */ int hhHand::HandleWeapon( hhPlayer *player, hhHand *hand, int weaponDelay, bool doNow ) { int raiseHandDelay = gameLocal.time; bool weaponReady = ( hand == NULL ) && !player->ChangingWeapons(); // If we don't have a weapon, then just return if ( !player->weapon.IsValid() || player->GetIdealWeapon() == 0 ) { return( 0 ); } // If we have a hand to compare to if ( hand ) { // If the hand wants the weapon lowered if ( hand->lowerWeapon || ( hand->handedness & player->weapon->GetHandedness() ) ) { player->weapon->PutAway(); if ( doNow ) { player->weapon->SetState( "Down", 0 ); } else { // Foce the gun to go, so we can know when it'll be done playing the anim. player->weapon->UpdateScript(); raiseHandDelay = player->weapon->GetAnimDoneTime(); } /* // If it isn't lowered, lower it if ( !player->weapon->IsLowered() ) { player->weapon->PutAway(); raiseHandDelay = player->weapon->GetAnimDoneTime(); } */ } //. lower the weapon // If the hand wants the weapon to the side else if ( hand->asideWeapon ) { player->weapon->PutAside(); if ( doNow ) { player->weapon->SetState( "Aside", 0 ); } // We only want a delay if the weapon should pop up? else if ( player->weapon->IsLowered() ) { // Foce the gun to go, so we can know when it'll be done playing the anim. player->weapon->UpdateScript(); raiseHandDelay = player->weapon->GetAnimDoneTime(); player->weapon->SetState( "PutAside", 4 ); } /* // If it is lowered, if ( player->weapon->IsLowered() ) { // First raise it, then set it aside int done = weaponDelay; idAnim *anim = player->weapon->GetAnimator()->GetAnim( "raise" ); // UGLY Hack, as hardcoded to the name player->weapon->PostEventMS( &EV_Weapon_WeaponRising, weaponDelay ); if ( anim ) { done = anim->Length(); } player->weapon->PostEventMS( &EV_Weapon_Aside, done ); } // Else if it is ready/upright and not aside else if ( !player->weapon->IsAside() ) { // Just set it aside player->weapon->PutAside(); } */ } // Weapon should be in ready mode else { weaponReady = true; } } // Valid hand // If we don't have a hand, so just make sure the weapon is raised if ( weaponReady ) { player->weapon->Raise(); if ( doNow ) { player->weapon->SetState( "Idle", 0 ); } /* // Clear any Aside events that may be posted. Happens when it was lowered, and the previous hand wanted aside player->weapon->CancelEvents( &EV_Weapon_Aside ); // If the weapon is aside, put it upright if ( player->weapon->IsAside() ) { player->weapon->PutUpright(); } // Else if it is lowering, raise it else if ( !player->weapon->IsRising() && player->weapon->IsLowered() ) { player->weapon->Raise(); } // Else if we aren't ready, how can we not be??!? else if ( player->weapon->IsLowered() || player->weapon->IsLowered() ) { gameLocal.Warning( "hhHand::HandleWeapon ERROR: Have a weapon that is lowered/lowering when shouldn't be!" ); } */ } return( raiseHandDelay ); } /* ============ hhHand::RemoveHand Plays the animations and schedules pointer changes ============ */ bool hhHand::RemoveHand( void ) { hhPlayer *player; int animDone = gameLocal.time; if ( owner.IsValid() && owner.GetEntity() ) { if ( owner->IsType( hhPlayer::Type ) ) { player = static_cast( owner.GetEntity() ); } else { gameLocal.Warning( "ERROR: RemoveHand: Tried to remove from a non player" ); return( false ); } } else { gameLocal.Warning( "ERROR: RemoveHand: Tried to remove with no owner!!" ); return( false ); } if ( !CheckHandRemove( player ) ) { return( false ); } // Unaside is asap if we asided it, and the prev hand isn't gonna want it asided // NLA - If we want to upright the GUI hhHand *prevHand = GetPreviousHand( player ); if ( ( !prevHand || !prevHand->asideWeapon ) && ( asideWeapon && player->weapon.IsValid() && player->weapon->IsAside() ) ){ player->weapon->PutUpright(); } CancelEvents( &EV_Hand_DoRemove ); // Play down anim if( !IsLowering() ) { PutAway(); animDone = GetAnimDoneTime(); PostEventMS( &EV_Hand_DoRemove, animDone - gameLocal.time ); } else if ( IsLowered() ) { // If already down, just remove it now Event_DoHandRemove(); } return( true ); } /* ============ hhHand::SetOwner ============ */ void hhHand::SetOwner( idActor *owner ) { this->owner = owner; if (owner) { if( GetPhysics() && GetPhysics()->IsType(hhPhysics_StaticWeapon::Type) ) { static_cast(GetPhysics())->SetSelfOwner( owner ); } // only show the surface in player view renderEntity.allowSurfaceInViewID = owner->entityNumber+1; } } /* ============ hhHand::ReplaceHand ============ */ void hhHand::ReplaceHand( hhPlayer *player) { //int animDone; hhHand *hand = NULL; //! What to do if hand is null? if( !player || !player->hand.IsValid() ) { return; } hand = player->hand.GetEntity(); // Copy over any key info needed this->previousHand = hand->previousHand; HandleWeapon( player, this ); player->hand = this; hand->attached = false; hand->owner = NULL; hand->Hide(); hand->PostEventMS( &EV_Remove, 0 ); gameLocal.hands.Remove( hand ); attached = true; Show(); Raise(); } /* ============ hhHand::CheckHandAttach ============ */ bool hhHand::CheckHandAttach( hhPlayer *owner ) { bool debug = 0; if ( attached ) { gameLocal.Warning( "Tried to attach an already attached hand." ); return( false ); } if( !owner ) { if ( debug ) { gameLocal.Printf( "Out because of owner\n" ); } return( false ); } // More important hand there. Abort if ( owner->hand.IsValid() && ( GetPriority() < owner->hand->GetPriority() ) ) { if ( debug ) { gameLocal.Printf( "Out because of hand priority\n" ); } return( false ); } // More important hand about to be there. Abort if ( owner->handNext.IsValid() && ( GetPriority() < owner->handNext->GetPriority() ) ) { if ( debug ) { gameLocal.Printf( "Out because of next hand priority\n" ); } return( false ); } //? What do to if both equal priority? if ( owner->hand.IsValid() && ( GetPriority() == owner->hand->GetPriority() ) ) { // Same hand, abort! if ( ( owner->hand->spawnArgs.GetString( "classname" ) == spawnArgs.GetString( "classname" ) ) && ( owner->hand != this ) ) { if ( debug ) { gameLocal.Printf( "Out because of same hand\n" ); } return( false ); } } //? What do to if both equal priority? if ( owner->handNext.IsValid() && ( GetPriority() == owner->handNext->GetPriority() ) ) { // Same hand, abort! hhHand* currentHand = owner->handNext.GetEntity(); if ( ( owner->handNext->spawnArgs.GetString( "classname" ) == spawnArgs.GetString( "classname" ) ) && ( owner->handNext != this ) ) { if ( debug ) { gameLocal.Printf( "Out because of same next hand\n" ); } return( false ); } } return( true ); } /* ============ hhHand::CheckHandRemove ============ */ bool hhHand::CheckHandRemove( hhPlayer *player ) { // Check have a valid player if ( player == NULL ) { gameLocal.Warning( "ERROR: DoHandRemove: We are not attached to a player!"); return( false ); } // We aren't here! if ( IsAttached() && ( player->hand != this ) ) { gameLocal.Warning( "ERROR: DoHandRemove: Tried to detach from an player we are not assigned to %d %p %p", (int) IsAttached(), player->hand, this ); return( false ); } return( true ); } /* ============ hhHand::SetModel ============ */ void hhHand::SetModel( const char *modelname ) { hhAnimatedEntity::SetModel( modelname ); } /* ============ hhHand::Present ============ */ void hhHand::Present() { hhAnimatedEntity::Present(); } /* ============ hhHand::Event_DoHandAttach ============ */ void hhHand::Event_DoHandAttach( idEntity *owner ) { DoHandAttach( owner ); } /* ============ hhHand::DoHandAttach ============ */ bool hhHand::DoHandAttach( idEntity *owner ) { hhPlayer *player; if ( owner && owner->IsType( hhPlayer::Type ) ) { player = static_cast( owner ); } else { gameLocal.Warning("ERROR: Tried to attach to a non-player"); return( false ); } if ( !CheckHandAttach( player ) ) { PostEventMS( &EV_Remove, 0 ); return( false ); } // Sanity check, we should be the top dogh if ( player->handNext != this && !gameLocal.isClient ) { //rww - don't care on client, because we cannot rely on handNext gameLocal.Warning( "ERROR: We (%p) should be the next hand (%p) but aren't", this, player->handNext ); } // Make sure nothing has changed HandleWeapon( player, this ); // We are top dog, replace! :) if ( !replacePrevious ) { previousHand = player->hand.GetEntity(); } else { previousHand = NULL; } player->hand = this; player->handNext = NULL; attached = true; attachTime = -1; Raise(); Show(); return( true ); } /* ============ hhHand::Event_DoHandRemove ============ */ void hhHand::Event_DoHandRemove( void ) { DoHandRemove( ); } /* ============ hhHand::ForceRemove - Should only be called when you don't need other hands/weaspons adjusted ============ */ void hhHand::ForceRemove( void ) { gameLocal.hands.Remove( this ); owner = NULL; Hide(); } /* ============ hhHand::DoHandRemove ============ */ bool hhHand::DoHandRemove( void ) { hhPlayer *player; gameLocal.hands.Remove( this ); if ( owner.IsValid() && owner.GetEntity() && owner->IsType( hhPlayer::Type ) ) { player = static_cast( owner.GetEntity() ); } else { gameLocal.Warning( "ERROR: DoHandRemove: Tried to remove from a non player" ); return( false ); } // Play up anim RaiseHandOrWeapon( player ); if ( !CheckHandRemove( player ) ) { return( false ); } // Do the actual removal logic Hide(); PostEventMS( &EV_Remove, 0 ); if ( IsAttached() ) { player->hand = previousHand; previousHand = NULL; attached = false; } // If we were the next hand, clear it if ( player->handNext == this ) { player->handNext = NULL; } owner = NULL; return( true ); } /* ============ hhHand::Event_RemoveHand Plays the animations and schedules pointer changes ============ */ void hhHand::Event_RemoveHand( void ) { RemoveHand(); } /* =========== hhHand::AddHand =========== */ hhHand *hhHand::AddHand( hhPlayer *player, const char *handclass, bool attachNow ) { idDict args; hhHand *hand = NULL; args.SetVector( "origin", player->GetEyePosition() ); args.SetMatrix( "rotation", player->viewAngles.ToMat3() ); hand = static_cast< hhHand * >( gameLocal.SpawnObject( handclass, &args) ); if ( hand ) { hand->SetOwner( player ); // We have a prob if you can't attach. Should return NULL if ( !hand->AttachHand( player, attachNow ) ) { hand->SetOwner( NULL ); gameLocal.hands.Remove( hand ); hand->PostEventMS( &EV_Remove, 0 ); return( NULL ); } } return( hand ); } /* ============ hhHand::PrintHandInfo ============ */ void hhHand::PrintHandInfo( idPlayer *player ) { hhPlayer *hhplayer = NULL; hhHand *hand = NULL; int count = 0; idList< idEntityPtr< hhHand > > orphaned; if ( player->IsType( hhPlayer::Type ) ) { hhplayer = (hhPlayer *) player; } else { return; } orphaned = gameLocal.hands; gameLocal.Printf( "Weapon: %p Class: %s State: %d\n", player->weapon, (const char *) player->weapon->GetDict()->GetString("classname"), (int) player->weapon->GetStatus() ); hand = hhplayer->hand.GetEntity(); while ( hand != NULL ) { count++; gameLocal.Printf( "Hand %d: %p Class: %s State: %d\n", count, hand, hand->spawnArgs.GetString("classname"), (int) hand->GetStatus() ); orphaned.Remove( hand ); hand = hand->previousHand; } count = 0; for ( int i = 0; i < orphaned.Num(); ++i ){ count++; hand = orphaned[ i ].GetEntity(); gameLocal.Printf( "Orphaned Hand %d: %p Class: %s State: %d\n", count, hand, hand->spawnArgs.GetString("classname"), (int) hand->GetStatus() ); } } /* ================ hhHand::GetMasterDefaultPosition ================ */ void hhHand::GetMasterDefaultPosition( idVec3 &masterOrigin, idMat3 &masterAxis ) const { idActor* actor = NULL; idEntity* master = GetBindMaster(); if( master ) { if( master->IsType(idActor::Type) ) { actor = static_cast( master ); actor->DetermineOwnerPosition( masterOrigin, masterAxis ); masterOrigin = actor->ApplyLandDeflect( masterOrigin, 1.1f ); } else { hhAnimatedEntity::GetMasterDefaultPosition( masterOrigin, masterAxis ); } } }