AFlareSpacecraft* UFlareSpacecraftNavigationSystem::GetDockStation() { if (IsDocked()) { for (int32 SpacecraftIndex = 0; SpacecraftIndex < Spacecraft->GetGame()->GetActiveSector()->GetSpacecrafts().Num(); SpacecraftIndex++) { AFlareSpacecraft* Station = Spacecraft->GetGame()->GetActiveSector()->GetSpacecrafts()[SpacecraftIndex]; if (Station && Station->GetParent()->GetImmatriculation() == Data->DockedTo) { return Station; } } } return NULL; }
bool UFlareSpacecraftNavigationSystem::Undock() { // Try undocking if (IsDocked()) { FLOGV("UFlareSpacecraftNavigationSystem::Undock : '%s' undocking from '%s'", *Spacecraft->GetParent()->GetImmatriculation().ToString(), *Data->DockedTo.ToString()); // Detach from station if(DockConstraint) { DockConstraint->BreakConstraint(); DockConstraint->DestroyComponent(); DockConstraint = NULL; } AFlareSpacecraft* DockStation = GetDockStation(); DockStation->GetDockingSystem()->ReleaseDock(Spacecraft, Data->DockedAt); // Update data SetStatus(EFlareShipStatus::SS_AutoPilot); Data->DockedTo = NAME_None; Data->DockedAt = -1; // Update Angular acceleration rate : when it's docked the mass is the ship mass + the station mass Spacecraft->SetRCSDescription(Spacecraft->GetRCSDescription()); Spacecraft->OnUndocked(DockStation); // Leave PushCommandLocation(Spacecraft->GetRootComponent()->GetComponentTransform().TransformPositionNoScale(5000 * FVector(-1, 0, 0))); FLOG("UFlareSpacecraftNavigationSystem::Undock : successful"); // Hack for bug #195: for ue4 to reweld all. Spacecraft->Airframe->SetSimulatePhysics(false); Spacecraft->Airframe->SetSimulatePhysics(true); return true; } // Failed else { FLOGV("UFlareSpacecraftNavigationSystem::Undock : '%s' is not docked", *Spacecraft->GetParent()->GetImmatriculation().ToString()); return false; } }
void UFlareSpacecraftNavigationSystem::AbortAllCommands() { FFlareShipCommandData Command; while (CommandData.Dequeue(Command)) { FLOGV("UFlareSpacecraftNavigationSystem::AbortAllCommands : '%s' has aborted command '%s'", *Spacecraft->GetParent()->GetImmatriculation().ToString(), *EFlareCommandDataType::ToString(Command.Type)); if (Command.Type == EFlareCommandDataType::CDT_Dock) { // Release dock grant AFlareSpacecraft* Station = Command.ActionTarget; Station->GetDockingSystem()->ReleaseDock(Spacecraft, Command.ActionTargetParam); } } SetStatus(EFlareShipStatus::SS_Manual); }
AFlareSpacecraft* UFlareSpacecraftNavigationSystem::GetNearestShip(AFlareSpacecraft* DockingStation) const { // For now an host ship is a the nearest host ship with the following critera: // - Alive or not // - From any company // - Is the nearest // - Is not me FVector PilotLocation = Spacecraft->GetActorLocation(); float MinDistanceSquared = -1; AFlareSpacecraft* NearestShip = NULL; for (int32 SpacecraftIndex = 0; SpacecraftIndex < Spacecraft->GetGame()->GetActiveSector()->GetSpacecrafts().Num(); SpacecraftIndex++) { AFlareSpacecraft* ShipCandidate = Spacecraft->GetGame()->GetActiveSector()->GetSpacecrafts()[SpacecraftIndex]; if (ShipCandidate != Spacecraft && ShipCandidate != DockingStation) { if (DockingStation && (DockingStation->GetDockingSystem()->IsGrantedShip(ShipCandidate) || DockingStation->GetDockingSystem()->IsDockedShip(ShipCandidate))) { // Ignore ship docked or docking at the same station continue; } float DistanceSquared = (PilotLocation - ShipCandidate->GetActorLocation()).SizeSquared(); if (NearestShip == NULL || DistanceSquared < MinDistanceSquared) { MinDistanceSquared = DistanceSquared; NearestShip = ShipCandidate; } } } return NearestShip; }
void UFlareAsteroidComponent::TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) { Super::TickComponent(DeltaTime, TickType, ThisTickFunction); float CollisionSize = GetCollisionShape().GetExtent().Size(); EffectsUpdateTimer += DeltaTime; // Get player ship AFlareGame* Game = Cast<AFlareGame>(GetWorld()->GetAuthGameMode()); FCHECK(Game); AFlarePlayerController* PC = Game->GetPC(); FCHECK(PC); AFlareSpacecraft* ShipPawn = PC->GetShipPawn(); // Update if close to player and visible if (ShipPawn && (ShipPawn->GetActorLocation() - GetComponentLocation()).Size() < 500000 && (GetWorld()->TimeSeconds - LastRenderTime) < 0.5) { if (EffectsUpdateTimer > EffectsUpdatePeriod) { // World data FVector AsteroidLocation = GetComponentLocation(); FVector SunDirection = Game->GetPlanetarium()->GetSunDirection(); SunDirection.Normalize(); // Compute new FX locations for (int32 Index = 0; Index < Effects.Num(); Index++) { FVector RandomDirection = FVector::CrossProduct(SunDirection, EffectsKernels[Index]); RandomDirection.Normalize(); FVector StartPoint = AsteroidLocation + RandomDirection * CollisionSize; // Trace params FHitResult HitResult(ForceInit); FCollisionQueryParams TraceParams(FName(TEXT("Asteroid Trace")), false, NULL); TraceParams.bTraceComplex = true; TraceParams.bReturnPhysicalMaterial = false; ECollisionChannel CollisionChannel = ECollisionChannel::ECC_WorldDynamic; // Trace bool FoundHit = GetWorld()->LineTraceSingleByChannel(HitResult, StartPoint, AsteroidLocation, CollisionChannel, TraceParams); if (FoundHit && HitResult.Component == this && Effects[Index]) { FVector EffectLocation = HitResult.Location; if (!Effects[Index]->IsActive()) { Effects[Index]->Activate(); } Effects[Index]->SetWorldLocation(EffectLocation); Effects[Index]->SetWorldRotation(SunDirection.Rotation()); } else { Effects[Index]->Deactivate(); } } EffectsUpdateTimer = 0; } } // Disable all else { for (int32 Index = 0; Index < Effects.Num(); Index++) { Effects[Index]->Deactivate(); } } }
AFlareSpacecraft* UFlareSector::LoadSpacecraft(UFlareSimulatedSpacecraft* ParentSpacecraft) { AFlareSpacecraft* Spacecraft = NULL; FLOGV("UFlareSector::LoadSpacecraft : Start loading ('%s')", *ParentSpacecraft->GetImmatriculation().ToString()); // Spawn parameters FActorSpawnParameters Params; Params.bNoFail = true; Params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; // Create and configure the ship Spacecraft = GetGame()->GetWorld()->SpawnActor<AFlareSpacecraft>(ParentSpacecraft->GetDescription()->Template->GeneratedClass, ParentSpacecraft->GetData().Location, ParentSpacecraft->GetData().Rotation, Params); if (Spacecraft && !Spacecraft->IsPendingKillPending()) { Spacecraft->Load(ParentSpacecraft); UPrimitiveComponent* RootComponent = Cast<UPrimitiveComponent>(Spacecraft->GetRootComponent()); if (Spacecraft->IsStation()) { SectorStations.Add(Spacecraft); } else { SectorShips.Add(Spacecraft); } SectorSpacecrafts.Add(Spacecraft); FLOGV("%s spawn mode = %d", *Spacecraft->GetImmatriculation().ToString(), ParentSpacecraft->GetData().SpawnMode+0) switch (ParentSpacecraft->GetData().SpawnMode) { // Already known to be correct case EFlareSpawnMode::Safe: FLOGV("UFlareSector::LoadSpacecraft : Safe spawn '%s' at (%f,%f,%f)", *ParentSpacecraft->GetImmatriculation().ToString(), ParentSpacecraft->GetData().Location.X, ParentSpacecraft->GetData().Location.Y, ParentSpacecraft->GetData().Location.Z); RootComponent->SetPhysicsLinearVelocity(ParentSpacecraft->GetData().LinearVelocity, false); RootComponent->SetPhysicsAngularVelocity(ParentSpacecraft->GetData().AngularVelocity, false); break; // First spawn case EFlareSpawnMode::Spawn: PlaceSpacecraft(Spacecraft, ParentSpacecraft->GetData().Location); { FVector NewLocation = Spacecraft->GetActorLocation(); FLOGV("UFlareSector::LoadSpacecraft : Placing '%s' at (%f,%f,%f)", *ParentSpacecraft->GetImmatriculation().ToString(), NewLocation.X, NewLocation.Y, NewLocation.Z); } RootComponent->SetPhysicsLinearVelocity(FVector::ZeroVector, false); RootComponent->SetPhysicsAngularVelocity(FVector::ZeroVector, false); break; // Incoming in sector case EFlareSpawnMode::Travel: { FLOGV("UFlareSector::LoadSpacecraft : Travel '%s' at (%f, %f, %f)", *ParentSpacecraft->GetImmatriculation().ToString(), ParentSpacecraft->GetData().Location.X, ParentSpacecraft->GetData().Location.Y, ParentSpacecraft->GetData().Location.Z); FVector SpawnDirection; TArray<AFlareSpacecraft*> FriendlySpacecrafts = GetCompanySpacecrafts(Spacecraft->GetCompany()); FVector FriendlyShipLocationSum = FVector::ZeroVector; int FriendlyShipCount = 0; for (int SpacecraftIndex = 0 ; SpacecraftIndex < FriendlySpacecrafts.Num(); SpacecraftIndex++) { AFlareSpacecraft *SpacecraftCandidate = FriendlySpacecrafts[SpacecraftIndex]; if (!SpacecraftCandidate->IsStation() && SpacecraftCandidate != Spacecraft) { FriendlyShipLocationSum += SpacecraftCandidate->GetActorLocation(); FriendlyShipCount++; } } if (FriendlyShipCount == 0) { FVector NotFriendlyShipLocationSum = FVector::ZeroVector; int NotFriendlyShipCount = 0; for (int SpacecraftIndex = 0 ; SpacecraftIndex < SectorShips.Num(); SpacecraftIndex++) { AFlareSpacecraft *SpacecraftCandidate = SectorShips[SpacecraftIndex]; if (SpacecraftCandidate != Spacecraft && SpacecraftCandidate->GetCompany() != Spacecraft->GetCompany()) { NotFriendlyShipLocationSum += SpacecraftCandidate->GetActorLocation(); NotFriendlyShipCount++; } } if (NotFriendlyShipCount == 0) { SpawnDirection = FMath::VRand(); } else { FVector NotFriendlyShipLocationMean = NotFriendlyShipLocationSum / NotFriendlyShipCount; SpawnDirection = (GetSectorCenter() - NotFriendlyShipLocationMean).GetUnsafeNormal(); } } else { FVector FriendlyShipLocationMean = FriendlyShipLocationSum / FriendlyShipCount; SpawnDirection = (FriendlyShipLocationMean - GetSectorCenter()).GetUnsafeNormal() ; } float SpawnDistance = GetSectorRadius() + 1; if (GetSimulatedSector()->GetSectorBattleState(Spacecraft->GetCompany()) != EFlareSectorBattleState::NoBattle) { SpawnDistance += 500000; // 5 km } SpawnDistance = FMath::Min(SpawnDistance, GetSectorLimits()); FVector Location = GetSectorCenter() + SpawnDirection * SpawnDistance; FVector CenterDirection = (GetSectorCenter() - Location).GetUnsafeNormal(); Spacecraft->SetActorRotation(CenterDirection.Rotation()); PlaceSpacecraft(Spacecraft, Location); float SpawnVelocity = 0; if (GetSimulatedSector()->GetSectorBattleState(Spacecraft->GetCompany()) != EFlareSectorBattleState::NoBattle) { SpawnVelocity = 10000; } RootComponent->SetPhysicsLinearVelocity(CenterDirection * SpawnVelocity, false); RootComponent->SetPhysicsAngularVelocity(FVector::ZeroVector, false); } break; case EFlareSpawnMode::Exit: { float SpawnDistance = GetSectorLimits() * 0.9; float SpawnVelocity = ParentSpacecraft->GetData().LinearVelocity.Size() * 0.6; FVector SpawnDirection = ParentSpacecraft->GetData().Location.GetUnsafeNormal(); FVector Location = SpawnDirection * SpawnDistance; FVector CenterDirection = (GetSectorCenter() - Location).GetUnsafeNormal(); FLOGV("UFlareSector::LoadSpacecraft : Exit '%s' at (%f, %f, %f)", *ParentSpacecraft->GetImmatriculation().ToString(), Location.X, Location.Y, Location.Z); PlaceSpacecraft(Spacecraft, Location); Spacecraft->SetActorRotation(CenterDirection.Rotation()); RootComponent->SetPhysicsLinearVelocity(CenterDirection * SpawnVelocity, false); RootComponent->SetPhysicsAngularVelocity(FVector::ZeroVector, false); } break; } ParentSpacecraft->SetSpawnMode(EFlareSpawnMode::Safe); }
void UFlareSpacecraftDamageSystem::OnCollision(class AActor* Other, FVector HitLocation, FVector NormalImpulse) { // If receive hit from over actor, like a ship we must apply collision damages. // The applied damage energy is 0.2% of the kinetic energy of the other actor. The kinetic // energy is calculated from the relative speed between the 2 actors, and only with the relative // speed projected in the axis of the collision normal: if 2 very fast ship only slightly touch, // only few energy will be decipated by the impact. // // The damages are applied only to the current actor, the ReceiveHit method of the other actor // will also call an it will apply its collision damages itself. // If the other actor is a projectile, specific weapon damage code is done in the projectile hit // handler: in this case we ignore the collision AFlareShell* OtherProjectile = Cast<AFlareShell>(Other); if (OtherProjectile) { return; } // No primitive component, ignore UPrimitiveComponent* OtherRoot = Cast<UPrimitiveComponent>(Other->GetRootComponent()); if (!OtherRoot) { return; } // Ignore debris AStaticMeshActor* OtherActor = Cast<AStaticMeshActor>(Other); if (OtherActor) { if (OtherActor->GetName().StartsWith("Debris")) { return; } } // Relative velocity FVector DeltaVelocity = ((OtherRoot->GetPhysicsLinearVelocity() - Spacecraft->Airframe->GetPhysicsLinearVelocity()) / 100); // Compute the relative velocity in the impact axis then compute kinetic energy /*float ImpactSpeed = DeltaVelocity.Size(); float ImpactMass = FMath::Min(Spacecraft->GetSpacecraftMass(), OtherRoot->GetMass()); */ //200 m /s -> 6301.873047 * 20000 -> 300 / 2 damage float ImpactSpeed = 0; float ImpactEnergy = 0; float ImpactMass = Spacecraft->GetSpacecraftMass(); // Check if the mass was set and is valid if (ImpactMass > KINDA_SMALL_NUMBER) { ImpactSpeed = NormalImpulse.Size() / (ImpactMass * 100.f); ImpactEnergy = ImpactMass * ImpactSpeed / 8402.f; } float Radius = 0.2 + FMath::Sqrt(ImpactEnergy) * 0.11; //FLOGV("OnCollision %s", *Spacecraft->GetImmatriculation().ToString()); //FLOGV(" OtherRoot->GetPhysicsLinearVelocity()=%s", *OtherRoot->GetPhysicsLinearVelocity().ToString()); //FLOGV(" OtherRoot->GetPhysicsLinearVelocity().Size()=%f", OtherRoot->GetPhysicsLinearVelocity().Size()); //FLOGV(" Spacecraft->Airframe->GetPhysicsLinearVelocity()=%s", *Spacecraft->Airframe->GetPhysicsLinearVelocity().ToString()); //FLOGV(" Spacecraft->Airframe->GetPhysicsLinearVelocity().Size()=%f", Spacecraft->Airframe->GetPhysicsLinearVelocity().Size()); //FLOGV(" dot=%f", FVector::DotProduct(DeltaVelocity.GetUnsafeNormal(), HitNormal.GetUnsafeNormal())); /*FLOGV(" DeltaVelocity=%s", *DeltaVelocity.ToString()); FLOGV(" ImpactSpeed=%f", ImpactSpeed); FLOGV(" ImpactMass=%f", ImpactMass); FLOGV(" ImpactEnergy=%f", ImpactEnergy); FLOGV(" Radius=%f", Radius);*/ bool HasHit = false; FHitResult BestHitResult; float BestHitDistance = 0; for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++) { UFlareSpacecraftComponent* Component = Cast<UFlareSpacecraftComponent>(Components[ComponentIndex]); if (Component) { FHitResult HitResult(ForceInit); FCollisionQueryParams TraceParams(FName(TEXT("Fragment Trace")), true); TraceParams.bTraceComplex = true; TraceParams.bReturnPhysicalMaterial = false; Component->LineTraceComponent(HitResult, HitLocation, HitLocation + Spacecraft->GetLinearVelocity().GetUnsafeNormal() * 10000, TraceParams); if (HitResult.Actor.IsValid()){ float HitDistance = (HitResult.Location - HitLocation).Size(); if (!HasHit || HitDistance < BestHitDistance) { BestHitDistance = HitDistance; BestHitResult = HitResult; } //FLOGV("Collide hit %s at a distance=%f", *Component->GetReadableName(), HitDistance); HasHit = true; } } } if (HasHit) { //DrawDebugLine(Spacecraft->GetWorld(), HitLocation, BestHitResult.Location, FColor::Magenta, true); } else { int32 BestComponentIndex = -1; BestHitDistance = 0; for (int32 ComponentIndex = 0; ComponentIndex < Components.Num(); ComponentIndex++) { UFlareSpacecraftComponent* Component = Cast<UFlareSpacecraftComponent>(Components[ComponentIndex]); if (Component) { float ComponentDistance = (Component->GetComponentLocation() - HitLocation).Size(); if (BestComponentIndex == -1 || BestHitDistance > ComponentDistance) { BestComponentIndex = ComponentIndex; BestHitDistance = ComponentDistance; } } } UFlareSpacecraftComponent* Component = Cast<UFlareSpacecraftComponent>(Components[BestComponentIndex]); FCollisionQueryParams TraceParams(FName(TEXT("Fragment Trace")), true); TraceParams.bTraceComplex = true; TraceParams.bReturnPhysicalMaterial = false; Component->LineTraceComponent(BestHitResult, HitLocation, Component->GetComponentLocation(), TraceParams); //DrawDebugLine(Spacecraft->GetWorld(), HitLocation, BestHitResult.Location, FColor::Yellow, true); } AFlareSpacecraft* OtherSpacecraft = Cast<AFlareSpacecraft>(Other); UFlareCompany* DamageSource = NULL; if (OtherSpacecraft) { DamageSource = OtherSpacecraft->GetParent()->GetCompany(); LastDamageCauser = OtherSpacecraft; } else { LastDamageCauser = NULL; } ApplyDamage(ImpactEnergy, Radius, BestHitResult.Location, EFlareDamage::DAM_Collision, DamageSource); }
void UFlareSpacecraftNavigationSystem::CheckCollisionDocking(AFlareSpacecraft* DockingCandidate) { if (IsAutoPilot()) { FFlareShipCommandData CurrentCommand; if (CommandData.Peek(CurrentCommand)) { if (CurrentCommand.Type == EFlareCommandDataType::CDT_Dock) { // We are in a automatic docking process AFlareSpacecraft* DockStation = CurrentCommand.ActionTarget; if (DockStation != DockingCandidate) { // The hit spacecraft is not the docking target station return; } // Check dock alignement // TODO Put to external constants float DockingAngleLimit = 2; // 1° of angle error to dock float DockingVelocityLimit = 200; // 1 m/s float DockingLateralVelocityLimit = 20; // 10 cm/s float DockingAngularVelocityLimit = 10; // 5 °/s int32 DockId = CurrentCommand.ActionTargetParam; FVector ShipAngularVelocity = Spacecraft->Airframe->GetPhysicsAngularVelocity(); FVector ShipDockLocation = GetDockLocation(); FFlareDockingInfo StationDockInfo = DockStation->GetDockingSystem()->GetDockInfo(DockId); FVector StationCOM = DockStation->Airframe->GetBodyInstance()->GetCOMPosition(); FVector StationAngularVelocity = DockStation->Airframe->GetPhysicsAngularVelocity(); FVector StationDockLocation = DockStation->Airframe->GetComponentTransform().TransformPosition(StationDockInfo.LocalLocation); FVector StationDockSelfRotationInductedLinearVelocity = (PI / 180.f) * FVector::CrossProduct(StationAngularVelocity, StationDockLocation- StationCOM); FVector ShipDockSelfRotationInductedLinearVelocity = (PI / 180.f) * FVector::CrossProduct(ShipAngularVelocity, ShipDockLocation-COM); FVector StationDockLinearVelocity = StationDockSelfRotationInductedLinearVelocity + DockStation->GetLinearVelocity() * 100; FVector ShipDockLinearVelocity = ShipDockSelfRotationInductedLinearVelocity + Spacecraft->GetLinearVelocity() * 100; FVector ShipDockAxis = Spacecraft->Airframe->GetComponentToWorld().GetRotation().RotateVector(FVector(1, 0, 0)); // Ship docking port are always at front FVector StationDockAxis = DockStation->Airframe->GetComponentToWorld().GetRotation().RotateVector(StationDockInfo.LocalAxis); float DockToDockAngle = FMath::RadiansToDegrees(FMath::Acos(FVector::DotProduct(-ShipDockAxis, StationDockAxis))); FVector RelativeDockToDockLinearVelocity = StationDockLinearVelocity - ShipDockLinearVelocity; // Angular velocity must be the same FVector RelativeDockAngularVelocity = ShipAngularVelocity - StationAngularVelocity; // Check if dockable bool OkForDocking = true; if (DockToDockAngle > DockingAngleLimit) { //FLOG("OkForDocking ? Not aligned"); // Not aligned OkForDocking = false; } else if (RelativeDockToDockLinearVelocity.Size() > DockingVelocityLimit) { //FLOG("OkForDocking ? Too fast"); // Too fast OkForDocking = false; } else if (FVector::VectorPlaneProject(RelativeDockToDockLinearVelocity, StationDockAxis).Size() > DockingLateralVelocityLimit) { //FLOG("OkForDocking ? Too much lateral velocity"); // Too much lateral velocity OkForDocking = false; } else if (RelativeDockAngularVelocity.Size() > DockingAngularVelocityLimit) { //FLOG("OkForDocking ? Too much angular velocity"); // Too much angular velocity OkForDocking = false; } if (OkForDocking) { // All is ok for docking, perform dock //FLOG(" Ok for docking after hit"); ConfirmDock(DockStation, DockId); return; } } } } }