From 3126621645c9959744a5a23f7064e6905f3dd9d8 Mon Sep 17 00:00:00 2001 From: sigonasr2 Date: Sat, 26 Aug 2023 16:11:37 -0500 Subject: [PATCH] Add in basic units. RAMBank animation --- olcCodeJam2023Entry/Constant.cpp | 4 +- olcCodeJam2023Entry/Constant.h | 2 + olcCodeJam2023Entry/Image.h | 7 ++ olcCodeJam2023Entry/Info.txt | 3 + olcCodeJam2023Entry/Unit.cpp | 150 ++++++++++++++++++++++-- olcCodeJam2023Entry/Unit.h | 48 +++++++- olcCodeJam2023Entry/VirusAttack.cpp | 110 +++++++++-------- olcCodeJam2023Entry/VirusAttack.h | 2 + olcCodeJam2023Entry/assets/ram_bank.png | Bin 0 -> 2264 bytes olcCodeJam2023Entry/assets/shell.png | Bin 0 -> 6874 bytes 10 files changed, 259 insertions(+), 67 deletions(-) create mode 100644 olcCodeJam2023Entry/assets/ram_bank.png create mode 100644 olcCodeJam2023Entry/assets/shell.png diff --git a/olcCodeJam2023Entry/Constant.cpp b/olcCodeJam2023Entry/Constant.cpp index ffecd6a..75c2481 100644 --- a/olcCodeJam2023Entry/Constant.cpp +++ b/olcCodeJam2023Entry/Constant.cpp @@ -10,4 +10,6 @@ Pixel CONSTANT::PROCEDURE_COLOR={212, 11, 162}; vf2d CONSTANT::UNSELECTED={-99,-99}; vi2d CONSTANT::TILE_SIZE={24,24}; -vi2d CONSTANT::WORLD_SIZE={64,64}; \ No newline at end of file +vi2d CONSTANT::WORLD_SIZE={64,64}; + +float CONSTANT::SCROLL_BOUNDARY=36; \ No newline at end of file diff --git a/olcCodeJam2023Entry/Constant.h b/olcCodeJam2023Entry/Constant.h index 70a14db..aa73b4f 100644 --- a/olcCodeJam2023Entry/Constant.h +++ b/olcCodeJam2023Entry/Constant.h @@ -16,4 +16,6 @@ public: static vi2d TILE_SIZE; static vi2d WORLD_SIZE; + + static float SCROLL_BOUNDARY; }; \ No newline at end of file diff --git a/olcCodeJam2023Entry/Image.h b/olcCodeJam2023Entry/Image.h index 44fceae..c36ec41 100644 --- a/olcCodeJam2023Entry/Image.h +++ b/olcCodeJam2023Entry/Image.h @@ -10,5 +10,12 @@ enum Image{ SELECTION_CIRCLE, MATRIX, MEMORY_COLLECTION_POINT, + LEFT_SHIFTER, + RIGHT_SHIFTER, + BIT_RESTORER, + MEMORY_SWAPPER, + CORRUPTER, + UNIT_ALLOCATOR, + RAM_BANK, }; diff --git a/olcCodeJam2023Entry/Info.txt b/olcCodeJam2023Entry/Info.txt index d2d99e9..f6c70d3 100644 --- a/olcCodeJam2023Entry/Info.txt +++ b/olcCodeJam2023Entry/Info.txt @@ -8,6 +8,9 @@ Corrupter (Randomly destroys bits) Memory Structure (Allocators) RAM Bank (Creates new Memory Structures) has its own rate of creation based on Procedure amount. +Range: 2 Points = 1 Tile (24 pixels) 1 Point = 12 Pixels +Attack Speed: 1/(AtkSpd/2) attacks per second + Range/Attack indicators System has limited resources, both sides fight for resources. diff --git a/olcCodeJam2023Entry/Unit.cpp b/olcCodeJam2023Entry/Unit.cpp index 4910b10..20a258d 100644 --- a/olcCodeJam2023Entry/Unit.cpp +++ b/olcCodeJam2023Entry/Unit.cpp @@ -2,35 +2,153 @@ #include "Constant.h" #include "olcUTIL_Geometry2D.h" #include "TileManager.h" +#include "util.h" -BasicUnit::BasicUnit(vf2d pos,Renderable&img,bool friendly) +BasicUnit::BasicUnit(vf2d pos,std::map>&IMAGES,bool friendly) :Unit({ {HEALTH,4}, {RANGE,2}, {ATKSPD,2}, {MOVESPD,3}, {PROCEDURE,1}, - },pos,img,friendly){} + },pos,*IMAGES[VIRUS_IMG1],friendly){} void BasicUnit::Attack(Unit&victim){ victim<<=1; } -BasicUnit2::BasicUnit2(vf2d pos,Renderable&img,bool friendly) +BasicUnit2::BasicUnit2(vf2d pos,std::map>&IMAGES,bool friendly) :Unit({ {RANGE,2}, {ATKSPD,2}, {MOVESPD,3}, {PROCEDURE,1}, {HEALTH,4}, - },pos,img,friendly){} + },pos,*IMAGES[VIRUS_IMG1],friendly){} void BasicUnit2::Attack(Unit&victim){ } +LeftShifter::LeftShifter(vf2d pos,std::map>&IMAGES,bool friendly) + :Unit({ + {RANGE,2}, + {ATKSPD,2}, + {MOVESPD,3}, + {PROCEDURE,1}, + {HEALTH,4}, + },pos,*IMAGES[LEFT_SHIFTER],friendly){} + +void LeftShifter::Attack(Unit&victim){ + +} + +RightShifter::RightShifter(vf2d pos,std::map>&IMAGES,bool friendly) + :Unit({ + {HEALTH,4}, + {RANGE,2}, + {ATKSPD,2}, + {MOVESPD,3}, + {PROCEDURE,1}, + },pos,*IMAGES[RIGHT_SHIFTER],friendly){} + +void RightShifter::Attack(Unit&victim){ + +} + +BitRestorer::BitRestorer(vf2d pos,std::map>&IMAGES,bool friendly) + :Unit({ + {PROCEDURE,6}, + {RANGE,1}, + {ATKSPD,1}, + {MOVESPD,1}, + {HEALTH,2}, + },pos,*IMAGES[BIT_RESTORER],friendly){} + +void BitRestorer::Attack(Unit&victim){ + +} + +MemorySwapper::MemorySwapper(vf2d pos,std::map>&IMAGES,bool friendly) + :Unit({ + {RANGE,3}, + {ATKSPD,1}, + {HEALTH,3}, + {PROCEDURE,3}, + {MOVESPD,2}, + },pos,*IMAGES[MEMORY_SWAPPER],friendly){} + +void MemorySwapper::Attack(Unit&victim){ +} + +Corrupter::Corrupter(vf2d pos,std::map>&IMAGES,bool friendly) + :Unit({ + {ATKSPD,3}, + {RANGE,1}, + {PROCEDURE,8}, + {MOVESPD,4}, + {HEALTH,4}, + },pos,*IMAGES[CORRUPTER],friendly){} + +void Corrupter::Attack(Unit&victim){ + +} + +MemoryAllocator::MemoryAllocator(vf2d pos,std::map>&IMAGES,bool friendly) + :Unit({ + {RANGE,1}, + {ATKSPD,1}, + {MOVESPD,1}, + {PROCEDURE,1}, + {HEALTH,1}, + },pos,*IMAGES[UNIT_ALLOCATOR],friendly){} + +void MemoryAllocator::Attack(Unit&victim){ + +} + +RAMBank::RAMBank(PixelGameEngine*pge,vf2d pos,std::map>&IMAGES,bool friendly) + :Unit({ + {RANGE,0}, + {ATKSPD,0}, + {MOVESPD,0}, + {PROCEDURE,25}, + {HEALTH,16}, + },pos,*IMAGES[RAM_BANK],friendly),randomOffset({util::random(128),util::random(128)}),matrixImg(*IMAGES[MATRIX]), + originalImg(*IMAGES[RAM_BANK]){ + img.Create(IMAGES[RAM_BANK]->Sprite()->width,IMAGES[RAM_BANK]->Sprite()->height); + pge->SetDrawTarget(img.Sprite()); + pge->Clear(BLANK); + pge->DrawSprite({0,0},IMAGES[RAM_BANK]->Sprite()); + pge->SetDrawTarget(nullptr); +} + +void RAMBank::Attack(Unit&victim){ + +} + +void RAMBank::Update(PixelGameEngine*pge){ + pge->SetDrawTarget(img.Sprite()); + for(int y=0;yheight;y++){ + for(int x=0;xwidth;x++){ + Pixel col = originalImg.Sprite()->GetPixel(x,y); + if(col==WHITE){ + pge->Draw(x,y,matrixImg.Sprite()->GetPixel(int(x+randomOffset.x),int(y+randomOffset.y))); + } + } + } + img.Decal()->Update(); + pge->SetDrawTarget(nullptr); +} + +void RAMBank::Draw(TileTransformedView&game,std::map>&IMAGES){ + game.DrawRotatedDecal(GetGhostPos(),img.Decal(),0,img.Sprite()->Size()/2,{1,1},friendly?Pixel{192,192,255}:Pixel{255,192,192}); + if(IsSelected()){ + game.DrawRotatedDecal(GetGhostPos(),IMAGES[SELECTION_CIRCLE]->Decal(),0,IMAGES[SELECTION_CIRCLE]->Sprite()->Size()/2,vf2d(img.Sprite()->Size())/IMAGES[SELECTION_CIRCLE]->Sprite()->Size(),WHITE); + } +} Unit::Unit(std::vectormemory,vf2d pos,Renderable&img,bool friendly) :pos(pos),ghostPos(pos),img(img),friendly(friendly){ @@ -76,7 +194,7 @@ void Unit::Draw(TileTransformedView&game,std::map>&IMAGES){ - int initialBarX=ghostPos.x-GetMemorySize()/2*CONSTANT::BAR_SQUARE_SIZE.x; + int initialBarX=ghostPos.x-GetMemorySize()/2*CONSTANT::BAR_SQUARE_SIZE.x-CONSTANT::BAR_SQUARE_SIZE.x/2; int initialBarY=ghostPos.y-CONSTANT::BAR_SQUARE_SIZE.y-img.Sprite()->height/2-2; Pixel col=0; @@ -143,11 +261,11 @@ int Unit::GetMemorySize(){ return memory.size(); } -void Unit::Update(float fElapsedTime){ +void Unit::_Update(PixelGameEngine*pge){ if(!target.expired()){ auto ptrTarget=target.lock(); if(!InRange(ptrTarget)){ - SetPos(GetPos()+(ptrTarget->GetPos()-pos).norm()*GetMoveSpd()*24*fElapsedTime); + SetPos(GetPos()+(ptrTarget->GetPos()-pos).norm()*GetMoveSpd()*24*pge->GetElapsedTime()); } else { //TODO Attack here. } @@ -155,7 +273,7 @@ void Unit::Update(float fElapsedTime){ if(targetLoc!=CONSTANT::UNSELECTED){ float dist=geom2d::line(pos,targetLoc).length(); if(dist>24){ - SetPos(GetPos()+(targetLoc-pos).norm()*GetMoveSpd()*24*fElapsedTime); + SetPos(GetPos()+(targetLoc-pos).norm()*GetMoveSpd()*24*pge->GetElapsedTime()); } } @@ -177,11 +295,17 @@ void Unit::Update(float fElapsedTime){ }break; } } - SetPos(GetPos()+movementVel*fElapsedTime); - changeDirTimer=std::max(0.f,changeDirTimer-fElapsedTime); + SetPos(GetPos()+movementVel*pge->GetElapsedTime()); + changeDirTimer=std::max(0.f,changeDirTimer-pge->GetElapsedTime()); + } + + if(!GhostInFogOfWar()&&InFogOfWar()){ + HideGhost(); } - reloadTimer=std::max(0.f,reloadTimer-fElapsedTime); + reloadTimer=std::max(0.f,reloadTimer-pge->GetElapsedTime()); + + Update(pge); } std::vector operator <<(Unit&u,const int n){ @@ -277,9 +401,11 @@ void Unit::AttemptAttack(Unit*unit){ } } +void Unit::Update(PixelGameEngine*pge){} + void Unit::_Attack(Unit*finalTarget){ Attack(*finalTarget); - reloadTimer=1.f/GetAtkSpd(); + reloadTimer=1.f/(GetAtkSpd()/2.f); } bool Unit::InFogOfWar(){ diff --git a/olcCodeJam2023Entry/Unit.h b/olcCodeJam2023Entry/Unit.h index 47b70ed..d5c5c76 100644 --- a/olcCodeJam2023Entry/Unit.h +++ b/olcCodeJam2023Entry/Unit.h @@ -34,7 +34,7 @@ public: int GetMemorySize(); std::vectormemory; std::vectorghostMemory; - void Update(float fElapsedTime); + virtual void Update(PixelGameEngine*pge); virtual void Attack(Unit&victim)=0; virtual void Draw(TileTransformedView&game,std::map>&IMAGES); virtual void DrawHud(TileTransformedView&game,std::map>&IMAGES); @@ -53,6 +53,7 @@ public: bool GhostInFogOfWar(); void HideGhost(); vf2d GetGhostPos(); + void _Update(PixelGameEngine*pge); std::vector& operator <<=(const int n){ for(int i=0;i>&IMAGES,bool friendly=false); void Attack(Unit&victim)override; }; struct BasicUnit2:Unit{ - BasicUnit2(vf2d pos,Renderable&img,bool friendly=false); + BasicUnit2(vf2d pos,std::map>&IMAGES,bool friendly=false); void Attack(Unit&victim)override; +}; + +struct LeftShifter:Unit{ + LeftShifter(vf2d pos,std::map>&IMAGES,bool friendly=false); + void Attack(Unit&victim)override; +}; + +struct RightShifter:Unit{ + RightShifter(vf2d pos,std::map>&IMAGES,bool friendly=false); + void Attack(Unit&victim)override; +}; + +struct BitRestorer:Unit{ + BitRestorer(vf2d pos,std::map>&IMAGES,bool friendly=false); + void Attack(Unit&victim)override; +}; + +struct MemorySwapper:Unit{ + MemorySwapper(vf2d pos,std::map>&IMAGES,bool friendly=false); + void Attack(Unit&victim)override; +}; + +struct Corrupter:Unit{ + Corrupter(vf2d pos,std::map>&IMAGES,bool friendly=false); + void Attack(Unit&victim)override; +}; + +struct MemoryAllocator:Unit{ + MemoryAllocator(vf2d pos,std::map>&IMAGES,bool friendly=false); + void Attack(Unit&victim)override; +}; + +struct RAMBank:Unit{ + vf2d randomOffset; + Renderable img; + Renderable&originalImg; + Renderable&matrixImg; + RAMBank(PixelGameEngine*pge,vf2d pos,std::map>&IMAGES,bool friendly=false); + void Update(PixelGameEngine*pge)override; + void Attack(Unit&victim)override; + void Draw(TileTransformedView&game,std::map>&IMAGES)override; }; \ No newline at end of file diff --git a/olcCodeJam2023Entry/VirusAttack.cpp b/olcCodeJam2023Entry/VirusAttack.cpp index 774c9ae..46d1e95 100644 --- a/olcCodeJam2023Entry/VirusAttack.cpp +++ b/olcCodeJam2023Entry/VirusAttack.cpp @@ -24,6 +24,13 @@ void VirusAttack::InitializeImages(){ 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"); } bool VirusAttack::OnUserCreate(){ @@ -40,14 +47,14 @@ bool VirusAttack::OnUserCreate(){ IMAGES[MATRIX]->Create(64,64,false,false); IMAGES[MATRIX]->Sprite()->SetSampleMode(Sprite::PERIODIC); - units.push_back(std::make_unique(vf2d{32,32},*IMAGES[VIRUS_IMG1],true)); - for(int i=0;i<10;i++){ - if(rand()%2==0){ - units.push_back(std::make_unique(vf2d{float(rand()%ScreenWidth()),float(rand()%ScreenHeight())},*IMAGES[VIRUS_IMG1],true)); - } else { - units.push_back(std::make_unique(vf2d{float(rand()%ScreenWidth()),float(rand()%ScreenHeight())},*IMAGES[VIRUS_IMG1],false)); - } - } + units.push_back(std::make_unique(vf2d{128,128},IMAGES,true)); + units.push_back(std::make_unique(vf2d{129,129},IMAGES,true)); + units.push_back(std::make_unique(vf2d{130,130},IMAGES,true)); + units.push_back(std::make_unique(vf2d{131,131},IMAGES,true)); + units.push_back(std::make_unique(vf2d{132,132},IMAGES,true)); + units.push_back(std::make_unique(vf2d{133,133},IMAGES,true)); + units.push_back(std::make_unique(this,vf2d{134,134},IMAGES,true)); + for(int i=0;i<5;i++){ collectionPoints.push_back(std::make_unique(this,vf2d{32.f+48*i,32.f},0,*IMAGES[MEMORY_COLLECTION_POINT],MemoryType(i))); @@ -161,16 +168,16 @@ void VirusAttack::DrawMinimap(){ void VirusAttack::HandlePanAndZoom(float fElapsedTime){ float speedScale=std::min(1.f,game.GetWorldScale().x); - if(GetKey(A).bHeld){ + if(GetKey(A).bHeld/*||GetMouseX()<=CONSTANT::SCROLL_BOUNDARY*/){ game.MoveWorldOffset(vf2d{-256*fElapsedTime,0}/speedScale); } - if(GetKey(W).bHeld){ + if(GetKey(W).bHeld/*||GetMouseY()<=CONSTANT::SCROLL_BOUNDARY*/){ game.MoveWorldOffset(vf2d{0,-256*fElapsedTime}/speedScale); } - if(GetKey(S).bHeld){ + if(GetKey(S).bHeld/*||GetMouseY()>=ScreenHeight()-CONSTANT::SCROLL_BOUNDARY*/){ game.MoveWorldOffset(vf2d{0,256*fElapsedTime}/speedScale); } - if(GetKey(D).bHeld){ + if(GetKey(D).bHeld/*||GetMouseX()>=ScreenWidth()-CONSTANT::SCROLL_BOUNDARY*/){ game.MoveWorldOffset(vf2d{256*fElapsedTime,0}/speedScale); } @@ -179,11 +186,11 @@ void VirusAttack::HandlePanAndZoom(float fElapsedTime){ game.ZoomAtScreenPos(1.25,GetMousePos()); } } else - if(GetMouseWheel()<0){ - if(game.GetWorldScale().x>0.5){ - game.ZoomAtScreenPos(0.75,GetMousePos()); - } + if(GetMouseWheel()<0){ + if(game.GetWorldScale().x>0.5){ + game.ZoomAtScreenPos(0.75,GetMousePos()); } + } } void VirusAttack::HandleMinimapClick(){ @@ -244,6 +251,32 @@ void VirusAttack::UpdateMatrixTexture(float fElapsedTime){ std::erase_if(activeLetters,[](Letter&letter){return letter.pos.y<-32;}); } +void VirusAttack::RenderCollectionPoints(CollectionPoint*cp){ + geom2d::rectcpRect=geom2d::rect({cp->pos-cp->img.Sprite()->Size()/2,cp->img.Sprite()->Size()}); + geom2d::rectviewRegion=geom2d::rect({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); + } +} + bool VirusAttack::OnUserUpdate(float fElapsedTime){ UpdateMatrixTexture(fElapsedTime); HandleDraggingSelection(); @@ -272,12 +305,7 @@ bool VirusAttack::OnUserUpdate(float fElapsedTime){ } } u->AttemptAttack(closestUnit); - u->Update(fElapsedTime); - } - for(auto&u:units){ - if(!u->GhostInFogOfWar()&&u->InFogOfWar()){ - u->HideGhost(); - } + u->_Update(this); } game.DrawPartialDecal({0,0},CONSTANT::WORLD_SIZE*CONSTANT::TILE_SIZE,IMAGES[TILE]->Decal(),{0,0},CONSTANT::WORLD_SIZE*CONSTANT::TILE_SIZE,DARK_GREEN); @@ -287,29 +315,7 @@ bool VirusAttack::OnUserUpdate(float fElapsedTime){ for(auto&collectionPoint:collectionPoints){ collectionPoint->Update(this,*IMAGES[MATRIX]); - geom2d::rectcpRect=geom2d::rect({collectionPoint->pos-collectionPoint->img.Sprite()->Size()/2,collectionPoint->img.Sprite()->Size()}); - geom2d::rectviewRegion=geom2d::rect({game.GetWorldTL(),game.GetWorldVisibleArea()}); - if(geom2d::overlaps(cpRect,viewRegion)){ - Pixel col; - switch(collectionPoint->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(collectionPoint->pos,collectionPoint->img.Decal(),collectionPoint->rot,collectionPoint->img.Sprite()->Size()/2,{1,1},col); - } + RenderCollectionPoints(collectionPoint.get()); } for(auto&u:units){ @@ -317,6 +323,14 @@ bool VirusAttack::OnUserUpdate(float fElapsedTime){ } DrawSelectionRectangle(); + RenderFogOfWar(); + + DrawMinimap(); + + return true; +} + +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){ @@ -326,12 +340,6 @@ bool VirusAttack::OnUserUpdate(float fElapsedTime){ } } } - - DrawMinimap(); - - DrawDecal({0,0},IMAGES[MATRIX]->Decal(),{1,1},{128,0,128}); - - return true; } VirusAttack::CollectionPoint::CollectionPoint(PixelGameEngine*pge,vf2d pos,float rot,Renderable&collectionPointImg,MemoryType type) diff --git a/olcCodeJam2023Entry/VirusAttack.h b/olcCodeJam2023Entry/VirusAttack.h index 22fe37f..fa315ae 100644 --- a/olcCodeJam2023Entry/VirusAttack.h +++ b/olcCodeJam2023Entry/VirusAttack.h @@ -49,6 +49,8 @@ private: void HandleMinimapClick(); void InitializeImages(); void UpdateMatrixTexture(float fElapsedTime); + void RenderCollectionPoints(CollectionPoint*cp); + void RenderFogOfWar(); public: VirusAttack(); diff --git a/olcCodeJam2023Entry/assets/ram_bank.png b/olcCodeJam2023Entry/assets/ram_bank.png new file mode 100644 index 0000000000000000000000000000000000000000..c5deb58772137f4b9848ebcb4f8002a1f5eafdc0 GIT binary patch literal 2264 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 eTUl?K(;>6k_s0|4fd8XFAbT4}>+|b9BmV