Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include "TulpaPlayer.h"
- #include "TulpaGame.h"
- #include "TulpaConstants.h"
- #include "NControllerComponent.h"
- #include "NPostureComponent.h"
- #include "EyeHeightComponent.h"
- #include "FocusComponent.h"
- #include "SprintComponent.h"
- #include "NCameraComponent.h"
- #include "NDebugLines.h"
- #include "NarrationComponent.h"
- #include "EditModeComponent.h"
- #include "ChimeComponent.h"
- #include "Door.h"
- #include "NLocMgr.h"
- #include "CamTrackComponent.h"
- #include "Trigger.h"
- #include "TwoTrigger.h"
- #include "CodexComponent.h"
- TulpaPlayer::TulpaPlayer(EEnforcement) : Super(EE_Valid)
- {
- bCanPlayMantleFoley = true;
- }
- TulpaPlayer::~TulpaPlayer()
- {
- }
- bool TulpaPlayer::IsIndisposed()
- {
- return (IsIntroHappening() ||
- NMenuMgr::GetInstance()->IsAnyMenuOpen());
- }
- bool TulpaPlayer::IsIntroHappening()
- {
- MAKESTRINGWITH(SubName1, "Game Intro (Chapter)");
- MAKESTRINGWITH(SubName2, "Game Intro (Fade)");
- MAKESTRINGWITH(SubName3, "Vinyl Scaling");
- return
- NSubroutineMgr::GetInstance()->IsSubroutineRunning(SubName1) ||
- NSubroutineMgr::GetInstance()->IsSubroutineRunning(SubName2)/* ||
- NSubroutineMgr::GetInstance()->IsSubroutineRunning(SubName3)*/;
- }
- void TulpaPlayer::ReactToSurfaceType(const NHashedString& Type, const NCollisionResult& ThisHitRes)
- {
- Type, ThisHitRes;
- }
- void TulpaPlayer::PostDefinition()
- {
- }
- void TulpaPlayer::PostBuildEntity()
- {
- MAKESTRING(DevMode);
- if (NConfigFile::GetInstance()->Access(gNullStr, DevMode))
- {
- ChimeComponent* Chime = GetComponent<ChimeComponent>();
- if (Chime)
- {
- for (int i = 0; i < CA_MAX; ++i)
- {
- Chime->AddChime((ChimeAbility)(i), true);
- }
- }
- CodexComponent* pCodex = GetComponent<CodexComponent>();
- if (pCodex)
- {
- pCodex->SetHasCodex(true);
- }
- }
- NSpatialComponent* Space = GetComponent<NSpatialComponent>();
- if (Space)
- {
- //Space->Game_Curr.Position = NVec3(0,0,4) * VOXEL_SCALE;
- //Space->Init();
- }
- TrySetPropsByPosture(EP_Standing);
- EyeHeightComponent* Eye = GetComponent<EyeHeightComponent>();
- FocusComponent* Focus = GetComponent<FocusComponent>();
- SprintComponent* Sprint = GetComponent<SprintComponent>();
- MAKESTRING(CrouchSpeed);
- MAKESTRING(FocusSpeed);
- MAKESTRING(SprintSpeed);
- if (Eye && Focus && Sprint)
- {
- Eye->SetSmoothingSpeed(NConfigFile::GetInstance()->Access(gNullStr, CrouchSpeed));
- Focus->SetSmoothingSpeed(NConfigFile::GetInstance()->Access(gNullStr, FocusSpeed));
- Sprint->SetSmoothingSpeed(NConfigFile::GetInstance()->Access(gNullStr, SprintSpeed));
- MAKESTRING(EyeHeight);
- Eye->SetTargetEyeHeight(NConfigFile::GetInstance()->Access(gNullStr, EyeHeight));
- Focus->SetTargetFocus(1.0f, true);
- Sprint->SetTargetFocus(1.0f, true);
- }
- NPhysicsComponent* Phys = GetComponent<NPhysicsComponent>();
- if (Phys)
- {
- Phys->OnJump += OnJump;
- Phys->OnLand += OnLand;
- Phys->OnSlide += OnSlide;
- Phys->OnUnground += OnUnground;
- Phys->OnHitWall += OnHitWall;
- Phys->OnWallHug += OnWallHug;
- Phys->CanWallHugCallback += CanWallHug;
- Phys->CanDownJumpCallback += CanDownJump;
- Phys->PreserveGroundSpeedCallback += PreserveGroundSpeed;
- Phys->IgnoreShallowLedgesCallback += IgnoreShallowLedges;
- // Oh heeeeyyyyy,
- Phys->SetStickyWalking(true);
- Phys->SetStickyWalkingScalar(FEET);//100.0f * INCHES);
- }
- if (Space)
- {
- Space->OnCollision += OnCollision;
- }
- NBasingComponent* Base = GetComponent<NBasingComponent>();
- if (Base)
- {
- Base->OnBaseMovedCallback += OnBaseMoved;
- }
- NCollisionComponent* Coll = GetComponent<NCollisionComponent>();
- if (Coll)
- {
- Coll->dOnAnyHit += OnAnyHit;
- Coll->dOnActivatedQueuedPrimitive += OnActivatedQueuedPrimitive;
- }
- NDataComponent* Data = GetComponent<NDataComponent>();
- if (Data)
- {
- Data->dWriteCallback += OnDataWritten;
- }
- NDialogConsumerComponent* DialogCon = GetComponent<NDialogConsumerComponent>();
- if (DialogCon)
- {
- DialogCon->dDialogOpened += OnDialogOpened;
- DialogCon->dDialogClosed += OnDialogClosed;
- DialogCon->dModifyIncomingDialog += ModifyDialog;
- DialogCon->TULPAHACK_dSignalWantsToClose += HACK_SignalWantsToClose;
- }
- NCameraComponent* Camera = GetComponent<NCameraComponent>();
- if (Camera)
- {
- Camera->Camera = TulpaGame::GetInstance()->GetGameSceneCamera();
- }
- // I miiiiight want this to be shorter for general interacting-with-things purposes, buuuuut...
- // It was 20 (e.g., 16.667 feet) in Banon, which sounds...long, BUT, 5 voxels is 16 feet, so.
- GetComponent<NFrobComponent>()->TraceDistance = VOXEL_SCALE * 3.0f;//5.0f;// * 2.0f;//20.0f;
- GetComponent<NFrobComponent>()->PrimitiveName = NHashedString("Trace");
- //GetComponent<NFrobComponent>()->SetFilter(CFT_All/*CFT_Frobbable*/);
- GetComponent<NFrobComponent>()->dCanFrobCallback = CanFrob;
- GetComponent<NFrobComponent>()->dOnFoundFrobbable = OnFoundFrobbable;
- GetComponent<NFrobComponent>()->dFormatString = FormatFrobString;
- GetComponent<NFrobComponent>()->dPositionString = PositionFrobString;
- GetComponent<NFrobComponent>()->SetFilter(CFT_Frobbable);
- //// This can be moved to the definition, alongside the "Trace" primitive.
- //NCollisionPoint* FrobBounds = new NCollisionPoint(EE_Valid);
- //NCollisionSpace::GetInstance()->AddCollision(FrobBounds); // Required as it's no longer automatic in light of the list providers update.
- //GetComponent<NCollisionComponent>()->AddPrimitive(FrobBounds, NHashedString("Trace"));
- //FrobBounds->SetController(GetComponent<NSpatialComponent>()); // Must have a controller so it can early out of intersection tests against the player's collision box.
- //FrobBounds->SetCollisionFilterType(CFT_None);
- //GetComponent<NarrationComponent>()->PlayClip(NHashedString("Intro"));
- GetComponent<EditModeComponent>()->dOnEditModeToggled += OnEditModeToggled;
- }
- void TulpaPlayer::TryGround(float LiftDist, float DropDist)
- {
- LiftDist, DropDist;
- }
- void TulpaPlayer::OnInitialLevelLoad(float OverridePlayTime)
- {
- OverridePlayTime;
- }
- void TulpaPlayer::Tick_CalcUpdate(float DeltaTime, ETickGroup TickGroup)
- {
- DeltaTime, TickGroup;
- if (IS_TICK(TickGroup, ETG_Game))
- {
- // The TulpaPlayerControllerComponent now handles all input gathering, physics propagation, and state changes.
- }
- }
- void TulpaPlayer::Tick_CommitUpdate(float DeltaTime, ETickGroup TickGroup)
- {
- DeltaTime, TickGroup;
- if (IS_TICK(TickGroup, ETG_Game))
- {
- // Moved the commit phase of movement to the controller.
- }
- if (IS_TICK(TickGroup, ETG_Render))
- {
- // This is where frob, dialog, and popup stuff would go. (See Xanadu's PlayerBase Commit step.)
- NCamera* FPSCam = TulpaGame::GetInstance()->GetGameSceneCamera();
- NSpatialComponent* Space = GetComponent<NSpatialComponent>();
- NCollisionComponent* Coll = GetComponent<NCollisionComponent>();
- CamTrackComponent* CamTrack = GetComponent<CamTrackComponent>();
- if (FPSCam && Space && Coll)
- {
- NCollisionCapsule* Prim = Cast<NCollisionCapsule>(Coll->GetActivePrimitive());
- if (Prim)
- {
- EyeHeightComponent* Eye = GetComponent<EyeHeightComponent>();
- if (Eye)
- {
- // TODO: Include collision primitive's offset if I ever have one.
- float EyeOffset = Eye->GetCurrentEyeHeight() - (Prim->HalfHeight + Prim->Radius);
- float PrimOffsetCompensation = Dot(Prim->GetStandardOffset(), NPhysicsGlobals::GetWorldUp());
- EyeOffset += PrimOffsetCompensation;
- FPSCam->setPos(Space->Game_Curr.Position + (NPhysicsGlobals::GetWorldUp() * EyeOffset));
- }
- FocusComponent* Focus = GetComponent<FocusComponent>();
- SprintComponent* SprintComp = GetComponent<SprintComponent>();
- if (Focus && SprintComp)
- {
- MAKESTRING(CamFOV);
- float EffFov = NConfigFile::GetInstance()->Access(gNullStr, CamFOV);
- MAKESTRING(FocusScalar);
- float FocusRange = Focus->GetCurrentFocus();
- float SprintRange = SprintComp->GetCurrentFocus();
- EffFov = atan(tan(EffFov * DEG2RAD * 0.5f) * FocusRange * SprintRange) * 2.0f * RAD2DEG;
- FPSCam->setFOV(EffFov);
- }
- }
- if (CamTrack && CamTrack->IsPlaying())
- {
- CamTrack->Update(DeltaTime);
- CamTrack->ModifyCamera(FPSCam);
- }
- }
- NDialogConsumerComponent* Dialog = GetComponent<NDialogConsumerComponent>();
- // This depends on having a pixel-perfect font at the moment.
- // I need a better system for drawing HUD elements of arbitrary sizes.
- // I thought I had something like that, but maybe not?
- MAKESTRING(AimingDotOpacity);
- float DotOpac = NConfigFile::GetInstance()->Access(gNullStr, AimingDotOpacity);
- if (DotOpac >= (0.5f / 256.0f) &&
- (!Dialog || !Dialog->HasDialog()) &&
- !IsIntroHappening())
- {
- NFontParams Params;
- Params.Text = NString::ResolveMarkup("<img src=\"hud:empty\" />");
- Params.Pos = NVec2(HALF_BANON_SCENE_WIDTH - 2, HALF_BANON_SCENE_HEIGHT - 2);
- Params.Color[0] = NColor8(255,255,255, (uchar)Clamp(DotOpac * 256.0f, 0.0f, 255.0f));
- Params.Scale = 8.0f / TulpaGame::GetInstance()->TheGameFont->GetFontGlyphHeight();
- TulpaGame::GetInstance()->TheGameFont->DrawText(Params);
- }
- if (Dialog)
- {
- //DialogAnchor Anchor = DA_Top;
- // TODO: Always top-anchor the status screen?
- if (Resolve<NEntity>(Dialog->GetProvider()) != this)
- {
- if (GetComponent<NSpatialComponent>() && GetComponent<NCameraComponent>() && GetComponent<NCameraComponent>()->Camera)
- {
- NVec3 Diff = GetComponent<NSpatialComponent>()->Render_Curr.Position - GetComponent<NCameraComponent>()->Camera->getPos();
- if (Diff.y < -0.0f)
- {
- //Anchor = DA_Bottom;
- }
- }
- }
- Dialog->Tick();
- Dialog->Render(/*Anchor*/);
- }
- NFrobComponent* Frob = GetComponent<NFrobComponent>();
- if (Frob && (!Dialog || !Dialog->HasDialog()))
- {
- Frob->Render();
- }
- // This should maybe use engine time and not render time, so it continues in events in which
- // the game is paused but audio is still playing, as when dialog boxes are open.
- // This doesn't ever GET an engine tick though because it's a fixed game tickable.
- // Sooooo......
- NarrationComponent* Narr = GetComponent<NarrationComponent>();
- if (Narr)
- {
- Narr->Render(DeltaTime);
- }
- EditModeComponent* Edit = GetComponent<EditModeComponent>();
- if (Edit)
- {
- Edit->DrawDebugOutput();
- }
- NPopupComponent* Popup = GetComponent<NPopupComponent>();
- if (Popup)
- {
- // TODO: Add the position-based anchoring from Valk here? (See above, commented out dialog stuff.)
- Popup->Tick(DeltaTime);
- Popup->Render();
- }
- }
- }
- VOID_TYPE TulpaPlayer::OnJump(NEntity* OwnerEntity, int JumpNum)
- {
- OwnerEntity, JumpNum;
- return VOID_TYPE();
- }
- VOID_TYPE TulpaPlayer::OnLand(NEntity* OwnerEntity, const NCollisionResult& ThisHitRes, bool bEffectsReady)
- {
- OwnerEntity, ThisHitRes, bEffectsReady;
- NSpatialComponent* Space = OwnerEntity->GetComponent<NSpatialComponent>();
- NPhysicsComponent* Phys = OwnerEntity->GetComponent<NPhysicsComponent>();
- if (Space && Phys)
- {
- float PrevSpeed = Dot(Projection(Space->Game_Prev.Velocity, NPhysicsGlobals::GetWorldUp()), NPhysicsGlobals::GetWorldUp());
- float CurrSpeed = Dot(Projection(Space->Game_Curr.Velocity, NPhysicsGlobals::GetWorldUp()), NPhysicsGlobals::GetWorldUp());
- float TermVel = Phys->GetJumpSpeed();//Phys->GetTerminalVelocity();
- PrevSpeed, CurrSpeed, TermVel;
- ANCHOR;
- if (Abs(PrevSpeed) >= Abs(TermVel * 0.9f))
- {
- // We landed hard enough to warrant animating eye height. (This will happen on any jump!)
- EyeHeightComponent* EyeHeight = OwnerEntity->GetComponent<EyeHeightComponent>();
- if (EyeHeight)
- {
- // We want to preserve the downward speed and smoothly bring it back to the current/target.
- // A Bezier curve might work here.
- EyeHeight->QueueAnimation(PrevSpeed);
- }
- }
- }
- return VOID_TYPE();
- }
- VOID_TYPE TulpaPlayer::OnUnground(NEntity* OwnerEntity)
- {
- OwnerEntity;
- NPostureComponent* Posture = OwnerEntity->GetComponent<NPostureComponent>();
- if (Posture->IsInPosture(EP_Crouching))
- {
- // This works, but I'm gonna try commenting it out for Tulpa.
- //Cast<TulpaPlayer>(OwnerEntity)->TrySetPropsByPosture(EP_Standing);
- }
- Cast<TulpaPlayer>(OwnerEntity)->bCanPlayMantleFoley = true;
- return VOID_TYPE();
- }
- VOID_TYPE TulpaPlayer::OnWallHug(NEntity* OwnerEntity, const NCollisionResult& ThisHitRes)
- {
- OwnerEntity, ThisHitRes;
- return VOID_TYPE();
- }
- bool TulpaPlayer::CanWallHug(NEntity* OwnerEntity, const NCollisionResult& ThisHitRes)
- {
- OwnerEntity, ThisHitRes;
- return false;
- }
- bool TulpaPlayer::CanDownJump(NEntity* OwnerEntity, const NCollisionResult& ThisHitRes)
- {
- OwnerEntity, ThisHitRes;
- return false;
- }
- bool TulpaPlayer::PreserveGroundSpeed(NEntity* OwnerEntity)
- {
- OwnerEntity;
- return false;
- }
- bool TulpaPlayer::IgnoreShallowLedges(NEntity* OwnerEntity)
- {
- OwnerEntity;
- return false;
- }
- VOID_TYPE TulpaPlayer::OnHitWall(NEntity* OwnerEntity, const NCollisionResult& ThisHitRes)
- {
- OwnerEntity, ThisHitRes;
- TulpaWorld* AWorld = Resolve<TulpaWorld>(ThisHitRes.HitPrimitive);
- if (AWorld)
- {
- NPostureComponent* Posture = OwnerEntity->GetComponent<NPostureComponent>();
- NSpatialComponent* Space = OwnerEntity->GetComponent<NSpatialComponent>();
- NPhysicsComponent* Phys = OwnerEntity->GetComponent<NPhysicsComponent>();
- NCameraComponent* Cam = OwnerEntity->GetComponent<NCameraComponent>();
- TulpaPlayerControllerComponent* Controller = OwnerEntity->GetComponent<TulpaPlayerControllerComponent>();
- NCollisionComponent* Coll = OwnerEntity->GetComponent<NCollisionComponent>();
- if (Posture && Space && Phys && Cam && Cam->Camera && Controller && Coll)
- {
- // This is a little too aggressive.
- // We want to be able to sprint and slide along a wall.
- // Let's only do this when the wall normal strongly opposes our velocity (both taken in 2D, probably).
- NVec3 PrevVel = Space->Game_Prev.Velocity;
- NVec3 HitNorm = ThisHitRes.HitNormal;
- PrevVel = Normalize(PrevVel - Projection(PrevVel, NPhysicsGlobals::GetWorldUp()));
- HitNorm = Normalize(HitNorm - Projection(HitNorm, NPhysicsGlobals::GetWorldUp()));
- const float STOP_SPRINT_DOT = -0.866025f; // 30 degrees in either direction.
- float DotProduct = Dot(PrevVel, HitNorm);
- if (DotProduct <= STOP_SPRINT_DOT)
- {
- // Flag the controller so we can't start sprinting next tick (to prevent "bouncing" the FOV)...
- Controller->bSuppressSprintNextTick = true;
- // ... and kill the sprint if there was one.
- if (Posture->IsInPosture(EP_Sprinting))
- {
- Cast<TulpaPlayer>(OwnerEntity)->TrySetPropsByPosture(EP_Standing);
- }
- }
- // Let's try to mantle!!
- // Conditions for mantling:
- // 1. We're ungrounded.
- // 2. We're actively pushing against the wall.
- // 3. Our upward velocity is less than the mantling velocity?
- // 4. There is room to mantle to.
- ChimeComponent* Chime = OwnerEntity->GetComponent<ChimeComponent>();
- if (Chime && Chime->HasChime(CA_Mantle))
- {
- // Do we accelerate or alter velocity to mantle?
- MAKESTRING(Gameplay);
- MAKESTRING(Jump);
- if ((!Phys->IsGrounded() || Phys->IsSliding()) &&
- Controller->IsDown(Gameplay, Jump))
- {
- // TODO: Maybe request the actual move attempt from the PC?
- NVec3 MoveAttemptGuess = Space->Game_Prev.Velocity;
- MoveAttemptGuess -= Projection(MoveAttemptGuess, NPhysicsGlobals::GetWorldUp());
- MoveAttemptGuess = Normalize(MoveAttemptGuess);
- NVec3 Facing = Cam->Camera->getFwdFPS();
- NVec3 FlatNormal = ThisHitRes.HitNormal;
- FlatNormal -= Projection(FlatNormal, NPhysicsGlobals::GetWorldUp());
- FlatNormal = Normalize(FlatNormal);
- if (Dot(MoveAttemptGuess, FlatNormal) <= -0.866025f && // A little more lenient, can be within 60 degrees of opposing
- Dot(MoveAttemptGuess, Facing) >= 0.866025f) // A little less lenient, must be within 30 degrees of facing
- {
- MAKESTRING(DEBUG_NOTIFY_MANTLE);
- const char* DebugMantleString = "Mantling...";
- NBaseGame::GetInstance()->AddDebugString(DebugMantleString, 0.0f, DEBUG_NOTIFY_MANTLE);
- // TODO: Fix the case of endlessly bumping against this ceiling due to discrepancies in voxel testing locations between player and target.
- /*
- XXX
- XXXXXX
- XXXXXX
- XXXXXXXXXXXXXXXX
- */
- // Let's fudge this and TargetVoxPos a bit so we can trust they're at the same height.
- // These heights should all be the same, or about the same.
- // If the hit position matches one of them exactly, it will be the previous one,
- // when we're starting from already against the wall.
- // I can worry about making this explicitly z-aligned later. Who cares.
- Space->Game_Curr.Position.z;
- Space->Game_Prev.Position.z;
- ThisHitRes.HitPosition.z;
- float AuthHeight = ThisHitRes.HitPosition.z;
- ANCHOR;
- NCollisionInterface* Prim = Coll->GetActivePrimitive();
- NCollisionCapsule* Capsule = Cast<NCollisionCapsule>(Prim);
- opt_assert(Capsule);
- // Lower it to the floor.
- AuthHeight -= Capsule->Radius;
- AuthHeight -= Capsule->HalfHeight;
- // Now raise it half a voxel.
- AuthHeight += (VOXEL_SCALE * 0.5f);
- ANCHOR;
- NVec3 CurrVoxPos = Space->Game_Curr.Position;
- CurrVoxPos.z = AuthHeight;
- CurrVoxPos /= VOXEL_SCALE;
- NVec3 TargetVoxPos = ThisHitRes.HitPosition;
- TargetVoxPos -= (FlatNormal * ((Capsule->Radius) + (VOXEL_SCALE * 0.5f)));
- TargetVoxPos.z = AuthHeight;
- TargetVoxPos /= VOXEL_SCALE;
- NPoint3 CurrPt;
- CurrPt.x = (int)floor(CurrVoxPos.x + 0.5f);
- CurrPt.y = (int)floor(CurrVoxPos.y + 0.5f);
- CurrPt.z = (int)floor(CurrVoxPos.z + 0.5f);
- NPoint3 TargetPt;
- TargetPt.x = (int)floor(TargetVoxPos.x + 0.5f);
- TargetPt.y = (int)floor(TargetVoxPos.y + 0.5f);
- TargetPt.z = (int)floor(TargetVoxPos.z + 0.5f);
- opt_assert(CurrPt != TargetPt);
- // Typically if this fails, it's because we're approaching from a diagonal.
- // I wonder if I should check all those cases. Hrrrmmmm.
- opt_assert((Abs(CurrPt.x - TargetPt.x) + Abs(CurrPt.y - TargetPt.y) + Abs(CurrPt.z - TargetPt.z) ) == 1);
- TulpaWorld* TheWorld = Cast<TulpaWorld>(NBaseGame::GetInstance()->GetWorldHandle());
- if (TheWorld)
- {
- //const Voxel& CurrVox = TheWorld->GetVoxel(CurrPt);
- //const Voxel& TargetVox = TheWorld->GetVoxel(TargetPt);
- //CurrVox, TargetVox;
- //opt_assert(CurrVox.Kernel.Type == VOX_Empty);
- ////opt_assert(TargetVox.Kernel.Type != VOX_Empty); // May not be true, as this is the one at our feet when we're on or very near the ground.
- bool bFoundLedge = false;
- //const Voxel& TargetVox1 = TheWorld->GetVoxel(TargetPt + NPoint3(0,0,0)); // Usually the one at our feet unless we're in the air, then maybe the one in front of our eyes.
- //const Voxel& TargetVox2 = TheWorld->GetVoxel(TargetPt + NPoint3(0,0,1)); // Usually the one in front of our eyes unless we're in the air, then maybe the one above it.
- //const Voxel& TargetVox3 = TheWorld->GetVoxel(TargetPt + NPoint3(0,0,2)); // Definitely one above us.
- //const Voxel& TargetVox4 = TheWorld->GetVoxel(TargetPt + NPoint3(0,0,3)); // One above that for good measure?
- TulpaPlayer* TheTulpaPlayer = Cast<TulpaPlayer>(OwnerEntity);
- for (int z = -1; z < 1 && !bFoundLedge; ++z)
- {
- const Voxel& TargetVoxLower = TheWorld->GetVoxel(TargetPt + NPoint3(0,0,z));
- if (TargetVoxLower.IsCollider())
- {
- const Voxel& TargetVoxUpper = TheWorld->GetVoxel(TargetPt + NPoint3(0,0,z+1));
- if (!TargetVoxUpper.IsCollider())
- {
- const Voxel& TargetVoxUppermost = TheWorld->GetVoxel(TargetPt + NPoint3(0,0,z+2));
- if (!TargetVoxUppermost.IsCollider() || Posture->IsInPosture(EP_Crouching))
- {
- const Voxel& CurrVoxUpper = TheWorld->GetVoxel(CurrPt + NPoint3(0,0,z+1));
- const Voxel& CurrVoxUppermost = TheWorld->GetVoxel(CurrPt + NPoint3(0,0,z+2));
- if (!CurrVoxUpper.IsCollider() &&
- (!CurrVoxUppermost.IsCollider() || Posture->IsInPosture(EP_Crouching)))
- {
- bFoundLedge = true;
- // Also see whether we should play the audio based on floor height:
- if (TheTulpaPlayer->bCanPlayMantleFoley)
- {
- const Voxel& CurrVoxLowermost = TheWorld->GetVoxel(CurrPt + NPoint3(0,0,z-1));
- if (CurrVoxLowermost.IsCollider())
- {
- TheTulpaPlayer->bCanPlayMantleFoley = false;
- }
- }
- }
- }
- }
- }
- }
- if (bFoundLedge)
- {
- // This is relative to foot speed, so it changes when crouched.
- const float MANTLE_SPEED = 1.75f;
- // Basically act like forward movement is redirected upward, I guess?
- // Kill any lateral movement?
- // Instead of strictly using the up vector, let's use a vector that rounds up around the corner of the capsuled edge.
- NVec3 Cross1 = Cross(ThisHitRes.HitNormal, NPhysicsGlobals::GetWorldUp());
- NVec3 Cross2 = Cross(Cross1, ThisHitRes.HitNormal);
- NVec3 EffectiveUpVector = Cross2;
- NVec3 UpwardVel = Projection(Space->Game_Curr.Velocity, EffectiveUpVector);
- float CurrentUp = Dot(UpwardVel, EffectiveUpVector);
- NVec3 UpwardPrevVel = Projection(Space->Game_Prev.Velocity, EffectiveUpVector);
- //float NewUp = (Space->Game_Prev.Velocity - UpwardPrevVel).length() * MANTLE_SPEED;
- float NewUp = Phys->GetFootSpeed() * MANTLE_SPEED;
- if (NewUp > CurrentUp)
- {
- Space->Game_Curr.Velocity -= UpwardVel;
- Space->Game_Curr.Velocity += (EffectiveUpVector * NewUp);
- // TODO: If we reach a point where ONLY the one at our feet is clear and NOT the one above it, auto-crouch?
- // TODO: Handle low ceilings, possibly the same case.
- {
- {
- {
- // Mantle audio!!
- if (TheTulpaPlayer->bCanPlayMantleFoley)
- {
- TheTulpaPlayer->bCanPlayMantleFoley = false;
- MAKESTRINGWITH(MantleSound, "Mantle Foley");
- NStreamHandle hMantleStream = NAudioMgr::GetInstance()->GetStream(MantleSound);
- if (hMantleStream)
- {
- if (NAudioMgr::GetInstance()->GetNumSamplers(hMantleStream) == 0)
- {
- NAudioMgr::GetInstance()->CreateAndPlay(MantleSound, NBaseGame::GetInstance()->GetAudioBusSFX());
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- }
- return VOID_TYPE();
- }
- VOID_TYPE TulpaPlayer::OnSlide(NEntity* OwnerEntity, const NCollisionResult& ThisHitRes, bool bEffectsReady)
- {
- OwnerEntity, ThisHitRes, bEffectsReady;
- return VOID_TYPE();
- }
- VOID_TYPE TulpaPlayer::OnBaseMoved(NEntity* Owner, NEntity* Parent)
- {
- Owner, Parent;
- return VOID_TYPE();
- }
- VOID_TYPE TulpaPlayer::OnDialogOpened(NEntity* OwnerEntity)
- {
- OwnerEntity;
- // If I comment this out, the audio keeps running but the text doesn't advance because the dialog component zeroes out the delta time,
- // which I probably don't want for this game specifically. (Normally I would, so I should make sure it's labeled as such.)
- //OwnerEntity->GetComponent<NarrationComponent>()->PauseClip(true);
- return VOID_TYPE();
- }
- VOID_TYPE TulpaPlayer::OnDialogClosed(NEntity* OwnerEntity)
- {
- OwnerEntity;
- //OwnerEntity->GetComponent<NarrationComponent>()->PauseClip(false);
- return VOID_TYPE();
- }
- VOID_TYPE TulpaPlayer::ModifyDialog(NHandle hProvider, NHandle hConsumer, NString& InOutDialog)
- {
- CodexComponent* pCodex = Resolve<CodexComponent>(hConsumer);
- if (pCodex && !pCodex->GetHasCodex())
- {
- InOutDialog = NLocMgr::GetInstance()->GetString(NHashedString("TulpaGame"), NHashedString("Dialog"), NHashedString("NoCodex"));
- }
- return VOID_TYPE();
- }
- bool TulpaPlayer::HACK_SignalWantsToClose(NHandle hProvider, NHandle hConsumer)
- {
- bool bRetVal = false;
- CodexComponent* pCodex = Resolve<CodexComponent>(hConsumer);
- if (pCodex && !pCodex->GetHasCodex())
- {
- bRetVal = true;
- }
- return bRetVal;
- }
- VOID_TYPE TulpaPlayer::HandleAttachment(NEntity* OwnerEntity, NEntity* AttEnt, NHashedString EntDef)
- {
- OwnerEntity, AttEnt, EntDef;
- return VOID_TYPE();
- }
- void TulpaPlayer::SetPhysicsProps()
- {
- }
- VOID_TYPE TulpaPlayer::OnCollision(NEntity* OwnerEntity, const NCollisionResult& ThisHitRes)
- {
- OwnerEntity, ThisHitRes;
- return VOID_TYPE();
- }
- VOID_TYPE TulpaPlayer::OnAnyHit(NEntity* OwnerEntity, NCollisionInterface* Self, NCollisionInterface* Other, const NCollisionResult& ThisHitRes)
- {
- OwnerEntity, Self, Other, ThisHitRes;
- RESOLVE(NCollectComponent, OtherColl, Other);
- RESOLVE(NDeleteComponent, OtherDel, Other);
- if (OtherColl && OtherDel)
- {
- ANCHOR;
- OtherDel->TryDelete(true);
- // Do collection stuff.
- // This can be hardcoded for Tulpa, whatever.
- // This probably duplicates other implementations of collection elsewhere.
- // Whatever.
- {
- MAKESTRINGWITH(OnCollectedStr, "OnCollected");
- OtherColl->RunEvent(OnCollectedStr, OtherColl->GetHandle(), OwnerEntity->GetHandle());
- OtherColl->dOnCollected(OtherColl->GetOwner(), OwnerEntity->GetHandle());
- }
- }
- return VOID_TYPE();
- }
- //bool TulpaPlayer::BlocksOther(NCollisionInterface* Self, NCollisionInterface* Other)
- //{
- // Self, Other;
- //
- // return false;
- //}
- //
- //bool TulpaPlayer::PingsOther(NCollisionInterface* Self, NCollisionInterface* Other)
- //{
- // Self, Other;
- //
- // return true;
- //}
- void TulpaPlayer::PostApplySerialData()
- {
- }
- VOID_TYPE TulpaPlayer::OnDataWritten(NEntity* CallbackOwner, NHashedString Key, GenericData& InOutValue)
- {
- CallbackOwner, Key, InOutValue;
- return VOID_TYPE();
- }
- void TulpaPlayer::TrySetPropsByPosture(EPosture InPosture)
- {
- NPostureComponent* Posture = GetComponent<NPostureComponent>();
- if (Posture && !Posture->IsInPosture(InPosture))
- {
- SetPropsByPosture(InPosture);
- }
- }
- void TulpaPlayer::SetPropsByPosture(EPosture InPosture)
- {
- NPostureComponent* Posture = GetComponent<NPostureComponent>();
- EyeHeightComponent* Eye = GetComponent<EyeHeightComponent>();
- FocusComponent* Focus = GetComponent<FocusComponent>();
- SprintComponent* Sprint = GetComponent<SprintComponent>();
- NPhysicsComponent* Phys = GetComponent<NPhysicsComponent>();
- NCollisionComponent* Coll = GetComponent<NCollisionComponent>();
- EditModeComponent* Edit = GetComponent<EditModeComponent>();
- if (Posture && Eye && Focus && Sprint && Phys && Coll && Edit && !Edit->GetEditMode())
- {
- MAKESTRING(EyeHeight);
- MAKESTRING(EyeHeightCrouched);
- MAKESTRING(CrouchSpeed);
- MAKESTRING(FocusScalar);
- MAKESTRING(FocusSpeed);
- MAKESTRING(Crouch);
- MAKESTRING(Default);
- MAKESTRING(SprintFOVScalar);
- MAKESTRINGWITH(SprintStr, "Sprint");
- // If we're uncrouching, clear our max jumps so it resets to two (or whatever).
- // This would be nice to roll into the physics component itself, but it clashes with some other rules.
- if (Posture->GetPosture() == EP_Crouching)
- {
- Phys->SetMaxJumps(-1);
- }
- if (InPosture == EP_Standing)
- {
- if (Coll->ActivatePrimitive(Default))
- {
- Sprint->SetTargetFocus(1.0f);
- Posture->SetPosture(EP_Standing);
- Eye->SetTargetEyeHeight(NConfigFile::GetInstance()->Access(gNullStr, EyeHeight));
- Phys->InitNamedConstantsSet(gNullStr);
- }
- }
- else if (InPosture == EP_Sprinting)
- {
- if (Coll->ActivatePrimitive(Default))
- {
- Sprint->SetTargetFocus(NConfigFile::GetInstance()->Access(gNullStr, SprintFOVScalar));
- Posture->SetPosture(EP_Sprinting);
- Eye->SetTargetEyeHeight(NConfigFile::GetInstance()->Access(gNullStr, EyeHeight));
- Phys->InitNamedConstantsSet(SprintStr);
- }
- }
- else if (InPosture == EP_Crouching)
- {
- if (Coll->ActivatePrimitive(Crouch))
- {
- Sprint->SetTargetFocus(1.0f);
- Posture->SetPosture(EP_Crouching);
- Eye->SetTargetEyeHeight(NConfigFile::GetInstance()->Access(gNullStr, EyeHeightCrouched));
- Phys->InitNamedConstantsSet(Crouch);
- }
- }
- }
- }
- VOID_TYPE TulpaPlayer::OnActivatedQueuedPrimitive(NEntity* OwnerEntity, NHashedString InPrimitiveName)
- {
- ANCHOR;
- // Let's just assume we'll never switch to sprinting here.
- // (Power slide?)
- MAKESTRING(Crouch);
- MAKESTRING(Default);
- TulpaPlayer* Player = Cast<TulpaPlayer>(OwnerEntity);
- if (InPrimitiveName == Crouch)
- {
- Player->SetPropsByPosture(EP_Crouching);
- }
- else
- {
- Player->SetPropsByPosture(EP_Standing);
- }
- return VOID_TYPE();
- }
- bool TulpaPlayer::CanFrob(NEntity* OwnerEntity, NHandle Other)
- {
- OwnerEntity, Other;
- bool bRetVal = true;
- // This works but it's bad UX; I should be ABLE to frob it; it just should FAIL and play some LOCKED effects.
- //Door* OtherDoor = Resolve<Door>(Other);
- //if (OtherDoor)
- //{
- // bRetVal = OwnerEntity->GetComponent<ChimeComponent>()->HasChime(CA_Open);
- //}
- bRetVal = !(OwnerEntity->GetComponent<CamTrackComponent>()->IsPlaying());
- return bRetVal;
- }
- VOID_TYPE TulpaPlayer::OnFoundFrobbable(NEntity* OwnerEntity, NHandle Frobbable, const NCollisionResult& Result)
- {
- OwnerEntity;
- if (Frobbable)
- {
- if (Frobbable->IsA(TulpaWorld::StaticGetClassName()))
- // Unnecessary, as we toggle the world's frobbability on edit mode toggled instead.
- //if (OwnerEntity->GetComponent<EditModeComponent>()->GetEditMode())
- {
- // Show widget at position
- //NDebugLines::GetInstance()->Draw(Result.HitPosition, Result.HitPosition + (Result.HitNormal * VOXEL_SCALE * 0.5f), 0.0f, NColor8(255,0,0));
- // Also draw a quantized box.
- // THIS IS COPIED FROM THE PC. GENERALIZE IT!!
- {
- {
- {
- NPoint3 FrontPt, BackPt;
- NVec3 OtherAxis1, OtherAxis2;
- Resolve<TulpaPlayerControllerComponent>(OwnerEntity)->GetVoxelsFromTrace(Result, FrontPt, BackPt, OtherAxis1, OtherAxis2);
- {
- {
- {
- ANCHOR;
- NVec3 vi = -Result.HitNormal;
- NVec3 vj = -OtherAxis1;
- NVec3 vk = -OtherAxis2;
- float fx = (float)FrontPt.x;
- float fy = (float)FrontPt.y;
- float fz = (float)FrontPt.z;
- NVec3 Corner[4];
- Corner[0] = NVec3(fx,fy,fz) + (vi * +0.5f) + (vj * +0.5f) + (vk * +0.5f);
- Corner[1] = NVec3(fx,fy,fz) + (vi * +0.5f) + (vj * -0.5f) + (vk * +0.5f);
- Corner[2] = NVec3(fx,fy,fz) + (vi * +0.5f) + (vj * +0.5f) + (vk * -0.5f);
- Corner[3] = NVec3(fx,fy,fz) + (vi * +0.5f) + (vj * -0.5f) + (vk * -0.5f);
- // Push these in a bit to address z-fighting.
- const float PushAmt = -0.025f;
- Corner[0] += ((vi + vj + vk) * PushAmt);
- Corner[1] += ((vi - vj + vk) * PushAmt);
- Corner[2] += ((vi + vj - vk) * PushAmt);
- Corner[3] += ((vi - vj - vk) * PushAmt);
- float Brightness = fastsin(NBaseGame::GetInstance()->renderAbsTime * TAU * 3.0f);
- Brightness = (Brightness + 1.0f) * 0.5f;
- Brightness = Lerp(0.25f, 0.75f, Brightness);
- uchar ucBright = (uchar)(Brightness * 255);
- NDebugLines::GetInstance()->Draw(Corner[0] * VOXEL_SCALE, Corner[1] * VOXEL_SCALE, 0.0f, NColor8(ucBright));
- NDebugLines::GetInstance()->Draw(Corner[0] * VOXEL_SCALE, Corner[2] * VOXEL_SCALE, 0.0f, NColor8(ucBright));
- NDebugLines::GetInstance()->Draw(Corner[1] * VOXEL_SCALE, Corner[3] * VOXEL_SCALE, 0.0f, NColor8(ucBright));
- NDebugLines::GetInstance()->Draw(Corner[2] * VOXEL_SCALE, Corner[3] * VOXEL_SCALE, 0.0f, NColor8(ucBright));
- }
- }
- }
- }
- }
- }
- }
- else
- {
- ANCHOR;
- }
- }
- else
- {
- // Hide widget
- }
- return VOID_TYPE();
- }
- VOID_TYPE TulpaPlayer::FormatFrobString(const NString& InString, NString& InOutString, bool bInputChanged)
- {
- if (bInputChanged)
- {
- // TODO: Provide this string somewhere else.
- //InOutString = NString("Press ") + InString + NString(" to Use");
- MAKESTRINGWITH(TulpaGameStr, "TulpaGame");
- MAKESTRING(Prompts);
- MAKESTRING(FrobPrompt);
- InOutString = NLocMgr::GetInstance()->GetString(TulpaGameStr, Prompts, FrobPrompt).Replace("%s", InString);
- }
- return VOID_TYPE();
- }
- VOID_TYPE TulpaPlayer::PositionFrobString(const NVec2& InDims, NVec2& OutPosition)
- {
- // TODO: Use a delegate to position these on-screen, given the entity (or its transform only?) and the dimensions of the prompt.
- // TODO: TEST THIS IN NFML!! The floor() may not be correct there. It PROBABLY is fine, but I SHOULD KNOW.
- OutPosition = NVec2(320.0f, 160.0f);
- OutPosition.x = floor(OutPosition.x - InDims.x*0.5f);
- OutPosition.y = floor(OutPosition.y - InDims.y*0.5f);
- return VOID_TYPE();
- }
- VOID_TYPE TulpaPlayer::OnEditModeToggled(NEntity* OwnerEntity, bool bEditMode)
- {
- RESOLVE(NCollisionComponent, Coll, OwnerEntity);
- RESOLVE(NPhysicsComponent, Phys, OwnerEntity);
- opt_assert(Coll && Phys);
- ANCHOR;
- if (bEditMode)
- {
- Coll->SetSweepFilter(CFT_None);
- //Coll->SetDefaultPrimitiveName(NHashedString("DOES NOT EXIST"));
- //Phys->SetPhysicsDisabled(true);
- Phys->InitNamedConstantsSet(NHashedString("EditFlying"));
- #ifdef _DEBUG
- OwnerEntity->GetComponent<NFrobComponent>()->TraceDistance = VOXEL_SCALE * 6.0f;
- #else
- OwnerEntity->GetComponent<NFrobComponent>()->TraceDistance = VOXEL_SCALE * 6.0f;//20.0f;
- #endif
- }
- else
- {
- Coll->SetSweepFilter(0x0a000105);
- //Coll->SetDefaultPrimitiveName(NHashedString("Default"));
- //Phys->SetPhysicsDisabled(false);
- Phys->InitNamedConstantsSet(gNullStr);
- OwnerEntity->GetComponent<NFrobComponent>()->TraceDistance = VOXEL_SCALE * 3.0f;//5.0f;// * 2.0f;//20.0f;
- }
- //// Why had I commented this out?
- //// (Because it doesn't work.)
- //TulpaWorld* TheWorld = Cast<TulpaWorld>(NBaseGame::GetInstance()->GetWorldHandle());
- //if (TheWorld)
- //{
- // NFrobComponent* WorldFrob = TheWorld->GetComponent<NFrobComponent>();
- // if (WorldFrob)
- // {
- // WorldFrob->SetFrobbable(bEditMode);
- // }
- //}
- OwnerEntity->GetComponent<NFrobComponent>()->SetFilter(bEditMode ? (CFT_Frobbable | CFT_FrobbableInEditModeOnly) : (CFT_Frobbable));
- // Toggle vis of all triggers:
- {
- {
- {
- NUtilVector<Trigger*> Triggers = TulpaGame::GetInstance()->FindAllDynamicEntitiesOfType<Trigger>();
- for (int i = 0; i < Triggers.size(); ++i)
- {
- Triggers[i]->GetComponent<NRenderableComponent>()->SetVisibility(bEditMode);
- }
- NUtilVector<TwoTrigger*> TwoTriggers = TulpaGame::GetInstance()->FindAllDynamicEntitiesOfType<TwoTrigger>();
- for (int i = 0; i < TwoTriggers.size(); ++i)
- {
- TwoTriggers[i]->GetComponent<NRenderableComponent>()->SetVisibility(bEditMode);
- }
- }
- }
- }
- return VOID_TYPE();
- }
- //VOID_TYPE TulpaPlayer::OnVinylIntroEnded(const NAudioEventRecord& Record, void* pContext)
- //{
- // Record, pContext;
- //
- // // TODO: Handle the case of this happening after the menu already closed because we're hammering the button.
- //
- // MAKESTRING(InEarBus);
- // NSamplerHandle hSampler = NAudioMgr::GetInstance()->CreateAndPlay(NHashedString("Vinyl (Loop)"), InEarBus/*NBaseGame::GetInstance()->GetAudioBusSFX()*/, true);
- // hSampler;
- //
- // return VOID_TYPE();
- //}
Add Comment
Please, Sign In to add comment