#include "precompiled.h" #pragma hdrstop #include "prey_local.h" //========================================================================== // // hhBindController // // These can be bound to anything. They can be used to attach a player, monster, almost any entity // and take control of their movement through space. Players are optionally allowed to look around // while bound. This class was necessary to abstract all the physics differences away from the // objects doing the binding. //========================================================================== const idEventDef EV_BindAttach("bindattach", "ed"); const idEventDef EV_BindDetach("binddrop", NULL); const idEventDef EV_BindAttachBody("bindattachbody", "edd"); CLASS_DECLARATION(idEntity, hhBindController) EVENT( EV_BindAttach, hhBindController::Event_Attach ) EVENT( EV_BindDetach, hhBindController::Event_Detach ) EVENT( EV_BindAttachBody, hhBindController::Event_AttachBody ) END_CLASS void hhBindController::Spawn() { boundRider = NULL; hand = NULL; yawLimit = 180.0f; // no limit float tension = spawnArgs.GetFloat("tension", "0.01"); const char *anim = spawnArgs.GetString("boundanim"); const char *hand = spawnArgs.GetString("def_hand"); float yaw = spawnArgs.GetFloat("yawlimit", "180"); float pitch = spawnArgs.GetFloat("pitchlimit", "75"); bOrientPlayer = spawnArgs.GetBool("orientplayer"); bLooseBind = false; SetRiderParameters(anim, hand, yaw, pitch); SetTension(tension); hangID = 0; } hhBindController::~hhBindController() { Detach(); } void hhBindController::Save(idSaveGame *savefile) const { boundRider.Save(savefile); savefile->WriteObject( hand ); savefile->WriteBool( bLooseBind ); savefile->WriteBool( bOrientPlayer ); savefile->WriteStaticObject( force ); savefile->WriteString( animationName ); savefile->WriteString( handName ); savefile->WriteFloat( yawLimit ); savefile->WriteFloat(pitchLimit); savefile->WriteInt( hangID ); } void hhBindController::Restore( idRestoreGame *savefile ) { boundRider.Restore(savefile); savefile->ReadObject( reinterpret_cast(hand) ); savefile->ReadBool( bLooseBind ); savefile->ReadBool( bOrientPlayer ); savefile->ReadStaticObject( force ); savefile->ReadString( animationName ); savefile->ReadString( handName ); savefile->ReadFloat( yawLimit ); savefile->ReadFloat(pitchLimit); savefile->ReadInt( hangID ); } idEntity *hhBindController::GetRider() const { // Since the entity could be deleted at any time, we get the rider from the force (if used) if (bLooseBind) { //TODO: Make this force use a safe entityptr too return force.GetEntity(); } return boundRider.GetEntity(); } void hhBindController::SetTension(float tension) { force.SetRestoreTime((1.0f - tension)*5.0f); force.SetRestoreFactor(tension * g_springConstant.GetFloat()); } void hhBindController::SetSlack(float slack) { force.SetRestoreSlack(slack); } void hhBindController::Think() { idEntity::Think(); //TODO: This needed? if (bLooseBind && GetRider()) { idVec3 loc = GetOrigin(); force.SetTarget(loc); //TODO: Cull hangID out of this class if not needed. // FIXME: Why is this needed? (Took out --pdm, test crane/rail before removing) /* idEntity *rider = GetRider(); if (rider->IsType(idAFEntity_Base::Type)) { // Update the offset depending on if actor is ragdoll idAFEntity_Base *af = static_cast(rider); if (af->IsActiveAF()) { force.SetEntity(rider, hangID, vec3_origin); } else { // non-ragdolled rider if (rider->IsType(idActor::Type)) { force.SetEntity(rider, 0, af->EyeOffset()); } else { force.SetEntity(rider, 0, rider->GetOrigin()); } } } */ force.Evaluate(gameLocal.time); } else if (GetRider() && GetRider()->IsType(hhPlayer::Type) && OrientPlayer()) { // This to insure that oriented players don't get out of sync hhPlayer *player = static_cast(GetRider()); idAngles angles = player->GetUntransformedViewAngles(); player->SetOrientation(GetOrigin(), GetAxis(), angles.ToMat3()[0], angles); } } // Create our invisible hand to get rid of the weapon void hhBindController::CreateHand(hhPlayer *player) { if (gameLocal.isClient) { //rww return; } if (handName.Length()) { ReleaseHand(); hand = hhHand::AddHand( player, handName.c_str() ); } } void hhBindController::ReleaseHand() { if (hand) { hand->RemoveHand(); hand = NULL; } } void hhBindController::SetRiderParameters(const char *animname, const char *handname, float yaw, float pitch) { animationName = animname; handName = handname; yawLimit = yaw; pitchLimit = pitch; } bool hhBindController::ValidRider(idEntity *ent) const { return (ent != NULL); } void hhBindController::Attach(idEntity *ent, bool loose, int bodyID, idVec3 &point) { hhPlayer *player = ent->IsType(hhPlayer::Type) ? static_cast(ent) : NULL; if (GetRider()==NULL && ValidRider(ent)) { bLooseBind = loose; if (bLooseBind) { idVec3 loc = GetOrigin(); force.SetEntity(ent, bodyID, point); force.SetTarget(loc); if (ent->spawnArgs.GetBool("stable_tractor", "0")) { force.SetAxisEntity(this); } else { force.SetAxisEntity(NULL); } BecomeActive(TH_THINK); } else { boundRider = ent; boundRider->SetOrigin(GetOrigin()); if (player && bOrientPlayer) { idAngles angles = player->GetUntransformedViewAngles(); player->SetOrientation(GetOrigin(), GetAxis(), angles.ToMat3()[0], angles); } else { boundRider->SetAxis(GetAxis()); } boundRider->Bind(this, false); } // Apply player parameters if (player && !gameLocal.isMultiplayer) { //rww - in mp, don't want to restrict players when bound player->maxRelativeYaw = yawLimit; player->maxRelativePitch = pitchLimit; player->bClampYaw = yawLimit < 180.0f; CreateHand(player); // player->hhweapon->Hide(); } // Play animation on actors (but not players in mp -rww) if (ent->IsType(idActor::Type) && !ent->fl.tooHeavyForTractor && (!gameLocal.isMultiplayer || !ent->IsType(hhPlayer::Type))) { idActor *actor = static_cast(ent); actor->AI_BOUND = true; if ( actor->GetHealth() > 0 ) { actor->ProcessEvent(&AI_SetAnimPrefix, animationName.c_str()); actor->SetAnimState( ANIMCHANNEL_LEGS, "Legs_Bound", 4 ); actor->SetAnimState( ANIMCHANNEL_TORSO, "Torso_Bound", 4 ); } actor->BecameBound(this); } hangID = 0; if (ent->IsType(idAFEntity_Base::Type)) { hangID = static_cast(ent)->spawnArgs.GetInt("hangID"); } } } void hhBindController::Detach() { idEntity *rider = GetRider(); hhPlayer *player = rider && rider->IsType(hhPlayer::Type) ? static_cast(rider) : NULL; if (rider) { if (player) { player->bClampYaw = false; if (gameLocal.isMultiplayer) { //do not fiddle with the player's anims in MP when he is grabbed, let him act normally // Release player animation player->AI_BOUND = false; player->SetAnimState( ANIMCHANNEL_TORSO, "Torso_Idle", 4 ); player->SetAnimState( ANIMCHANNEL_LEGS, "Legs_Idle", 4 ); } ReleaseHand(); } if (rider->IsType(idActor::Type)) { idActor *actor = static_cast(rider); actor->AI_BOUND = false; actor->BecameUnbound(this); } if (bLooseBind) { force.SetEntity(NULL); } else { rider->Unbind(); rider->SetAxis(mat3_identity); if (player && bOrientPlayer) { // Set everything back to normal at current view angles idAngles angles = player->GetUntransformedViewAngles(); player->SetOrientation(GetOrigin(), mat3_identity, angles.ToMat3()[0], angles); } boundRider = NULL; } } } idVec3 hhBindController::GetRiderBindPoint() const { idEntity* rider = GetRider(); if( !rider ) { return vec3_origin; } if( rider->IsType(idActor::Type) ) { idActor *actor = static_cast( rider ); if( actor->IsActiveAF() ) { return actor->GetOrigin( GetHangID() ); } else { return actor->GetEyePosition(); } } return rider->GetOrigin(); } void hhBindController::Event_Attach(idEntity *rider, bool loose) { Attach(rider, loose); } void hhBindController::Event_AttachBody(idEntity *rider, bool loose, int bodyID) { Attach(rider, loose, bodyID); } void hhBindController::Event_Detach() { Detach(); }