You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1123 lines
39 KiB
1123 lines
39 KiB
#define OLC_PGE_APPLICATION
|
|
#define OLC_SOUNDWAVE
|
|
#define OLC_PGEX_TRANSFORMEDVIEW
|
|
#define AUDIO_LISTENER_IMPLEMENTATION
|
|
#define AUDIO_SOURCE_IMPLEMENTATION
|
|
#define OLC_PGEX_QUICKGUI
|
|
|
|
#define SPLASH_ENABLED
|
|
#ifdef SPLASH_ENABLED
|
|
#define OLC_PGEX_SPLASHSCREEN
|
|
#endif
|
|
|
|
#include "olcUTIL_Geometry2D.h"
|
|
#include "TileManager.h"
|
|
#include "util.h"
|
|
#include "Scenario.h"
|
|
|
|
VirusAttack::VirusAttack()
|
|
{
|
|
// Name your application
|
|
sAppName = "Virus Attack";
|
|
}
|
|
|
|
void VirusAttack::InitializeImages(){
|
|
auto LoadImage=[&](Image img,std::string filepath,bool filter=false,bool clamp=true){
|
|
IMAGES[img]=std::make_unique<Renderable>();
|
|
IMAGES[img]->Load(filepath,nullptr,filter,clamp);
|
|
};
|
|
LoadImage(TILE,"assets/tile.png",false,false);
|
|
LoadImage(MINIMAP_HUD,"assets/minimap_hud.png");
|
|
LoadImage(OUTLINE,"assets/outline.png");
|
|
LoadImage(VIRUS_IMG1,"assets/unit.png");
|
|
LoadImage(SELECTION_CIRCLE,"assets/selection_circle.png");
|
|
LoadImage(MEMORY_COLLECTION_POINT,"assets/memory_collection_point.png");
|
|
LoadImage(LEFT_SHIFTER,"assets/left_shifter.png");
|
|
LoadImage(RIGHT_SHIFTER,"assets/right_shifter.png");
|
|
LoadImage(BIT_RESTORER,"assets/bit_restorer.png");
|
|
LoadImage(MEMORY_SWAPPER,"assets/memory_swapper.png");
|
|
LoadImage(CORRUPTER,"assets/corrupter.png");
|
|
LoadImage(UNIT_ALLOCATOR,"assets/shell.png");
|
|
LoadImage(RAM_BANK,"assets/ram_bank.png");
|
|
LoadImage(RANGE_INDICATOR,"assets/range_indicator.png");
|
|
LoadImage(DOWN_ARROW,"assets/down_arrow.png");
|
|
LoadImage(RED_X,"assets/red_x.png");
|
|
LoadImage(RLD,"assets/rld.png");
|
|
LoadImage(PRC,"assets/prc.png");
|
|
LoadImage(RNG,"assets/rng.png");
|
|
LoadImage(SPD,"assets/spd.png");
|
|
LoadImage(TARGETING_LINE,"assets/targetLine.png");
|
|
LoadImage(ATTACKING_LINE,"assets/attackLine.png");
|
|
LoadImage(RLD_ICON,"assets/rld_icon.png");
|
|
LoadImage(PRC_ICON,"assets/prc_icon.png");
|
|
LoadImage(RNG_ICON,"assets/rng_icon.png");
|
|
LoadImage(SPD_ICON,"assets/spd_icon.png");
|
|
LoadImage(RESOURCE,"assets/material.png");
|
|
LoadImage(MEMORY_COLLECTION_POINT_HIGHLIGHT,"assets/memory_collection_point_highlight.png");
|
|
LoadImage(MEMORY_GUARD,"assets/memoryguard.png");
|
|
LoadImage(REFRESHER,"assets/refresher.png");
|
|
LoadImage(TURRET,"assets/turret.png");
|
|
LoadImage(PLATFORM,"assets/platform.png");
|
|
LoadImage(GUARD_ICON,"assets/shieldIcon.png");
|
|
LoadImage(GUIDE,"assets/guide.png");
|
|
LoadImage(ROUND_BAR,"assets/round_bar.png");
|
|
LoadImage(SEGMENT_BAR,"assets/segmentBar.png",false,false);
|
|
LoadImage(HOODED_FIGURE,"assets/hooded_figure.png");
|
|
LoadImage(SPOOK_HOODED_FIGURE,"assets/spook_hooded_figure.png");
|
|
LoadImage(RESTART,"assets/restart.png");
|
|
LoadImage(RESTART_HOVER,"assets/restartHover.png");
|
|
}
|
|
|
|
void VirusAttack::InitializeLevelData(){
|
|
#pragma region Stage 1
|
|
{
|
|
//Stage 1 data.
|
|
LevelName stage=STAGE1;
|
|
levelData[stage].name=stage;
|
|
levelData[stage].cameraStart={96,96};
|
|
levelData[stage].worldZoom={1,1};
|
|
levelData[stage].scenario=scenarios[0];
|
|
levelData[stage].levelColor=DARK_RED;
|
|
levelData[stage].size={24,24};
|
|
levelData[stage].bgm=Sound::GRAVITY;
|
|
levelData[stage].player_starting_resources={500,500,500,500,500};
|
|
levelData[stage].enemy_starting_resources={0,0,0,0,0};
|
|
{
|
|
std::vector<UnitData>&units=levelData[stage].unitPlacement;
|
|
std::vector<CPData>&collectionPoints=levelData[stage].cpPlacement;
|
|
|
|
units.push_back({UnitType::LeftShifter,vf2d{128,128},true});
|
|
units.push_back({UnitType::LeftShifter,vf2d{128,128},true});
|
|
units.push_back({UnitType::RAMBank,vf2d{320,320},false});
|
|
}
|
|
}
|
|
#pragma endregion
|
|
#pragma region Stage 2
|
|
{
|
|
//Stage 2 data.
|
|
LevelName stage=STAGE2;
|
|
levelData[stage].name=stage;
|
|
levelData[stage].cameraStart={96,96};
|
|
levelData[stage].worldZoom={1,1};
|
|
levelData[stage].size={16,16};
|
|
levelData[stage].scenario=scenarios[1];
|
|
levelData[stage].levelColor=DARK_GREEN;
|
|
levelData[stage].bgm=Sound::GRAVITY;
|
|
levelData[stage].player_starting_resources={10,10,10,10,10};
|
|
levelData[stage].enemy_starting_resources={0,0,0,0,0};
|
|
{
|
|
std::vector<UnitData>&units=levelData[stage].unitPlacement;
|
|
std::vector<CPData>&collectionPoints=levelData[stage].cpPlacement;
|
|
|
|
units.push_back({UnitType::RAMBank,vf2d{134,134},true});
|
|
|
|
units.push_back({UnitType::RAMBank,vf2d{1200,1200},false});
|
|
}
|
|
}
|
|
#pragma endregion
|
|
}
|
|
|
|
bool VirusAttack::OnUserCreate(){
|
|
srand(time(NULL));
|
|
SetPixelMode(Pixel::MASK);
|
|
|
|
game.Initialise(GetScreenSize());
|
|
|
|
InitializeImages();
|
|
|
|
unitCreationBox.Initialize("Hello world, this is a test of the textbox system.\nMaybe even with some newline characters snuck in there.",
|
|
{});
|
|
unitCreationBox.SetVisible(false);
|
|
testBox.Initialize("Hello world, this is a test of the textbox system.\nMaybe even with some newline characters snuck in there.",{});
|
|
testBox.SetVisible(false);
|
|
memoryAllocatorBox.Initialize(CONSTANT::MEMORY_ALLOCATOR_BOX_DISPLAY_STRING,{},CONSTANT::MEMORY_ALLOCATOR_BOX_HEADER_STRING);
|
|
memoryAllocatorBox.SetVisible(false);
|
|
platformCreationBox.SetVisible(false);
|
|
restartBox.SetVisible(false);
|
|
|
|
IMAGES[MINIMAP_OUTLINE]=std::make_unique<Renderable>();
|
|
IMAGES[MINIMAP_OUTLINE]->Create(64,64);
|
|
|
|
IMAGES[MATRIX]=std::make_unique<Renderable>();
|
|
IMAGES[MATRIX]->Create(64,64,false,false);
|
|
IMAGES[MATRIX]->Sprite()->SetSampleMode(Sprite::PERIODIC);
|
|
|
|
AL.AudioSystemInit();
|
|
InitializeSounds();
|
|
|
|
InitializeGUIs();
|
|
InitializeScenarios();
|
|
InitializeLevelData();
|
|
|
|
LoadLevel(STAGE1);
|
|
currentLevel->scenario->Start();
|
|
return true;
|
|
}
|
|
|
|
void VirusAttack::LoadLevel(LevelName level){
|
|
Level&selectedLevel=levelData[level];
|
|
|
|
currentLevel=&selectedLevel;
|
|
|
|
WORLD_SIZE=selectedLevel.size;
|
|
|
|
game.SetWorldScale(selectedLevel.worldZoom);
|
|
game.SetWorldOffset(selectedLevel.cameraStart-vf2d(GetScreenSize())/2.f/game.GetWorldScale());
|
|
|
|
if(bgm==nullptr){
|
|
bgm=SOUNDS[selectedLevel.bgm].get();
|
|
bgm->PlayCentered(1,1,true);
|
|
} else
|
|
if(bgm!=SOUNDS[selectedLevel.bgm].get()){
|
|
bgm->Stop();
|
|
bgm=SOUNDS[selectedLevel.bgm].get();
|
|
bgm->PlayCentered(1,1,true);
|
|
}
|
|
player_resources=selectedLevel.player_starting_resources;
|
|
enemy_resources=selectedLevel.enemy_starting_resources;
|
|
|
|
TileManager::visibleTiles.clear();
|
|
std::for_each(units.begin(),units.end(),[&](auto&u){
|
|
u->OnDeath(SOUNDS);
|
|
});
|
|
units.clear();
|
|
collectionPoints.clear();
|
|
|
|
for(auto&u:selectedLevel.unitPlacement){
|
|
#define TranslateUnit(type) \
|
|
case UnitType::type:{ \
|
|
units.push_back(std::make_unique<type>(this,u.pos,IMAGES,u.friendly)); \
|
|
}break;
|
|
|
|
switch(u.type){
|
|
TranslateUnit(LeftShifter)
|
|
TranslateUnit(RightShifter)
|
|
TranslateUnit(BitRestorer)
|
|
TranslateUnit(MemorySwapper)
|
|
TranslateUnit(Corrupter)
|
|
TranslateUnit(MemoryAllocator)
|
|
TranslateUnit(RAMBank)
|
|
TranslateUnit(_Platform)
|
|
TranslateUnit(MemoryGuard)
|
|
TranslateUnit(Refresher)
|
|
TranslateUnit(Turret)
|
|
default:{
|
|
std::cout<<"Could not load unit type "<<int(u.type)<<"!"<<std::endl;
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
for(auto&cp:selectedLevel.cpPlacement){
|
|
collectionPoints.push_back(std::make_unique<CollectionPoint>(this,cp.pos,cp.rot,*IMAGES[MEMORY_COLLECTION_POINT],cp.type));
|
|
}
|
|
|
|
randomBackgroundOffset={util::random(128),util::random(128)};
|
|
|
|
objective="";
|
|
}
|
|
|
|
void VirusAttack::InitializeGUIs(){
|
|
restartButton=new QuickGUI::TransparentImageButton(restartManager,*IMAGES[RESTART],*IMAGES[RESTART_HOVER],{1,1},{float(ScreenWidth()-32),12.f},{28,28});
|
|
|
|
unitCreationList.colNormal = olc::DARK_GREEN;
|
|
unitCreationList.colHover = olc::GREEN;
|
|
unitCreationList.colClick = olc::YELLOW;
|
|
unitCreationList.colDisable = olc::DARK_GREY;
|
|
unitCreationList.colBorder = olc::YELLOW;
|
|
unitCreationList.colText = olc::WHITE;
|
|
platformCreationList.CopyThemeFrom(unitCreationList);
|
|
leftShifterButton=new QuickGUI::ImageButton(unitCreationList,*IMAGES[LEFT_SHIFTER],{0.5,0.5},{16.f+32*0,float(ScreenHeight()-48)},{20,20});
|
|
rightShifterButton=new QuickGUI::ImageButton(unitCreationList,*IMAGES[RIGHT_SHIFTER],{0.5,0.5},{16.f+32*1,float(ScreenHeight()-48)},{20,20});
|
|
bitRestorerButton=new QuickGUI::ImageButton(unitCreationList,*IMAGES[BIT_RESTORER],{0.5,0.5},{16.f+32*2,float(ScreenHeight()-48)},{20,20});
|
|
memorySwapperButton=new QuickGUI::ImageButton(unitCreationList,*IMAGES[MEMORY_SWAPPER],{0.5,0.5},{16.f+32*3,float(ScreenHeight()-48)},{20,20});
|
|
corrupterButton=new QuickGUI::ImageButton(unitCreationList,*IMAGES[CORRUPTER],{0.5,0.5},{16.f+32*4,float(ScreenHeight()-48)},{20,20});
|
|
platformButton=new QuickGUI::ImageButton(unitCreationList,*IMAGES[PLATFORM],{0.25,0.25},{16.f+32*5,float(ScreenHeight()-48)},{20,20});
|
|
|
|
ramBankButton=new QuickGUI::ImageButton(platformCreationList,*IMAGES[RAM_BANK],{0.125,0.125},{float(ScreenWidth()-48),48.f+32*0},{20,20});
|
|
refresherButton=new QuickGUI::ImageButton(platformCreationList,*IMAGES[REFRESHER],{0.25,0.25},{float(ScreenWidth()-48),48.f+32*1},{20,20});
|
|
turretButton=new QuickGUI::ImageButton(platformCreationList,*IMAGES[TURRET],{0.25,0.25},{float(ScreenWidth()-48),48.f+32*2},{20,20});
|
|
memoryGuardButton=new QuickGUI::ImageButton(platformCreationList,*IMAGES[MEMORY_GUARD],{0.25,0.25},{float(ScreenWidth()-48),48.f+32*3},{20,20});
|
|
|
|
unitCreationList.DisplayAllControls(false);
|
|
platformCreationList.DisplayAllControls(false);
|
|
|
|
campaignStartButton=new QuickGUI::TransparentButton(mainMenu,"Start Campaign",{float(ScreenWidth()/2)-120.f,80+47.5f*0+10},{240,24},CONSTANT::INCREASE_VALUE_COLOR);
|
|
audioToggleButton=new QuickGUI::TransparentButton(mainMenu,"Audio: On",{float(ScreenWidth()/2)-120.f,80+47.5f*1+10},{240,24},CONSTANT::INCREASE_VALUE_COLOR);
|
|
difficultyToggleButton=new QuickGUI::TransparentButton(mainMenu,"Difficulty: Normal",{float(ScreenWidth()/2)-120.f,80+47.5f*2+10},{240,24},CONSTANT::INCREASE_VALUE_COLOR);
|
|
exitGameButton=new QuickGUI::TransparentButton(mainMenu,"Exit Game",{float(ScreenWidth()/2)-120.f,80+47.5f*3+10},{240,24},CONSTANT::INCREASE_VALUE_COLOR);
|
|
}
|
|
|
|
void VirusAttack::InitializeScenarios(){
|
|
scenarios.emplace_back(new Stage1(this));
|
|
scenarios.emplace_back(new Stage2(this));
|
|
scenarios.emplace_back(new Stage3(this));
|
|
scenarios.emplace_back(new Stage4(this));
|
|
scenarios.emplace_back(new Stage5(this));
|
|
scenarios.emplace_back(new Stage6(this));
|
|
scenarios.emplace_back(new Stage7(this));
|
|
scenarios.emplace_back(new Stage8(this));
|
|
}
|
|
|
|
void VirusAttack::InitializeSounds(){
|
|
int soundIndex=0;
|
|
auto LoadSound=[&](Sound sound,std::string soundFilename){
|
|
SOUNDS[sound]=std::make_unique<Audio>();
|
|
SOUNDS[sound]->AL=&AL;
|
|
SOUNDS[sound]->LoadAudioSample(soundIndex,std::string("./assets/"+soundFilename).c_str());
|
|
soundIndex++;
|
|
};
|
|
|
|
LoadSound(Sound::HUM,"machine2.wav");
|
|
LoadSound(Sound::GRAVITY,"gravity.mp3");
|
|
LoadSound(Sound::COSMOS,"cosmos.mp3");
|
|
LoadSound(Sound::BOSS1,"boss1.mp3");
|
|
LoadSound(Sound::BOSS2,"boss2.mp3");
|
|
LoadSound(Sound::VOICEOVER,"voice.mp3");
|
|
LoadSound(Sound::PING,"ping.mp3");
|
|
}
|
|
|
|
bool VirusAttack::UnitCreationClickHandled(){
|
|
#define CheckClick(UnitClass,Button,Validator) \
|
|
if(Button->bPressed){ \
|
|
for(auto&u:units){ \
|
|
if(u->IsSelected()&&u->Validator()&&CanAfford(player_resources,UnitClass::resourceCost)) { \
|
|
std::unique_ptr<UnitClass>buildUnit=std::make_unique<UnitClass>(this,u->GetPos(),IMAGES,u->IsFriendly()); \
|
|
u->SetBuildUnit(CONSTANT::UNIT_BUILD_TIME,std::move(buildUnit)); \
|
|
ExpendResources(player_resources,UnitClass::resourceCost); \
|
|
CalculateUsedMemory(); \
|
|
} \
|
|
} \
|
|
return true; \
|
|
}
|
|
|
|
CheckClick(LeftShifter,leftShifterButton,IsAllocator)
|
|
CheckClick(RightShifter,rightShifterButton,IsAllocator)
|
|
CheckClick(BitRestorer,bitRestorerButton,IsAllocator)
|
|
CheckClick(MemorySwapper,memorySwapperButton,IsAllocator)
|
|
CheckClick(BitRestorer,bitRestorerButton,IsAllocator)
|
|
CheckClick(Corrupter,corrupterButton,IsAllocator)
|
|
CheckClick(_Platform,platformButton,IsAllocator)
|
|
CheckClick(RAMBank,ramBankButton,IsPlatform)
|
|
CheckClick(Refresher,refresherButton,IsPlatform)
|
|
CheckClick(Turret,turretButton,IsPlatform)
|
|
CheckClick(MemoryGuard,memoryGuardButton,IsPlatform)
|
|
return false;
|
|
}
|
|
#define EnableAndHoverCheck(UnitClass,Button,box,limited) \
|
|
Button->Enable(CanAfford(player_resources,UnitClass::resourceCost)&&(!limited||limited&&!limitedBuildOptions)); \
|
|
if(limited&&!limitedBuildOptions){Button->bVisible=false;} \
|
|
if(Button->bHover){ \
|
|
box.Initialize(UnitClass::unitDescription, GetMousePos(), UnitClass::unitName,nullptr,{120,36},nullptr,UnitClass::resourceCost); \
|
|
hovering=true; \
|
|
if(CanAfford(player_resources,UnitClass::resourceCost)){ \
|
|
box.SetBackgroundColor(CONSTANT::MESSAGE_BOX_DEFAULT_BACKCOL); \
|
|
} else { \
|
|
box.SetBackgroundColor(VERY_DARK_GREY/2); \
|
|
} \
|
|
}
|
|
|
|
void VirusAttack::UpdateUnitCreationListGUI(bool allocatorSelected){
|
|
unitCreationList.DisplayAllControls(allocatorSelected);
|
|
|
|
bool hovering=false;
|
|
EnableAndHoverCheck(LeftShifter,leftShifterButton,unitCreationBox,false)
|
|
EnableAndHoverCheck(RightShifter,rightShifterButton,unitCreationBox,false)
|
|
EnableAndHoverCheck(BitRestorer,bitRestorerButton,unitCreationBox,true)
|
|
EnableAndHoverCheck(MemorySwapper,memorySwapperButton,unitCreationBox,true)
|
|
EnableAndHoverCheck(BitRestorer,bitRestorerButton,unitCreationBox,true)
|
|
EnableAndHoverCheck(Corrupter,corrupterButton,unitCreationBox,true)
|
|
EnableAndHoverCheck(_Platform,platformButton,unitCreationBox,true)
|
|
|
|
if(!hovering){
|
|
unitCreationBox.SetVisible(false);
|
|
}
|
|
|
|
unitCreationList.Update(this);
|
|
}
|
|
|
|
void VirusAttack::UpdatePlatformCreationListGUI(bool platformSelected){
|
|
platformCreationList.DisplayAllControls(platformSelected);
|
|
bool hovering=false;
|
|
EnableAndHoverCheck(RAMBank,ramBankButton,platformCreationBox,false)
|
|
EnableAndHoverCheck(Refresher,refresherButton,platformCreationBox,true)
|
|
EnableAndHoverCheck(Turret,turretButton,platformCreationBox,true)
|
|
EnableAndHoverCheck(MemoryGuard,memoryGuardButton,platformCreationBox,true)
|
|
|
|
if(!hovering){
|
|
platformCreationBox.SetVisible(false);
|
|
}
|
|
|
|
platformCreationList.Update(this);
|
|
}
|
|
|
|
void VirusAttack::HandleDraggingSelection(){
|
|
auto NotClickingOnMinimap=[&](){return !(GetMouseX()>=ScreenWidth()-64&&GetMouseY()>=ScreenHeight()-64);};
|
|
bool allocatorSelected=false;
|
|
bool platformSelected=false;
|
|
bool memoryAllocatorBoxHovered=false;
|
|
for(auto&u:units){
|
|
u->UpdateGUIState(game,player_resources,memoryAllocatorBox,memoryAllocatorBoxHovered,GetTotalUsedMemory(),currentLevel->availableMemory);
|
|
if(u->IsSelected()){
|
|
if(u->IsAllocator()){
|
|
allocatorSelected=true;
|
|
} else
|
|
if(u->IsPlatform()){
|
|
platformSelected=true;
|
|
}
|
|
}
|
|
}
|
|
if(!memoryAllocatorBoxHovered)memoryAllocatorBox.SetVisible(false);
|
|
UpdateUnitCreationListGUI(allocatorSelected);
|
|
UpdatePlatformCreationListGUI(platformSelected);
|
|
if(GetMouse(0).bPressed){
|
|
if(NotClickingOnMinimap()){
|
|
for(auto&u:units){
|
|
if(u->ClickHandled(game,player_resources,units,IMAGES)){
|
|
goto skipLeftClick; //Break out early because this instance reported it handled a click for us.
|
|
}
|
|
}
|
|
if(UnitCreationClickHandled()){
|
|
goto skipLeftClick;
|
|
}
|
|
for(auto&u:units){
|
|
u->Deselect();
|
|
}
|
|
if(startingDragPos==CONSTANT::UNSELECTED){
|
|
startingDragPos=GetWorldMousePos();
|
|
}
|
|
}
|
|
}
|
|
skipLeftClick:
|
|
if(GetMouse(0).bReleased&&startingDragPos!=CONSTANT::UNSELECTED){
|
|
vf2d endDragPos=GetWorldMousePos();
|
|
if(endDragPos.x<startingDragPos.x){std::swap(startingDragPos.x,endDragPos.x);}
|
|
if(endDragPos.y<startingDragPos.y){std::swap(startingDragPos.y,endDragPos.y);}
|
|
geom2d::rect<float> selectionRegion(startingDragPos,endDragPos-startingDragPos);
|
|
for(auto&u:units){
|
|
if(u->IsFriendly()){
|
|
if(geom2d::overlaps(selectionRegion,geom2d::circle<float>(u->GetPos(),u->GetUnitSize().x/2))){
|
|
u->Select();
|
|
}
|
|
}
|
|
}
|
|
startingDragPos=CONSTANT::UNSELECTED;
|
|
}
|
|
}
|
|
|
|
vf2d VirusAttack::GetWorldMousePos(){
|
|
return game.ScreenToWorld(GetMousePos());
|
|
}
|
|
|
|
void VirusAttack::DrawSelectionRectangle(){
|
|
if(startingDragPos!=CONSTANT::UNSELECTED){
|
|
game.FillRectDecal(startingDragPos,GetWorldMousePos()-startingDragPos,{255,255,0,128});
|
|
}
|
|
}
|
|
|
|
void VirusAttack::HandleRightClickMove(){
|
|
if (GetMouse(1).bHeld){
|
|
bool selectedTarget=false;
|
|
if(GetMouseX()>=ScreenWidth()-64&&GetMouseY()>=ScreenHeight()-64){//Clicked on Minimap.
|
|
for(auto&u:units){
|
|
if(u->IsFriendly()&&u->IsSelected()&&u->CanMove()){
|
|
vf2d minimapTL=GetScreenSize()-vf2d{64,64};
|
|
vf2d minimapCenterClick=vf2d(GetMousePos()-minimapTL)/64*WORLD_SIZE*CONSTANT::TILE_SIZE-vf2d(GetScreenSize())/game.GetWorldScale()/2.f+vf2d(GetScreenSize()/2.f)/game.GetWorldScale();
|
|
u->SetTargetLocation(minimapCenterClick);
|
|
}
|
|
}
|
|
goto targetFound;
|
|
}
|
|
for(auto&u:units){
|
|
if(geom2d::overlaps(geom2d::circle<float>(u->GetPos(),u->GetUnitSize().x/2),GetWorldMousePos())){
|
|
for(auto&u2:units){
|
|
if(&u!=&u2){
|
|
if(!u->IsFriendly()&&u2->IsFriendly()&&u2->IsSelected()&&u2->CanInteractWithEnemies()){
|
|
u2->SetTargetUnit(u);
|
|
} else
|
|
if(u->IsFriendly()&&u2->IsFriendly()&&u2->IsSelected()&&u2->CanInteractWithAllies()){
|
|
u2->SetTargetUnit(u);
|
|
}
|
|
}
|
|
}
|
|
selectedTarget=true;
|
|
break;
|
|
}
|
|
}
|
|
if(!selectedTarget){
|
|
for(auto&u:units){
|
|
if(u->IsFriendly()&&u->IsSelected()&&u->CanMove()){
|
|
//First see if we can attach to a collection point.
|
|
for(auto&cp:collectionPoints){
|
|
geom2d::rect<float>cpRect=geom2d::rect<float>({cp->pos-cp->img.Sprite()->Size()/2,cp->img.Sprite()->Size()});
|
|
if(geom2d::overlaps(cpRect,GetWorldMousePos())){
|
|
if(cp->attachedUnit.expired()){
|
|
u->SetTargetCollectionPoint(cp,u);
|
|
goto targetFound; //We found a target, so now we can just leave.
|
|
}
|
|
}
|
|
}
|
|
|
|
//Okay, nothing to do here. Simply move to the selected position.
|
|
u->SetTargetLocation(GetWorldMousePos());
|
|
}
|
|
}
|
|
}
|
|
targetFound:
|
|
int a;
|
|
}
|
|
}
|
|
|
|
void VirusAttack::CollisionChecking(std::shared_ptr<Unit>u,std::shared_ptr<Unit>u2){
|
|
if(u!=u2&&geom2d::overlaps(geom2d::circle<float>(u->GetPos(),u->GetUnitSize().x/2),geom2d::circle<float>(u2->GetPos(),u2->GetUnitSize().x/2))){
|
|
geom2d::line<float>collisionLine(u->GetPos(),u2->GetPos()+vf2d{0.001,0.001});
|
|
float maxDist=u->GetUnitSize().x/2+u2->GetUnitSize().x/2;
|
|
float dist=maxDist-collisionLine.length();
|
|
vf2d dir=collisionLine.vector().norm();
|
|
if(u->IsMoveable()||(!u->IsMoveable()&&!u2->IsMoveable())){
|
|
u->SetPos(u->GetPos()-dir*dist/2);
|
|
}
|
|
if(u2->IsMoveable()||(!u->IsMoveable()&&!u2->IsMoveable())){
|
|
u2->SetPos(u2->GetPos()+dir*(dist)/2);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VirusAttack::IdentifyClosestTarget(std::weak_ptr<Unit>&closestUnit,float&closestDist,std::shared_ptr<Unit>u,std::shared_ptr<Unit>u2){
|
|
if(u==u2)return;
|
|
bool canInteract;
|
|
canInteract=
|
|
(u->IsFriendly()&&u->CanInteractWithEnemies()&&!u2->IsFriendly())||
|
|
(u->IsFriendly()&&u->CanInteractWithAllies()&&u2->IsFriendly()&&u->AutoAcquiresFriendlyTargets())||
|
|
(!u->IsFriendly()&&u->CanInteractWithEnemies()&&u2->IsFriendly())||
|
|
(!u->IsFriendly()&&u->CanInteractWithAllies()&&!u2->IsFriendly()&&u->AutoAcquiresFriendlyTargets());
|
|
if(canInteract){
|
|
geom2d::line<float>unitLine(u->GetPos(),u2->GetPos());
|
|
if(unitLine.length()<closestDist){
|
|
closestUnit=u2;
|
|
closestDist=unitLine.length();
|
|
}
|
|
}
|
|
}
|
|
|
|
void VirusAttack::DrawMinimap(){
|
|
DrawDecal(GetScreenSize()-IMAGES[MINIMAP_HUD]->Sprite()->Size(),IMAGES[MINIMAP_HUD]->Decal(),{1,1});
|
|
vf2d minimapTL=GetScreenSize()-vf2d{64,64};
|
|
vi2d worldPixelSize=WORLD_SIZE*CONSTANT::TILE_SIZE;
|
|
vf2d viewingTilesPct=vf2d{float(ScreenWidth()),float(ScreenHeight())}/CONSTANT::TILE_SIZE/WORLD_SIZE;
|
|
SetDrawTarget(IMAGES[MINIMAP_OUTLINE]->Sprite());
|
|
Clear(BLANK);
|
|
DrawRect((game.GetWorldOffset()/worldPixelSize*64),viewingTilesPct*64/game.GetWorldScale());
|
|
for(auto&cp:collectionPoints){
|
|
Pixel col;
|
|
switch(cp->type){
|
|
case HEALTH:{
|
|
col=CONSTANT::HEALTH_COLOR;
|
|
}break;
|
|
case ATKSPD:{
|
|
col=CONSTANT::ATKSPD_COLOR;
|
|
}break;
|
|
case MOVESPD:{
|
|
col=CONSTANT::MOVESPD_COLOR;
|
|
}break;
|
|
case RANGE:{
|
|
col=CONSTANT::RANGE_COLOR;
|
|
}break;
|
|
case PROCEDURE:{
|
|
col=CONSTANT::PROCEDURE_COLOR;
|
|
}break;
|
|
}
|
|
FillCircle(cp->pos/worldPixelSize*64-vf2d{1,0},1,col);
|
|
DrawCircle(cp->pos/worldPixelSize*64-vf2d{1,0},1,BLACK);
|
|
}
|
|
for(auto&u:units){
|
|
vf2d squareSize=u->GetUnitSize()/vf2d(WORLD_SIZE);
|
|
squareSize.x=std::round(std::max(1.f,squareSize.x));
|
|
squareSize.y=std::round(std::max(1.f,squareSize.y));
|
|
float colorMult=1;
|
|
if(u->IsAttached()){colorMult=0.5;}
|
|
FillRect(u->GetGhostPos()/worldPixelSize*64-squareSize,squareSize*2,(u->IsFriendly()?GREEN:RED)*colorMult);
|
|
}
|
|
IMAGES[MINIMAP_OUTLINE]->Decal()->Update();
|
|
SetDrawTarget(nullptr);
|
|
DrawDecal(minimapTL,IMAGES[MINIMAP_OUTLINE]->Decal());
|
|
}
|
|
|
|
void VirusAttack::HandlePanAndZoom(float fElapsedTime){
|
|
float speedScale=std::min(1.f,game.GetWorldScale().x);
|
|
bool canMouseScroll=!memoryAllocatorBox.IsVisible()&&!unitCreationBox.IsVisible()&&
|
|
(GetMouseY()<=ScreenHeight()-64||GetMouseX()<=ScreenWidth()-64
|
|
||GetMouseScreenX()>=GetWindowPos().x+GetWindowSize().x||GetMouseScreenY()>=GetWindowPos().y+GetWindowSize().y);
|
|
if(GetKey(A).bHeld||canMouseScroll&&GetMouseScreenX()<=GetWindowPos().x+CONSTANT::SCROLL_BOUNDARY){
|
|
vf2d amt=vf2d{-300*fElapsedTime,0}/speedScale;
|
|
game.MoveWorldOffset(amt);
|
|
}
|
|
if(GetKey(W).bHeld||canMouseScroll&&GetMouseScreenY()<=GetWindowPos().y+CONSTANT::SCROLL_BOUNDARY+24){
|
|
game.MoveWorldOffset(vf2d{0,-300*fElapsedTime}/speedScale);
|
|
}
|
|
if(GetKey(S).bHeld||canMouseScroll&&GetMouseScreenY()>=GetWindowPos().y+GetWindowSize().y-CONSTANT::SCROLL_BOUNDARY){
|
|
game.MoveWorldOffset(vf2d{0,300*fElapsedTime}/speedScale);
|
|
}
|
|
if(GetKey(D).bHeld||canMouseScroll&&GetMouseScreenX()>=GetWindowPos().x+GetWindowSize().x-CONSTANT::SCROLL_BOUNDARY){
|
|
vf2d amt=vf2d{300*fElapsedTime,0}/speedScale;
|
|
game.MoveWorldOffset(amt);
|
|
}
|
|
|
|
if(GetMouseWheel()>0){
|
|
if(game.GetWorldScale().x<2){
|
|
game.ZoomAtScreenPos(1.25,GetMousePos());
|
|
}
|
|
} else
|
|
if(GetMouseWheel()<0){
|
|
if(game.GetWorldScale().x>0.5){
|
|
game.ZoomAtScreenPos(0.75,GetMousePos());
|
|
}
|
|
}
|
|
}
|
|
|
|
void VirusAttack::HandleMinimapClick(){
|
|
if(startingDragPos==CONSTANT::UNSELECTED&&GetMouse(0).bHeld&&GetMouseX()>=ScreenWidth()-64&&GetMouseY()>=ScreenHeight()-64){
|
|
vf2d minimapTL=GetScreenSize()-vf2d{64,64};
|
|
game.SetWorldOffset(vf2d(GetMousePos()-minimapTL)/64*WORLD_SIZE*CONSTANT::TILE_SIZE-vf2d(GetScreenSize())/game.GetWorldScale()/2.f);
|
|
}
|
|
vf2d offset=game.GetWorldOffset();
|
|
offset.x=std::clamp(float(offset.x),-ScreenWidth()/2/game.GetWorldScale().x,WORLD_SIZE.x*24-(ScreenWidth()/2/game.GetWorldScale().x));
|
|
offset.y=std::clamp(float(offset.y),-ScreenHeight()/2/game.GetWorldScale().y,WORLD_SIZE.y*24-(ScreenHeight()/2/game.GetWorldScale().y));
|
|
game.SetWorldOffset(offset);
|
|
|
|
}
|
|
|
|
void VirusAttack::UpdateMatrixTexture(float fElapsedTime){
|
|
if(matrixTimer==0){
|
|
activeLetters.emplace_back(vf2d{float(rand()%64),float(64)},util::random(-40)-20,matrixLetters[rand()%matrixLetters.size()]);
|
|
matrixTimer=util::random(0.125);
|
|
}
|
|
if(updatePixelsTimer==0){
|
|
SetDrawTarget(IMAGES[MATRIX]->Sprite());
|
|
Sprite*img=IMAGES[MATRIX]->Sprite();
|
|
for(int y=63;y>=0;y--){
|
|
for(int x=63;x>=0;x--){
|
|
Pixel col=img->GetPixel(x,y);
|
|
if(col.r>0){
|
|
if(x>0){
|
|
Pixel leftCol=img->GetPixel(x-1,y);
|
|
if(leftCol.r<col.r){
|
|
leftCol=PixelLerp(col,leftCol,0.125);
|
|
}
|
|
Draw(x-1,y,leftCol);
|
|
}
|
|
if(x<img->width-1){
|
|
Pixel rightCol=img->GetPixel(x+1,y);
|
|
if(rightCol.r<col.r){
|
|
rightCol=PixelLerp(col,rightCol,0.125);
|
|
}
|
|
Draw(x+1,y,rightCol);
|
|
}
|
|
col/=8;
|
|
Draw(x,y,col);
|
|
}
|
|
}
|
|
}
|
|
for(int y=0;y<64;y++){
|
|
Draw({0,y},img->GetPixel(1,y));
|
|
}
|
|
SetDrawTarget(nullptr);
|
|
updatePixelsTimer=0.1;
|
|
}
|
|
if(activeLetters.size()>0){
|
|
SetDrawTarget(IMAGES[MATRIX]->Sprite());
|
|
for(Letter&letter:activeLetters){
|
|
letter.pos.y+=letter.spd*fElapsedTime;
|
|
DrawString(letter.pos,std::string(1,letter.c));
|
|
}
|
|
SetDrawTarget(nullptr);
|
|
IMAGES[MATRIX]->Decal()->Update();
|
|
}
|
|
matrixTimer=std::max(0.f,matrixTimer-fElapsedTime);
|
|
updatePixelsTimer=std::max(0.f,updatePixelsTimer-fElapsedTime);
|
|
std::erase_if(activeLetters,[](Letter&letter){return letter.pos.y<-32;});
|
|
}
|
|
|
|
void VirusAttack::RenderCollectionPoints(CollectionPoint*cp){
|
|
geom2d::rect<float>cpRect=geom2d::rect<float>({cp->pos-cp->img.Sprite()->Size()/2,cp->img.Sprite()->Size()});
|
|
geom2d::rect<float>viewRegion=geom2d::rect<float>({game.GetWorldTL(),game.GetWorldVisibleArea()});
|
|
if(geom2d::overlaps(cpRect,viewRegion)){
|
|
Pixel col;
|
|
switch(cp->type){
|
|
case HEALTH:{
|
|
col=CONSTANT::HEALTH_COLOR;
|
|
}break;
|
|
case RANGE:{
|
|
col=CONSTANT::RANGE_COLOR;
|
|
}break;
|
|
case ATKSPD:{
|
|
col=CONSTANT::ATKSPD_COLOR;
|
|
}break;
|
|
case MOVESPD:{
|
|
col=CONSTANT::MOVESPD_COLOR;
|
|
}break;
|
|
case PROCEDURE:{
|
|
col=CONSTANT::PROCEDURE_COLOR;
|
|
}break;
|
|
}
|
|
game.DrawRotatedDecal(cp->pos,cp->img.Decal(),cp->rot,cp->img.Sprite()->Size()/2,{1,1},col);
|
|
if(geom2d::overlaps(cpRect,GetWorldMousePos())){
|
|
game.DrawRotatedDecal(cp->pos,IMAGES[MEMORY_COLLECTION_POINT_HIGHLIGHT]->Decal(),cp->rot,cp->img.Sprite()->Size()/2,{1,1},col);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool VirusAttack::OnUserUpdate(float fElapsedTime){
|
|
UpdateMatrixTexture(fElapsedTime);
|
|
switch(state){
|
|
#pragma region MAIN_MENU
|
|
case GameState::MAIN_MENU:{
|
|
mainMenu.Update(this);
|
|
mainMenu.DrawDecal(this);
|
|
}break;
|
|
#pragma endregion
|
|
#pragma region GAMEPLAY
|
|
case GameState::GAMEPLAY:{
|
|
if(playerInControl){
|
|
HandleDraggingSelection();
|
|
HandleRightClickMove();
|
|
HandlePanAndZoom(fElapsedTime);
|
|
HandleMinimapClick();
|
|
}
|
|
restartManager.Update(this);
|
|
HandleRestartButton(fElapsedTime);
|
|
PerformLevelTransition(fElapsedTime);
|
|
currentLevel->scenario->_Update();
|
|
AL.vecPos=game.ScreenToWorld(GetScreenSize()/2);
|
|
AL.fSoundFXVolume=std::min(1.f,game.GetWorldScale().x);
|
|
AL.OnUserUpdate(fElapsedTime);
|
|
|
|
for(auto&tile:TileManager::visibleTiles){
|
|
tile.second-=fElapsedTime;
|
|
}
|
|
|
|
std::erase_if(TileManager::visibleTiles,[](std::pair<vf2d,float> key){return key.second<=0;});
|
|
|
|
CalculateUsedMemory();
|
|
for(auto&u:units){
|
|
u->SaveMemory();
|
|
std::weak_ptr<Unit>closestUnit;
|
|
float closestDist=999999;
|
|
for(auto&u2:units){
|
|
IdentifyClosestTarget(closestUnit,closestDist,u,u2);
|
|
CollisionChecking(u,u2);
|
|
}
|
|
if(u->IsFriendly()){
|
|
for(int y=-2;y<3;y++){
|
|
for(int x=-2;x<3;x++){
|
|
if(abs(x)+abs(y)<=2){
|
|
TileManager::visibleTiles[u->GetPos()/24/4+vi2d(x,y)]=5;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
u->AttemptAttack(u,closestUnit,units,debuffIcons,IMAGES);
|
|
u->_Update(this,SOUNDS,player_resources,enemy_resources,queuedUnits,resourceGainTimer,resourceGainIcons,IMAGES);
|
|
}
|
|
|
|
std::erase_if(units,[&](std::shared_ptr<Unit>u){
|
|
if(u->GetHealth()==0){
|
|
u->OnDeath(SOUNDS);
|
|
deathAnimations.emplace_back(std::make_unique<DeathAnimation>(this,u->GetPos(),u->GetImage(),*IMAGES[MATRIX],u->IsFriendly()));
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
});
|
|
|
|
for(auto&queuedUnit:queuedUnits){
|
|
units.push_back(std::move(queuedUnit));
|
|
}
|
|
|
|
queuedUnits.clear();
|
|
|
|
DrawPartialDecal({0,0},GetScreenSize(),IMAGES[MATRIX]->Decal(),randomBackgroundOffset+game.GetWorldOffset()*(vf2d{32,32}/vf2d(GetScreenSize()))*game.GetWorldScale(),{32,32},Pixel{currentLevel->levelColor.r,currentLevel->levelColor.g,currentLevel->levelColor.b,164}/2);
|
|
game.DrawPartialDecal({0,0},WORLD_SIZE*CONSTANT::TILE_SIZE,IMAGES[TILE]->Decal(),{0,0},WORLD_SIZE*CONSTANT::TILE_SIZE,currentLevel->levelColor);
|
|
|
|
for(auto&u:units){
|
|
u->DrawRangeIndicator(this,game,IMAGES);
|
|
}
|
|
|
|
for(auto&u:units){
|
|
u->Draw(game,IMAGES);
|
|
if(u->IsGuarded()){
|
|
game.DrawDecal(u->GetPos()+vf2d{float(u->GetUnitSize().x/2),-float(u->GetUnitSize().y/2)}-vf2d{float(IMAGES[GUARD_ICON]->Sprite()->width),0.f}*0.375,IMAGES[GUARD_ICON]->Decal(),{0.375,0.375});
|
|
}
|
|
}
|
|
for(auto&deadUnit:deathAnimations){
|
|
deadUnit->Update(fElapsedTime);
|
|
deadUnit->Draw(game,this);
|
|
}
|
|
|
|
std::erase_if(deathAnimations,[](auto&u){return u->IsDone();});
|
|
|
|
for(auto&collectionPoint:collectionPoints){
|
|
collectionPoint->Update(this,*IMAGES[MATRIX]);
|
|
RenderCollectionPoints(collectionPoint.get());
|
|
}
|
|
|
|
for(auto&u:units){
|
|
u->DrawUnitDamageStats(this,game,IMAGES);
|
|
}
|
|
|
|
for(DebuffIcon&icon:debuffIcons){
|
|
icon.Update(fElapsedTime);
|
|
icon.Draw(game);
|
|
}
|
|
|
|
for(ResourceGainIcon&icon:resourceGainIcons){
|
|
icon.Update(fElapsedTime);
|
|
icon.Draw(game);
|
|
}
|
|
|
|
std::erase_if(debuffIcons,[](DebuffIcon&icon){return icon.lifetime<=0;});
|
|
std::erase_if(resourceGainIcons,[](ResourceGainIcon&icon){return icon.lifetime<=0;});
|
|
|
|
for(auto&u:units){
|
|
u->_DrawHud(game,IMAGES,unitMetersGreyedOut);
|
|
}
|
|
|
|
DrawSelectionRectangle();
|
|
RenderFogOfWar();
|
|
|
|
unitCreationList.DrawDecal(this);
|
|
platformCreationList.DrawDecal(this);
|
|
restartManager.DrawDecal(this);
|
|
|
|
DrawSystemMemoryBar(fElapsedTime);
|
|
DrawResourceBar(fElapsedTime);
|
|
if(guideEnabled){
|
|
DrawDecal({float(ScreenWidth()-74-IMAGES[GUIDE]->Sprite()->width*0.75),float(ScreenHeight()+6-IMAGES[GUIDE]->Sprite()->height*0.75)},IMAGES[GUIDE]->Decal(),{0.75,0.75});
|
|
}
|
|
DrawMinimap();
|
|
currentLevel->scenario->_Draw();
|
|
|
|
unitCreationBox.UpdateAndDraw(GetMousePos()+vi2d{8,-28},this,player_resources,IMAGES,GetTotalUsedMemory(),currentLevel->availableMemory);
|
|
testBox.UpdateAndDraw(GetMousePos()-testBox.GetSize()/2,this,player_resources,IMAGES,GetTotalUsedMemory(),currentLevel->availableMemory);
|
|
memoryAllocatorBox.UpdateAndDraw(GetMousePos()+vi2d{8,-28},this,player_resources,IMAGES,GetTotalUsedMemory(),currentLevel->availableMemory);
|
|
platformCreationBox.UpdateAndDraw(GetMousePos()+vi2d{8,-28},this,player_resources,IMAGES,GetTotalUsedMemory(),currentLevel->availableMemory);
|
|
|
|
std::sort(units.begin(),units.end(),[&](auto&u1,auto&u2){
|
|
float dist1=geom2d::line<float>(u1->GetGhostPos(),GetWorldMousePos()).length();
|
|
float dist2=geom2d::line<float>(u2->GetGhostPos(),GetWorldMousePos()).length();
|
|
return dist1>dist2;});
|
|
|
|
for(auto&u:units){
|
|
if(u->IsGuarded()){
|
|
bool changeOccured=false;
|
|
int changedBit=-1;
|
|
for(int i=0;i<u->memory.size();i++){
|
|
if(u->memory[i]!=u->savedMemory[i]){
|
|
changeOccured=true;
|
|
changedBit=i;
|
|
break;
|
|
}
|
|
}
|
|
if(changeOccured&&util::random(1)<=0.3){
|
|
u->memory[changedBit]=u->savedMemory[changedBit];
|
|
}
|
|
}
|
|
}
|
|
|
|
DrawPartialDecal({0,0},GetScreenSize(),IMAGES[MATRIX]->Decal(),randomBackgroundOffset+game.GetWorldOffset()*(vf2d{32,32}/vf2d(GetScreenSize()))*game.GetWorldScale(),{32,32},Pixel{currentLevel->levelColor.r,currentLevel->levelColor.g,currentLevel->levelColor.b,uint8_t(levelForegroundFade*255)}/2);
|
|
}break;
|
|
#pragma endregion
|
|
#pragma region COMPLETED
|
|
case GameState::COMPLETED:{
|
|
DrawPartialDecal({0,0},GetScreenSize(),IMAGES[MATRIX]->Decal(),randomBackgroundOffset+game.GetWorldOffset()*(vf2d{32,32}/vf2d(GetScreenSize()))*game.GetWorldScale(),{32,32},Pixel{currentLevel->levelColor.r,currentLevel->levelColor.g,currentLevel->levelColor.b,164}/2);
|
|
}break;
|
|
#pragma endregion
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void VirusAttack::DrawSystemMemoryBar(float fElapsedTime){
|
|
memoryChangeTimer=std::max(0.f,memoryChangeTimer-fElapsedTime);
|
|
memoryDisplayDelay-=fElapsedTime;
|
|
if(memoryDisplayDelay<=0){
|
|
if(memoryDisplayAmt>lastTotalMemory){
|
|
memoryDisplayAmt-=1;
|
|
} else
|
|
if(memoryDisplayAmt<lastTotalMemory){
|
|
memoryDisplayAmt+=1;
|
|
}
|
|
memoryDisplayDelay=0.01;
|
|
}
|
|
lastDisplayMemoryUpdateTimer-=fElapsedTime;
|
|
if(lastDisplayMemoryUpdateTimer<=0){
|
|
for(int i=0;i<playerUsedMemory.size();i++){
|
|
if(playerUsedMemory[i]<playerUsedDisplayMemory[i]){
|
|
playerUsedDisplayMemory[i]--;
|
|
} else
|
|
if(playerUsedMemory[i]>playerUsedDisplayMemory[i]){
|
|
playerUsedDisplayMemory[i]++;
|
|
}
|
|
}
|
|
for(int i=0;i<enemyUsedMemory.size();i++){
|
|
if(enemyUsedMemory[i]<enemyUsedDisplayMemory[i]){
|
|
enemyUsedDisplayMemory[i]--;
|
|
} else
|
|
if(enemyUsedMemory[i]>enemyUsedDisplayMemory[i]){
|
|
enemyUsedDisplayMemory[i]++;
|
|
}
|
|
}
|
|
lastDisplayMemoryUpdateTimer=0.01;
|
|
}
|
|
|
|
vf2d barPos={2,float(ScreenHeight()-7)};
|
|
float barOffset=0;
|
|
float barWidth=240;
|
|
float actualBarWidth=barWidth+2;
|
|
for(int i=0;i<playerUsedDisplayMemory.size();i++){
|
|
float barSegmentWidth=(float(playerUsedDisplayMemory[i])/currentLevel->availableMemory)*actualBarWidth;
|
|
Pixel col;
|
|
switch(i){
|
|
case 0:{
|
|
col=CONSTANT::HEALTH_COLOR;
|
|
}break;
|
|
case 1:{
|
|
col=CONSTANT::RANGE_COLOR;
|
|
}break;
|
|
case 2:{
|
|
col=CONSTANT::ATKSPD_COLOR;
|
|
}break;
|
|
case 3:{
|
|
col=CONSTANT::MOVESPD_COLOR;
|
|
}break;
|
|
case 4:{
|
|
col=CONSTANT::PROCEDURE_COLOR;
|
|
}break;
|
|
}
|
|
DrawPartialDecal(barPos+vf2d{barOffset+1,1.f},{barSegmentWidth,3},IMAGES[SEGMENT_BAR]->Decal(),{0,0},{float(playerUsedDisplayMemory[i]),3.f},col);
|
|
barOffset+=barSegmentWidth;
|
|
}
|
|
FillRectDecal(barPos+vf2d{barOffset+1,1.f},{2,3},GREEN);
|
|
barOffset=0;
|
|
for(int i=0;i<enemyUsedDisplayMemory.size();i++){
|
|
float barSegmentWidth=(float(enemyUsedDisplayMemory[i])/currentLevel->availableMemory)*actualBarWidth;
|
|
Pixel col;
|
|
switch(i){
|
|
case 0:{
|
|
col=CONSTANT::HEALTH_COLOR;
|
|
}break;
|
|
case 1:{
|
|
col=CONSTANT::RANGE_COLOR;
|
|
}break;
|
|
case 2:{
|
|
col=CONSTANT::ATKSPD_COLOR;
|
|
}break;
|
|
case 3:{
|
|
col=CONSTANT::MOVESPD_COLOR;
|
|
}break;
|
|
case 4:{
|
|
col=CONSTANT::PROCEDURE_COLOR;
|
|
}break;
|
|
}
|
|
DrawPartialDecal(barPos+vf2d{barOffset+actualBarWidth+3-barSegmentWidth,1.f},{barSegmentWidth,3},IMAGES[SEGMENT_BAR]->Decal(),{0,0},{float(enemyUsedDisplayMemory[i]),3.f},col);
|
|
barOffset-=barSegmentWidth;
|
|
}
|
|
FillRectDecal(barPos+vf2d{barOffset+actualBarWidth+3-2,1.f},{2,3},RED);
|
|
|
|
DrawPartialDecal(barPos,{3,5},IMAGES[ROUND_BAR]->Decal(),{0,0},{3,5});
|
|
for(int i=barPos.x+3;i<barWidth;i++){
|
|
DrawPartialDecal(barPos+vf2d{3,0},{barWidth,5},IMAGES[ROUND_BAR]->Decal(),{2,0},{1,5});
|
|
}
|
|
DrawPartialDecal(barPos+vf2d{3+barWidth,0},{3,5},IMAGES[ROUND_BAR]->Decal(),{2,0},{3,5});
|
|
vf2d textPos=barPos+vf2d{barWidth+6+4,0};
|
|
if(GetTotalUsedMemory()>lastTotalMemory){
|
|
memoryIncreased=true;
|
|
memoryChangeTimer=2;
|
|
} else
|
|
if(GetTotalUsedMemory()<lastTotalMemory){
|
|
memoryIncreased=false;
|
|
memoryChangeTimer=2;
|
|
}
|
|
lastTotalMemory=GetTotalUsedMemory();
|
|
DrawShadowStringPropDecal(textPos,std::to_string(memoryDisplayAmt)+"/"+std::to_string(currentLevel->availableMemory)+"b",WHITE,PixelLerp(BLACK,memoryIncreased?CONSTANT::INCREASE_VALUE_COLOR:CONSTANT::DECREASE_VALUE_COLOR,memoryChangeTimer/2.0f),{0.6,0.6},0.6);
|
|
}
|
|
|
|
void VirusAttack::DrawResourceBar(float fElapsedTime){
|
|
for(int i=0;i<resourceGainTimer.size();i++){
|
|
resourceGainTimer[i]=std::max(0.f,resourceGainTimer[i]-fElapsedTime);
|
|
}
|
|
GradientFillRectDecal({0,0},{float(ScreenWidth()),12.f},BLACK,{VERY_DARK_BLUE.r,VERY_DARK_BLUE.g,VERY_DARK_BLUE.b,128},{VERY_DARK_BLUE.r,VERY_DARK_BLUE.g,VERY_DARK_BLUE.b,128},BLACK);
|
|
DrawRectDecal({0,0},{float(ScreenWidth()),12.f},{3, 194, 252});
|
|
auto DrawResourceDisplay=[&](int index,int resourceValue,int&previousResourceValue,int&displayResourceValue,Pixel col){
|
|
resourceDisplayValueUpdateTimer[index]-=fElapsedTime;
|
|
if(previousResourceValue<resourceValue){
|
|
resourceIncreased[index]=true;
|
|
resourceGainTimer[index]=2;
|
|
previousResourceValue=resourceValue;
|
|
} else
|
|
if(previousResourceValue>resourceValue){
|
|
resourceIncreased[index]=false;
|
|
resourceGainTimer[index]=2;
|
|
previousResourceValue=resourceValue;
|
|
}
|
|
if(resourceDisplayValueUpdateTimer[index]<=0){
|
|
if(displayResourceValue<resourceValue){
|
|
displayResourceValue++;
|
|
} else
|
|
if(displayResourceValue>resourceValue){
|
|
displayResourceValue--;
|
|
}
|
|
resourceDisplayValueUpdateTimer[index]=0.01;
|
|
}
|
|
DrawDecal({6.f+index*42,1.f},IMAGES[RESOURCE]->Decal(),{1,1},col);
|
|
DrawShadowStringDecal({6.f+index*42+20,2.f},std::to_string(displayResourceValue),col,PixelLerp(BLACK,resourceIncreased[index]?CONSTANT::INCREASE_VALUE_COLOR:CONSTANT::DECREASE_VALUE_COLOR,resourceGainTimer[index]/2.0f));
|
|
};
|
|
DrawResourceDisplay(0,player_resources.health,player_prev_resources.health,player_display_resources.health,CONSTANT::HEALTH_COLOR);
|
|
DrawResourceDisplay(1,player_resources.atkSpd,player_prev_resources.atkSpd,player_display_resources.atkSpd,CONSTANT::ATKSPD_COLOR);
|
|
DrawResourceDisplay(2,player_resources.moveSpd,player_prev_resources.moveSpd,player_display_resources.moveSpd,CONSTANT::MOVESPD_COLOR);
|
|
DrawResourceDisplay(3,player_resources.range,player_prev_resources.range,player_display_resources.range,CONSTANT::RANGE_COLOR);
|
|
DrawResourceDisplay(4,player_resources.procedure,player_prev_resources.procedure,player_display_resources.procedure,CONSTANT::PROCEDURE_COLOR);
|
|
}
|
|
|
|
void VirusAttack::RenderFogOfWar(){
|
|
for(int y=game.GetTopLeftTile().y/96-1;y<=game.GetBottomRightTile().y/96+1;y++){
|
|
for(int x=game.GetTopLeftTile().x/96-1;x<=game.GetBottomRightTile().x/96+1;x++){
|
|
if(TileManager::visibleTiles.count(vi2d{x,y})==0){
|
|
if(x>=0&&y>=0&&x<WORLD_SIZE.x/4&&y<WORLD_SIZE.y/4){
|
|
game.FillRectDecal(vf2d{float(x),float(y)}*96,{96,96},{0,0,0,128});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool VirusAttack::CanAfford(Resources&resources,std::vector<Memory>&unitCosts){
|
|
int totalMemoryCost=0;
|
|
for(Memory&mem:unitCosts){
|
|
switch(mem.type){
|
|
case HEALTH:{
|
|
if(resources.health<mem.size)return false;
|
|
}break;
|
|
case ATKSPD:{
|
|
if(resources.atkSpd<mem.size)return false;
|
|
}break;
|
|
case MOVESPD:{
|
|
if(resources.moveSpd<mem.size)return false;
|
|
}break;
|
|
case RANGE:{
|
|
if(resources.range<mem.size)return false;
|
|
}break;
|
|
case PROCEDURE:{
|
|
if(resources.procedure<mem.size)return false;
|
|
}break;
|
|
}
|
|
totalMemoryCost+=mem.size;
|
|
}
|
|
return totalMemoryCost+GetTotalUsedMemory()<=currentLevel->availableMemory;
|
|
}
|
|
|
|
void VirusAttack::ExpendResources(Resources&resources,std::vector<Memory>&unitCosts){
|
|
for(Memory&mem:unitCosts){
|
|
switch(mem.type){
|
|
case HEALTH:{
|
|
resources.health-=mem.size;
|
|
}break;
|
|
case ATKSPD:{
|
|
resources.atkSpd-=mem.size;
|
|
}break;
|
|
case MOVESPD:{
|
|
resources.moveSpd-=mem.size;
|
|
}break;
|
|
case RANGE:{
|
|
resources.range-=mem.size;
|
|
}break;
|
|
case PROCEDURE:{
|
|
resources.procedure-=mem.size;
|
|
}break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int VirusAttack::GetTotalUsedMemory(){
|
|
return GetPlayerUsedMemory()+GetEnemyUsedMemory();
|
|
}
|
|
int VirusAttack::GetPlayerUsedMemory(){
|
|
int sum=0;
|
|
for(int i=0;i<5;i++){
|
|
sum+=playerUsedMemory[i];
|
|
}
|
|
return sum;
|
|
}
|
|
int VirusAttack::GetEnemyUsedMemory(){
|
|
int sum=0;
|
|
for(int i=0;i<5;i++){
|
|
sum+=enemyUsedMemory[i];
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
void VirusAttack::CalculateUsedMemory(){
|
|
playerUsedMemory=enemyUsedMemory={0};
|
|
for(auto&u:units){
|
|
std::array<size_t,5>costs={u->health.size,u->range.size,u->atkSpd.size,u->moveSpd.size,u->procedure.size};
|
|
|
|
if(u->IsBuilding()){
|
|
costs={u->GetBuildUnit()->health.size,u->GetBuildUnit()->range.size,u->GetBuildUnit()->atkSpd.size,u->GetBuildUnit()->moveSpd.size,u->GetBuildUnit()->procedure.size};
|
|
}
|
|
|
|
std::array<int,5>&targetArr=u->IsFriendly()?playerUsedMemory:enemyUsedMemory;
|
|
for(int i=0;i<targetArr.size();i++){
|
|
targetArr[i]+=costs[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
bool VirusAttack::OnUserDestroy(){
|
|
std::for_each(scenarios.begin(),scenarios.end(),[](auto&scenario){delete scenario;});
|
|
return true;
|
|
}
|
|
|
|
void VirusAttack::PerformLevelTransition(float fElapsedTime){
|
|
if(levelToLoad!=currentLevel->name||reloadLevel){
|
|
levelForegroundFade=std::min(1.f,levelForegroundFade+fElapsedTime);
|
|
if(levelForegroundFade>=1){
|
|
LoadLevel(levelToLoad);
|
|
reloadLevel=false;
|
|
}
|
|
}else
|
|
if(levelForegroundFade>0){
|
|
levelForegroundFade=std::max(0.f,levelForegroundFade-fElapsedTime);
|
|
}
|
|
}
|
|
|
|
void VirusAttack::RestartLevel(){
|
|
reloadLevel=true;
|
|
}
|
|
|
|
void VirusAttack::HandleRestartButton(float fElapsedTime){
|
|
if(restartButton->bHover){
|
|
restartBox.Initialize("Click and hold to restart the level.",GetMousePos()+vf2d{0,10},"Restart Level",nullptr,{72,1});
|
|
restartBox.SetVisible(true);
|
|
if(restartButton->bPressed){
|
|
restartButtonHeldDown=true;
|
|
}
|
|
if(restartButtonHeldDown){
|
|
restartButtonHoldTime+=fElapsedTime;
|
|
if(restartButtonHoldTime>=CONSTANT::RESTART_BUTTON_HOLD_TIME){
|
|
RestartLevel();
|
|
restartButtonHeldDown=false;
|
|
restartButtonHoldTime=0;
|
|
}
|
|
}
|
|
}else{
|
|
restartBox.SetVisible(false);
|
|
restartButtonHeldDown=false;
|
|
restartButtonHoldTime=0;
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
VirusAttack app;
|
|
if (app.Construct(426, 320, 4, 4))
|
|
app.Start();
|
|
return 0;
|
|
} |