#include "Scenario.h" #include "TileManager.h" #include "olcUTIL_Geometry2D.h" Scenario::Scenario(std::vector>&units,std::vector>&IMAGES,std::vector>&SOUNDS,std::string&objective,TileTransformedView&game,GameFlags&flags) :units(units),IMAGES(IMAGES),SOUNDS(SOUNDS),objective(objective),game(game),flags(flags){} Scenario::~Scenario(){}; void Scenario::_Start(){ state=0; camera=utils::Camera2D{game.GetPGE()->GetScreenSize(),game.GetWorldOffset()}; camera.SetLazyFollowRate(2); camera.SetMode(utils::Camera2D::Mode::LazyFollow); targetPos={96,96}; box.SetVisible(false); initialWaitTime=3; camera.SetTarget(targetPos); missionCompletedTimer=0; transitionToNextLevel=false; setupEasyMode=false; Start(); } void Scenario::Start(){}; void Scenario::_Update(Resources&enemy_resources,std::vector>&collectionPoints,int availableMemory,std::vector>&queuedUnits,std::vector>&SOUNDS){ initialWaitTime=std::max(0.f,initialWaitTime-game.GetPGE()->GetElapsedTime()); missionFinishWaitTime=std::max(0.f,missionFinishWaitTime-game.GetPGE()->GetElapsedTime()); smallTimePass=std::min(1.f,smallTimePass+game.GetPGE()->GetElapsedTime()); if(flags.playerInControl){ smallTimePass=0; } if(missionCompleted){ missionCompletedTimer+=game.GetPGE()->GetElapsedTime(); } else { missionCompleted=MissionCompleted(); } if(flags.playerInControl){ RunAI(enemy_resources,collectionPoints,availableMemory,queuedUnits,SOUNDS); } if(initialWaitTime==0){ Update(); } }; void Scenario::RunAI(Resources&enemy_resources,std::vector>&collectionPoints,int availableMemory,std::vector>&queuedUnits,std::vector>&SOUNDS){ if(!setupEasyMode&&flags.difficulty==0){ enemy_resources={100,100,100,100,100}; } unitBuildTimer=std::max(0.f,unitBuildTimer-game.GetPGE()->GetElapsedTime()); std::weak_ptrbaseOfOperations; for(auto&u:units){ if(!u->IsFriendly()&&u->IsRAMBank()){ baseOfOperations=u; break; } } if(baseOfOperations.expired()){ for(auto&u:units){ if(!u->IsFriendly()){ baseOfOperations=u; break; } } } //See if there are collectors, if so send memory allocator units towards them. if(!baseOfOperations.expired()){ std::sort(collectionPoints.begin(),collectionPoints.end(),[&](std::shared_ptrcp1,std::shared_ptrcp2){ geom2d::linetoCP1={cp1->pos,baseOfOperations.lock()->GetPos()}; geom2d::linetoCP2={cp2->pos,baseOfOperations.lock()->GetPos()}; return toCP1.length()attachedUnit.expired()){ if(cpCheckTimer.count(cp.get())==0||cpCheckTimer[cp.get()]<=0){ for(auto&u:units){ if(!u->IsFriendly()&&u->IsAllocator()&&u->attachTarget.expired()){ //Tell this unit to move towards that collection point. u->SetTargetCollectionPoint(cp,u); break; } } //Hasn't been checked recently. cpCheckTimer[cp.get()]=60; } } } for(auto&key:cpCheckTimer){ cpCheckTimer[key.first]=std::max(0.0f,game.GetPGE()->GetElapsedTime()); } int memoryAllocatorCount=0; for(auto&u:units){ if(!u->IsFriendly()&&u->IsAllocator()){ memoryAllocatorCount++; } } if(memoryAllocatorCount<3){ for(auto&u:units){ if(u->IsRAMBank()){ AttemptBuild(UnitType::MemoryAllocator,u->GetPos(),MemoryAllocator::resourceCost,enemy_resources,availableMemory,queuedUnits); } } } //Randomly turn memory allocators into other units. if(unitBuildTimer==0){ for(auto&u:units){ if(!u->IsFriendly()&&u->IsAllocator()){ std::arrayunitChoiceList={UnitType::LeftShifter,UnitType::LeftShifter,UnitType::RightShifter,UnitType::RightShifter, UnitType::Corrupter,UnitType::MemorySwapper,UnitType::BitRestorer,UnitType::BitRestorer,UnitType::_Platform,UnitType::Corrupter}; std::array,10>unitResourceCostList={LeftShifter::resourceCost,LeftShifter::resourceCost,RightShifter::resourceCost,RightShifter::resourceCost, Corrupter::resourceCost,MemorySwapper::resourceCost,BitRestorer::resourceCost,BitRestorer::resourceCost,_Platform::resourceCost,Corrupter::resourceCost}; int randomIndex=rand()%unitChoiceList.size(); UnitType buildUnit=unitChoiceList[randomIndex]; int totalCost=0; for(int i=0;iSetBuildUnit(8,std::make_shared(game.GetPGE(),u->GetPos(),IMAGES,false),SOUNDS); \ }break; switch(buildUnit){ Build(LeftShifter) Build(RightShifter) Build(BitRestorer) Build(_Platform) Build(Corrupter) Build(MemorySwapper) } if(enemy_resources.health>0){enemy_resources.health=std::max(0,enemy_resources.health-totalCost);}else if(enemy_resources.atkSpd>0){enemy_resources.atkSpd=std::max(0,enemy_resources.atkSpd-totalCost);}else if(enemy_resources.moveSpd>0){enemy_resources.moveSpd=std::max(0,enemy_resources.moveSpd-totalCost);}else if(enemy_resources.range>0){enemy_resources.range=std::max(0,enemy_resources.range-totalCost);}else {enemy_resources.procedure=std::max(0,enemy_resources.procedure-totalCost); } } } switch(flags.difficulty){ case 0:{ unitBuildTimer=120; }break; case 1:{ unitBuildTimer=60; }break; case 2:{ unitBuildTimer=10; }break; } } } } /* std::shared_ptrbuildUnit=std::make_shared(this,u->GetPos(),IMAGES,u->IsFriendly()); \ u->SetBuildUnit(CONSTANT::UNIT_BUILD_TIME,std::move(buildUnit),SOUNDS); \ */ bool Scenario::AttemptBuild(UnitType unit,vf2d pos,std::vector&resourceCost,Resources&enemy_resources,int availableMemory,std::vector>&queuedUnits){ int enemyTotalResources=enemy_resources.atkSpd+enemy_resources.health+enemy_resources.moveSpd+enemy_resources.procedure+enemy_resources.range; int totalCost=0; for(int i=0;iavailableMemory)return false; if(enemyTotalResources>=totalCost){ if(enemy_resources.health>0){enemy_resources.health=std::max(0,enemy_resources.health-totalCost);}else if(enemy_resources.atkSpd>0){enemy_resources.atkSpd=std::max(0,enemy_resources.atkSpd-totalCost);}else if(enemy_resources.moveSpd>0){enemy_resources.moveSpd=std::max(0,enemy_resources.moveSpd-totalCost);}else if(enemy_resources.range>0){enemy_resources.range=std::max(0,enemy_resources.range-totalCost);}else {enemy_resources.procedure=std::max(0,enemy_resources.procedure-totalCost);} #define TranslateUnit(type) \ case UnitType::type:{ \ queuedUnits.emplace_back(std::make_shared(game.GetPGE(),pos,IMAGES,false)); \ }break; switch(unit){ TranslateUnit(MemoryAllocator) TranslateUnit(LeftShifter) TranslateUnit(RightShifter) TranslateUnit(BitRestorer) TranslateUnit(MemorySwapper) TranslateUnit(Corrupter) TranslateUnit(RAMBank) TranslateUnit(MemoryGuard) TranslateUnit(Refresher) TranslateUnit(Turret) TranslateUnit(_Platform) } } return true; } bool Scenario::MissionCompleted(){return false;} void Scenario::Update(){}; void Scenario::Draw(){ if(objective.length()>0){ game.GetPGE()->DrawShadowStringDecal({4,24},"Objective:"); game.GetPGE()->DrawShadowStringDecal({6,36},objective); game.GetPGE()->DrawShadowStringDecal({4,24},missionCompleted?"Objective Complete!":"Objective:",missionCompleted?GREEN:WHITE); vf2d textSize=game.GetPGE()->GetTextSize(objective); game.GetPGE()->DrawShadowStringDecal({6,36},objective,missionCompleted?GREEN:WHITE); if(missionCompleted&&missionFinishWaitTime==0){ game.GetPGE()->FillRectDecal(vf2d{6,36}+vf2d{0,textSize.y/2-1},{textSize.x,3}); std::string continueText="< Press [Spacebar] to continue >"; vf2d textScale={2,3}; vf2d textSize=game.GetPGE()->GetTextSizeProp(continueText)*textScale; game.GetPGE()->DrawShadowStringPropDecal(game.GetPGE()->GetScreenSize()/2-textSize/2+vf2d{0,72},continueText,{255,255,255,uint8_t(abs(sin(2*missionCompletedTimer))*255)},{0,0,0,uint8_t(abs(sin(2*missionCompletedTimer))*255)},textScale); if(game.GetPGE()->GetKey(SPACE).bPressed){ transitionToNextLevel=true; } } } Resources temp={0,0,0,0,0}; box.UpdateAndDraw({24,64},game.GetPGE(),temp,IMAGES,0,0); } void Scenario::SetCameraTarget(vf2d pos,bool instant){ if(instant){camera.SetMode(utils::Camera2D::Mode::Simple);} else {camera.SetMode(utils::Camera2D::Mode::LazyFollow);} targetPos=pos; camera.SetTarget(targetPos); camera.Update(game.GetPGE()->GetElapsedTime()); game.SetWorldOffset(camera.GetViewPosition()); } void Scenario::SetObjective(std::string objective){ this->objective=objective; } void Scenario::RevealTiles(vf2d pos){ for(int y=-1;y<=1;y++){ for(int x=-1;x<=1;x++){ vi2d basePos={int(pos.x+x*96),int(pos.y+y*96)}; TileManager::visibleTiles[{basePos.x/96,basePos.y/96}]=30; } } } Stage1::Stage1(std::vector>&units,std::vector>&IMAGES,std::vector>&SOUNDS,std::string&objective,TileTransformedView&game,GameFlags&flags) :Scenario(units,IMAGES,SOUNDS,objective,game,flags){} void Stage1::Start(){ flags.unitMetersGreyedOut=true; flags.playerInControl=false; nextLevel=LevelName::STAGE2; }; void Scenario::DisplayBox(std::string text,bool scaryHoodedFigure){ if(smallTimePass==1){ box.Initialize(text,{24,64},"",scaryHoodedFigure?IMAGES[SPOOK_HOODED_FIGURE].get():IMAGES[HOODED_FIGURE].get(),{378,28},SOUNDS[Sound::VOICEOVER].get()); } } void Stage1::Update(){ switch(state){ case 0:{ DisplayBox("Hello Hacker, thank you for taking on this request for me."); if(box.bPressed){ state=1; } }break; case 1:{ DisplayBox("It appears we have no time to waste, many sectors are already infected and the virus spread will just keep getting worse."); if(box.bPressed){ state=2; SOUNDS[Sound::PING]->PlayCentered(); RevealTiles({320,320}); } }break; case 2:{ SetCameraTarget({320,320}); if(camera.ReachedTarget()){ state=3; } }break; case 3:{ DisplayBox("Your mission is to take out all the RAM banks from the system, this will stop any further creation and spread of viruses."); if(box.bPressed){ state=4; } }break; case 4:{ SetCameraTarget({128,128}); if(camera.ReachedTarget()){ state=5; } }break; case 5:{ DisplayBox("The yellow bars represent units' allocated Health memory. Take out the enemies' and make sure you always have at least 1 bit of it."); if(box.bPressed){ state=6; } }break; case 6:{ DisplayBox("Drag over your target unit and then select a target location via right-click."); if(box.bPressed){ state=7; } }break; case 7:{ SetObjective("Defeat the RAM bank"); DisplayBox("That should be all you need for now. I'll be back after my coffee break."); if(box.bPressed){ state=8; box.SetVisible(false); flags.playerInControl=true; } }break; case 8:{ }break; } }; bool Stage1::MissionCompleted(){ for(auto&u:units){ if(!u->IsFriendly()&&u->IsRAMBank()){ return false; } } return true; } Stage2::Stage2(std::vector>&units,std::vector>&IMAGES,std::vector>&SOUNDS,std::string&objective,TileTransformedView&game,GameFlags&flags) :Scenario(units,IMAGES,SOUNDS,objective,game,flags){} void Stage2::Start(){ flags.playerInControl=false; flags.limitedBuildOptions=true; flags.flashMemoryBar=false; SetCameraTarget({7*24,10*24},true); nextLevel=LevelName::STAGE3; }; void Stage2::Update(){ switch(state){ case 0:{ DisplayBox("You took care of that sector flawlessly Hacker, this next one needs a bit more hand-holding."); if(box.bPressed){ state=1; } }break; case 1:{ SetObjective("Create a RAM bank."); DisplayBox("We have analyzed the data from the RAM bank and can now create one. Go ahead and select the Platform here and construct one."); if(box.bPressed){ state=2; box.SetVisible(false); flags.playerInControl=true; } }break; case 2:{ for(auto&u:units){ if(u->IsRAMBank()&&u->IsFriendly()){ state=3; flags.playerInControl=false; SetObjective(""); break; } } }break; case 3:{ DisplayBox("Excellent, each RAM bank has the capability to allocate memory into the system. See that indicator down below?"); flags.flashMemoryBar=true; if(box.bPressed){ state=4; } }break; case 4:{ DisplayBox("This sector lets us allocate 30 bytes of RAM. That's 240 bits for the savvy folks out there."); if(box.bPressed){ state=5; flags.flashMemoryBar=false; } }break; case 5:{ DisplayBox("Some of it has already been used up by our RAM bank and other system resources... "); if(box.bPressed){ state=6; } }break; case 6:{ SetObjective("Build a Memory Allocator."); DisplayBox("To allocate 5 bits, select the RAM bank and click the Memory Allocator button.\n\nGive it a try now."); if(box.bPressed){ state=7; box.SetVisible(false); flags.playerInControl=true; } }break; case 7:{ for(auto&u:units){ if(u->IsAllocator()&&u->IsFriendly()){ state=8; flags.playerInControl=false; SetObjective(""); break; } } }break; case 8:{ SetObjective("Build a Left or Right Bit Shifter."); DisplayBox("Now select the memory allocator and let's make a Shifter unit."); if(box.bPressed){ state=9; box.SetVisible(false); flags.playerInControl=true; } }break; case 9:{ for(auto&u:units){ if(!u->IsAllocator()&&!u->IsRAMBank()&&u->IsFriendly()){ state=10; flags.playerInControl=false; SetObjective(""); break; } } }break; case 10:{ DisplayBox("The memory shifters will be your primary way to delete memory from units."); if(box.bPressed){ state=11; SOUNDS[Sound::ALARM]->PlayCentered(); } }break; case 11:{ SetCameraTarget({22*24,23*24}); RevealTiles({22*24,23*24}); if(camera.ReachedTarget()){ state=12; } }break; case 12:{ SetObjective("Defeat all enemy units."); DisplayBox("I have detected viruses in the system again. Please eradicate them and free system resources."); if(box.bPressed){ state=13; flags.playerInControl=true; box.SetVisible(false); } }break; } }; bool Stage2::MissionCompleted(){ for(auto&u:units){ if(!u->IsFriendly()){ return false; } } return true; } Stage3::Stage3(std::vector>&units,std::vector>&IMAGES,std::vector>&SOUNDS,std::string&objective,TileTransformedView&game,GameFlags&flags) :Scenario(units,IMAGES,SOUNDS,objective,game,flags){} void Stage3::Start(){ flags.playerInControl=false; flags.unitMetersGreyedOut=true; flags.guideEnabled=false; flags.limitedBuildOptions=true; SetCameraTarget({3*24,6*24},true); oopsTimer=0.3; nextLevel=LevelName::STAGE4; }; void Stage3::Update(){ switch(state){ case 0:{ DisplayBox("I haven't touched on what the other meters on your units are, but they are important!"); if(box.bPressed){ SOUNDS[Sound::SWITCH]->PlayCentered(); flags.unitMetersGreyedOut=false; state=1; } }break; case 1:{ DisplayBox("The Blue bits indicates movement capabilities of a unit."); if(box.bPressed){ state=2; } }break; case 2:{ DisplayBox("The Green bits indicates the range of a unit."); if(box.bPressed){ state=3; } }break; case 3:{ DisplayBox("The Red bits are the attack speed bits."); if(box.bPressed){ state=4; } }break; case 4:{ DisplayBox("And Purple are the Procedure bits. Without these, your unit will fail to recall how to function."); if(box.bPressed){ state=5; } }break; case 5:{ DisplayBox("As units attack each other, their bits are going to get shuffled around, impeding their ability to perform."); if(box.bPressed){ state=6; } }break; case 6:{ DisplayBox("Your immediate goal is to always take out the Yellow bits but sometimes taking out other bits is important too."); if(box.bPressed){ SOUNDS[Sound::SWITCH]->PlayCentered(); flags.guideEnabled=true; state=7; } }break; case 7:{ DisplayBox("I'll leave a guide by the map in case your memory betrays you."); if(box.bPressed){ state=8; } }break; case 8:{ SetObjective("Defeat all units"); DisplayBox("You now have access to more units as well. Do check them out!"); if(box.bPressed){ SetObjective("Defeat all units (Oops)"); state=9; } }break; case 9:{ oopsTimer=std::max(0.f,oopsTimer-game.GetPGE()->GetElapsedTime()); if(oopsTimer==0){ SetObjective("Defeat all enemy units."); state=10; flags.limitedBuildOptions=false; flags.playerInControl=true; box.SetVisible(false); } }break; case 10:{ }break; } }; bool Stage3::MissionCompleted(){ for(auto&u:units){ if(!u->IsFriendly()){ return false; } } return true; } Stage4::Stage4(std::vector>&units,std::vector>&IMAGES,std::vector>&SOUNDS,std::string&objective,TileTransformedView&game,GameFlags&flags) :Scenario(units,IMAGES,SOUNDS,objective,game,flags){} void Stage4::Start(){ nextLevel=LevelName::STAGE5; flags.playerInControl=false; flags.guideEnabled=true; flags.limitedBuildOptions=false; flags.unitMetersGreyedOut=false; flags.flashMemoryBar=false; SetCameraTarget({4*24,4*24},true); }; void Stage4::Update(){ switch(state){ case 0:{ DisplayBox("Hacker, I have unfortunate news. I can't supply you with any more bits to construct things with."); if(box.bPressed){ state=1; } }break; case 1:{ DisplayBox("You've only got 3 health bits to start with this time. To allocate memory you always need at least 5 bits of memory."); if(box.bPressed){ state=2; } }break; case 2:{ SetCameraTarget({8*24,1*24}); if(camera.ReachedTarget()){ state=3; } }break; case 3:{ DisplayBox("But we can collect bits from the system using these collection points."); if(box.bPressed){ state=4; } }break; case 4:{ DisplayBox("Simply bring over any unit to these and attach them to it. They will start providing you with system resources to make what you need."); if(box.bPressed){ state=5; } }break; case 5:{ flags.flashMemoryBar=true; SetObjective("Setup units at collection points"); DisplayBox("Remember that the system has limited memory available. You'll always be fighting for free space from the system."); if(box.bPressed){ state=6; box.SetVisible(false); flags.playerInControl=true; } }break; case 6:{ collectorsAttached=0; for(auto&u:units){ if(u->IsAttached()){ collectorsAttached++; } } if(collectorsAttached>=2){ std::shared_ptrtarget; for(auto&u:units){ if(u->IsRAMBank()){ target=u; } } state=7; SOUNDS[Sound::ALARM]->PlayCentered(); flags.playerInControl=false; auto u1=std::make_shared(game.GetPGE(),vf2d{4*24,10*24},IMAGES,false); auto u2=std::make_shared(game.GetPGE(),vf2d{4*24,10*24},IMAGES,false); auto u3=std::make_shared(game.GetPGE(),vf2d{4*24,10*24},IMAGES,false); units.push_back(u1); units.push_back(u2); units.push_back(u3); u1->SetTargetUnit(target); u2->SetTargetUnit(target); u3->SetTargetUnit(target); } }break; case 7:{ SetCameraTarget({4*24,4*24}); if(camera.ReachedTarget()){ state=8; SOUNDS[Sound::WIND]->PlayCentered(); } }break; case 8:{ DisplayBox("An ambush...? I, WHAT? -- THIS IS NOT-"); if(box.bPressed){ state=9; box.SetVisible(false); nextLevelTimer=5; } }break; case 9:{ nextLevelTimer=std::max(0.f,nextLevelTimer-game.GetPGE()->GetElapsedTime()); if(nextLevelTimer==0){ state=10; } }break; case 10:{ transitionToNextLevel=true; state=11; }break; } }; bool Stage4::MissionCompleted(){ return false; } Stage5::Stage5(std::vector>&units,std::vector>&IMAGES,std::vector>&SOUNDS,std::string&objective,TileTransformedView&game,GameFlags&flags) :Scenario(units,IMAGES,SOUNDS,objective,game,flags){} void Stage5::Start(){ SetCameraTarget({4*24,4*24},true); flags.playerInControl=true; SetObjective("Defeat all enemy RAM banks."); nextLevel=LevelName::STAGE6; }; void Stage5::Update(){ switch(state){ case 0:{ }break; } }; bool Stage5::MissionCompleted(){ for(auto&u:units){ if(!u->IsFriendly()&&u->IsRAMBank()){ return false; } } return true; } Stage6::Stage6(std::vector>&units,std::vector>&IMAGES,std::vector>&SOUNDS,std::string&objective,TileTransformedView&game,GameFlags&flags) :Scenario(units,IMAGES,SOUNDS,objective,game,flags){} void Stage6::Start(){ SetCameraTarget({4*24,4*24},true); flags.playerInControl=true; SetObjective("Defeat all enemy RAM banks."); nextLevel=LevelName::STAGE7; }; void Stage6::Update(){ switch(state){ case 0:{ }break; } }; bool Stage6::MissionCompleted(){ for(auto&u:units){ if(!u->IsFriendly()&&u->IsRAMBank()){ return false; } } return true; } Stage7::Stage7(std::vector>&units,std::vector>&IMAGES,std::vector>&SOUNDS,std::string&objective,TileTransformedView&game,GameFlags&flags) :Scenario(units,IMAGES,SOUNDS,objective,game,flags){} void Stage7::Start(){ SetCameraTarget({4*24,4*24},true); flags.playerInControl=true; SetObjective("Defeat all enemy units."); nextLevel=LevelName::STAGE8; }; void Stage7::Update(){ switch(state){ case 0:{ }break; } }; bool Stage7::MissionCompleted(){ for(auto&u:units){ if(!u->IsFriendly()){ return false; } } return true; } Stage8::Stage8(std::vector>&units,std::vector>&IMAGES,std::vector>&SOUNDS,std::string&objective,TileTransformedView&game,GameFlags&flags) :Scenario(units,IMAGES,SOUNDS,objective,game,flags){} void Stage8::Start(){ SetCameraTarget({4*24,4*24},true); flags.playerInControl=true; SetObjective("Defeat all enemy units."); nextLevel=LevelName::FINISH; }; void Stage8::Update(){ switch(state){ case 0:{ }break; } }; bool Stage8::MissionCompleted(){ for(auto&u:units){ if(!u->IsFriendly()){ return false; } } return true; }