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.
1128 lines
42 KiB
1128 lines
42 KiB
#include "Unit.h"
|
|
#include "olcUTIL_Geometry2D.h"
|
|
#include "TileManager.h"
|
|
#include "util.h"
|
|
#include "DebuffIcon.h"
|
|
#include "olcPGEX_QuickGUI.h"
|
|
#include "Textbox.h"
|
|
|
|
Unit::~Unit(){};
|
|
|
|
void Unit::RandomHit(std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
switch(rand()%3){
|
|
case 0:{SOUNDS[Sound::HIT1]->Play(GetPos(),1,0.6);}break;
|
|
case 1:{SOUNDS[Sound::HIT2]->Play(GetPos(),1,0.6);}break;
|
|
case 2:{SOUNDS[Sound::HIT3]->Play(GetPos(),1,0.6);}break;
|
|
}
|
|
}
|
|
|
|
std::string LeftShifter::unitName="Left Shifter";
|
|
std::string LeftShifter::unitDescription="Shifts target memory 1 bit to the left.";
|
|
std::vector<Memory> LeftShifter::resourceCost={{RANGE,2},{ATKSPD,2},{MOVESPD,6},{PROCEDURE,1},{HEALTH,4}};
|
|
LeftShifter::LeftShifter(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly,bool moveable)
|
|
:Unit(pge,LeftShifter::resourceCost,pos,12,*IMAGES[LEFT_SHIFTER],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,moveable){}
|
|
|
|
void LeftShifter::Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits ,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
victim<<=1;
|
|
RandomHit(SOUNDS);
|
|
}
|
|
|
|
std::string RightShifter::unitName="Right Shifter";
|
|
std::string RightShifter::unitDescription="Shifts target memory 1 bit to the right.";
|
|
std::vector<Memory> RightShifter::resourceCost={{HEALTH,4},{RANGE,2},{ATKSPD,2},{MOVESPD,6},{PROCEDURE,1}};
|
|
RightShifter::RightShifter(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly,bool moveable)
|
|
:Unit(pge,RightShifter::resourceCost,pos,12,*IMAGES[RIGHT_SHIFTER],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,moveable){}
|
|
|
|
void RightShifter::Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits ,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
victim>>=1;
|
|
RandomHit(SOUNDS);
|
|
}
|
|
|
|
std::string BitRestorer::unitName="Bit Restorer";
|
|
std::string BitRestorer::unitDescription="Randomly restores 1 missing bit to a target.";
|
|
std::vector<Memory> BitRestorer::resourceCost={{PROCEDURE,6},{RANGE,1},{ATKSPD,1},{MOVESPD,2},{HEALTH,2}};
|
|
BitRestorer::BitRestorer(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly,bool moveable)
|
|
:Unit(pge,BitRestorer::resourceCost,pos,12,*IMAGES[BIT_RESTORER],CONSTANT::HEALER_TARGET_COL,CONSTANT::HEALER_ATTACK_COL,friendly,moveable,true,false){}
|
|
|
|
void BitRestorer::Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits ,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
std::vector<int>emptyMemoryPositions;
|
|
for(int i=0;i<victim.GetMemorySize();i++){
|
|
if(!victim.memory[i]){
|
|
emptyMemoryPositions.emplace_back(i);
|
|
}
|
|
}
|
|
if(emptyMemoryPositions.size()==0){
|
|
//First see if we can find another damaged target, if we can, then we try healing them. Otherwise we exit.
|
|
appliedTarget.reset();
|
|
AttemptToHealOtherAllies(otherUnits,SOUNDS);
|
|
return;
|
|
}
|
|
int randomBit=emptyMemoryPositions[rand()%emptyMemoryPositions.size()];
|
|
victim.memory[randomBit]=true;
|
|
SOUNDS[Sound::HEAL]->Play(GetPos(),1,0.6);
|
|
}
|
|
|
|
void BitRestorer::AttemptToHealOtherAllies(std::vector<std::shared_ptr<Unit>>&otherUnits,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
std::vector<int>emptyMemoryPositions;
|
|
for(auto&u:otherUnits){
|
|
if(u.get()!=this&&u->IsFriendly()&&InRange(u)){
|
|
for(int i=0;i<u->GetMemorySize();i++){
|
|
if(!u->memory[i]){
|
|
emptyMemoryPositions.emplace_back(i);
|
|
}
|
|
}
|
|
if(emptyMemoryPositions.size()!=0){
|
|
int randomBit=emptyMemoryPositions[rand()%emptyMemoryPositions.size()];
|
|
u->memory[randomBit]=true;
|
|
appliedTarget=u;
|
|
SOUNDS[Sound::HEAL]->Play(GetPos(),1,0.6);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string MemorySwapper::unitName="Memory Swapper";
|
|
std::string MemorySwapper::unitDescription="Flips the orientation of all bits of a target around.";
|
|
std::vector<Memory> MemorySwapper::resourceCost={{RANGE,3},{ATKSPD,1},{HEALTH,3},{PROCEDURE,3},{MOVESPD,4}};
|
|
MemorySwapper::MemorySwapper(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly,bool moveable)
|
|
:Unit(pge,MemorySwapper::resourceCost,pos,12,*IMAGES[MEMORY_SWAPPER],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,moveable,true){
|
|
autoAcquireFriendlyTarget=false;
|
|
}
|
|
|
|
void MemorySwapper::Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits ,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
std::vector<bool>oldMemory=victim.memory;
|
|
for(int i=0;i<oldMemory.size();i++){
|
|
victim.memory[i]=oldMemory[oldMemory.size()-i-1];
|
|
}
|
|
std::vector<Marker*>order;
|
|
for(int i=0;i<5;i++){
|
|
int lowestInd=999999;
|
|
Marker*lowest=nullptr;
|
|
if(victim.health.index<lowestInd){lowest=&victim.health;lowestInd=lowest->index;}
|
|
if(victim.range.index<lowestInd){lowest=&victim.range;lowestInd=lowest->index;}
|
|
if(victim.atkSpd.index<lowestInd){lowest=&victim.atkSpd;lowestInd=lowest->index;}
|
|
if(victim.moveSpd.index<lowestInd){lowest=&victim.moveSpd;lowestInd=lowest->index;}
|
|
if(victim.procedure.index<lowestInd){lowest=&victim.procedure;lowestInd=lowest->index;}
|
|
|
|
if(lowest!=nullptr){
|
|
order.push_back(lowest);
|
|
lowest->index=9999999;
|
|
}
|
|
}
|
|
std::reverse(order.begin(),order.end());
|
|
int i=0;
|
|
int marker=0;
|
|
while(marker<oldMemory.size()){
|
|
order[i]->index=marker;
|
|
marker+=order[i]->size;
|
|
i++;
|
|
}
|
|
//Clean up after ourselves for any 0-sized stats.
|
|
if(victim.health.index==9999999)victim.health.index=0;
|
|
if(victim.range.index==9999999)victim.range.index=0;
|
|
if(victim.atkSpd.index==9999999)victim.atkSpd.index=0;
|
|
if(victim.moveSpd.index==9999999)victim.moveSpd.index=0;
|
|
if(victim.procedure.index==9999999)victim.procedure.index=0;
|
|
RandomHit(SOUNDS);
|
|
}
|
|
|
|
std::string Corrupter::unitName="Corrupter";
|
|
std::string Corrupter::unitDescription="Chooses a random bit and negates it on a target.";
|
|
std::vector<Memory> Corrupter::resourceCost={{ATKSPD,3},{RANGE,1},{PROCEDURE,8},{MOVESPD,8},{HEALTH,4}};
|
|
Corrupter::Corrupter(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly,bool moveable)
|
|
:Unit(pge,Corrupter::resourceCost,pos,12,*IMAGES[CORRUPTER],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,moveable){}
|
|
|
|
void Corrupter::Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits ,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
//Chooses a bit at random and corrupts it.
|
|
int randomBit=rand()%victim.memory.size();
|
|
if(victim.memory[randomBit]){
|
|
RandomHit(SOUNDS);
|
|
}
|
|
victim.memory[randomBit]=false;
|
|
}
|
|
|
|
std::string MemoryAllocator::unitName="Memory Allocator";
|
|
std::string MemoryAllocator::unitDescription="A unit that builds other units.";
|
|
std::vector<Memory> MemoryAllocator::resourceCost={{RANGE,1},{ATKSPD,1},{MOVESPD,2},{PROCEDURE,1},{HEALTH,1}};
|
|
MemoryAllocator::MemoryAllocator(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly,bool moveable)
|
|
:Unit(pge,MemoryAllocator::resourceCost,pos,12,*IMAGES[UNIT_ALLOCATOR],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,true,false){
|
|
isAllocator=true;
|
|
}
|
|
|
|
void MemoryAllocator::Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits ,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
|
|
}
|
|
|
|
void MemoryAllocator::Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,std::vector<std::shared_ptr<Unit>>&queuedUnits){
|
|
if(IsBuilding()){
|
|
SetTargetLocation(CONSTANT::UNSELECTED);
|
|
target.reset();
|
|
buildTime-=pge->GetElapsedTime();
|
|
if(buildTime<=0){
|
|
for(int i=0;i<GetMemorySize();i++){
|
|
memory[i]=false; //Kill the unit.
|
|
}
|
|
SOUNDS[Sound::SPAWN]->Play(GetPos());
|
|
queuedUnits.push_back(std::move(buildTransformUnit));
|
|
}
|
|
}
|
|
}
|
|
|
|
void MemoryAllocator::Draw(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES){
|
|
if(IsBuilding()&&!InFogOfWar()){
|
|
game.GetPGE()->SetDrawTarget(img.Sprite());
|
|
game.GetPGE()->Clear(BLANK);
|
|
Renderable&targetImg=buildTransformUnit->GetImage();
|
|
game.GetPGE()->SetDrawTarget(nullptr);
|
|
game.DrawPartialRotatedDecal(GetGhostPos(),img.Decal(),0,img.Sprite()->Size()/2,{0,0},{float(img.Sprite()->width),float((buildTime/CONSTANT::UNIT_BUILD_TIME)*img.Sprite()->height)},{1,1},GetUnitColor()/3);
|
|
game.DrawPartialRotatedDecal(GetGhostPos()+vf2d{0.f,(buildTime/CONSTANT::UNIT_BUILD_TIME)*buildTransformUnit->GetImage().Sprite()->height},buildTransformUnit->GetImage().Decal(),0,buildTransformUnit->GetImage().Sprite()->Size()/2,{0.f,(buildTime/CONSTANT::UNIT_BUILD_TIME)*buildTransformUnit->GetImage().Sprite()->height},{float(buildTransformUnit->GetImage().Sprite()->width),float(buildTransformUnit->GetImage().Sprite()->height-(buildTime/CONSTANT::UNIT_BUILD_TIME)*buildTransformUnit->GetImage().Sprite()->height)},{1,1},GetUnitColor()/1.5f);
|
|
if(fmod(buildTime,1)>0.5){
|
|
game.DrawRotatedDecal(GetGhostPos(),IMAGES[SELECTION_CIRCLE]->Decal(),0,IMAGES[SELECTION_CIRCLE]->Sprite()->Size()/2,vf2d(img.Sprite()->Size())/IMAGES[SELECTION_CIRCLE]->Sprite()->Size(),{0, 202, 217});
|
|
}
|
|
} else {
|
|
game.DrawRotatedDecal(GetGhostPos(),img.Decal(),0,img.Sprite()->Size()/2,{1,1},GetUnitColor());
|
|
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);
|
|
}
|
|
if(IsAttached()){
|
|
game.DrawRotatedDecal(GetGhostPos(),IMAGES[SELECTION_CIRCLE]->Decal(),0,IMAGES[SELECTION_CIRCLE]->Sprite()->Size()/2,vf2d(img.Sprite()->Size())/IMAGES[SELECTION_CIRCLE]->Sprite()->Size(),IsFriendly()?GREEN:RED);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string RAMBank::unitName="RAM Bank";
|
|
std::string RAMBank::unitDescription="Allows for the construction of Memory Allocators.";
|
|
std::vector<Memory> RAMBank::resourceCost={{PROCEDURE,25},{HEALTH,16}};
|
|
RAMBank::RAMBank(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly)
|
|
:Unit(pge,RAMBank::resourceCost,pos,41,*IMAGES[RAM_BANK],WHITE,WHITE,friendly,false
|
|
,false,false
|
|
),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);
|
|
allocatorManager.colNormal = olc::VERY_DARK_GREEN;
|
|
allocatorManager.colHover = olc::GREEN;
|
|
allocatorManager.colClick = olc::YELLOW;
|
|
allocatorManager.colDisable = olc::DARK_GREY;
|
|
allocatorManager.colBorder = olc::YELLOW;
|
|
allocatorManager.colText = olc::WHITE;
|
|
allocatorButton = new QuickGUI::ImageButton(allocatorManager,*IMAGES[UNIT_ALLOCATOR],{0.5f,0.5f},pos-vf2d{8,48},{20,20});
|
|
isRAMBank=true;
|
|
}
|
|
|
|
void RAMBank::Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits ,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
|
|
}
|
|
|
|
void RAMBank::Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,std::vector<std::shared_ptr<Unit>>&queuedUnits){
|
|
pge->SetDrawTarget(img.Sprite());
|
|
for(int y=0;y<img.Sprite()->height;y++){
|
|
for(int x=0;x<img.Sprite()->width;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)));
|
|
}
|
|
}
|
|
}
|
|
if(!soundStarted){
|
|
soundStarted=true;
|
|
soundHandle=SOUNDS[int(Sound::HUM)]->Play(GetPos(),0.4,0.4,true);
|
|
}
|
|
img.Decal()->Update();
|
|
pge->SetDrawTarget(nullptr);
|
|
}
|
|
|
|
void RAMBank::DrawHud(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES){
|
|
if(IsSelected()){
|
|
allocatorManager.DrawDecal(game);
|
|
}
|
|
}
|
|
|
|
void RAMBank::Draw(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES){
|
|
game.DrawRotatedDecal(GetGhostPos(),img.Decal(),0,img.Sprite()->Size()/2,{1,1},GetUnitColor());
|
|
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);
|
|
}
|
|
if(IsAttached()){
|
|
game.DrawRotatedDecal(GetGhostPos(),IMAGES[SELECTION_CIRCLE]->Decal(),0,IMAGES[SELECTION_CIRCLE]->Sprite()->Size()/2,vf2d(img.Sprite()->Size())/IMAGES[SELECTION_CIRCLE]->Sprite()->Size(),IsFriendly()?GREEN:RED);
|
|
}
|
|
}
|
|
|
|
void RAMBank::OnDeath(std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
SOUNDS[int(Sound::HUM)]->Stop(soundHandle);
|
|
}
|
|
|
|
void RAMBank::UpdateGUIState(TileTransformedView&game,Resources&player_resources,Textbox&displayBox,bool&hovered,int totalUsedMemory,int availableMemory){
|
|
allocatorManager.Update(game);
|
|
|
|
auto TotalResources=[&](){
|
|
return player_resources.atkSpd+player_resources.health+player_resources.moveSpd+player_resources.procedure+player_resources.range;
|
|
};
|
|
|
|
bool buttonEnabled=IsSelected()&&TotalResources()>=5&&GetProcedure()==procedure.size&&5+totalUsedMemory<=availableMemory;
|
|
|
|
if(buttonEnabled&&allocatorButton->bHover){
|
|
displayBox.Initialize(CONSTANT::MEMORY_ALLOCATOR_BOX_DISPLAY_STRING,{},CONSTANT::MEMORY_ALLOCATOR_BOX_HEADER_STRING);
|
|
//displayBox.SetVisible(true);
|
|
hovered=true;
|
|
}
|
|
|
|
allocatorButton->Enable(buttonEnabled);
|
|
}
|
|
|
|
bool RAMBank::ClickHandled(TileTransformedView&game,Resources&player_resources,std::vector<std::shared_ptr<Unit>>&units,std::vector<std::unique_ptr<Renderable>>&IMAGES){
|
|
if(allocatorButton->bPressed){
|
|
//First try to take one of each.
|
|
if(player_resources.atkSpd>0&&player_resources.health>0&&player_resources.moveSpd>0&&player_resources.procedure>0&&player_resources.range>0){
|
|
player_resources.atkSpd--;
|
|
player_resources.health--;
|
|
player_resources.moveSpd--;
|
|
player_resources.procedure--;
|
|
player_resources.range--;
|
|
} else {
|
|
//Remove from the highest resource amount until 5 are removed.
|
|
for(int i=0;i<5;i++){
|
|
int*maxAmt=&player_resources.atkSpd;
|
|
if(player_resources.health>*maxAmt){
|
|
maxAmt=&player_resources.health;
|
|
}
|
|
if(player_resources.moveSpd>*maxAmt){
|
|
maxAmt=&player_resources.moveSpd;
|
|
}
|
|
if(player_resources.procedure>*maxAmt){
|
|
maxAmt=&player_resources.procedure;
|
|
}
|
|
if(player_resources.range>*maxAmt){
|
|
maxAmt=&player_resources.range;
|
|
}
|
|
(*maxAmt)--;
|
|
}
|
|
}
|
|
units.push_back(std::make_shared<MemoryAllocator>(game.GetPGE(),GetPos()+vf2d{0,24},IMAGES,friendly));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::string _Platform::unitName="Platform";
|
|
std::string _Platform::unitDescription="Anchored to the ground, this unit is an intermediate step for larger units.";
|
|
std::vector<Memory> _Platform::resourceCost={{HEALTH,6}};
|
|
_Platform::_Platform(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly,bool moveable)
|
|
:Unit(pge,_Platform::resourceCost,pos,24,*IMAGES[PLATFORM],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,false){
|
|
isPlatform=true;
|
|
}
|
|
|
|
void _Platform::Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits ,std::vector<std::unique_ptr<Audio>>&SOUNDS){};
|
|
|
|
void _Platform::Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,std::vector<std::shared_ptr<Unit>>&queuedUnits){
|
|
if(IsBuilding()){
|
|
SetTargetLocation(CONSTANT::UNSELECTED);
|
|
target.reset();
|
|
buildTime-=pge->GetElapsedTime();
|
|
if(buildTime<=0){
|
|
for(int i=0;i<GetMemorySize();i++){
|
|
memory[i]=false; //Kill the unit.
|
|
}
|
|
SOUNDS[Sound::SPAWN]->Play(GetPos());
|
|
queuedUnits.push_back(std::move(buildTransformUnit));
|
|
}
|
|
}
|
|
}
|
|
|
|
void _Platform::Draw(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES){
|
|
if(IsBuilding()){
|
|
game.GetPGE()->SetDrawTarget(img.Sprite());
|
|
game.GetPGE()->Clear(BLANK);
|
|
Renderable&targetImg=buildTransformUnit->GetImage();
|
|
game.GetPGE()->SetDrawTarget(nullptr);
|
|
game.DrawPartialRotatedDecal(GetGhostPos(),img.Decal(),0,img.Sprite()->Size()/2,{0,0},{float(img.Sprite()->width),float((buildTime/CONSTANT::UNIT_BUILD_TIME)*img.Sprite()->height)},{1,1},GetUnitColor()/3);
|
|
game.DrawPartialRotatedDecal(GetGhostPos()+vf2d{0.f,(buildTime/CONSTANT::UNIT_BUILD_TIME)*buildTransformUnit->GetImage().Sprite()->height},buildTransformUnit->GetImage().Decal(),0,buildTransformUnit->GetImage().Sprite()->Size()/2,{0.f,(buildTime/CONSTANT::UNIT_BUILD_TIME)*buildTransformUnit->GetImage().Sprite()->height},{float(buildTransformUnit->GetImage().Sprite()->width),float(buildTransformUnit->GetImage().Sprite()->height-(buildTime/CONSTANT::UNIT_BUILD_TIME)*buildTransformUnit->GetImage().Sprite()->height)},{1,1},GetUnitColor()/1.5f);
|
|
if(fmod(buildTime,1)>0.5){
|
|
game.DrawRotatedDecal(GetGhostPos(),IMAGES[SELECTION_CIRCLE]->Decal(),0,IMAGES[SELECTION_CIRCLE]->Sprite()->Size()/2,vf2d(img.Sprite()->Size())/IMAGES[SELECTION_CIRCLE]->Sprite()->Size(),{0, 202, 217});
|
|
}
|
|
} else {
|
|
game.DrawRotatedDecal(GetGhostPos(),img.Decal(),0,img.Sprite()->Size()/2,{1,1},GetUnitColor());
|
|
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);
|
|
}
|
|
if(IsAttached()){
|
|
game.DrawRotatedDecal(GetGhostPos(),IMAGES[SELECTION_CIRCLE]->Decal(),0,IMAGES[SELECTION_CIRCLE]->Sprite()->Size()/2,vf2d(img.Sprite()->Size())/IMAGES[SELECTION_CIRCLE]->Sprite()->Size(),IsFriendly()?GREEN:RED);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string Refresher::unitName="Refresher";
|
|
std::string Refresher::unitDescription="Repairs missing bits to surrounding units.";
|
|
std::vector<Memory> Refresher::resourceCost={{ATKSPD,3},{RANGE,1},{PROCEDURE,8},{HEALTH,4}};
|
|
Refresher::Refresher(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly,bool moveable)
|
|
:Unit(pge,Refresher::resourceCost,pos,24,*IMAGES[REFRESHER],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,false
|
|
,true,false){}
|
|
|
|
void Refresher::Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits ,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
target.reset(); //Doesn't acquire a target.
|
|
for(auto&u:otherUnits){
|
|
if(IsFriendly()==u->IsFriendly()&&InRange(u.get())){
|
|
int maxAttempts=1000;
|
|
int attempts=0;
|
|
while(attempts<maxAttempts){
|
|
int randomBit=rand()%u->GetMemorySize();
|
|
if(!u->memory[randomBit]){
|
|
u->memory[randomBit]=true;
|
|
return;
|
|
}
|
|
attempts++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void Refresher::OnDeath(std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
SOUNDS[Sound::REFRESHER]->Stop(soundHandle);
|
|
};
|
|
void Refresher::Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,std::vector<std::shared_ptr<Unit>>&queuedUnits){
|
|
if(!soundStarted){
|
|
soundStarted=true;
|
|
soundHandle=SOUNDS[Sound::REFRESHER]->Play(GetPos(),1,1,true);
|
|
}
|
|
};
|
|
|
|
std::string Turret::unitName="Turret";
|
|
std::string Turret::unitDescription="Automatically targets attack and movement speed memory ranges before others.";
|
|
std::vector<Memory> Turret::resourceCost={{ATKSPD,4},{RANGE,5},{HEALTH,6},{PROCEDURE,16}};
|
|
Turret::Turret(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly,bool moveable)
|
|
:Unit(pge,Turret::resourceCost,pos,24,*IMAGES[TURRET],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,false){}
|
|
|
|
void Turret::Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits ,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
if(victim.GetMoveSpd()>0){
|
|
for(int i=0;i<victim.moveSpd.size;i++){
|
|
if(victim.memory[victim.moveSpd.index+i]){
|
|
victim.memory[victim.moveSpd.index+i]=false;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if(victim.GetAtkSpd()>0){
|
|
for(int i=0;i<victim.atkSpd.size;i++){
|
|
if(victim.memory[victim.atkSpd.index+i]){
|
|
victim.memory[victim.atkSpd.index+i]=false;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
int maxAttempts=1000;
|
|
int attempts=0;
|
|
while(attempts<maxAttempts){
|
|
int randomBit=rand()%victim.GetMemorySize();
|
|
if(victim.memory[randomBit]){
|
|
victim.memory[randomBit]=false;
|
|
return;
|
|
}
|
|
attempts++;
|
|
}
|
|
}
|
|
void Turret::OnDeath(std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
SOUNDS[Sound::TURRET]->Stop(soundHandle);
|
|
};
|
|
void Turret::Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,std::vector<std::shared_ptr<Unit>>&queuedUnits){
|
|
if(!soundStarted){
|
|
soundStarted=true;
|
|
soundHandle=SOUNDS[Sound::TURRET]->Play(GetPos(),1,1,true);
|
|
}
|
|
};
|
|
|
|
std::string MemoryGuard::unitName="Memory Guard";
|
|
std::string MemoryGuard::unitDescription="Reduces the chance of bit modification for all surrounding units by 30%";
|
|
std::vector<Memory> MemoryGuard::resourceCost={{HEALTH,10},{ATKSPD,4},{RANGE,4},{PROCEDURE,12}};
|
|
MemoryGuard::MemoryGuard(PixelGameEngine*pge,vf2d pos,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool friendly,bool moveable)
|
|
:Unit(pge,MemoryGuard::resourceCost,pos,24,*IMAGES[MEMORY_GUARD],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,false
|
|
,true,false){}
|
|
|
|
void MemoryGuard::Attack(Unit&victim,std::vector<std::shared_ptr<Unit>>&otherUnits ,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
target.reset(); //Doesn't acquire a target.
|
|
for(auto&u:otherUnits){
|
|
if(IsFriendly()==u->IsFriendly()&&InRange(u.get())){
|
|
u->SetGuardTime(3);
|
|
}
|
|
}
|
|
}
|
|
void MemoryGuard::OnDeath(std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
SOUNDS[Sound::MEMORY_GUARD]->Stop(soundHandle);
|
|
};
|
|
void MemoryGuard::Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,std::vector<std::shared_ptr<Unit>>&queuedUnits){
|
|
if(!soundStarted){
|
|
soundStarted=true;
|
|
soundHandle=SOUNDS[Sound::MEMORY_GUARD]->Play(GetPos(),1,1,true);
|
|
}
|
|
};
|
|
|
|
Unit::Unit(PixelGameEngine*pge,std::vector<Memory>memory,vf2d pos,float radius,Renderable&img,Pixel targetLineColor,Pixel attackingLineColor,bool friendly,bool moveable,bool friendlyInteractable,bool enemyInteractable)
|
|
:pos(pos),radius(radius),ghostPos({-999999,-999999}),img(img),targetLineCol(targetLineColor),attackingLineCol(attackingLineColor),friendly(friendly),moveable(moveable),friendlyInteractable(friendlyInteractable),enemyInteractable(enemyInteractable){
|
|
int marker=0;
|
|
for(Memory&mem:memory){
|
|
for(int i=0;i<mem.size;i++){
|
|
this->memory.push_back(true);
|
|
this->ghostMemory.push_back(true);
|
|
}
|
|
switch(mem.type){
|
|
case HEALTH:{
|
|
health.index=marker;
|
|
health.size=mem.size;
|
|
}break;
|
|
case RANGE:{
|
|
range.index=marker;
|
|
range.size=mem.size;
|
|
}break;
|
|
case ATKSPD:{
|
|
atkSpd.index=marker;
|
|
atkSpd.size=mem.size;
|
|
}break;
|
|
case MOVESPD:{
|
|
moveSpd.index=marker;
|
|
moveSpd.size=mem.size;
|
|
}break;
|
|
case PROCEDURE:{
|
|
procedure.index=marker;
|
|
procedure.size=mem.size;
|
|
}break;
|
|
}
|
|
marker+=mem.size;
|
|
}
|
|
|
|
attackingLine.Create(25,24,false,false);
|
|
targetingLine.Create(25,24,false,false);
|
|
}
|
|
|
|
void Unit::DrawRangeIndicator(PixelGameEngine*pge,TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES){
|
|
if(!CanInteractWithAllies()&&!CanInteractWithEnemies())return;
|
|
float dist=geom2d::line<float>(game.ScreenToWorld(pge->GetMousePos()),GetGhostPos()).length();
|
|
float range=12*(GetRange()+1);
|
|
float totalRange=GetUnitSize().x/2+range;
|
|
|
|
if(IsSelected()){
|
|
Pixel col;
|
|
if(CanInteractWithAllies()&&!CanInteractWithEnemies()){
|
|
col={40,127,173};
|
|
} else {
|
|
col={0,196,0};
|
|
}
|
|
game.DrawRotatedDecal(GetGhostPos(),IMAGES[RANGE_INDICATOR]->Decal(),0,IMAGES[RANGE_INDICATOR]->Sprite()->Size()/2,{totalRange/12,totalRange/12},col);
|
|
|
|
}else
|
|
if(dist<range*2){
|
|
Pixel col;
|
|
if(IsFriendly()){
|
|
if(CanInteractWithAllies()&&!CanInteractWithEnemies()){
|
|
col={40,127,173};
|
|
} else {
|
|
col={0,196,0};
|
|
}
|
|
}else{
|
|
if(CanInteractWithAllies()&&!CanInteractWithEnemies()){
|
|
col={194, 37, 168};
|
|
} else {
|
|
col={196,130,0};
|
|
}
|
|
}
|
|
uint8_t transparency=uint8_t((1.f-(dist/(range*2)))*255);
|
|
game.DrawRotatedDecal(GetGhostPos(),IMAGES[RANGE_INDICATOR]->Decal(),0,IMAGES[RANGE_INDICATOR]->Sprite()->Size()/2,{totalRange/12,totalRange/12},{col.r,col.g,col.b,transparency});
|
|
}
|
|
}
|
|
|
|
void Unit::Draw(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES){
|
|
game.DrawRotatedDecal(GetGhostPos(),img.Decal(),0,img.Sprite()->Size()/2,{1,1},GetUnitColor());
|
|
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);
|
|
}
|
|
if(IsAttached()){
|
|
game.DrawRotatedDecal(GetGhostPos(),IMAGES[SELECTION_CIRCLE]->Decal(),0,IMAGES[SELECTION_CIRCLE]->Sprite()->Size()/2,vf2d(img.Sprite()->Size())/IMAGES[SELECTION_CIRCLE]->Sprite()->Size(),IsFriendly()?GREEN:RED);
|
|
}
|
|
}
|
|
|
|
void Unit::DrawHud(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES){}
|
|
|
|
void Unit::_DrawHud(TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES,bool unitMetersGreyedOut){
|
|
DrawHud(game,IMAGES);
|
|
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;
|
|
|
|
|
|
auto CheckColor=[&](int i,Pixel&col){
|
|
if(health.index==i&&health.size>0){
|
|
col=CONSTANT::HEALTH_COLOR;
|
|
}
|
|
if(range.index==i&&range.size>0){
|
|
col=unitMetersGreyedOut?VERY_DARK_GREY:CONSTANT::RANGE_COLOR;
|
|
}
|
|
if(atkSpd.index==i&&atkSpd.size>0){
|
|
col=unitMetersGreyedOut?VERY_DARK_GREY:CONSTANT::ATKSPD_COLOR;
|
|
}
|
|
if(moveSpd.index==i&&moveSpd.size>0){
|
|
col=unitMetersGreyedOut?VERY_DARK_GREY:CONSTANT::MOVESPD_COLOR;
|
|
}
|
|
if(procedure.index==i&&procedure.size>0){
|
|
col=unitMetersGreyedOut?VERY_DARK_GREY:CONSTANT::PROCEDURE_COLOR;
|
|
}
|
|
};
|
|
|
|
if(GetHealth()>0){
|
|
game.FillRectDecal(vf2d{float(initialBarX)+health.index*CONSTANT::BAR_SQUARE_SIZE.x,
|
|
float(initialBarY)}-vf2d{0,1},CONSTANT::BAR_SQUARE_SIZE+vf2d{CONSTANT::BAR_SQUARE_SIZE.x*health.size-1,2},CONSTANT::HEALTH_COLOR);
|
|
}
|
|
if(GetAtkSpd()>0){
|
|
game.FillRectDecal(vf2d{float(initialBarX)+atkSpd.index*CONSTANT::BAR_SQUARE_SIZE.x,
|
|
float(initialBarY)}-vf2d{0,1},CONSTANT::BAR_SQUARE_SIZE+vf2d{CONSTANT::BAR_SQUARE_SIZE.x*atkSpd.size-1,2},unitMetersGreyedOut?VERY_DARK_GREY:CONSTANT::ATKSPD_COLOR);
|
|
}
|
|
if(GetMoveSpd()>0){
|
|
game.FillRectDecal(vf2d{float(initialBarX)+moveSpd.index*CONSTANT::BAR_SQUARE_SIZE.x,
|
|
float(initialBarY)}-vf2d{0,1},CONSTANT::BAR_SQUARE_SIZE+vf2d{CONSTANT::BAR_SQUARE_SIZE.x*moveSpd.size-1,2},unitMetersGreyedOut?VERY_DARK_GREY:CONSTANT::MOVESPD_COLOR);
|
|
}
|
|
if(GetProcedure()>0){
|
|
game.FillRectDecal(vf2d{float(initialBarX)+procedure.index*CONSTANT::BAR_SQUARE_SIZE.x,
|
|
float(initialBarY)}-vf2d{0,1},CONSTANT::BAR_SQUARE_SIZE+vf2d{CONSTANT::BAR_SQUARE_SIZE.x*procedure.size-1,2},unitMetersGreyedOut?VERY_DARK_GREY:CONSTANT::PROCEDURE_COLOR);
|
|
}
|
|
if(GetRange()>0){
|
|
game.FillRectDecal(vf2d{float(initialBarX)+range.index*CONSTANT::BAR_SQUARE_SIZE.x,
|
|
float(initialBarY)}-vf2d{0,1},CONSTANT::BAR_SQUARE_SIZE+vf2d{CONSTANT::BAR_SQUARE_SIZE.x*range.size-1,2},unitMetersGreyedOut?VERY_DARK_GREY:CONSTANT::RANGE_COLOR);
|
|
}
|
|
|
|
for(int i=0;i<GetMemorySize();i++){
|
|
CheckColor(i,col);
|
|
|
|
game.FillRectDecal({float(initialBarX)+i*CONSTANT::BAR_SQUARE_SIZE.x,
|
|
float(initialBarY)},CONSTANT::BAR_SQUARE_SIZE,ghostMemory[i]?col:col/4);
|
|
game.DrawRectDecal({float(initialBarX)+i*CONSTANT::BAR_SQUARE_SIZE.x,
|
|
float(initialBarY)},CONSTANT::BAR_SQUARE_SIZE,BLACK);
|
|
}
|
|
}
|
|
|
|
void Unit::DrawUnitDamageStats(PixelGameEngine*pge,TileTransformedView&game,std::vector<std::unique_ptr<Renderable>>&IMAGES){
|
|
if(!InFogOfWar()){
|
|
if(!target.expired()){
|
|
geom2d::line<float>lineToTarget(pos,target.lock()->pos);
|
|
lineToTarget.start=lineToTarget.rpoint(GetUnitSize().x/2);
|
|
lineToTarget.end=lineToTarget.rpoint(lineToTarget.length()-GetUnitSize().x/4);
|
|
util::ApplyMatrixEffect(game.GetPGE(),targetingLine,*IMAGES[TARGETING_LINE],IMAGES[MATRIX]);
|
|
game.DrawPartialRotatedDecal(lineToTarget.upoint(0.5),targetingLine.Decal(),lineToTarget.vector().polar().y,{lineToTarget.length()/2,12},{lineShift*10,0},{lineToTarget.length(),24},{1,1},targetLineCol);
|
|
} else
|
|
if(targetLoc!=CONSTANT::UNSELECTED){
|
|
geom2d::line<float>lineToTarget(pos,targetLoc);
|
|
lineToTarget.start=lineToTarget.rpoint(GetUnitSize().x/2);
|
|
lineToTarget.end=lineToTarget.rpoint(lineToTarget.length()-GetUnitSize().x/4);
|
|
util::ApplyMatrixEffect(game.GetPGE(),targetingLine,*IMAGES[TARGETING_LINE],IMAGES[MATRIX]);
|
|
game.DrawPartialRotatedDecal(lineToTarget.upoint(0.5),targetingLine.Decal(),lineToTarget.vector().polar().y,{lineToTarget.length()/2,12},{lineShift*10,0},{lineToTarget.length(),24},{1,0.6},CONSTANT::MOVE_LINE_COL);
|
|
}
|
|
if(!appliedTarget.expired()){
|
|
geom2d::line<float>lineToTarget(pos,appliedTarget.lock()->pos);
|
|
lineToTarget.start=lineToTarget.rpoint(GetUnitSize().x/2);
|
|
lineToTarget.end=lineToTarget.rpoint(lineToTarget.length()-GetUnitSize().x/4);
|
|
if(reloadTimer>0){
|
|
util::ApplyMatrixEffect(game.GetPGE(),attackingLine,*IMAGES[ATTACKING_LINE],IMAGES[MATRIX]);
|
|
float reloadSpd=1.f/(GetAtkSpd()/2.f);
|
|
game.DrawPartialRotatedDecal(lineToTarget.upoint(0.5),attackingLine.Decal(),lineToTarget.vector().polar().y,{lineToTarget.length()/2,12},{lineShift*30,0},{lineToTarget.length(),24},{1,1+(attackFailed?-0.3f:(reloadTimer/reloadSpd)*0.25f)},attackFailed?Pixel{192,192,192,130}:Pixel{attackingLineCol.r,attackingLineCol.g,attackingLineCol.b,uint8_t(IsFriendly()?200:160)});
|
|
}
|
|
}
|
|
}
|
|
float dist=geom2d::line<float>(game.ScreenToWorld(pge->GetMousePos()),GetGhostPos()).length();
|
|
float range=12*(GetRange()+1);
|
|
|
|
|
|
|
|
auto DrawStatDown=[&](Marker&maxStat,int currentStat,vf2d pos,vf2d downDisplayPos,Image img,uint8_t transparency=255){
|
|
if(maxStat.size>0){
|
|
if(currentStat!=maxStat.size){
|
|
game.DrawDecal(this->pos+pos,IMAGES[img]->Decal(),{1,1},currentStat==0?Pixel{192,64,64,transparency}:Pixel{255,255,255,transparency});
|
|
if(currentStat>0){
|
|
game.DrawDecal(this->pos+downDisplayPos,IMAGES[DOWN_ARROW]->Decal(),{1,1},{255,255,255,transparency});
|
|
} else {
|
|
game.DrawDecal(this->pos+downDisplayPos,IMAGES[RED_X]->Decal(),{1,1},{255,255,255,transparency});
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
if(IsSelected()){
|
|
DrawStatDown(atkSpd,GetAtkSpd(),{-48-8,-24},{-48+8,-18},RLD);
|
|
DrawStatDown(moveSpd,GetMoveSpd(),{-48-8,8},{-36+8,2},SPD);
|
|
DrawStatDown(this->range,GetRange(),{8,-24},{20,-18},RNG);
|
|
DrawStatDown(procedure,GetProcedure(),{8,8},{26,2},PRC);
|
|
}else
|
|
if(dist<range*2){
|
|
uint8_t transparency=uint8_t((1.f-(dist/(range*2)))*255);
|
|
DrawStatDown(atkSpd,GetAtkSpd(),{-48-8,-24},{-48+8,-18},RLD,transparency);
|
|
DrawStatDown(moveSpd,GetMoveSpd(),{-48-8,8},{-36+8,2},SPD,transparency);
|
|
DrawStatDown(this->range,GetRange(),{8,-24},{20,-18},RNG,transparency);
|
|
DrawStatDown(procedure,GetProcedure(),{8,8},{26,2},PRC,transparency);
|
|
}
|
|
}
|
|
|
|
int Unit::GetBits(Marker&m){
|
|
int activeBits=0;
|
|
for(int i=0;i<m.size;i++){
|
|
if(memory[i+m.index]){
|
|
activeBits++;
|
|
}
|
|
}
|
|
return activeBits;
|
|
}
|
|
|
|
void Unit::_OnDeath(std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
switch(rand()%3){
|
|
case 0:{
|
|
SOUNDS[Sound::DEAD1]->Play(GetPos());
|
|
}break;
|
|
case 1:{
|
|
SOUNDS[Sound::DEAD2]->Play(GetPos());
|
|
}break;
|
|
case 2:{
|
|
SOUNDS[Sound::DEAD3]->Play(GetPos());
|
|
}break;
|
|
}
|
|
OnDeath(SOUNDS);
|
|
}
|
|
void Unit::OnDeath(std::vector<std::unique_ptr<Audio>>&SOUNDS){}
|
|
|
|
int Unit::GetHealth(){
|
|
return GetBits(health);
|
|
}
|
|
|
|
int Unit::GetRange(){
|
|
return GetBits(range);
|
|
}
|
|
|
|
int Unit::GetAtkSpd(){
|
|
return GetBits(atkSpd);
|
|
}
|
|
|
|
int Unit::GetMoveSpd(){
|
|
return GetBits(moveSpd);
|
|
}
|
|
|
|
int Unit::GetProcedure(){
|
|
return GetBits(procedure);
|
|
}
|
|
|
|
int Unit::GetMemorySize(){
|
|
return memory.size();
|
|
}
|
|
|
|
void Unit::RunAI(PixelGameEngine*pge){}
|
|
|
|
void Unit::_RunAI(PixelGameEngine*pge){
|
|
RunAI(pge);
|
|
}
|
|
|
|
void Unit::_Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,Resources&player_resources,Resources&enemy_resources,std::vector<std::shared_ptr<Unit>>&queuedUnits,std::array<float,5>&resourceGainTimer,std::vector<ResourceGainIcon>&resourceGainIcons,std::vector<std::unique_ptr<Renderable>>&IMAGES,GameFlags&flags){
|
|
if(!target.expired()){
|
|
auto ptrTarget=target.lock();
|
|
if(!InRange(ptrTarget)&&CanMove()){
|
|
SetPos(GetPos()+(ptrTarget->GetPos()-pos).norm()*GetMoveSpd()*12*pge->GetElapsedTime());
|
|
}
|
|
} else
|
|
if(targetLoc!=CONSTANT::UNSELECTED){
|
|
float dist=geom2d::line<float>(pos,targetLoc).length();
|
|
if(dist>24){
|
|
if(CanMove()){
|
|
SetPos(GetPos()+(targetLoc-pos).norm()*GetMoveSpd()*12*pge->GetElapsedTime());
|
|
}
|
|
} else {
|
|
if(willAttachWhenReachingDestination&&!attachTarget.expired()&&attachTarget.lock()->attachedUnit.expired()){
|
|
attachedPoint=attachTarget;
|
|
attachedPoint.lock()->attachedUnit=self_ptr;
|
|
}
|
|
willAttachWhenReachingDestination=false;
|
|
targetLoc=CONSTANT::UNSELECTED;
|
|
}
|
|
}
|
|
|
|
if(!attachedPoint.expired()){
|
|
SetPos(attachedPoint.lock()->pos+vf2d{cos(float(attachedPoint.lock()->rot+PI/2))*16,sin(float(attachedPoint.lock()->rot+PI/2))*16});
|
|
collectionTime-=pge->GetElapsedTime();
|
|
if(collectionTime<=0){
|
|
collectionTime=CONSTANT::COLLECTION_WAIT_TIME;
|
|
Resources&targetResource=IsFriendly()?player_resources:enemy_resources;
|
|
switch(attachedPoint.lock()->type){
|
|
case HEALTH:{
|
|
if(!IsFriendly()&&flags.difficulty==2){
|
|
targetResource.health+=3;
|
|
}
|
|
if(!IsFriendly()&&flags.difficulty==1){
|
|
targetResource.health++;
|
|
}
|
|
if(!IsFriendly()&&flags.difficulty==0){
|
|
targetResource.health--;
|
|
}
|
|
targetResource.health++;
|
|
}break;
|
|
case RANGE:{
|
|
if(!IsFriendly()&&flags.difficulty==2){
|
|
targetResource.range+=3;
|
|
}
|
|
if(!IsFriendly()&&flags.difficulty==1){
|
|
targetResource.range++;
|
|
}
|
|
if(!IsFriendly()&&flags.difficulty==0){
|
|
targetResource.range--;
|
|
}
|
|
targetResource.range++;
|
|
}break;
|
|
case ATKSPD:{
|
|
if(!IsFriendly()&&flags.difficulty==2){
|
|
targetResource.atkSpd+=3;
|
|
}
|
|
if(!IsFriendly()&&flags.difficulty==1){
|
|
targetResource.atkSpd++;
|
|
}
|
|
if(!IsFriendly()&&flags.difficulty==0){
|
|
targetResource.atkSpd--;
|
|
}
|
|
targetResource.atkSpd++;
|
|
}break;
|
|
case MOVESPD:{
|
|
if(!IsFriendly()&&flags.difficulty==2){
|
|
targetResource.moveSpd+=3;
|
|
}
|
|
if(!IsFriendly()&&flags.difficulty==1){
|
|
targetResource.moveSpd++;
|
|
}
|
|
if(!IsFriendly()&&flags.difficulty==0){
|
|
targetResource.moveSpd--;
|
|
}
|
|
targetResource.moveSpd++;
|
|
}break;
|
|
case PROCEDURE:{
|
|
if(!IsFriendly()&&flags.difficulty==2){
|
|
targetResource.procedure+=3;
|
|
}
|
|
if(!IsFriendly()&&flags.difficulty==1){
|
|
targetResource.procedure++;
|
|
}
|
|
if(!IsFriendly()&&flags.difficulty==0){
|
|
targetResource.procedure--;
|
|
}
|
|
targetResource.procedure++;
|
|
}break;
|
|
}
|
|
if(!InFogOfWar()){
|
|
resourceGainIcons.push_back({IMAGES[RESOURCE].get(),attachedPoint.lock()->type,GetPos()});
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!IsFriendly()){
|
|
_RunAI(pge);
|
|
}
|
|
|
|
if(!GhostInFogOfWar()&&InFogOfWar()){
|
|
HideGhost();
|
|
}
|
|
|
|
if(!InFogOfWar()){
|
|
ghostPos=pos;
|
|
ghostMemory=memory;
|
|
}
|
|
|
|
reloadTimer=std::max(0.f,reloadTimer-pge->GetElapsedTime());
|
|
guardTime=std::max(0.f,guardTime-pge->GetElapsedTime());
|
|
lineShift-=pge->GetElapsedTime();
|
|
if(lineShift<-25)lineShift+=25;
|
|
|
|
Update(pge,SOUNDS,queuedUnits);
|
|
}
|
|
|
|
std::vector<bool> operator <<(Unit&u,const int n){
|
|
std::vector<bool>tempMem=u.memory;
|
|
for(int i=0;i<u.GetMemorySize()-1;i++){
|
|
tempMem[i]=tempMem[i+1];
|
|
}
|
|
tempMem[u.GetMemorySize()-1]=0;
|
|
return tempMem;
|
|
}
|
|
|
|
std::vector<bool> operator >>(Unit&u,const int n){
|
|
std::vector<bool>tempMem=u.memory;
|
|
for(int i=1;i<u.GetMemorySize();i++){
|
|
tempMem[i]=tempMem[i-1];
|
|
}
|
|
tempMem[0]=0;
|
|
return tempMem;
|
|
}
|
|
|
|
bool Unit::IsFriendly(){
|
|
return friendly;
|
|
}
|
|
|
|
|
|
bool Unit::IsSelected(){
|
|
return selected;
|
|
}
|
|
|
|
void Unit::Select(){
|
|
selected=true;
|
|
}
|
|
|
|
void Unit::Deselect(){
|
|
selected=false;
|
|
}
|
|
|
|
vf2d Unit::GetPos(){
|
|
return pos;
|
|
}
|
|
|
|
bool Unit::IsDead(){
|
|
return dead;
|
|
}
|
|
|
|
vf2d Unit::GetUnitSize(){
|
|
return vf2d{radius,radius}*2;
|
|
}
|
|
|
|
void Unit::SetTargetUnit(std::weak_ptr<Unit>target){
|
|
this->target=target;
|
|
this->targetLoc=CONSTANT::UNSELECTED;
|
|
willAttachWhenReachingDestination=false;
|
|
}
|
|
|
|
void Unit::SetTargetLocation(vf2d targetLoc){
|
|
this->target.reset();
|
|
this->targetLoc=targetLoc;
|
|
willAttachWhenReachingDestination=false;
|
|
}
|
|
|
|
bool Unit::InRange(std::shared_ptr<Unit>target){
|
|
return InRange(target.get());
|
|
}
|
|
|
|
bool Unit::InRange(Unit*target){
|
|
float range=12*(GetRange()+1);
|
|
float totalRange=GetUnitSize().x/2+range;
|
|
return geom2d::overlaps(geom2d::circle<float>{GetPos(),totalRange},geom2d::circle<float>{target->GetPos(),target->GetUnitSize().x/2});
|
|
}
|
|
|
|
bool Unit::InRange(vf2d pos){
|
|
float range=12*(GetRange()+1);
|
|
float totalRange=GetUnitSize().x/2+range;
|
|
return geom2d::overlaps(geom2d::circle<float>{GetPos(),totalRange},geom2d::circle<float>{pos,0.1});
|
|
}
|
|
|
|
void Unit::SetPos(vf2d newPos){
|
|
pos=newPos;
|
|
}
|
|
|
|
void Unit::AttemptAttack(std::weak_ptr<Unit>attacker,std::weak_ptr<Unit>unit,std::vector<std::shared_ptr<Unit>>&otherUnits,std::vector<DebuffIcon>&debuffIcons,std::vector<std::unique_ptr<Renderable>>&IMAGES,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
if(reloadTimer>0)return;
|
|
std::weak_ptr<Unit>finalTarget;
|
|
if(!unit.expired()){
|
|
finalTarget=unit;
|
|
if(!target.expired()){
|
|
auto ptrTarget=target.lock();
|
|
if(InRange(ptrTarget)){
|
|
finalTarget=ptrTarget;
|
|
}
|
|
}
|
|
}
|
|
if(!finalTarget.expired()){
|
|
if(InRange(finalTarget.lock())){
|
|
appliedTarget=finalTarget;
|
|
bool hadAtkSpd=finalTarget.lock()->GetAtkSpd()>0;
|
|
bool hadMoveSpd=finalTarget.lock()->GetMoveSpd()>0;
|
|
bool hadRange=finalTarget.lock()->GetRange()>0;
|
|
bool hadProcedure=finalTarget.lock()->GetProcedure()>0;
|
|
_Attack(attacker,finalTarget,otherUnits,SOUNDS); //Call the parent function first, followed by the child.
|
|
if(hadAtkSpd&&finalTarget.lock()->GetAtkSpd()==0){
|
|
debuffIcons.emplace_back(IMAGES[RLD_ICON].get(),IMAGES[RED_X].get(),finalTarget.lock()->GetPos()-vf2d{util::random(12)-6,4});
|
|
}
|
|
if(hadMoveSpd&&finalTarget.lock()->GetMoveSpd()==0){
|
|
debuffIcons.emplace_back(IMAGES[SPD_ICON].get(),IMAGES[RED_X].get(),finalTarget.lock()->GetPos()-vf2d{util::random(12)-6,4});
|
|
}
|
|
if(hadRange&&finalTarget.lock()->GetRange()==0){
|
|
debuffIcons.emplace_back(IMAGES[RNG_ICON].get(),IMAGES[RED_X].get(),finalTarget.lock()->GetPos()-vf2d{util::random(12)-6,4});
|
|
}
|
|
if(hadProcedure&&finalTarget.lock()->GetProcedure()==0){
|
|
debuffIcons.emplace_back(IMAGES[PRC_ICON].get(),IMAGES[RED_X].get(),finalTarget.lock()->GetPos()-vf2d{util::random(12)-6,4});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Unit::Update(PixelGameEngine*pge,std::vector<std::unique_ptr<Audio>>&SOUNDS,std::vector<std::shared_ptr<Unit>>&queuedUnits){}
|
|
|
|
void Unit::Attacked(std::weak_ptr<Unit>attacker){}
|
|
|
|
void Unit::_Attacked(std::weak_ptr<Unit>attacker,std::vector<std::shared_ptr<Unit>>&otherUnits){
|
|
Attacked(attacker);
|
|
if(attacker.lock()->IsFriendly()!=IsFriendly()&&CanInteractWithEnemies()){
|
|
SetTargetUnit(attacker);
|
|
}
|
|
if(!IsFriendly()&&!attacker.lock()->IsFriendly()&&!attacker.lock()->CanInteractWithAllies()){
|
|
attacker.lock()->SetTargetLocation(attacker.lock()->GetPos()); //This effectively negates enemies targeting each other for attacking if they're not supposed to. Somehow this occurs sometimes.
|
|
}
|
|
if(attacker.lock()->IsFriendly()!=IsFriendly()){
|
|
for(auto&u:otherUnits){
|
|
if(this!=u.get()&&!u->IsFriendly()&&u->GetCurrentTarget().expired()&&u->CanMove()&&!u->IsAllocator()){
|
|
geom2d::line<float>distLine={GetPos(),u->GetPos()};
|
|
if(distLine.length()<320){
|
|
if(u->CanInteractWithEnemies()){
|
|
u->SetTargetUnit(attacker);
|
|
}else
|
|
if(u->CanInteractWithAllies()){
|
|
u->SetTargetLocation(GetPos());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Unit::_Attack(std::weak_ptr<Unit>attacker,std::weak_ptr<Unit>finalTarget,std::vector<std::shared_ptr<Unit>>&otherUnits,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
if(GetProcedure()>0&&GetAtkSpd()>0){
|
|
attackFailed=false;
|
|
float procChance=float(GetProcedure())/procedure.size;
|
|
if(util::random(1)>=1-procChance){
|
|
Attack(*finalTarget.lock(),otherUnits,SOUNDS);
|
|
finalTarget.lock()->_Attacked(attacker,otherUnits);
|
|
reloadTimer=1.f/(GetAtkSpd()/2.f);
|
|
if(GetCurrentTarget().expired()&&!IsFriendly()){
|
|
if(finalTarget.lock()->IsFriendly()!=IsFriendly()&&CanInteractWithEnemies()){
|
|
SetTargetUnit(finalTarget);
|
|
}
|
|
}
|
|
} else {
|
|
reloadTimer=1.f/(GetAtkSpd()/2.f);
|
|
attackFailed=true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Unit::InFogOfWar(){
|
|
return TileManager::visibleTiles.count(GetPos()/96)==0;
|
|
}
|
|
|
|
bool Unit::GhostInFogOfWar(){
|
|
return TileManager::visibleTiles.count(ghostPos/96)==0;
|
|
}
|
|
|
|
void Unit::HideGhost(){
|
|
ghostPos={99999,-99999};
|
|
}
|
|
|
|
vf2d Unit::GetGhostPos(){
|
|
return ghostPos;
|
|
}
|
|
|
|
bool Unit::IsMoveable(){
|
|
return moveable&&attachedPoint.expired();
|
|
}
|
|
|
|
bool Unit::CanInteractWithAllies(){
|
|
return friendlyInteractable&&attachedPoint.expired()&&buildTime<=0;
|
|
}
|
|
|
|
bool Unit::CanInteractWithEnemies(){
|
|
return enemyInteractable&&attachedPoint.expired()&&buildTime<=0;
|
|
}
|
|
|
|
Renderable&Unit::GetImage(){
|
|
return img;
|
|
}
|
|
|
|
std::weak_ptr<Unit>Unit::GetCurrentTarget(){
|
|
return target;
|
|
}
|
|
|
|
bool Unit::AutoAcquiresFriendlyTargets(){
|
|
return autoAcquireFriendlyTarget;
|
|
}
|
|
|
|
bool Unit::CanMove(){
|
|
return moveSpd.size>0&&attachedPoint.expired()&&buildTime<=0;
|
|
}
|
|
|
|
void Unit::SetTargetCollectionPoint(std::weak_ptr<CollectionPoint>targetCP,std::weak_ptr<Unit>self_ptr){
|
|
SetTargetLocation(targetCP.lock()->pos);
|
|
attachTarget=targetCP;
|
|
willAttachWhenReachingDestination=true;
|
|
this->self_ptr=self_ptr;
|
|
}
|
|
|
|
Pixel Unit::GetUnitColor(){
|
|
Pixel col;
|
|
if(!attachedPoint.expired()){
|
|
switch(attachedPoint.lock()->type){
|
|
case HEALTH:{
|
|
return CONSTANT::HEALTH_COLOR/2;
|
|
}break;
|
|
case RANGE:{
|
|
return CONSTANT::RANGE_COLOR/2;
|
|
}break;
|
|
case ATKSPD:{
|
|
return CONSTANT::ATKSPD_COLOR/2;
|
|
}break;
|
|
case MOVESPD:{
|
|
return CONSTANT::MOVESPD_COLOR/2;
|
|
}break;
|
|
case PROCEDURE:{
|
|
return CONSTANT::PROCEDURE_COLOR/2;
|
|
}break;
|
|
}
|
|
} else {
|
|
return friendly?Pixel{192,192,255}:Pixel{255,192,192};
|
|
}
|
|
}
|
|
|
|
bool Unit::ClickHandled(TileTransformedView&game,Resources&player_resources,std::vector<std::shared_ptr<Unit>>&units,std::vector<std::unique_ptr<Renderable>>&IMAGES){
|
|
return false;
|
|
};
|
|
|
|
void Unit::UpdateGUIState(TileTransformedView&game,Resources&player_resources,Textbox&displayBox,bool&hovered,int totalUsedMemory,int availableMemory){};
|
|
|
|
bool Unit::IsAllocator(){
|
|
return isAllocator&&attachedPoint.expired()&&buildTime<=0;
|
|
}
|
|
|
|
void Unit::SetBuildUnit(float buildTime,std::shared_ptr<Unit>finalUnit,std::vector<std::unique_ptr<Audio>>&SOUNDS){
|
|
if(this->IsAllocator()){
|
|
SOUNDS[Sound::SMALLBUILD]->Play(GetPos());
|
|
} else
|
|
if(this->IsPlatform()){
|
|
SOUNDS[Sound::BIGBUILD]->Play(GetPos());
|
|
}
|
|
//Once the units start building they aren't considered allocators/platforms anymore, must do it before.
|
|
this->buildTime=buildTime;
|
|
this->buildTransformUnit=std::move(finalUnit);
|
|
}
|
|
|
|
bool Unit::IsBuilding(){
|
|
return buildTime>0;
|
|
}
|
|
|
|
void Unit::SetGuardTime(float time){
|
|
guardTime=time;
|
|
}
|
|
|
|
bool Unit::IsGuarded(){
|
|
return guardTime>0;
|
|
}
|
|
|
|
void Unit::SaveMemory(){
|
|
savedMemory=memory;
|
|
}
|
|
|
|
bool Unit::IsPlatform(){
|
|
return isPlatform&&attachedPoint.expired()&&buildTime<=0;
|
|
}
|
|
|
|
bool Unit::IsAttached(){
|
|
return !attachedPoint.expired();
|
|
}
|
|
|
|
Unit*Unit::GetBuildUnit(){
|
|
return buildTransformUnit.get();
|
|
}
|
|
|
|
bool Unit::IsRAMBank(){
|
|
return isRAMBank;
|
|
} |