#include "Unit.h" #include "olcUTIL_Geometry2D.h" #include "TileManager.h" #include "util.h" BasicUnit::BasicUnit(PixelGameEngine*pge,vf2d pos,std::map>&IMAGES,bool friendly,bool moveable) :Unit(pge,{ {HEALTH,4}, {RANGE,2}, {ATKSPD,2}, {MOVESPD,3}, {PROCEDURE,1}, },pos,12,*IMAGES[VIRUS_IMG1],WHITE,WHITE,friendly,moveable){} void BasicUnit::Attack(Unit&victim,std::vector>&otherUnits){ victim<<=1; } BasicUnit2::BasicUnit2(PixelGameEngine*pge,vf2d pos,std::map>&IMAGES,bool friendly,bool moveable) :Unit(pge,{ {RANGE,2}, {ATKSPD,2}, {MOVESPD,3}, {PROCEDURE,1}, {HEALTH,4}, },pos,12,*IMAGES[VIRUS_IMG1],WHITE,WHITE,friendly,moveable){} void BasicUnit2::Attack(Unit&victim,std::vector>&otherUnits){ victim>>=1; } LeftShifter::LeftShifter(PixelGameEngine*pge,vf2d pos,std::map>&IMAGES,bool friendly,bool moveable) :Unit(pge,{ {RANGE,2}, {ATKSPD,2}, {MOVESPD,3}, {PROCEDURE,1}, {HEALTH,4}, },pos,12,*IMAGES[LEFT_SHIFTER],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,moveable){} void LeftShifter::Attack(Unit&victim,std::vector>&otherUnits){ victim<<=1; } RightShifter::RightShifter(PixelGameEngine*pge,vf2d pos,std::map>&IMAGES,bool friendly,bool moveable) :Unit(pge,{ {HEALTH,4}, {RANGE,2}, {ATKSPD,2}, {MOVESPD,3}, {PROCEDURE,1}, },pos,12,*IMAGES[RIGHT_SHIFTER],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,moveable){} void RightShifter::Attack(Unit&victim,std::vector>&otherUnits){ victim>>=1; } BitRestorer::BitRestorer(PixelGameEngine*pge,vf2d pos,std::map>&IMAGES,bool friendly,bool moveable) :Unit(pge,{ {PROCEDURE,6}, {RANGE,1}, {ATKSPD,1}, {MOVESPD,1}, {HEALTH,2}, },pos,12,*IMAGES[BIT_RESTORER],CONSTANT::HEALER_TARGET_COL,CONSTANT::HEALER_ATTACK_COL,friendly,moveable,true,false){} void BitRestorer::Attack(Unit&victim,std::vector>&otherUnits){ std::vectoremptyMemoryPositions; for(int i=0;i>&otherUnits){ std::vectoremptyMemoryPositions; for(auto&u:otherUnits){ if(u.get()!=this&&u->IsFriendly()&&InRange(u)){ for(int i=0;iGetMemorySize();i++){ if(!u->memory[i]){ emptyMemoryPositions.emplace_back(i); } } if(emptyMemoryPositions.size()!=0){ int randomBit=emptyMemoryPositions[rand()%emptyMemoryPositions.size()]; u->memory[randomBit]=u->ghostMemory[randomBit]=true; appliedTarget=u; return; } } } } MemorySwapper::MemorySwapper(PixelGameEngine*pge,vf2d pos,std::map>&IMAGES,bool friendly,bool moveable) :Unit(pge,{ {RANGE,3}, {ATKSPD,1}, {HEALTH,3}, {PROCEDURE,3}, {MOVESPD,2}, },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>&otherUnits){ } Corrupter::Corrupter(PixelGameEngine*pge,vf2d pos,std::map>&IMAGES,bool friendly,bool moveable) :Unit(pge,{ {ATKSPD,3}, {RANGE,1}, {PROCEDURE,8}, {MOVESPD,4}, {HEALTH,4}, },pos,12,*IMAGES[CORRUPTER],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,moveable){} void Corrupter::Attack(Unit&victim,std::vector>&otherUnits){ //Chooses a bit at random and corrupts it. int randomBit=rand()%victim.memory.size(); victim.memory[randomBit]=victim.ghostMemory[randomBit]=false; } MemoryAllocator::MemoryAllocator(PixelGameEngine*pge,vf2d pos,std::map>&IMAGES,bool friendly,bool moveable) :Unit(pge,{ {RANGE,1}, {ATKSPD,1}, {MOVESPD,1}, {PROCEDURE,1}, {HEALTH,1}, },pos,12,*IMAGES[UNIT_ALLOCATOR],CONSTANT::ATTACKER_TARGET_COL,CONSTANT::ATTACKER_ATTACK_COL,friendly,true,false){} void MemoryAllocator::Attack(Unit&victim,std::vector>&otherUnits){ } RAMBank::RAMBank(PixelGameEngine*pge,vf2d pos,std::map>&IMAGES,bool friendly) :Unit(pge,{ {RANGE,0}, {ATKSPD,0}, {MOVESPD,0}, {PROCEDURE,25}, {HEALTH,16}, },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); } void RAMBank::Attack(Unit&victim,std::vector>&otherUnits){ } void RAMBank::Update(PixelGameEngine*pge,std::map>&SOUNDS){ 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))); } } } if(!soundStarted){ soundStarted=true; soundHandle=SOUNDS[Sound::HUM]->Play(GetPos(),0.4,0.4,true); } 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); } } void RAMBank::OnDeath(std::map>&SOUNDS){ SOUNDS[Sound::HUM]->Stop(soundHandle); } Unit::Unit(PixelGameEngine*pge,std::vectormemory,vf2d pos,float radius,Renderable&img,Pixel targetLineColor,Pixel attackingLineColor,bool friendly,bool moveable,bool friendlyInteractable,bool enemyInteractable) :pos(pos),radius(radius),ghostPos(pos),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;imemory.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::map>&IMAGES){ if(!CanInteractWithAllies()&&!CanInteractWithEnemies())return; float dist=geom2d::line(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(distDecal(),0,IMAGES[RANGE_INDICATOR]->Sprite()->Size()/2,{totalRange/12,totalRange/12},{col.r,col.g,col.b,transparency}); } } void Unit::Draw(TileTransformedView&game,std::map>&IMAGES){ game.DrawRotatedDecal(ghostPos,img.Decal(),0,img.Sprite()->Size()/2,{1,1},friendly?Pixel{192,192,255}:Pixel{255,192,192}); if(IsSelected()){ game.DrawRotatedDecal(ghostPos,IMAGES[SELECTION_CIRCLE]->Decal(),0,IMAGES[SELECTION_CIRCLE]->Sprite()->Size()/2,vf2d(img.Sprite()->Size())/IMAGES[SELECTION_CIRCLE]->Sprite()->Size(),WHITE); } } void Unit::DrawHud(TileTransformedView&game,std::map>&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){ col=CONSTANT::HEALTH_COLOR; } if(range.index==i){ col=CONSTANT::RANGE_COLOR; } if(atkSpd.index==i){ col=CONSTANT::ATKSPD_COLOR; } if(moveSpd.index==i){ col=CONSTANT::MOVESPD_COLOR; } if(procedure.index==i){ col=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},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},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},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},CONSTANT::RANGE_COLOR); } for(int i=0;i>&IMAGES){ if(!target.expired()){ geom2d::linelineToTarget(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); } if(!appliedTarget.expired()){ geom2d::linelineToTarget(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(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(distrange,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>&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::map>&SOUNDS){ if(!target.expired()){ auto ptrTarget=target.lock(); if(!InRange(ptrTarget)){ SetPos(GetPos()+(ptrTarget->GetPos()-pos).norm()*GetMoveSpd()*24*pge->GetElapsedTime()); } } else if(targetLoc!=CONSTANT::UNSELECTED){ float dist=geom2d::line(pos,targetLoc).length(); if(dist>24){ SetPos(GetPos()+(targetLoc-pos).norm()*GetMoveSpd()*24*pge->GetElapsedTime()); } } if(!IsFriendly()){ _RunAI(pge); } if(!GhostInFogOfWar()&&InFogOfWar()){ HideGhost(); } reloadTimer=std::max(0.f,reloadTimer-pge->GetElapsedTime()); lineShift-=pge->GetElapsedTime(); if(lineShift<-25)lineShift+=25; Update(pge,SOUNDS); } std::vector operator <<(Unit&u,const int n){ std::vectortempMem=u.memory; for(int i=0;i operator >>(Unit&u,const int n){ std::vectortempMem=u.memory; for(int i=1;itarget){ this->target=target; this->targetLoc=CONSTANT::UNSELECTED; } void Unit::SetTargetLocation(vf2d targetLoc){ this->target.reset(); this->targetLoc=targetLoc; } bool Unit::InRange(std::shared_ptrtarget){ 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{GetPos(),totalRange},geom2d::circle{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{GetPos(),totalRange},geom2d::circle{pos,0.1}); } void Unit::SetPos(vf2d newPos){ pos=newPos; if(!InFogOfWar()){ ghostPos=pos; } } void Unit::AttemptAttack(std::weak_ptrattacker,std::weak_ptrunit,std::vector>&otherUnits){ if(reloadTimer>0)return; std::weak_ptrfinalTarget; 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; _Attack(attacker,finalTarget,otherUnits); //Call the parent function first, followed by the child. } } } void Unit::Update(PixelGameEngine*pge,std::map>&SOUNDS){} void Unit::Attacked(std::weak_ptrattacker){} void Unit::_Attacked(std::weak_ptrattacker){ Attacked(attacker); if(attacker.lock()->IsFriendly()!=IsFriendly()&&CanInteractWithEnemies()){ SetTargetUnit(attacker); } } void Unit::_Attack(std::weak_ptrattacker,std::weak_ptrfinalTarget,std::vector>&otherUnits){ if(GetProcedure()>0&&GetAtkSpd()>0){ attackFailed=false; float procChance=float(GetProcedure())/procedure.size; if(util::random(1)>=1-procChance){ Attack(*finalTarget.lock(),otherUnits); finalTarget.lock()->_Attacked(attacker); 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; } bool Unit::CanInteractWithAllies(){ return friendlyInteractable; } bool Unit::CanInteractWithEnemies(){ return enemyInteractable; } Renderable&Unit::GetImage(){ return img; } std::weak_ptrUnit::GetCurrentTarget(){ return target; } bool Unit::AutoAcquiresFriendlyTargets(){ return autoAcquireFriendlyTarget; }