Files

101 lines
14 KiB
C++

#include "IronsGame.h"
#include "Components/StaticMeshComponent.h"
#include "Components/DirectionalLightComponent.h"
#include "Components/PointLightComponent.h"
#include "Components/SkyLightComponent.h"
#include "Components/ExponentialHeightFogComponent.h"
#include "Engine/DirectionalLight.h"
#include "Engine/PointLight.h"
#include "Engine/SkyLight.h"
#include "Engine/ExponentialHeightFog.h"
#include "Components/SkyAtmosphereComponent.h"
#include "Engine/StaticMeshActor.h"
#include "Engine/World.h"
#include "EngineUtils.h"
#include "Camera/CameraActor.h"
#include "Kismet/GameplayStatics.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Materials/Material.h"
#include "DrawDebugHelpers.h"
namespace {
UStaticMeshComponent* Block(UWorld* W,FVector P,FVector Size,FLinearColor Color,bool Collision=true){
auto A=W->SpawnActor<AStaticMeshActor>(P,FRotator::ZeroRotator);auto C=A->GetStaticMeshComponent();C->SetMobility(EComponentMobility::Movable);C->SetStaticMesh(LoadObject<UStaticMesh>(nullptr,TEXT("/Engine/BasicShapes/Cube.Cube")));C->SetWorldScale3D(Size/100);C->SetCollisionEnabled(Collision?ECollisionEnabled::QueryAndPhysics:ECollisionEnabled::NoCollision);
auto M=LoadObject<UMaterialInterface>(nullptr,TEXT("/Game/IRONSPORE/Art/Materials/M_Industrial.M_Industrial"));if(!M)M=LoadObject<UMaterialInterface>(nullptr,TEXT("/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial"));auto D=UMaterialInstanceDynamic::Create(M,C);D->SetVectorParameterValue(TEXT("BaseColor"),Color);D->SetVectorParameterValue(TEXT("Color"),Color);C->SetMaterial(0,D);return C;
}
void Light(UWorld* W,FVector P,FLinearColor Color,float Power,float Radius){auto L=W->SpawnActor<APointLight>(P,FRotator::ZeroRotator);L->SetMobility(EComponentMobility::Movable);auto C=L->PointLightComponent;C->SetLightColor(Color);C->SetIntensity(Power);C->SetAttenuationRadius(Radius);}
AIronsMarker* Marker(UWorld* W,FVector P,const FString& Kind,const FString& Id=TEXT(""),int Count=1,const FString& Source=TEXT("")){
auto M=W->SpawnActor<AIronsMarker>(P,FRotator::ZeroRotator);M->Kind=Kind;M->ItemId=Id;M->Quantity=Count;M->SourceKey=Source;
auto Material=LoadObject<UMaterialInterface>(nullptr,TEXT("/Game/IRONSPORE/Art/Materials/M_Industrial.M_Industrial"));if(Material){auto D=UMaterialInstanceDynamic::Create(Material,M);D->SetVectorParameterValue(TEXT("BaseColor"),Kind==TEXT("extract")?FLinearColor(.04f,.6f,.3f):Kind==TEXT("loot")?FLinearColor(.25f,.34f,.37f):FLinearColor(.8f,.4f,.05f));M->Body->SetMaterial(0,D);}return M;
}
}
AIronsMarker::AIronsMarker(){Body=CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MarkerBody"));SetRootComponent(Body);Body->SetStaticMesh(LoadObject<UStaticMesh>(nullptr,TEXT("/Engine/BasicShapes/Cube.Cube")));Body->SetCollisionProfileName(TEXT("BlockAllDynamic"));SetActorScale3D(FVector(1.1f,1.1f,.75f));}
AIronsGameMode::AIronsGameMode(){PrimaryActorTick.bCanEverTick=true;DefaultPawnClass=nullptr;PlayerControllerClass=AIronsController::StaticClass();}
void AIronsGameMode::BeginPlay(){Super::BeginPlay();auto H=Cast<UIronsGameInstance>(GetGameInstance());
if(GetWorld()->GetMapName().Contains(TEXT("L_Hangar")))BuildHangar();else{BuildBattle();if(H&&H->bReady){auto PC=Cast<AIronsController>(UGameplayStatics::GetPlayerController(this,0));if(PC){FActorSpawnParameters Params;auto Mech=GetWorld()->SpawnActor<AIronsCharacter>(FVector(-2300,-2300,175),FRotator(0,45,0),Params);PC->Possess(Mech);PC->SetPage(TEXT("play"));}}}
}
void AIronsGameMode::BuildHangar(){
auto W=GetWorld();Block(W,FVector(0,0,-35),FVector(4500,4000,70),FLinearColor(.08f,.10f,.12f));
auto A=W->SpawnActor<AStaticMeshActor>(FVector(0,0,0),FRotator::ZeroRotator);auto C=A->GetStaticMeshComponent();C->SetMobility(EComponentMobility::Movable);C->SetStaticMesh(LoadObject<UStaticMesh>(nullptr,TEXT("/Game/IRONSPORE/Art/Environment/SM_HangarBay.SM_HangarBay")));
auto Mech=W->SpawnActor<AStaticMeshActor>(FVector(200,150,60),FRotator(0,-130,0));auto B=Mech->GetStaticMeshComponent();B->SetMobility(EComponentMobility::Movable);B->SetStaticMesh(LoadObject<UStaticMesh>(nullptr,TEXT("/Game/IRONSPORE/Art/Mechs/SM_MechScout.SM_MechScout")));B->SetWorldScale3D(FVector(2.0f));
Block(W,FVector(200,150,10),FVector(800,800,35),FLinearColor(.12f,.16f,.18f));
Light(W,FVector(300,-500,700),FLinearColor(.5f,.8f,1),120000,1700);Light(W,FVector(-600,200,400),FLinearColor(1,.45f,.16f),80000,1400);Light(W,FVector(0,0,850),FLinearColor(.8f,.88f,1),100000,1800);
for(int N=0;N<6;N++){Block(W,FVector(-1000+N*400,450,380),FVector(15,20,700),FLinearColor(.12f,.16f,.19f));Light(W,FVector(-1000+N*400,450,730),FLinearColor(.2f,.8f,1),3000,400);}
auto Cam=W->SpawnActor<ACameraActor>(FVector(780,-660,480),FRotator::ZeroRotator);Cam->SetActorRotation((FVector(200,150,340)-Cam->GetActorLocation()).Rotation());auto PC=UGameplayStatics::GetPlayerController(this,0);if(PC)PC->SetViewTarget(Cam);
}
void AIronsGameMode::BuildBattle(){
auto H=Cast<UIronsGameInstance>(GetGameInstance());if(!H||!H->bReady)return;auto W=GetWorld();auto& S=H->Core();S.clearEnemies();S.resetField();auto R=S.state().run;
int Biome=R.biome;FLinearColor Ground=Biome==0?FLinearColor(.16f,.21f,.22f):Biome==1?FLinearColor(.25f,.20f,.12f):FLinearColor(.12f,.16f,.22f);
Block(W,FVector(0,0,-50),FVector(13000,13000,100),Ground);
auto Sun=W->SpawnActor<ADirectionalLight>(FVector(0,0,3000),FRotator(-45,-35,0));Sun->SetMobility(EComponentMobility::Movable);Sun->GetLightComponent()->SetIntensity(3.5f);
W->SpawnActor<ASkyAtmosphere>();
auto Sky=W->SpawnActor<ASkyLight>();Sky->GetLightComponent()->SetMobility(EComponentMobility::Movable);Sky->GetLightComponent()->SetIntensity(1.2f);Sky->GetLightComponent()->bRealTimeCapture=true;
auto Fog=W->SpawnActor<AExponentialHeightFog>();Fog->GetComponent()->SetFogDensity(.007f);Fog->GetComponent()->SetFogInscatteringColor(Biome==1?FLinearColor(.35f,.27f,.18f):FLinearColor(.15f,.23f,.3f));
FRandomStream Rand(R.seed+R.stage*871);for(int I=0;I<30;I++){FVector P(Rand.FRandRange(-5300,5300),Rand.FRandRange(-5300,5300),0);if(P.Size2D()<1000||FVector::Dist2D(P,FVector(-2300,-2300,0))<800)continue;float Height=Rand.FRandRange(180,600);P.Z=Height/2;Block(W,P,FVector(Rand.FRandRange(250,600),Rand.FRandRange(200,500),Height),Ground*.7f);}
for(int I=0;I<4;I++){FVector P(I%2?6200:-6200,I<2?0:0,350);if(I>1)P=FVector(0,I==2?6200:-6200,350);Block(W,P,I<2?FVector(100,12400,700):FVector(12400,100,700),Ground*.5f);}
Extraction=Marker(W,FVector(-2700,-2300,60),TEXT("extract"));Extraction->SetActorScale3D(FVector(4,4,.2f));
Light(W,Extraction->GetActorLocation()+FVector(0,0,250),FLinearColor(.2f,1,.8f),50000,1100);
static const TCHAR* LootIds[]={TEXT("alloy"),TEXT("cells"),TEXT("coil"),TEXT("vector"),TEXT("blueprint_coil_capacitor"),TEXT("blueprint_boost_efficient"),TEXT("pulse"),TEXT("reactive_guard"),TEXT("blueprint_reactive_guard"),TEXT("siege")};
for(int I=0;I<10;I++){FVector P(-1700+(I%5)*800,-1300+(I/5)*1800,70);auto M=Marker(W,P,TEXT("loot"),LootIds[I],I<2?5:1,FString::Printf(TEXT("stage%d-crate%d"),R.stage,I));if(std::find(R.searched.begin(),R.searched.end(),std::string(TCHAR_TO_UTF8(*M->SourceKey)))!=R.searched.end()){M->SetActorHiddenInGame(true);M->SetActorEnableCollision(false);}}
Marker(W,FVector(1800,1700,100),TEXT("objective"));Marker(W,FVector(2700,2500,100),TEXT("gate0"));Marker(W,FVector(3500,2500,100),TEXT("gate1"));
Marker(W,FVector(700,200,70),TEXT("upgrade"));
int Count=H->IsTraining()?2:6+R.stage*2;for(int I=0;I<Count;I++){FVector P=H->IsTraining()?FVector(2800+I*1200,3800+I*1000,0):FVector(-800+(I%4)*1100,900+(I/4)*1100,0);int T=I%3;if(T==1)P.Z=350;FTransform Transform(FRotator(0,-120,0),P);auto E=W->SpawnActorDeferred<AIronsEnemy>(AIronsEnemy::StaticClass(),Transform);E->EntityId=FString::Printf(TEXT("s%d-e%d"),R.stage,I);E->Type=T;UGameplayStatics::FinishSpawningActor(E,Transform);}
if(R.stage==4&&R.branch==0){FTransform T(FVector(2800,4000,0));auto Boss=W->SpawnActorDeferred<AIronsEnemy>(AIronsEnemy::StaticClass(),T);Boss->Type=3;Boss->bTerminal=true;Boss->EntityId=TEXT("boss");UGameplayStatics::FinishSpawningActor(Boss,T);}
if(R.stage==4&&R.branch==1){Escort=Marker(W,FVector(-1500,0,110),TEXT("escort"));Escort->SetActorScale3D(FVector(2.5f,3,1));}
if(H->IsTraining()){
S.rangeReset();Marker(W,FVector(-1800,-2100,60),TEXT("rangeReset"));Marker(W,FVector(-1300,-2100,60),TEXT("objective"));
auto Ramp=Block(W,FVector(4200,-1300,210),FVector(1000,2700,70),FLinearColor(.12f,.2f,.24f));Ramp->SetWorldRotation(FRotator(0,0,9));Block(W,FVector(4200,1300,400),FVector(1000,2500,80),FLinearColor(.12f,.2f,.24f));
const FVector Points[]={FVector(4200,-3000,150),FVector(4200,100,590),FVector(4200,2000,590)};
for(int I=0;I<3;I++){auto Gate=Marker(W,Points[I],FString::Printf(TEXT("rangeGate%d"),I));Gate->Body->SetCollisionEnabled(ECollisionEnabled::NoCollision);Gate->SetActorHiddenInGame(true);for(int Side:{-1,1})Block(W,Points[I]+FVector(Side*350,0,100),FVector(40,60,500),FLinearColor(.1f,.4f,.36f));Block(W,Points[I]+FVector(0,0,350),FVector(740,60,35),FLinearColor(.1f,.7f,.5f),false);}
for(int I=0;I<2;I++){FTransform T(FVector(I?-3500:-2000,1500,180));auto Target=W->SpawnActorDeferred<AIronsEnemy>(AIronsEnemy::StaticClass(),T);Target->Type=4+I;Target->EntityId=FString::Printf(TEXT("range-target-%d"),I);UGameplayStatics::FinishSpawningActor(Target,T);}
}
}
void AIronsGameMode::Interact(){
auto H=Cast<UIronsGameInstance>(GetGameInstance());auto PC=Cast<AIronsController>(UGameplayStatics::GetPlayerController(this,0));if(!H||!PC||!PC->Mech())return;
AIronsMarker* Nearest=nullptr;float Distance=500;for(TActorIterator<AIronsMarker> I(GetWorld());I;++I)if(!I->IsHidden()){float D=FVector::Dist2D(I->GetActorLocation(),PC->Mech()->GetActorLocation());if(D<Distance){Distance=D;Nearest=*I;}}
if(!Nearest){H->Notice(TEXT("ue.nothingNear"));return;}auto& S=H->Core();FString K=Nearest->Kind;
if(K==TEXT("loot")){if(H->Accept(S.addLoot(TCHAR_TO_UTF8(*Nearest->ItemId),Nearest->Quantity,TCHAR_TO_UTF8(*Nearest->SourceKey)))){Nearest->SetActorHiddenInGame(true);Nearest->SetActorEnableCollision(false);H->Notice(TEXT("ue.loot"),Nearest->ItemId);}}
else if(K==TEXT("extract")){if(S.field().ready){if(H->Accept(S.boardExtraction())){H->Notice(TEXT("ue.extracted"));PC->ReturnHome();}}else if(S.field().extraction<0){if(H->Accept(S.callExtraction()))H->Notice(TEXT("ue.called"));}}
else if(K==TEXT("objective")){if(S.state().run.stage<4||H->IsTraining()){if(H->Accept(S.objective()))H->Notice(TEXT("ue.objectiveDone"));}else H->Notice(TEXT("ue.finalObjective"));}
else if(K.StartsWith(TEXT("gate"))){int Branch=K.EndsWith(TEXT("1"))?1:0;if(H->Accept(S.nextStage(Branch)))UGameplayStatics::OpenLevel(this,TEXT("/Game/IRONSPORE/Maps/L_Warzone"));}
else if(K==TEXT("upgrade")){PC->SetPage(TEXT("upgrade"));}
else if(K==TEXT("rangeReset")){S.rangeReset();H->Notice(TEXT("ue.rangeReset"));}
else if(K==TEXT("escort"))H->Notice(TEXT("ue.escort"));
}
FString AIronsGameMode::Prompt()const{
auto PC=Cast<AIronsController>(UGameplayStatics::GetPlayerController(this,0));auto H=Cast<UIronsGameInstance>(GetGameInstance());if(!PC||!PC->Mech()||!H)return TEXT("");
AIronsMarker* Best=nullptr;float D=500;for(TActorIterator<AIronsMarker> I(GetWorld());I;++I)if(!I->IsHidden()){float T=FVector::Dist2D(I->GetActorLocation(),PC->Mech()->GetActorLocation());if(T<D){D=T;Best=*I;}}
if(!Best)return H->Label(TEXT("ue.explore"));if(Best->Kind==TEXT("loot"))return H->Keys[9]+TEXT(" ")+H->Label(TEXT("ue.search"))+TEXT(" ")+H->ItemName(TCHAR_TO_UTF8(*Best->ItemId));
FString Key=Best->Kind==TEXT("extract")?(bExtractionReady?TEXT("ue.board"):ExtractionWait>=0?TEXT("ue.wait"):TEXT("ue.call")):Best->Kind.StartsWith(TEXT("gate"))?TEXT("ue.nextStage"):Best->Kind==TEXT("objective")?TEXT("ue.activate"):Best->Kind==TEXT("upgrade")?TEXT("ue.upgrade"):TEXT("ue.escort");return H->Keys[9]+TEXT(" ")+H->Label(Key);
}
void AIronsGameMode::Tick(float Dt){
Super::Tick(Dt);auto H=Cast<UIronsGameInstance>(GetGameInstance());auto PC=Cast<AIronsController>(UGameplayStatics::GetPlayerController(this,0));if(!H||!PC||!PC->Playing()||!PC->Mech())return;
auto P=PC->Mech();bool WasReady=bExtractionReady;ExtractionWait=H->Core().field().extraction;bExtractionReady=H->Core().field().ready;if(!WasReady&&bExtractionReady)H->Notice(TEXT("ue.readyToBoard"));
if(H->IsTraining()){int Inside=-1;for(TActorIterator<AIronsMarker> I(GetWorld());I;++I)if(I->Kind.StartsWith(TEXT("rangeGate"))&&FVector::Dist2D(I->GetActorLocation(),P->GetActorLocation())<280&&FMath::Abs(I->GetActorLocation().Z-P->GetActorLocation().Z)<210){Inside=FCString::Atoi(*I->Kind.Right(1));break;}if(Inside>=0&&Inside!=LastCourseGate)H->Core().rangeGate(Inside);LastCourseGate=Inside;}
if(!H->Core().state().run.podUsed){bool Stable=P->GetCharacterMovement()->IsMovingOnGround()&&P->GetVelocity().Size()<20&&GetWorld()->GetTimeSeconds()-P->DamageTime>1;auto Result=H->Core().advanceRecovery(Dt,PC->bPodHeld,Stable,PC->Screen->SelectedPodItems());if(!Result){if(Result.detail=="interrupted")H->Notice(TEXT("ue.podInterrupted"));else{H->Accept(Result);PC->bPodHeld=false;}}if(H->Core().state().run.podUsed){H->Notice(TEXT("ue.podSent"));PC->bPodHeld=false;}}
PodProgress=H->Core().field().podProgress;
if(Escort&&!H->Core().state().run.objectiveDone&&FVector::Dist2D(Escort->GetActorLocation(),P->GetActorLocation())<1200){EscortProgress+=Dt;Escort->AddActorWorldOffset(FVector(75*Dt,0,0));int Threats=0;for(TActorIterator<AIronsEnemy> I(GetWorld());I;++I)if(!I->IsHidden()&&FVector::Dist2D(I->GetActorLocation(),Escort->GetActorLocation())<1500)Threats++;
H->Core().damageEscort(Threats*1.5f*Dt);if(H->Core().state().run.escortArmor<=0){H->Notice(TEXT("ue.escortLost"));if(H->FinishSortie(true))PC->ReturnHome();}else if(EscortProgress>=45){H->Accept(H->Core().objective());if(H->Core().state().run.stage==4)H->Accept(H->Core().completeRoute());}}
}