#include "Monster.h" #include "Player.h" #include "Crawler.h" #include "DamageNumber.h" #include "Bullet.h" #include "DEFINES.h" INCLUDE_MONSTER_DATA INCLUDE_MONSTER_LIST INCLUDE_ANIMATION_DATA INCLUDE_SPAWNER_LIST INCLUDE_DAMAGENUMBER_LIST INCLUDE_CLASS_DATA INCLUDE_PLAYER_BULLET_LIST INCLUDE_game const float Player::GROUND_SLAM_SPIN_TIME=0.6f; Player::Player(): state(State::NORMAL),lastReleasedMovementKey(DOWN),facingDirection(DOWN){} void Player::SetClass(Class cl){ this->cl=CLASS_DATA[cl].cl; rightClickAbility=CLASS_DATA[cl].rightClickAbility; ability1=CLASS_DATA[cl].ability1; ability2=CLASS_DATA[cl].ability2; ability3=CLASS_DATA[cl].ability3; UpdateIdleAnimation(DOWN); } void Player::SetX(float x){ vf2d newPos={x,pos.y}; vi2d tilePos=vi2d(newPos/24)*24; geom2d::rectcollisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos); if(collisionRect.pos==vi2d{0,0}&&collisionRect.size==vi2d{1,1}){ pos.x=std::clamp(x,12.f*GetSizeMult(),float(game->WORLD_SIZE.x*24-12*GetSizeMult())); } else { geom2d::rectcollision={collisionRect.pos,collisionRect.size}; collision.pos+=tilePos; if(!geom2d::overlaps(newPos,collision)){ pos.x=std::clamp(x,12.f*GetSizeMult(),float(game->WORLD_SIZE.x*24-12*GetSizeMult())); } } }; void Player::SetY(float y){ vf2d newPos={pos.x,y}; vi2d tilePos=vi2d(newPos/24)*24; geom2d::rectcollisionRect=game->GetTileCollision(game->GetCurrentLevel(),newPos); if(collisionRect.pos==vi2d{0,0}&&collisionRect.size==vi2d{1,1}){ pos.y=std::clamp(y,12.f*GetSizeMult(),float(game->WORLD_SIZE.y*24-12*GetSizeMult())); } else { geom2d::rectcollision={collisionRect.pos,collisionRect.size}; collision.pos+=tilePos; if(!geom2d::overlaps(newPos,collision)){ pos.y=std::clamp(y,12.f*GetSizeMult(),float(game->WORLD_SIZE.y*24-12*GetSizeMult())); } } } void Player::SetZ(float z){ this->z=z; } void Player::SetPos(vf2d pos){ this->pos=pos; } vf2d&Player::GetPos(){ return pos; } float Player::GetX(){ return pos.x; } float Player::GetY(){ return pos.y; } float Player::GetZ(){ return z; } int Player::GetHealth(){ return hp; } int Player::GetMaxHealth(){ return maxhp; } int Player::GetMana(){ return mana; } int Player::GetMaxMana() { return maxmana; } int Player::GetAttack(){ float mod_atk=atk; for(Buff&b:GetBuffs(BuffType::ATTACK_UP)){ mod_atk+=atk*b.intensity; } return int(mod_atk); } float Player::GetMoveSpdMult(){ float mod_moveSpd=moveSpd; for(Buff&b:GetBuffs(BuffType::SLOWDOWN)){ mod_moveSpd-=moveSpd*b.intensity; } return mod_moveSpd; } float Player::GetSizeMult(){ return size; } float Player::GetAttackRangeMult(){ return attack_range; } float Player::GetSpinAngle(){ return spin_angle; } State Player::GetState(){ return state; } void Player::Update(float fElapsedTime){ attack_cooldown_timer=std::max(0.f,attack_cooldown_timer-fElapsedTime); iframe_time=std::max(0.f,iframe_time-fElapsedTime); notEnoughManaDisplay.second=std::max(0.f,notEnoughManaDisplay.second-fElapsedTime); manaTickTimer-=fElapsedTime; while(manaTickTimer<=0){ manaTickTimer+=0.2; mana=std::min(maxmana,mana+1); } for(std::vector::iterator it=buffList.begin();it!=buffList.end();++it){ Buff&b=*it; b.duration-=fElapsedTime; if(b.duration<=0){ it=buffList.erase(it); if(it==buffList.end())break; } } //Class-specific update events. switch (cl) { case WARRIOR: { }break; case THIEF: { }break; case RANGER: { }break; case BARD: { }break; case WIZARD: { if(attack_cooldown_timer>0){ CLASS_DATA[cl].idle_n=AnimationState::WIZARD_IDLE_ATTACK_N; CLASS_DATA[cl].idle_e=AnimationState::WIZARD_IDLE_ATTACK_E; CLASS_DATA[cl].idle_s=AnimationState::WIZARD_IDLE_ATTACK_S; CLASS_DATA[cl].idle_w=AnimationState::WIZARD_IDLE_ATTACK_W; CLASS_DATA[cl].walk_n=AnimationState::WIZARD_ATTACK_N; CLASS_DATA[cl].walk_e=AnimationState::WIZARD_ATTACK_E; CLASS_DATA[cl].walk_s=AnimationState::WIZARD_ATTACK_S; CLASS_DATA[cl].walk_w=AnimationState::WIZARD_ATTACK_W; } else { CLASS_DATA[cl].idle_n=AnimationState::WIZARD_IDLE_N; CLASS_DATA[cl].idle_e=AnimationState::WIZARD_IDLE_E; CLASS_DATA[cl].idle_s=AnimationState::WIZARD_IDLE_S; CLASS_DATA[cl].idle_w=AnimationState::WIZARD_IDLE_W; CLASS_DATA[cl].walk_n=AnimationState::WIZARD_WALK_N; CLASS_DATA[cl].walk_e=AnimationState::WIZARD_WALK_E; CLASS_DATA[cl].walk_s=AnimationState::WIZARD_WALK_S; CLASS_DATA[cl].walk_w=AnimationState::WIZARD_WALK_W; } }break; case WITCH: { }break; } switch(state){ case SPIN:{ switch(facingDirection){ case UP:{ if(lastAnimationFlip==0){ lastAnimationFlip=0.03; facingDirection=DOWN; animation.ChangeState(internal_animState,AnimationState::WARRIOR_WALK_S); } }break; case DOWN:{ if(lastAnimationFlip==0){ lastAnimationFlip=0.03; facingDirection=UP; animation.ChangeState(internal_animState,AnimationState::WARRIOR_WALK_N); } }break; } if(facingDirection==RIGHT){ spin_angle+=spin_spd*fElapsedTime; } else { spin_angle-=spin_spd*fElapsedTime; } if(spin_attack_timer>0){ z=50*sin(3.3*(GROUND_SLAM_SPIN_TIME-spin_attack_timer)/GROUND_SLAM_SPIN_TIME); spin_attack_timer=std::max(0.f,spin_attack_timer-fElapsedTime); } else { SetState(NORMAL); spin_angle=0; z=0; game->HurtEnemies(pos,3*12,GetAttack()*2.5); game->AddEffect(Effect{GetPos(),0.5,AnimationState::GROUND_SLAM_ATTACK_FRONT,1.33f,0.6f},Effect{GetPos(),0.5,AnimationState::GROUND_SLAM_ATTACK_BACK,1.33f,0.6f}); } if(lastAnimationFlip>0){ lastAnimationFlip=std::max(0.f,lastAnimationFlip-fElapsedTime); } animation.UpdateState(internal_animState,fElapsedTime); }break; case BLOCK:{ if(rightClickAbility.COOLDOWN_TIME-rightClickAbility.cooldown>3){ SetState(NORMAL); } }break; case SWING_SONIC_SWORD:{ if(ability3.COOLDOWN_TIME-ability3.cooldown>0.5){ SetState(NORMAL); switch(facingDirection){ case DOWN:{ UpdateAnimation(AnimationState::WARRIOR_IDLE_S); }break; case RIGHT:{ UpdateAnimation(AnimationState::WARRIOR_IDLE_E); }break; case LEFT:{ UpdateAnimation(AnimationState::WARRIOR_IDLE_W); }break; case UP:{ UpdateAnimation(AnimationState::WARRIOR_IDLE_N); }break; } } animation.UpdateState(internal_animState,fElapsedTime); }break; default:{ //Update animations normally. animation.UpdateState(internal_animState,fElapsedTime); } } rightClickAbility.cooldown=std::max(0.f,rightClickAbility.cooldown-fElapsedTime); ability1.cooldown=std::max(0.f,ability1.cooldown-fElapsedTime); ability2.cooldown=std::max(0.f,ability2.cooldown-fElapsedTime); ability3.cooldown=std::max(0.f,ability3.cooldown-fElapsedTime); for(Monster&m:MONSTER_LIST){ if(iframe_time==0&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(m.GetPos(),12*m.GetSizeMult()/2))){ if(m.IsAlive()){ m.Collision(*this); } geom2d::line line(pos,m.GetPos()); float dist = line.length(); m.SetPosition(line.rpoint(dist*1.1)); if(m.IsAlive()){ vel=line.vector().norm()*-128; } } } if(vel.x>0){ vel.x=std::max(0.f,vel.x-friction*fElapsedTime); } else { vel.x=std::min(0.f,vel.x+friction*fElapsedTime); } if(vel.y>0){ vel.y=std::max(0.f,vel.y-friction*fElapsedTime); } else { vel.y=std::min(0.f,vel.y+friction*fElapsedTime); } if(vel!=vf2d{0,0}){ float newX=pos.x+vel.x*fElapsedTime; float newY=pos.y+vel.y*fElapsedTime; SetX(newX); SetY(newY); } if(attack_cooldown_timer==0&&game->GetMouse(0).bHeld){ switch (cl) { case WARRIOR:{ if(state!=State::SPIN){ bool attack=false; Monster*closest=nullptr; float closest_dist=999999; for(Monster&m:MONSTER_LIST){ if(m.IsAlive() &&geom2d::overlaps(geom2d::circle(pos-vf2d{size*12,size*12},attack_range*size*12),geom2d::circle(m.GetPos()-vf2d{m.GetSizeMult()*12,m.GetSizeMult()*12},m.GetSizeMult()*12)) &&geom2d::line(game->GetWorldMousePos(),m.GetPos()).length()(game->GetWorldMousePos(),m.GetPos()).length(); closest=&m; } } if(closest!=nullptr&&closest->Hurt(GetAttack())){ attack_cooldown_timer=ATTACK_COOLDOWN; swordSwingTimer=0.2; SetState(State::SWING_SWORD); switch(facingDirection){ case DOWN:{ UpdateAnimation(AnimationState::WARRIOR_SWINGSWORD_S); }break; case RIGHT:{ UpdateAnimation(AnimationState::WARRIOR_SWINGSWORD_E); }break; case LEFT:{ UpdateAnimation(AnimationState::WARRIOR_SWINGSWORD_W); }break; case UP:{ UpdateAnimation(AnimationState::WARRIOR_SWINGSWORD_N); }break; } } } }break; case THIEF: { }break; case RANGER: { }break; case BARD: { }break; case WIZARD: { attack_cooldown_timer=MAGIC_ATTACK_COOLDOWN; float angleToCursor=atan2(game->GetWorldMousePos().y-pos.y,game->GetWorldMousePos().x-pos.x); PLAYER_BULLET_LIST.push_back(Bullet(pos,{cos(angleToCursor)*200,sin(angleToCursor)*200},12,GetAttack(),AnimationState::ENERGY_BOLT,false,INFINITE,true)); }break; case WITCH: { }break; } } if(ability1.cooldown==0&&GetMana()>=ability1.manaCost&&game->GetKey(SHIFT).bHeld){ switch (cl) { case WARRIOR: { game->AddEffect(Effect(pos,0.1,AnimationState::BATTLECRY_EFFECT,1,0.3)); ability1.cooldown=ability1.COOLDOWN_TIME; AddBuff(BuffType::ATTACK_UP,10,0.1); AddBuff(BuffType::DAMAGE_REDUCTION,10,0.1); for(Monster&m:MONSTER_LIST){ if(m.GetSizeMult()<=1&&geom2d::overlaps(geom2d::circle(pos,12*3.5),geom2d::circle(m.GetPos(),m.GetSizeMult()*12))){ m.AddBuff(BuffType::SLOWDOWN,5,0.3); } } }break; case THIEF: { }break; case RANGER: { }break; case BARD: { }break; case WIZARD: { }break; case WITCH: { }break; } mana-=ability1.manaCost; } else if(ability1.cooldown==0&&GetMana()GetKey(SHIFT).bPressed){ notEnoughManaDisplay={ability1.name,1}; } if(ability2.cooldown==0&&GetMana()>=ability2.manaCost&&game->GetKey(SPACE).bPressed){ switch(cl){ case WARRIOR:{ Spin(GROUND_SLAM_SPIN_TIME,14*PI); iframe_time=GROUND_SLAM_SPIN_TIME+0.1; }break; case THIEF:{ }break; case RANGER:{ }break; case BARD:{ }break; case WIZARD:{ }break; case WITCH:{ }break; } mana-=ability2.manaCost; } else if(ability2.cooldown==0&&GetMana()GetKey(SPACE).bPressed){ notEnoughManaDisplay={ability2.name,1}; } if(ability3.cooldown==0&&GetMana()>=ability3.manaCost&&game->GetKey(CTRL).bPressed){ switch(cl){ case WARRIOR:{ ability3.cooldown=ability3.COOLDOWN_TIME; SetState(State::SWING_SONIC_SWORD); AddBuff(BuffType::SLOWDOWN,0.5,1); vf2d bulletVel={}; switch(GetFacingDirection()){ case UP:{ vel.y=70; bulletVel.y=-400; UpdateAnimation(AnimationState::WARRIOR_SWINGSONICSWORD_N); }break; case LEFT:{ vel.x=70; bulletVel.x=-400; UpdateAnimation(AnimationState::WARRIOR_SWINGSONICSWORD_W); }break; case RIGHT:{ vel.x=-70; bulletVel.x=400; UpdateAnimation(AnimationState::WARRIOR_SWINGSONICSWORD_E); }break; case DOWN:{ vel.y=-70; bulletVel.y=400; UpdateAnimation(AnimationState::WARRIOR_SWINGSONICSWORD_S); }break; } PLAYER_BULLET_LIST.push_back(Bullet(pos,bulletVel,30,GetAttack()*8,AnimationState::SONICSLASH,true,2.25,true)); game->SetupWorldShake(0.5); }break; case THIEF:{ }break; case RANGER:{ }break; case BARD:{ }break; case WIZARD:{ }break; case WITCH:{ }break; } mana-=ability3.manaCost; } else if(ability3.cooldown==0&&GetMana()GetKey(CTRL).bPressed){ notEnoughManaDisplay={ability3.name,1}; } if(rightClickAbility.cooldown==0&&GetMana()>=rightClickAbility.manaCost&&game->GetMouse(1).bHeld){ switch (cl) { case WARRIOR: { if(GetState()==State::NORMAL){ rightClickAbility.cooldown=rightClickAbility.COOLDOWN_TIME; SetState(State::BLOCK); AddBuff(BuffType::SLOWDOWN,3,0.3); } }break; case THIEF: { }break; case RANGER: { }break; case BARD: { }break; case WIZARD: { }break; case WITCH: { }break; } mana-=rightClickAbility.manaCost; } else if(rightClickAbility.cooldown==0&&GetMana()GetMouse(1).bPressed){ notEnoughManaDisplay={rightClickAbility.name,1}; } } float Player::GetSwordSwingTimer(){ return swordSwingTimer; } void Player::SetSwordSwingTimer(float val){ swordSwingTimer=val; } void Player::SetState(State newState){ state=newState; } vf2d Player::GetVelocity(){ return vel; } bool Player::HasIframes(){ return iframe_time>0; } bool Player::Hurt(int damage){ if(hp<=0||iframe_time!=0) return false; if(state==State::BLOCK)damage=0; float mod_dmg=damage; for(Buff&b:GetBuffs(BuffType::DAMAGE_REDUCTION)){ mod_dmg-=damage*b.intensity; } hp=std::max(0,hp-int(mod_dmg)); DAMAGENUMBER_LIST.push_back(DamageNumber(pos,int(mod_dmg))); return true; } void Player::AddAnimation(AnimationState state){ animation.AddState(state,ANIMATION_DATA[state]); } void Player::UpdateAnimation(AnimationState animState){ animation.ChangeState(internal_animState,animState); } Animate2D::Frame Player::GetFrame(){ return animation.GetFrame(internal_animState); } void Player::SetLastReleasedMovementKey(Key k){ lastReleasedMovementKey=k; } Key Player::GetLastReleasedMovementKey(){ return lastReleasedMovementKey; } void Player::SetFacingDirection(Key direction){ facingDirection=direction; } Key Player::GetFacingDirection(){ return facingDirection; } void Player::Moved(){ for(MonsterSpawner&spawner:SPAWNER_LIST){ if(!spawner.SpawnTriggered()&&geom2d::contains(geom2d::ellipse{spawner.GetPos(),spawner.GetRange()},pos)){ spawner.SetTriggered(true); } } } float Player::GetAbility2Cooldown(){ return ability2.cooldown; } float Player::GetRightClickCooldown(){ return rightClickAbility.cooldown; } void Player::Spin(float duration,float spinSpd){ state=State::SPIN; spin_attack_timer=duration; spin_spd=spinSpd; spin_angle=0; ability2.cooldown=ability2.COOLDOWN_TIME; } void Player::UpdateWalkingAnimation(Key direction){ AnimationState anim; switch(direction){ case UP:anim=CLASS_DATA[cl].walk_n;break; case RIGHT:anim=CLASS_DATA[cl].walk_e;break; case DOWN:anim=CLASS_DATA[cl].walk_s;break; case LEFT:anim=CLASS_DATA[cl].walk_w;break; } UpdateAnimation(anim); } void Player::UpdateIdleAnimation(Key direction){ AnimationState anim; switch(direction){ case UP:anim=CLASS_DATA[cl].idle_n;break; case RIGHT:anim=CLASS_DATA[cl].idle_e;break; case DOWN:anim=CLASS_DATA[cl].idle_s;break; case LEFT:anim=CLASS_DATA[cl].idle_w;break; } UpdateAnimation(anim); } void Player::AddBuff(BuffType type,float duration,float intensity){ buffList.push_back(Buff{type,duration,intensity}); } std::vectorPlayer::GetBuffs(BuffType buff){ std::vectorfilteredBuffs; std::copy_if(buffList.begin(),buffList.end(),std::back_inserter(filteredBuffs),[buff](Buff&b){return b.type==buff;}); return filteredBuffs; }