The open source repository for the action RPG game in development by Sig Productions titled 'Adventures in Lestoria'! https://forums.lestoria.net
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.
 
 
 
 
 
 
AdventuresInLestoria/Crawler/Monster.cpp

379 lines
11 KiB

#include "Monster.h"
#include "DamageNumber.h"
#include "Crawler.h"
#include "Bullet.h"
#include "DEFINES.h"
INCLUDE_ANIMATION_DATA
INCLUDE_MONSTER_DATA
INCLUDE_MONSTER_LIST
INCLUDE_DAMAGENUMBER_LIST
INCLUDE_game
INCLUDE_BULLET_LIST
MonsterData::MonsterData(){}
MonsterData::MonsterData(MonsterName type,int hp,int atk,std::vector<AnimationState>animations,float moveSpd,float size,MonsterStrategy strategy,int collisionDmg):
type(type),hp(hp),atk(atk),moveSpd(moveSpd),size(size),strategy(strategy),animations(animations),collisionDmg(collisionDmg){
}
int MonsterData::GetHealth(){
return hp;
}
int MonsterData::GetAttack(){
return atk;
}
float MonsterData::GetMoveSpdMult(){
return moveSpd;
}
float MonsterData::GetSizeMult(){
return size;
}
int MonsterData::GetCollisionDmg(){
return collisionDmg;
}
MonsterName MonsterData::GetType(){
return type;
}
MonsterStrategy MonsterData::GetAIStrategy(){
return strategy;
}
Monster::Monster(){}
Monster::Monster(vf2d pos,MonsterData data):
pos(pos),hp(data.GetHealth()),maxhp(data.GetHealth()),atk(data.GetAttack()),moveSpd(data.GetMoveSpdMult()),size(data.GetSizeMult()),strategy(data.GetAIStrategy()),type(data.GetType()){
bool firstAnimation=true;
for(AnimationState&anim:data.GetAnimations()){
animation.AddState(anim,ANIMATION_DATA[anim]);
if(firstAnimation){
animation.ChangeState(internal_animState,anim);
firstAnimation=false;
}
}
randomFrameOffset=(rand()%1000)/1000.f;
}
vf2d&Monster::GetPos(){
return pos;
}
int Monster::GetHealth(){
return hp;
}
int Monster::GetAttack(){
float mod_atk=atk;
for(Buff&b:GetBuffs(ATTACK_UP)){
mod_atk+=atk*b.intensity;
}
return int(mod_atk);
}
float Monster::GetMoveSpdMult(){
float mod_moveSpd=moveSpd;
for(Buff&b:GetBuffs(SLOWDOWN)){
mod_moveSpd-=moveSpd*b.intensity;
}
return mod_moveSpd;
}
float Monster::GetSizeMult(){
return size;
}
Animate2D::Frame Monster::GetFrame(){
return animation.GetFrame(internal_animState);
}
void Monster::UpdateAnimation(AnimationState state){
animation.ChangeState(internal_animState,state);
}
void Monster::PerformJumpAnimation(){
switch(type){
case SLIME_GREEN:{
animation.ChangeState(internal_animState,AnimationState::GREEN_SLIME_JUMP);
}break;
case SLIME_BLUE:{
animation.ChangeState(internal_animState,AnimationState::BLUE_SLIME_JUMP);
}break;
case SLIME_RED:{
animation.ChangeState(internal_animState,AnimationState::RED_SLIME_JUMP);
}break;
}
}
void Monster::PerformShootAnimation(){
switch(type){
case SLIME_GREEN:{
animation.ChangeState(internal_animState,AnimationState::GREEN_SLIME_SPIT);
}break;
case SLIME_BLUE:{
animation.ChangeState(internal_animState,AnimationState::BLUE_SLIME_SPIT);
}break;
case SLIME_RED:{
animation.ChangeState(internal_animState,AnimationState::RED_SLIME_SPIT);
}break;
}
}
void Monster::SetX(float x){
if(x-12*size>0&&x+12*size<game->WORLD_SIZE.x*24){
pos.x=x;
} else {
pos.x=std::min(game->WORLD_SIZE.x*24-12*size,std::max(12*size,pos.x));
}
}
void Monster::SetY(float y){
if(y-12*size>0&&y+12*size<game->WORLD_SIZE.y*24){
pos.y=y;
} else {
pos.y=std::min(game->WORLD_SIZE.y*24-12*size,std::max(12*size,pos.y));
}
}
bool Monster::Update(float fElapsedTime){
if(IsAlive()){
for(std::vector<Buff>::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;
}
}
for(Monster&m:MONSTER_LIST){
if(&m==this)continue;
if(geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(m.GetPos(),12*m.GetSizeMult()/2))){
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(!game->GetPlayer().HasIframes()&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(game->GetPlayer().GetPos(),12*game->GetPlayer().GetSizeMult()/2))){
geom2d::line line(pos,game->GetPlayer().GetPos());
float dist = line.length();
SetPosition(line.rpoint(-0.1));
vel=line.vector().norm()*-128;
}
if(state==NORMAL){
if(game->GetPlayer().GetX()>pos.x){
facingDirection=RIGHT;
} else {
facingDirection=LEFT;
}
}
switch(strategy){
case RUN_TOWARDS:{
targetAcquireTimer=std::max(0.f,targetAcquireTimer-fElapsedTime);
if(targetAcquireTimer==0){
targetAcquireTimer=3;
target=geom2d::line(pos,game->GetPlayer().GetPos()).upoint(1.2);
state=MOVE_TOWARDS;
}
if(state==MOVE_TOWARDS&&geom2d::line(pos,target).length()>100*fElapsedTime*GetMoveSpdMult()){
SetPosition(pos+geom2d::line(pos,target).vector().norm()*100*fElapsedTime*GetMoveSpdMult());
PerformJumpAnimation();
} else {
if(state==MOVE_TOWARDS){
state=NORMAL;//Revert state once we've finished moving towards target.
UpdateAnimation(MONSTER_DATA[type].GetAnimations()[0]);
}
}
}break;
case SHOOT_AFAR:{
targetAcquireTimer=std::max(0.f,targetAcquireTimer-fElapsedTime);
attackCooldownTimer=std::max(0.f,attackCooldownTimer-fElapsedTime);
if(queueShotTimer>0){
queueShotTimer-=fElapsedTime;
if(queueShotTimer<0){
queueShotTimer=0;
BULLET_LIST.push_back(Bullet(pos+vf2d{0,-4},geom2d::line(pos+vf2d{0,-4},game->GetPlayer().GetPos()).vector().norm()*24*3.f,2,GetAttack(),{75/2,162/2,225/2}));
}
}
geom2d::line line(pos,game->GetPlayer().GetPos());
if(targetAcquireTimer==0&&queueShotTimer==0){
targetAcquireTimer=1;
if(line.length()<24*6){
target=line.upoint(-1.2);
if(pos.x-12*size>1&&pos.x+12*size<game->WORLD_SIZE.x*24-1&&
pos.y-12*size>1&&pos.y+12*size<game->WORLD_SIZE.y*24-1){
state=MOVE_AWAY;
} else
if(pos.x-12*size<=1||pos.x+12*size>=game->WORLD_SIZE.x*24-1){
geom2d::line moveTowardsLine=geom2d::line(pos,target);
if(abs(moveTowardsLine.vector().norm().y)>=0.5){
state=MOVE_AWAY;
} else {
state=NORMAL;
}
} else
if(pos.y-12*size<=1||pos.y+12*size>=game->WORLD_SIZE.y*24-1){
geom2d::line moveTowardsLine=geom2d::line(pos,target);
if(abs(moveTowardsLine.vector().norm().x)>=0.5){
state=MOVE_AWAY;
} else {
state=NORMAL;
}
}
} else
if(line.length()>24*7){
target=line.upoint(1.2);
state=MOVE_TOWARDS;
} else {
state=NORMAL;
}
}
geom2d::line moveTowardsLine=geom2d::line(pos,target);
switch(state){
case MOVE_TOWARDS:{
if(moveTowardsLine.length()>1){
SetPosition(pos+moveTowardsLine.vector().norm()*100*fElapsedTime*GetMoveSpdMult());
}
if(line.length()<=24*7){
state=NORMAL;
}
if(moveTowardsLine.vector().x>0){
facingDirection=RIGHT;
} else {
facingDirection=LEFT;
}
PerformJumpAnimation();
}break;
case MOVE_AWAY:{
if(moveTowardsLine.length()>1){
SetPosition(pos+moveTowardsLine.vector().norm()*100*fElapsedTime*GetMoveSpdMult());
}
if(line.length()>=24*6){
state=NORMAL;
}
if(moveTowardsLine.vector().x>0){
facingDirection=RIGHT;
} else {
facingDirection=LEFT;
}
PerformJumpAnimation();
}break;
default:{
if(attackCooldownTimer==0){
attackCooldownTimer=1;
queueShotTimer=0.7;
PerformShootAnimation();
}
}
}
}break;
}
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);
}
SetX(pos.x+vel.x*fElapsedTime);
SetY(pos.y+vel.y*fElapsedTime);
}
if(hp<=0){
deathTimer+=fElapsedTime;
if(deathTimer>3){
return false;
}
}
animation.UpdateState(internal_animState,randomFrameOffset+fElapsedTime);
randomFrameOffset=0;
return true;
}
Key Monster::GetFacingDirection(){
return facingDirection;
}
void Monster::Draw(){
if(GetFacingDirection()==RIGHT){
game->view.DrawPartialDecal((GetPos()+vf2d{float(GetFrame().GetSourceRect().size.x),0}*GetSizeMult())-vf2d{12,12}*GetSizeMult(),GetFrame().GetSourceImage()->Decal(),GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,vf2d(GetSizeMult()*-1,GetSizeMult()),GetBuffs(BuffType::SLOWDOWN).size()>0?Pixel{uint8_t(255*abs(sin(1.4*GetBuffs(BuffType::SLOWDOWN)[0].duration))),uint8_t(255*abs(sin(1.4*GetBuffs(BuffType::SLOWDOWN)[0].duration))),uint8_t(128+127*abs(sin(1.4*GetBuffs(BuffType::SLOWDOWN)[0].duration)))}:WHITE);
} else {
game->view.DrawPartialDecal(GetPos()-vf2d{12,12}*GetSizeMult(),GetFrame().GetSourceImage()->Decal(),GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,vf2d(GetSizeMult(),GetSizeMult()),GetBuffs(BuffType::SLOWDOWN).size()>0?Pixel{uint8_t(255*abs(sin(1.4*GetBuffs(BuffType::SLOWDOWN)[0].duration))),uint8_t(255*abs(sin(1.4*GetBuffs(BuffType::SLOWDOWN)[0].duration))),uint8_t(128+127*abs(sin(1.4*GetBuffs(BuffType::SLOWDOWN)[0].duration)))}:WHITE);
}
}
void Monster::Collision(Player&p){
if(MONSTER_DATA[type].GetCollisionDmg()>0){
p.Hurt(MONSTER_DATA[type].GetCollisionDmg());
}
Collision();
}
void Monster::Collision(Monster&m){
Collision();
}
void Monster::Collision(){
if(strategy==RUN_TOWARDS&&state==MOVE_TOWARDS){
state=NORMAL;
}
}
void Monster::SetVelocity(vf2d vel){
this->vel=vel;
}
void Monster::SetPosition(vf2d pos){
SetX(pos.x);
SetY(pos.y);
}
AnimationState Monster::GetDeathAnimationName(){
switch(type){
case SLIME_GREEN:{
return AnimationState::GREEN_SLIME_DIE;
}
case SLIME_BLUE:{
return AnimationState::BLUE_SLIME_DIE;
}
case SLIME_RED:{
return AnimationState::RED_SLIME_DIE;
}
default:{
return AnimationState::WARRIOR_IDLE_S;
}
}
}
bool Monster::Hurt(int damage){
if(hp<=0) return false;
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)));
if(hp<=0){
animation.ChangeState(internal_animState,GetDeathAnimationName());
}
return true;
}
bool Monster::IsAlive(){
return hp>0;
}
vf2d&Monster::GetTargetPos(){
return target;
}
MonsterSpawner::MonsterSpawner(){}
MonsterSpawner::MonsterSpawner(vf2d pos,int range,std::vector<std::pair<MonsterName,vf2d>>monsters):
pos(pos),range(range),monsters(monsters){
}
bool MonsterSpawner::SpawnTriggered(){
return triggered;
}
int MonsterSpawner::GetRange(){
return range;
}
vf2d MonsterSpawner::GetPos(){
return pos;
}
void MonsterSpawner::SetTriggered(bool trigger,bool spawnMonsters){
triggered=trigger;
if(spawnMonsters){
for(std::pair<MonsterName,vf2d>&monsterInfo:monsters){
MONSTER_LIST.push_back(Monster(pos+monsterInfo.second,MONSTER_DATA[monsterInfo.first]));
}
}
}
void Monster::AddBuff(BuffType type,float duration,float intensity){
buffList.push_back(Buff{type,duration,intensity});
}
std::vector<Buff>Monster::GetBuffs(BuffType buff){
std::vector<Buff>filteredBuffs;
std::copy_if(buffList.begin(),buffList.end(),std::back_inserter(filteredBuffs),[buff](Buff&b){return b.type==buff;});
return filteredBuffs;
}