Finish implementing Pirate Captain and Parrot behaviors. Refactor monster phase system to be per-strategy instead of a global phase for all strategies. Release Build 11666.
This commit is contained in:
parent
a09ea9a9a7
commit
aae5726f3d
@ -52,11 +52,11 @@ INCLUDE_MONSTER_DATA
|
||||
using A=Attribute;
|
||||
|
||||
void Monster::STRATEGY::BEAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
switch(m.I(A::PHASE)){
|
||||
switch(PHASE()){
|
||||
case 0:{
|
||||
float distToPlayer=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).length();
|
||||
if(distToPlayer<operator""_Pixels(ConfigFloat("Attack Range"))){
|
||||
m.I(A::PHASE)=1;
|
||||
SETPHASE(1);
|
||||
m.PerformShootAnimation();
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Chargeup Time");
|
||||
//The bear slam attack indicator will move with the bear, and the LOCKON_POS variable will hold a polar coordinate indicating distance and angle for where it should be attacking relative to its position.
|
||||
@ -75,7 +75,7 @@ void Monster::STRATEGY::BEAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
case 1:{
|
||||
m.F(A::CASTING_TIMER)=std::max(0.f,m.F(A::CASTING_TIMER)-fElapsedTime);
|
||||
if(m.F(A::CASTING_TIMER)==0.f){
|
||||
m.I(A::PHASE)=2;
|
||||
SETPHASE(2);
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Attack Animation Wait Time");
|
||||
m.PerformAnimation("SLAM");
|
||||
}
|
||||
@ -85,7 +85,7 @@ void Monster::STRATEGY::BEAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
if(m.F(A::CASTING_TIMER)==0.f){
|
||||
float distToPlayer=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).length();
|
||||
SoundEffect::PlaySFX("Bear Slam Attack",m.GetPos()+m.V(A::LOCKON_POS).cart());
|
||||
m.I(A::PHASE)=0;
|
||||
SETPHASE(0);
|
||||
m.I(A::BEAR_STOMP_COUNT)++;
|
||||
geom2d::circle<float>attackCircle={m.GetPos()+m.V(A::LOCKON_POS).cart(),float(operator""_Pixels(ConfigFloat("Smash Attack Diameter"))/2.f)};
|
||||
if(geom2d::overlaps(attackCircle,game->GetPlayer()->Hitbox())){
|
||||
|
@ -57,7 +57,7 @@ void Monster::STRATEGY::BOAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
RECOVERY,
|
||||
};
|
||||
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case PhaseName::MOVE:{
|
||||
float distToPlayer=m.GetDistanceFrom(game->GetPlayer()->GetPos());
|
||||
if(m.canMove&&distToPlayer>=ConfigInt("Closein Range")/100.f*24){
|
||||
@ -75,7 +75,7 @@ void Monster::STRATEGY::BOAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
ScratchPhaseTransition:
|
||||
m.PerformAnimation("SCRATCH");
|
||||
m.F(A::CASTING_TIMER)=ConfigInt("Ground Scratch Count")*m.GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
m.phase=PhaseName::SCRATCH;
|
||||
SETPHASE(PhaseName::SCRATCH);
|
||||
|
||||
vf2d chargeTargetPoint=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).rpoint(ConfigFloat("Charge Distance")/100.f*24);
|
||||
float distanceToChargePoint=geom2d::line<float>(m.GetPos(),chargeTargetPoint).length();
|
||||
@ -89,7 +89,7 @@ void Monster::STRATEGY::BOAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0.f){
|
||||
m.PerformShootAnimation();
|
||||
m.phase=PhaseName::CHARGE;
|
||||
SETPHASE(PhaseName::CHARGE);
|
||||
|
||||
m.AddBuff(BuffType::SPEEDBOOST,INFINITE,ConfigFloat("Charge Movespeed")/100.f-1);
|
||||
|
||||
@ -102,7 +102,7 @@ void Monster::STRATEGY::BOAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
float distToTarget=geom2d::line<float>(m.GetPos(),m.target).length();
|
||||
|
||||
auto TransitionToRecoveryPhase=[&](){
|
||||
m.phase=PhaseName::RECOVERY;
|
||||
SETPHASE(PhaseName::RECOVERY);
|
||||
m.F(A::CHARGE_COOLDOWN)=ConfigFloat("Charge Recovery Time");
|
||||
m.PerformIdleAnimation();
|
||||
};
|
||||
@ -120,7 +120,7 @@ void Monster::STRATEGY::BOAR(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.F(A::CHARGE_COOLDOWN)-=fElapsedTime;
|
||||
m.targetAcquireTimer=0.f;
|
||||
m.RemoveBuff(BuffType::SPEEDBOOST);
|
||||
if(m.F(A::CHARGE_COOLDOWN)<=0)m.phase=PhaseName::MOVE;
|
||||
if(m.F(A::CHARGE_COOLDOWN)<=0)SETPHASE(PhaseName::MOVE);
|
||||
}break;
|
||||
}
|
||||
}
|
@ -56,11 +56,11 @@ void Monster::STRATEGY::BREAKING_PILLAR(Monster&m,float fElapsedTime,std::string
|
||||
m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Break Phase 1 Animation Name"));
|
||||
}else m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Break Phase 2 Animation Name"));
|
||||
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case INITIALIZE:{
|
||||
m.F(A::BREAK_TIME)=ConfigFloat("Break Time");
|
||||
m.F(A::SHAKE_TIMER)=0.2f;
|
||||
m.phase=RUN;
|
||||
SETPHASE(RUN);
|
||||
|
||||
m.SetStrategyDeathFunction([&](GameEvent&deathEvent,Monster&m,const std::string&strategy){
|
||||
m.lifetime=0.01f;
|
||||
|
@ -143,7 +143,7 @@ void DamageNumber::Draw(){
|
||||
|
||||
float DamageNumber::GetOriginalRiseSpd(){
|
||||
float riseSpd{20.f};
|
||||
if(type==INTERRUPT||type==MANA_GAIN)riseSpd=40.f;
|
||||
if(type==INTERRUPT||type==MANA_GAIN||type==HEALTH_GAIN)riseSpd=40.f;
|
||||
if(type==DOT)riseSpd=-10.f;
|
||||
return riseSpd;
|
||||
}
|
||||
|
@ -51,11 +51,11 @@ using A=Attribute;
|
||||
void Monster::STRATEGY::FROG(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.F(A::LOCKON_WAITTIME)=std::max(0.0f,m.F(A::LOCKON_WAITTIME)-fElapsedTime);
|
||||
phase:
|
||||
switch(m.I(A::PHASE)){
|
||||
switch(PHASE()){
|
||||
case 0:{ //Move towards phase.
|
||||
float distToPlayer=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).length();
|
||||
if(distToPlayer<24*ConfigInt("Range")/100.f){
|
||||
m.I(A::PHASE)++;
|
||||
SETPHASE(PHASE()+1);
|
||||
m.F(A::LOCKON_WAITTIME)=ConfigFloat("Lockon Wait Time");
|
||||
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos();
|
||||
m.RotateTowardsPos(m.V(A::LOCKON_POS));
|
||||
@ -72,23 +72,23 @@ void Monster::STRATEGY::FROG(Monster&m,float fElapsedTime,std::string strategy){
|
||||
SoundEffect::PlaySFX("Slime Shoot",m.pos);
|
||||
CreateBullet(FrogTongue)(m,tongueMaxRangePos,ConfigFloat("Attack Duration"),m.GetAttack(),m.OnUpperLevel(),ConfigFloat("Tongue Knockback Strength"),false,ConfigPixel("Tongue Color"))EndBullet;
|
||||
m.PerformShootAnimation();
|
||||
m.I(A::PHASE)=2;
|
||||
SETPHASE(2);
|
||||
}
|
||||
m.PerformIdleAnimation();
|
||||
}break;
|
||||
case 2:{
|
||||
if(m.F(A::LOCKON_WAITTIME)==0.0f){
|
||||
m.F(A::LOCKON_WAITTIME)=ConfigFloat("Attack Recovery Time");
|
||||
m.I(A::PHASE)=3;
|
||||
SETPHASE(3);
|
||||
}
|
||||
}break;
|
||||
case 3:{
|
||||
if(m.F(A::LOCKON_WAITTIME)==0.0f){
|
||||
m.I(A::PHASE)=0;
|
||||
SETPHASE(0);
|
||||
}
|
||||
}break;
|
||||
default:{
|
||||
ERR(std::format("Unhandled phase {} for {} strategy!",m.I(A::PHASE),strategy));
|
||||
ERR(std::format("Unhandled phase {} for {} strategy!",PHASE(),strategy));
|
||||
}
|
||||
}
|
||||
}
|
@ -60,10 +60,10 @@ void Monster::STRATEGY::GOBLIN_BOMB(Monster&m,float fElapsedTime,std::string str
|
||||
RUN,
|
||||
};
|
||||
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case INITIALIZE:{
|
||||
m.F(A::SHOOT_TIMER)=m.randomFrameOffset;
|
||||
m.phase=RUN;
|
||||
SETPHASE(RUN);
|
||||
}break;
|
||||
case RUN:{
|
||||
m.F(A::SHOOT_TIMER)+=fElapsedTime;
|
||||
|
@ -66,10 +66,10 @@ void Monster::STRATEGY::GOBLIN_BOW(Monster&m,float fElapsedTime,std::string stra
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case INITIALIZE_PERCEPTION:{
|
||||
m.F(A::PERCEPTION_LEVEL)=ConfigFloat("Starting Perception Level");
|
||||
m.phase=MOVE;
|
||||
SETPHASE(MOVE);
|
||||
}break;
|
||||
case MOVE:{
|
||||
m.F(A::ATTACK_COOLDOWN)+=fElapsedTime;
|
||||
@ -79,7 +79,7 @@ void Monster::STRATEGY::GOBLIN_BOW(Monster&m,float fElapsedTime,std::string stra
|
||||
const bool outsideMaxShootingRange=distToPlayer>=ConfigPixelsArr("Stand Still and Shoot Range",1);
|
||||
|
||||
auto PrepareToShoot=[&](){
|
||||
m.phase=LOCKON;
|
||||
SETPHASE(LOCKON);
|
||||
m.F(A::SHOOT_TIMER)=ConfigFloat("Attack Windup Time");
|
||||
|
||||
m.PerformAnimation("SHOOT",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
@ -118,7 +118,7 @@ void Monster::STRATEGY::GOBLIN_BOW(Monster&m,float fElapsedTime,std::string stra
|
||||
m.V(A::EXTENDED_LINE)=extendedLine;
|
||||
Arrow tempArrow{m.GetPos(),extendedLine,pointTowardsPlayer.vector().norm()*ConfigFloat("Arrow Spd"),"goblin_arrow.png",ConfigFloat("Arrow Hitbox Radius"),m.GetAttack(),m.OnUpperLevel()};
|
||||
m.V(A::FIRE_VELOCITY)=tempArrow.PointToBestTargetPath(m.F(A::PERCEPTION_LEVEL));
|
||||
m.phase=WINDUP;
|
||||
SETPHASE(WINDUP);
|
||||
}
|
||||
}break;
|
||||
case WINDUP:{
|
||||
@ -128,7 +128,7 @@ void Monster::STRATEGY::GOBLIN_BOW(Monster&m,float fElapsedTime,std::string stra
|
||||
CreateBullet(Arrow)(m.GetPos(),m.V(A::EXTENDED_LINE),m.V(A::FIRE_VELOCITY),"goblin_arrow.png",ConfigFloat("Arrow Hitbox Radius"),m.GetAttack(),m.OnUpperLevel())EndBullet;
|
||||
m.F(A::PERCEPTION_LEVEL)=std::min(ConfigFloat("Maximum Perception Level"),m.F(A::PERCEPTION_LEVEL)+ConfigFloat("Perception Level Increase"));
|
||||
m.PerformAnimation("IDLE",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
m.phase=MOVE;
|
||||
SETPHASE(MOVE);
|
||||
}
|
||||
m.B(A::RANDOM_DIRECTION)=util::random()%2;
|
||||
m.F(A::RANDOM_RANGE)=util::random_range(ConfigPixelsArr("Random Direction Range",0),ConfigPixelsArr("Random Direction Range",1));
|
||||
|
@ -66,13 +66,13 @@ void Monster::STRATEGY::GOBLIN_DAGGER(Monster&m,float fElapsedTime,std::string s
|
||||
SLASH
|
||||
};
|
||||
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case MOVE:{
|
||||
float distToPlayer=m.GetDistanceFrom(game->GetPlayer()->GetPos());
|
||||
if(distToPlayer>ConfigFloat("Attack Spacing")/100.f*24){
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
}else{
|
||||
m.phase=WINDUP;
|
||||
SETPHASE(WINDUP);
|
||||
m.I(A::ATTACK_TYPE)=util::random()%2; //Choose randomly between stab or slash.
|
||||
switch(m.I(A::ATTACK_TYPE)){
|
||||
case STAB:{
|
||||
@ -90,7 +90,7 @@ void Monster::STRATEGY::GOBLIN_DAGGER(Monster&m,float fElapsedTime,std::string s
|
||||
case WINDUP:{
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0){
|
||||
m.phase=RECOVERY;
|
||||
SETPHASE(RECOVERY);
|
||||
switch(m.I(A::ATTACK_TYPE)){
|
||||
case STAB:{
|
||||
vf2d stabTarget=game->GetPlayer()->GetPos();
|
||||
@ -113,7 +113,7 @@ void Monster::STRATEGY::GOBLIN_DAGGER(Monster&m,float fElapsedTime,std::string s
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
m.F(A::RECOVERY_TIME)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0){m.PerformIdleAnimation(m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));}
|
||||
if(m.F(A::RECOVERY_TIME)<=0)m.phase=MOVE;
|
||||
if(m.F(A::RECOVERY_TIME)<=0)SETPHASE(MOVE);
|
||||
}break;
|
||||
}
|
||||
}
|
@ -63,11 +63,11 @@ void Monster::STRATEGY::HAWK(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.SetZ(std::max(float(m.F(A::FLYING_HEIGHT)+ConfigFloat("Flight Oscillation Amount")*sin((PI*m.TimeSpentAlive())/1.5f)),0.f));
|
||||
#pragma endregion
|
||||
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case INITIALIZE:{
|
||||
m.F(A::TARGET_FLYING_HEIGHT)=m.F(A::FLYING_HEIGHT)=ConfigFloat("Flight Height")-ConfigFloat("Flight Height Variance")+util::random_range(0,ConfigFloat("Flight Height Variance")*2);
|
||||
m.B(A::RANDOM_DIRECTION)=int(util::random_range(0,2));
|
||||
m.phase=FLYING;
|
||||
SETPHASE(FLYING);
|
||||
m.AddBuff(BuffType::SELF_INFLICTED_SLOWDOWN,INFINITE,util::random_range(0,ConfigFloat("Flight Speed Variance")/100));
|
||||
m.F(A::ATTACK_COOLDOWN)=util::random_range(1.f,ConfigFloat("Flight Charge Cooldown"));
|
||||
}break;
|
||||
@ -76,7 +76,7 @@ void Monster::STRATEGY::HAWK(Monster&m,float fElapsedTime,std::string strategy){
|
||||
if(m.F(A::FLYING_HEIGHT)<m.F(A::TARGET_FLYING_HEIGHT))m.F(A::FLYING_HEIGHT)=std::min(m.F(A::TARGET_FLYING_HEIGHT),m.F(A::FLYING_HEIGHT)+ConfigFloat("Attack Z Speed")*fElapsedTime);
|
||||
if(m.F(A::ATTACK_COOLDOWN)<=0){
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Attack Wait Time");
|
||||
m.phase=PREPARE_CHARGE;
|
||||
SETPHASE(PREPARE_CHARGE);
|
||||
}else{
|
||||
float dirToPlayer{util::pointTo(game->GetPlayer()->GetPos(),m.GetPos()).polar().y};
|
||||
dirToPlayer+=m.B(A::RANDOM_DIRECTION)?0.25*PI:-0.25*PI;
|
||||
@ -89,7 +89,7 @@ void Monster::STRATEGY::HAWK(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.UpdateFacingDirection(game->GetPlayer()->GetPos());
|
||||
m.PerformAnimation("ATTACK");
|
||||
if(m.F(A::CASTING_TIMER)<=0){
|
||||
m.phase=CHARGE;
|
||||
SETPHASE(CHARGE);
|
||||
m.PerformAnimation("ATTACKING");
|
||||
m.target=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).rpoint(ConfigFloat("Flight Distance")*2.f);
|
||||
m.UpdateFacingDirection(m.target);
|
||||
@ -103,7 +103,7 @@ void Monster::STRATEGY::HAWK(Monster&m,float fElapsedTime,std::string strategy){
|
||||
float distToTarget=geom2d::line<float>(m.GetPos(),m.target).length();
|
||||
if(distToTarget<12.f){
|
||||
m.F(A::TARGET_FLYING_HEIGHT)=ConfigFloat("Flight Height")-ConfigFloat("Flight Height Variance")+util::random_range(0,ConfigFloat("Flight Height Variance")*2);
|
||||
m.phase=FLYING;
|
||||
SETPHASE(FLYING);
|
||||
m.F(A::ATTACK_COOLDOWN)=ConfigFloat("Flight Charge Cooldown");
|
||||
m.PerformJumpAnimation();
|
||||
}
|
||||
|
@ -81,6 +81,7 @@ Monster::Monster(vf2d pos,MonsterData data,bool upperLevel,bool bossMob):
|
||||
randomFrameOffset=(util::random()%1000)/1000.f;
|
||||
monsterWalkSoundTimer=util::random(1.f);
|
||||
UpdateFacingDirection(game->GetPlayer()->GetPos());
|
||||
animation.UpdateState(internal_animState,randomFrameOffset);
|
||||
}
|
||||
const vf2d&Monster::GetPos()const{
|
||||
return pos;
|
||||
@ -284,8 +285,8 @@ void Monster::Update(const float fElapsedTime){
|
||||
unconsciousTimer=std::max(0.f,unconsciousTimer-fElapsedTime);
|
||||
if(unconsciousTimer==0.f){
|
||||
Heal(GetMaxHealth());
|
||||
PerformIdleAnimation();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(IsSolid()&&FadeoutWhenStandingBehind()){
|
||||
@ -402,7 +403,7 @@ void Monster::Update(const float fElapsedTime){
|
||||
}
|
||||
if(!game->TestingModeEnabled()&&CanMove())Monster::STRATEGY::RUN_STRATEGY(*this,fElapsedTime);
|
||||
}
|
||||
animation.UpdateState(internal_animState,randomFrameOffset+fElapsedTime);
|
||||
animation.UpdateState(internal_animState,fElapsedTime);
|
||||
if(HasMountedMonster())mounted_animation.value().UpdateState(internal_mounted_animState,fElapsedTime);
|
||||
attackedByPlayer=false;
|
||||
}
|
||||
@ -1654,3 +1655,10 @@ const bool Monster::IsUnconscious()const{
|
||||
const float Monster::UnconsciousTime()const{
|
||||
return MONSTER_DATA.at(name).UnconsciousTime();
|
||||
}
|
||||
void Monster::SetPhase(const std::string&strategyName,int phase){
|
||||
this->phase[strategyName]=phase;
|
||||
}
|
||||
const int Monster::GetPhase(const std::string&strategyName){
|
||||
if(!phase.contains(strategyName))phase[strategyName]=0;
|
||||
return this->phase[strategyName];
|
||||
}
|
@ -60,6 +60,8 @@ enum class Attribute;
|
||||
|
||||
class GameEvent;
|
||||
|
||||
using StrategyName=std::string;
|
||||
|
||||
namespace MonsterTests{
|
||||
class MonsterTest;
|
||||
};
|
||||
@ -229,6 +231,8 @@ public:
|
||||
const bool FaceTarget()const;
|
||||
void ResetCurseOfDeathDamage();
|
||||
void MoveForward(const vf2d&moveForwardVec,const float fElapsedTime); //Moves the monster forward in given vector direction (will be auto-normalized) applying speeed boosts and other proper movement requirements as if you wanted to move on a frame-by-frame basis.
|
||||
void SetPhase(const std::string&strategyName,int phase);
|
||||
const int GetPhase(const std::string&strategyName);
|
||||
private:
|
||||
//NOTE: Marking a monster for deletion does not trigger any death events. It just simply removes the monster from the field!!
|
||||
// The way this works is that monsters marked for deletion will cause the monster update loop to detect there's at least one or more monsters that must be deleted and will call erase_if on the list at the end of the iteration loop.
|
||||
@ -273,7 +277,7 @@ private:
|
||||
float spriteRot=0;
|
||||
std::shared_ptr<DamageNumber>damageNumberPtr;
|
||||
std::shared_ptr<DamageNumber>dotNumberPtr;
|
||||
int phase=0;
|
||||
std::unordered_map<StrategyName,int>phase{}; //NOTE: THIS SHOULD NOT BE MODIFIED DIRECTLY!!! Use SetPhase(), GetPhase() / PHASE() SETPHASE() macros!
|
||||
bool diesNormally=true; //If set to false, the monster death is handled in a special way. Set it to true when it's time to die.
|
||||
float targetSize=0;
|
||||
bool isBoss=false;
|
||||
|
@ -79,7 +79,6 @@ enum class Attribute{
|
||||
ITEM_QUANTITY,
|
||||
LAST_INVENTORY_TYPE_OPENED,
|
||||
NEXT_MENU, //Set to 0 for New Game, Set to 1 for Load Game Menu. This is used for the username checks
|
||||
PHASE,
|
||||
LOCKON_WAITTIME,
|
||||
LOCKON_POS,
|
||||
TARGET_TIMER,
|
||||
@ -148,4 +147,6 @@ enum class Attribute{
|
||||
EXTENDED_LINE,
|
||||
ENCHANT,
|
||||
PARROT_FLY_TIMER,
|
||||
MID_PHASE,
|
||||
RUM_DRINK_COUNT,
|
||||
};
|
@ -50,3 +50,6 @@ All rights reserved.
|
||||
#define ConfigFloatArr(param,ind) _GetFloat(m,param,strategy,ind)
|
||||
#define ConfigStringArr(param,ind) _GetString(m,param,strategy,ind)
|
||||
#define ConfigVecArr(param,ind) _GetVec(m,param,strategy,ind)
|
||||
|
||||
#define PHASE() m.GetPhase(strategy)
|
||||
#define SETPHASE(phase) m.SetPhase(strategy,phase)
|
@ -53,7 +53,7 @@ INCLUDE_game
|
||||
INCLUDE_DATA
|
||||
|
||||
void Monster::STRATEGY::NPC(Monster&m,float fElapsedTime,std::string strategy){
|
||||
if(m.phase==0){ //Initialization.
|
||||
if(PHASE()==0){ //Initialization.
|
||||
if(m.npcData.function.length()>0){
|
||||
m.SetStrategyDrawOverlayFunction([](AiL*game,Monster&m,const std::string&strategy){
|
||||
vf2d nameTextSize=game->GetTextSizeProp(m.GetName());
|
||||
@ -62,7 +62,7 @@ void Monster::STRATEGY::NPC(Monster&m,float fElapsedTime,std::string strategy){
|
||||
game->KEY_CONFIRM.DrawPrimaryInput(&game->view,m.GetPos()+vf2d{ConfigFloatArr("Interaction Display Offset",0),ConfigFloatArr("Interaction Display Offset",1)},"Interact",alpha);
|
||||
});
|
||||
}
|
||||
m.phase=1;
|
||||
SETPHASE(1);
|
||||
}
|
||||
float distFromPlayer=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).length();
|
||||
if(distFromPlayer<ConfigFloat("Interaction Distance")/100.f*24.f){
|
||||
|
@ -51,18 +51,22 @@ void Monster::STRATEGY::PARROT(Monster&m,float fElapsedTime,std::string strategy
|
||||
RUN,
|
||||
FLY_AWAY,
|
||||
};
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case RUN:{
|
||||
if(!m.attachedTarget.expired())HAWK(m,fElapsedTime,"Hawk");
|
||||
else{
|
||||
if(!m.attachedTarget.expired()&&m.attachedTarget.lock()->IsAlive()){
|
||||
m.PerformAnimation("FLYING");
|
||||
HAWK(m,fElapsedTime,"Hawk");
|
||||
}else{
|
||||
m.lifetime=10.f;
|
||||
m.phase=FLY_AWAY;
|
||||
SETPHASE(FLY_AWAY);
|
||||
m.F(A::PATH_DIR)=1.f;
|
||||
if(util::random(1.f)<0.5f)m.F(A::PATH_DIR)=-1.f;
|
||||
m.manualIgnoreTerrain=true;
|
||||
}
|
||||
}break;
|
||||
case FLY_AWAY:{
|
||||
if(m.F(A::PATH_DIR)>0.f)m.PerformAnimation("FLYING",Direction::EAST);
|
||||
else m.PerformAnimation("FLYING",Direction::WEST);
|
||||
m.z+=fElapsedTime*ConfigFloat("Fly Away Z Speed");
|
||||
m.SetVelocity({m.F(A::PATH_DIR)*ConfigFloat("Fly Away Speed"),0.f});
|
||||
}break;
|
||||
|
@ -49,7 +49,6 @@ INCLUDE_game
|
||||
void Monster::STRATEGY::PIRATE_BUCCANEER(Monster&m,float fElapsedTime,std::string strategy){
|
||||
#pragma region Phase, Animation, and Helper function setup
|
||||
enum PhaseName{
|
||||
INIT,
|
||||
MOVE,
|
||||
LOCKON,
|
||||
WINDUP,
|
||||
@ -57,10 +56,7 @@ void Monster::STRATEGY::PIRATE_BUCCANEER(Monster&m,float fElapsedTime,std::strin
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
switch(m.phase){
|
||||
case INIT:{
|
||||
m.phase=MOVE;
|
||||
}break;
|
||||
switch(PHASE()){
|
||||
case MOVE:{
|
||||
m.F(A::ATTACK_COOLDOWN)+=fElapsedTime;
|
||||
|
||||
@ -69,7 +65,7 @@ void Monster::STRATEGY::PIRATE_BUCCANEER(Monster&m,float fElapsedTime,std::strin
|
||||
const bool outsideMaxShootingRange=distToPlayer>=ConfigPixelsArr("Stand Still and Shoot Range",1);
|
||||
|
||||
auto PrepareToShoot=[&](){
|
||||
m.phase=WINDUP;
|
||||
SETPHASE(WINDUP);
|
||||
m.F(A::SHOOT_TIMER)=ConfigFloat("Attack Windup Time");
|
||||
m.PerformAnimation("SHOOT",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
};
|
||||
@ -96,7 +92,7 @@ void Monster::STRATEGY::PIRATE_BUCCANEER(Monster&m,float fElapsedTime,std::strin
|
||||
CreateBullet(ChargedArrow)("musket_bullet.png","musket_trail.png",m.GetPos(),util::pointTo(m.GetPos(),m.V(A::LOCKON_POS))*ConfigFloat("Arrow Spd"),ConfigFloat("Arrow Hitbox Radius"),m.GetAttack(),m.OnUpperLevel())EndBullet;
|
||||
m.PerformAnimation("SHOOTING",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
m.SetStrategyDrawFunction([](AiL*game,Monster&monster,const std::string&strategy){});
|
||||
m.phase=FIRE_ANIMATION;
|
||||
SETPHASE(FIRE_ANIMATION);
|
||||
m.F(A::SHOOT_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
}else
|
||||
if(m.F(A::SHOOT_TIMER)>=ConfigFloat("Attack Lockon Time")){
|
||||
@ -115,7 +111,7 @@ void Monster::STRATEGY::PIRATE_BUCCANEER(Monster&m,float fElapsedTime,std::strin
|
||||
m.F(A::SHOOT_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::SHOOT_TIMER)<=0.f){
|
||||
m.PerformAnimation("IDLE",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
m.phase=MOVE;
|
||||
SETPHASE(MOVE);
|
||||
}
|
||||
}break;
|
||||
}
|
||||
|
@ -63,12 +63,7 @@ void Monster::STRATEGY::PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std::string
|
||||
SLASH
|
||||
};
|
||||
|
||||
if(!m.B(A::INITIALIZED)){
|
||||
m.B(A::INITIALIZED)=true;
|
||||
m.phase=INIT;
|
||||
}
|
||||
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case INIT:{
|
||||
m.F(A::TARGET_TIMER)=ConfigFloat("Shooting Frequency");
|
||||
m.F(A::PARROT_FLY_TIMER)=ConfigFloat("Parrot Fly Wait Time");
|
||||
@ -81,7 +76,7 @@ void Monster::STRATEGY::PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std::string
|
||||
}
|
||||
m.mountedSprOffset=ConfigVec("Imposed Monster Offset");
|
||||
m.deathData.emplace_back(ConfigString("Spawned Monster"),1U);
|
||||
m.phase=MOVE;
|
||||
SETPHASE(MOVE);
|
||||
}break;
|
||||
case MOVE:{
|
||||
if(m.F(A::PARROT_FLY_TIMER)>0.f){
|
||||
@ -99,24 +94,25 @@ void Monster::STRATEGY::PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std::string
|
||||
const float distToPlayer{util::distance(game->GetPlayer()->GetPos(),m.GetPos())};
|
||||
if(distToPlayer<=ConfigFloat("Shoot Max Range")/100.f*24){
|
||||
m.F(A::SHOOT_TIMER)=ConfigFloat("Shooting Delay");
|
||||
m.phase=PREPARE_SHOOT;
|
||||
SETPHASE(PREPARE_SHOOT);
|
||||
m.PerformAnimation("SHOOT",game->GetPlayer()->GetPos());
|
||||
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos();
|
||||
}
|
||||
}
|
||||
m.F(A::TARGET_TIMER)=ConfigFloat("Shooting Frequency");
|
||||
}else
|
||||
if(m.GetHealth()<ConfigInt("Rum Drink Threshold")){
|
||||
if(m.GetHealth()<ConfigInt("Rum Drink Threshold")&&m.I(A::RUM_DRINK_COUNT)<ConfigInt("Rum Drink Count")){
|
||||
m.PerformAnimation("DRINK");
|
||||
m.F(A::BREAK_TIME)=m.GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
m.phase=DRINK_RUM;
|
||||
SETPHASE(DRINK_RUM);
|
||||
m.I(A::RUM_DRINK_COUNT)++;
|
||||
}
|
||||
else{
|
||||
float distToPlayer=m.GetDistanceFrom(game->GetPlayer()->GetPos());
|
||||
if(distToPlayer>ConfigFloat("Attack Spacing")/100.f*24){
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
}else{
|
||||
m.phase=WINDUP;
|
||||
SETPHASE(WINDUP);
|
||||
m.I(A::ATTACK_TYPE)=util::random()%2; //Choose randomly between stab or slash.
|
||||
switch(m.I(A::ATTACK_TYPE)){
|
||||
case STAB:{
|
||||
@ -138,38 +134,38 @@ void Monster::STRATEGY::PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std::string
|
||||
CreateBullet(Bullet)(m.GetPos(),util::pointTo(m.GetPos(),m.V(A::LOCKON_POS))*ConfigFloat("Bullet Speed"),ConfigFloat("Bullet Radius"),m.GetAttack(),m.OnUpperLevel(),false,ConfigPixel("Bullet Color"),vf2d{1.f,1.f}*ConfigFloat("Bullet Radius")/3.f)EndBullet;
|
||||
m.PerformAnimation("SHOOTING");
|
||||
m.F(A::SHOOT_ANIMATION_TIME)=m.GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
m.phase=SHOOT_RELOAD;
|
||||
SETPHASE(SHOOT_RELOAD);
|
||||
}
|
||||
}break;
|
||||
case SHOOT_RELOAD:{
|
||||
m.F(A::SHOOT_ANIMATION_TIME)-=fElapsedTime;
|
||||
if(m.F(A::SHOOT_ANIMATION_TIME)<=0.f){
|
||||
m.PerformAnimation("IDLE");
|
||||
m.phase=MOVE;
|
||||
SETPHASE(MOVE);
|
||||
}
|
||||
}break;
|
||||
case DRINK_RUM:{
|
||||
m.F(A::BREAK_TIME)-=fElapsedTime;
|
||||
if(m.F(A::BREAK_TIME)<=0.f){
|
||||
m.Heal(ConfigInt("Rum Health Recovery"),true);
|
||||
m.phase=MOVE;
|
||||
SETPHASE(MOVE);
|
||||
}
|
||||
}break;
|
||||
case WINDUP:{
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0){
|
||||
m.phase=RECOVERY;
|
||||
SETPHASE(RECOVERY);
|
||||
switch(m.I(A::ATTACK_TYPE)){
|
||||
case STAB:{
|
||||
vf2d stabTarget=game->GetPlayer()->GetPos();
|
||||
m.PerformAnimation("STABBING",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
CreateBullet(DaggerStab)(m,ConfigString("Dagger Stab Image"),ConfigFloat("Dagger Hit Radius"),m.GetAttack(),ConfigFloat("Dagger Stab Knockback"),m.OnUpperLevel(),m.GetFacingDirectionToTarget(stabTarget),ConfigFloat("Dagger Frame Duration"),ConfigFloat("Dagger Stab Distance"),
|
||||
m.PerformAnimation("STABBING");
|
||||
CreateBullet(DaggerStab)(m,ConfigString("Dagger Stab Image"),ConfigFloat("Dagger Hit Radius"),m.GetAttack(),ConfigFloat("Dagger Stab Knockback"),m.OnUpperLevel(),m.GetFacingDirection(),ConfigFloat("Dagger Frame Duration"),ConfigFloat("Dagger Stab Distance"),
|
||||
DaggerStab::DirectionOffsets{ConfigVec("Dagger Up Offset"),ConfigVec("Dagger Down Offset"),ConfigVec("Dagger Right Offset"),ConfigVec("Dagger Left Offset")})EndBullet;
|
||||
}break;
|
||||
case SLASH:{
|
||||
vf2d slashTarget=game->GetPlayer()->GetPos();
|
||||
m.PerformAnimation("SLASHING",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
CreateBullet(DaggerSlash)(m,ConfigString("Dagger Slash Image"),ConfigFloat("Dagger Hit Radius"),m.GetAttack(),ConfigFloat("Dagger Slash Knockback"),m.OnUpperLevel(),m.GetFacingDirectionToTarget(slashTarget),ConfigFloat("Dagger Frame Duration"),ConfigFloat("Dagger Slash Distance"))EndBullet;
|
||||
m.PerformAnimation("SLASHING");
|
||||
CreateBullet(DaggerSlash)(m,ConfigString("Dagger Slash Image"),ConfigFloat("Dagger Hit Radius"),m.GetAttack(),ConfigFloat("Dagger Slash Knockback"),m.OnUpperLevel(),m.GetFacingDirection(),ConfigFloat("Dagger Frame Duration"),ConfigFloat("Dagger Slash Distance"))EndBullet;
|
||||
}break;
|
||||
default:ERR(std::format("WARNING! Invalid Attack type {} provided. THIS SHOULD NOT BE HAPPENING!",m.I(A::ATTACK_TYPE)));
|
||||
}
|
||||
@ -181,7 +177,7 @@ void Monster::STRATEGY::PIRATE_CAPTAIN(Monster&m,float fElapsedTime,std::string
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
m.F(A::RECOVERY_TIME)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0){m.PerformIdleAnimation(m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));}
|
||||
if(m.F(A::RECOVERY_TIME)<=0)m.phase=MOVE;
|
||||
if(m.F(A::RECOVERY_TIME)<=0)SETPHASE(MOVE);
|
||||
}break;
|
||||
}
|
||||
}
|
@ -57,10 +57,10 @@ void Monster::STRATEGY::PIRATE_MARAUDER(Monster&m,float fElapsedTime,std::string
|
||||
WHIRLWIND,
|
||||
};
|
||||
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case INIT:{
|
||||
m.F(A::CHASE_TIMER)=ConfigFloat("Ability Choose Timer");
|
||||
m.phase=MOVE;
|
||||
SETPHASE(MOVE);
|
||||
}break;
|
||||
case MOVE:{
|
||||
float distToPlayer=m.GetDistanceFrom(game->GetPlayer()->GetPos());
|
||||
@ -78,7 +78,7 @@ void Monster::STRATEGY::PIRATE_MARAUDER(Monster&m,float fElapsedTime,std::string
|
||||
std::pair<float,float>whirlwindAttackRollRange{jumpAttackRollRange.second,jumpAttackRollRange.second+ConfigFloat("Whirlwind Attack Chance")};
|
||||
|
||||
if(roll<jumpAttackRollRange.second&&distanceToPlayer>=ConfigFloatArr("Jump Attack Ranges",0)/100.f*24.f&&distanceToPlayer<=ConfigFloatArr("Jump Attack Ranges",1)/100.f*24.f){
|
||||
m.phase=LEAP;
|
||||
SETPHASE(LEAP);
|
||||
m.V(A::JUMP_TARGET_POS)=game->GetPlayer()->GetPos();
|
||||
m.I(A::ABILITY_COUNT)--;
|
||||
const float impactArea{ConfigFloat("Jump Attack Impact Area")};
|
||||
@ -92,7 +92,7 @@ void Monster::STRATEGY::PIRATE_MARAUDER(Monster&m,float fElapsedTime,std::string
|
||||
}else
|
||||
if(roll>=whirlwindAttackRollRange.first&&roll<whirlwindAttackRollRange.second
|
||||
&&distanceToPlayer>=ConfigFloatArr("Whirlwind Attack Ranges",0)/100.f*24.f&&distanceToPlayer<=ConfigFloatArr("Whirlwind Attack Ranges",1)/100.f*24.f){
|
||||
m.phase=PREPARE_WHIRLWIND;
|
||||
SETPHASE(PREPARE_WHIRLWIND);
|
||||
m.I(A::ABILITY_COUNT)--;
|
||||
vf2d aimingTarget{game->GetPlayer()->GetPos()};
|
||||
if(aimingTarget==m.GetPos()){ //Handle edge case.
|
||||
@ -109,7 +109,7 @@ void Monster::STRATEGY::PIRATE_MARAUDER(Monster&m,float fElapsedTime,std::string
|
||||
if(distToPlayer>ConfigFloat("Attack Spacing")/100.f*24){
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
}else{
|
||||
m.phase=WINDUP;
|
||||
SETPHASE(WINDUP);
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Slash Windup Time");
|
||||
m.PerformAnimation("SLASH",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
}
|
||||
@ -117,7 +117,7 @@ void Monster::STRATEGY::PIRATE_MARAUDER(Monster&m,float fElapsedTime,std::string
|
||||
case WINDUP:{
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0){
|
||||
m.phase=RECOVERY;
|
||||
SETPHASE(RECOVERY);
|
||||
vf2d slashTarget=game->GetPlayer()->GetPos();
|
||||
m.PerformAnimation("SLASHING",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
CreateBullet(DaggerSlash)(m,ConfigString("Dagger Slash Image"),ConfigFloat("Dagger Hit Radius"),m.GetAttack(),ConfigFloat("Dagger Slash Knockback"),m.OnUpperLevel(),m.GetFacingDirectionToTarget(slashTarget),ConfigFloat("Dagger Frame Duration"),ConfigFloat("Dagger Slash Distance"))EndBullet;
|
||||
@ -129,7 +129,7 @@ void Monster::STRATEGY::PIRATE_MARAUDER(Monster&m,float fElapsedTime,std::string
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
m.F(A::RECOVERY_TIME)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0){m.PerformIdleAnimation(m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));}
|
||||
if(m.F(A::RECOVERY_TIME)<=0)m.phase=MOVE;
|
||||
if(m.F(A::RECOVERY_TIME)<=0)SETPHASE(MOVE);
|
||||
}break;
|
||||
case LEAP:{
|
||||
m.F(A::JUMP_MOVE_TO_TARGET_TIMER)+=fElapsedTime;
|
||||
@ -143,7 +143,7 @@ void Monster::STRATEGY::PIRATE_MARAUDER(Monster&m,float fElapsedTime,std::string
|
||||
game->ProximityKnockback(m.GetPos(),ConfigFloat("Jump Attack Impact Area")/100.f*24.f,ConfigFloat("Jump Attack Knockback Amount"),HurtType::MONSTER|HurtType::PLAYER);
|
||||
m.SetZ(0.f);
|
||||
m.SetPos(m.V(A::JUMP_TARGET_POS));
|
||||
m.phase=MOVE;
|
||||
SETPHASE(MOVE);
|
||||
m.PerformIdleAnimation();
|
||||
m.SetStrategyDrawFunction([](AiL*game,Monster&monster,const std::string&strategy){});
|
||||
}
|
||||
@ -151,7 +151,7 @@ void Monster::STRATEGY::PIRATE_MARAUDER(Monster&m,float fElapsedTime,std::string
|
||||
case PREPARE_WHIRLWIND:{
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0.f){
|
||||
m.phase=WHIRLWIND;
|
||||
SETPHASE(WHIRLWIND);
|
||||
m.F(A::CHASE_TIMER)=0.f;
|
||||
m.PerformAnimation("SPINNING");
|
||||
}
|
||||
@ -159,7 +159,7 @@ void Monster::STRATEGY::PIRATE_MARAUDER(Monster&m,float fElapsedTime,std::string
|
||||
case WHIRLWIND:{
|
||||
m.F(A::CHASE_TIMER)+=fElapsedTime;
|
||||
if(m.F(A::CHASE_TIMER)>=ConfigFloat("Whirlwind Spin Time")){
|
||||
m.phase=MOVE;
|
||||
SETPHASE(MOVE);
|
||||
m.PerformIdleAnimation();
|
||||
}
|
||||
m.MoveForward(m.V(A::PATH_DIR),fElapsedTime);
|
||||
|
@ -136,7 +136,7 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
|
||||
};
|
||||
|
||||
const auto Recovered=[&](){
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case 2:{
|
||||
switch(m.I(A::JUMP_COUNT)){
|
||||
case 1:
|
||||
@ -211,7 +211,7 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
|
||||
lineToPlayer={m.GetPos(),m.GetPos()+vf2d{cos(randomDir),sin(randomDir)}*1};
|
||||
}
|
||||
game->GetPlayer()->Knockback(lineToPlayer.vector().norm()*float(ConfigInt("JumpKnockbackFactor")));
|
||||
if(m.phase!=2){
|
||||
if(PHASE()!=2){
|
||||
game->GetPlayer()->ApplyIframes(1.f);
|
||||
}else{ //In phase 2 you can get hit by multiple knockbacks, so the iframe time is a lot shorter.
|
||||
game->GetPlayer()->ApplyIframes(0.2f);
|
||||
@ -219,7 +219,7 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
|
||||
}
|
||||
}
|
||||
m.SetZ(0);
|
||||
Landed(m.phase);
|
||||
Landed(PHASE());
|
||||
m.SetStrategyDrawFunction([](AiL*game,Monster&m,const std::string&strategy){});
|
||||
} else
|
||||
if(m.F(A::JUMP_LANDING_TIMER)<=ConfigFloat("JumpWarningIndicatorTime")){
|
||||
@ -244,19 +244,19 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
|
||||
return;
|
||||
}
|
||||
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case 0:{
|
||||
m.size=ConfigInt("Phase1.Size")/100.f;
|
||||
m.diesNormally=false;
|
||||
m.F(A::IFRAME_TIME_UPON_HIT)=0;
|
||||
m.ApplyIframes(ConfigFloat("Phase5.IframeTimePerHit"));
|
||||
m.phase=ConfigInt("StartPhase");
|
||||
SETPHASE(ConfigInt("StartPhase"));
|
||||
}break;
|
||||
case 1:{
|
||||
if(m.GetHealthRatio()<=ConfigFloat("Phase2.Change")/100.f){
|
||||
m.phase=2;
|
||||
SETPHASE(2);
|
||||
m.SetSize(ConfigFloat("Phase2.Size")/100,false);
|
||||
TransitionPhase(m.phase);
|
||||
TransitionPhase(PHASE());
|
||||
return;
|
||||
}
|
||||
if(m.F(A::SHOOT_RING_TIMER)==0){
|
||||
@ -287,12 +287,12 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
|
||||
}break;
|
||||
case 2:{
|
||||
if(m.GetHealthRatio()<=ConfigFloat("Phase3.Change")/100.f){
|
||||
m.phase=3;
|
||||
SETPHASE(3);
|
||||
m.SetSize(ConfigFloat("Phase3.Size")/100,false);
|
||||
if(m.I(A::PATTERN_REPEAT_COUNT)==0){
|
||||
m.I(A::PATTERN_REPEAT_COUNT)=1;
|
||||
}
|
||||
TransitionPhase(m.phase);
|
||||
TransitionPhase(PHASE());
|
||||
return;
|
||||
}
|
||||
if(m.F(A::SHOOT_TIMER)==0){
|
||||
@ -315,10 +315,10 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
|
||||
}break;
|
||||
case 3:{
|
||||
if(m.GetHealthRatio()<=ConfigFloat("Phase4.Change")/100.f){
|
||||
m.phase=4;
|
||||
SETPHASE(4);
|
||||
m.SetSize(ConfigFloat("Phase4.Size")/100,false);
|
||||
m.AddBuff(BuffType::SLOWDOWN,99999,ConfigFloat("Phase4.MoveSpdModifier")/100);
|
||||
TransitionPhase(m.phase);
|
||||
TransitionPhase(PHASE());
|
||||
return;
|
||||
}
|
||||
if(m.I(A::PATTERN_REPEAT_COUNT)==0){
|
||||
@ -344,10 +344,10 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
|
||||
}break;
|
||||
case 4:{
|
||||
if(m.hp<=1){ //HP can't reach 0 when the dies normally flag is on.
|
||||
m.phase=5;
|
||||
SETPHASE(5);
|
||||
m.F(A::IFRAME_TIME_UPON_HIT)=1;
|
||||
m.I(A::HITS_UNTIL_DEATH)=int(m.GetSizeMult()*100/ConfigFloat("Phase5.SizeLossPerHit"))-1;
|
||||
TransitionPhase(m.phase);
|
||||
TransitionPhase(PHASE());
|
||||
return;
|
||||
}
|
||||
if(m.I(A::PHASE_REPEAT_COUNT)>=5){
|
||||
|
@ -157,14 +157,14 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case INITIALIZE:{
|
||||
m.F(A::RECOVERY_TIME)=ConfigFloat("Beginning Phase.Pillar Cast Delay Time");
|
||||
m.I(A::PATTERN_REPEAT_COUNT)=ConfigInt("Beginning Phase.Repeat Count");
|
||||
m.F(A::HEALTH_PCT_PHASE)=1.f;
|
||||
m.F(A::NEXT_HEALTH_PCT_PILLAR_PHASE)=ConfigFloat("Pillar Respawns.Start HP Threshold")/100.f;
|
||||
m.I(A::SHOCKWAVE_COLOR)=ConfigPixel("Shockwave.Danger Area Color").n;
|
||||
m.phase=SPAWN_PILLAR_PREPARE;
|
||||
SETPHASE(SPAWN_PILLAR_PREPARE);
|
||||
|
||||
if(ConfigIntArr("Pillar Respawns.Respawn Count",0)<ConfigIntArr("Pillar Respawns.Respawn Count",1))ERR(std::format("WARNING! {} Stone golem pillars were declared damaged when only {} will spawn. Please make sure the number is the same or equal to the total spawned pillars! (\"Pillar Respawns.Respawn Count\" strategy property)",ConfigIntArr("Pillar Respawns.Respawn Count",1),ConfigIntArr("Pillar Respawns.Respawn Count",0)));
|
||||
}break;
|
||||
@ -176,7 +176,7 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
|
||||
m.PerformAnimation("CAST",m.GetFacingDirectionToTarget(m.V(A::LOCKON_POS)));
|
||||
game->AddEffect(std::make_unique<SpellCircle>(m.V(A::LOCKON_POS),ConfigFloat("Beginning Phase.Pillar Cast Time"),"range_indicator.png","spell_insignia.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Golem Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Golem Pillar").GetSizeMult()/12.f)*1.25f,0.3f,vf2d{},ConfigPixel("Beginning Phase.Pillar Spell Circle Color"),util::random(2*PI),util::degToRad(ConfigFloat("Beginning Phase.Pillar Spell Circle Rotation Spd")),false,vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Golem Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Golem Pillar").GetSizeMult()/12.f)*0.9f,0.3f,vf2d{},ConfigPixel("Beginning Phase.Pillar Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Beginning Phase.Pillar Spell Insignia Rotation Spd"))),true);
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Beginning Phase.Pillar Cast Time");
|
||||
m.phase=SPAWN_PILLAR_CAST;
|
||||
SETPHASE(SPAWN_PILLAR_CAST);
|
||||
}
|
||||
}break;
|
||||
case SPAWN_PILLAR_CAST:{
|
||||
@ -188,11 +188,11 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
|
||||
game->SpawnMonster(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Golem Pillar"),m.OnUpperLevel());
|
||||
game->Hurt(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Golem Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Golem Pillar").GetSizeMult(),MONSTER_DATA.at("Stone Golem Pillar").GetAttack(),m.OnUpperLevel(),0.f,HurtType::PLAYER);
|
||||
if(m.I(A::PATTERN_REPEAT_COUNT)<=0){
|
||||
m.phase=STANDARD;
|
||||
SETPHASE(STANDARD);
|
||||
}else{
|
||||
m.PerformIdleAnimation(m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
m.F(A::RECOVERY_TIME)=ConfigFloat("Beginning Phase.Pillar Cast Delay Time");
|
||||
m.phase=SPAWN_PILLAR_PREPARE;
|
||||
SETPHASE(SPAWN_PILLAR_PREPARE);
|
||||
}
|
||||
}
|
||||
}break;
|
||||
@ -205,7 +205,7 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
|
||||
m.SIZET(A::PREVIOUS_MONSTER_COUNT)=MONSTER_LIST.size();
|
||||
m.PerformAnimation("CAST",m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
|
||||
PrepareSafeAreas();
|
||||
m.phase=SHOCKWAVE;
|
||||
SETPHASE(SHOCKWAVE);
|
||||
break;
|
||||
}
|
||||
if(m.F(A::NEXT_HEALTH_PCT_PILLAR_PHASE)>=m.GetHealthRatio()){
|
||||
@ -218,7 +218,7 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
|
||||
}
|
||||
|
||||
if(StoneThrowRollSucceeds){ //The intent is one or the other attack is supposed to happen. We can't do the slam and a throw, rerolling repeatedly each tick is unncessary.
|
||||
m.phase=STONE_THROW_CAST;
|
||||
SETPHASE(STONE_THROW_CAST);
|
||||
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos();
|
||||
m.PerformAnimation("TOSS ROCK CAST");
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Standard Attack.Stone Throw Cast Time");
|
||||
@ -233,7 +233,7 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
|
||||
|
||||
CreateBullet(LargeStone)(m.GetPos()+vf2d{0,ConfigFloat("Standard Attack.Stone Throw Height Offset")/2.f},ConfigFloat("Standard Attack.Stone Throw Time"),m.V(A::LOCKON_POS),m.F(A::CASTING_TIMER),ConfigPixels("Standard Attack.Stone Radius"),ConfigFloat("Standard Attack.Stone Throw Height Offset"),acc,ConfigInt("Standard Attack.Stone Damage"),ConfigFloat("Standard Attack.Stone Throw Knockback Factor"),m.OnUpperLevel(),false,INFINITY,false,WHITE,vf2d{1,1}*m.GetSizeMult(),util::random(2*PI))EndBullet;
|
||||
}else{
|
||||
m.phase=BEAR_ATTACK;
|
||||
SETPHASE(BEAR_ATTACK);
|
||||
m.F(A::CHASE_TIMER)=0.f;
|
||||
}
|
||||
}break;
|
||||
@ -243,13 +243,13 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
|
||||
SoundEffect::StopLoopingSFX(m.SIZET(A::LOOPING_SOUND_ID));
|
||||
m.PerformAnimation("TOSS ROCK");
|
||||
m.F(A::RECOVERY_TIME)=m.GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
m.phase=STONE_THROW_FINISH_ANIMATION;
|
||||
SETPHASE(STONE_THROW_FINISH_ANIMATION);
|
||||
}
|
||||
}break;
|
||||
case STONE_THROW_FINISH_ANIMATION:{
|
||||
m.F(A::RECOVERY_TIME)-=fElapsedTime;
|
||||
if(m.F(A::RECOVERY_TIME)<=0.f){
|
||||
m.phase=STANDARD;
|
||||
SETPHASE(STANDARD);
|
||||
}
|
||||
}break;
|
||||
case SHOCKWAVE:{
|
||||
@ -259,7 +259,7 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
|
||||
game->SetWorldColor(newCol);
|
||||
|
||||
if(m.SIZET(A::PREVIOUS_MONSTER_COUNT)!=MONSTER_LIST.size()){ //The monster list has changed...Whether it's a pillar getting added or removed, it's important we recalculate safe areas proper.
|
||||
m.phase=FIX_SAFE_AREAS; //HACK ALERT! Since spawning/removing monsters doesn't immediately occur in the MONSTER_LIST structure, we must defer the safe areas until the next tick and then recalculate them. To do this, we put the monster into another state and pause the shockwave attack for a frame to fix the new spawn areas on the next tick.
|
||||
SETPHASE(FIX_SAFE_AREAS); //HACK ALERT! Since spawning/removing monsters doesn't immediately occur in the MONSTER_LIST structure, we must defer the safe areas until the next tick and then recalculate them. To do this, we put the monster into another state and pause the shockwave attack for a frame to fix the new spawn areas on the next tick.
|
||||
m.F(A::SAFE_AREA_WAIT_TIMER)=0.01f;
|
||||
m.SIZET(A::PREVIOUS_MONSTER_COUNT)=MONSTER_LIST.size();
|
||||
}
|
||||
@ -288,14 +288,14 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
|
||||
});
|
||||
SoundEffect::PlaySFX("Shockwave",m.GetPos());
|
||||
game->AddEffect(std::make_unique<ExpandingRing>(m.GetPos(),ConfigFloat("Shockwave.Shockwave Ring Lifetime"),"finishring.png",m.OnUpperLevel(),vf2d{ConfigFloat("Shockwave.Ring Expand Speed"),ConfigFloat("Shockwave.Ring Expand Speed")},vf2d{1.f,1.f},ConfigFloat("Shockwave.Shockwave Fadeout Time"),vf2d{},ConfigPixel("Shockwave.Shockwave Color")),true);
|
||||
m.phase=STANDARD;
|
||||
SETPHASE(STANDARD);
|
||||
game->SetWorldColor(WHITE);
|
||||
}
|
||||
}break;
|
||||
case FIX_SAFE_AREAS:{
|
||||
if(m.F(A::SAFE_AREA_WAIT_TIMER)<=0.f){
|
||||
PrepareSafeAreas(); //Recalculate safe areas if the shockwave attack is going off.
|
||||
m.phase=SHOCKWAVE;
|
||||
SETPHASE(SHOCKWAVE);
|
||||
}else m.F(A::SAFE_AREA_WAIT_TIMER)-=fElapsedTime;
|
||||
}break;
|
||||
case BEAR_ATTACK:{
|
||||
@ -304,11 +304,11 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
|
||||
//Extending the bear script's variables to read the state of it...
|
||||
const bool SlamHasFinished=m.I(A::ATTACK_COUNT)!=m.I(A::BEAR_STOMP_COUNT); //The bear script uses the internal phase variable to determine the state of things.
|
||||
if(SlamHasFinished){
|
||||
m.phase=STANDARD;
|
||||
SETPHASE(STANDARD);
|
||||
m.I(A::ATTACK_COUNT)=m.I(A::BEAR_STOMP_COUNT);
|
||||
}else
|
||||
if(m.I(A::PHASE)==0&&m.F(A::CHASE_TIMER)>=ConfigFloat("Max Chase Time")){
|
||||
m.phase=DOUBLE_ROCK_TOSS;
|
||||
if(m.GetPhase("Bear")==0&&m.F(A::CHASE_TIMER)>=ConfigFloat("Max Chase Time")){
|
||||
SETPHASE(DOUBLE_ROCK_TOSS);
|
||||
m.PerformAnimation("RAISE ROCK");
|
||||
m.I(A::STONE_TOSS_COUNT)=ConfigInt("Stone Rain.Initial Stone Toss Count");
|
||||
m.F(A::STONE_TOSS_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
@ -325,7 +325,7 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
|
||||
game->AddEffect(std::make_unique<RockLaunch>(m.GetPos()+vf2d{util::random_range(throwPos.GetReal(0),throwPos.GetReal(2)),util::random_range(throwPos.GetReal(1),throwPos.GetReal(3))},10.f,"rock.png",ConfigFloat("Stone Rain.Stone Toss Delay"),ConfigFloat("Stone Rain.Stone Toss Rock Size Mult"),0.1f,vf2d{0.f,-ConfigFloat("Stone Rain.Stone Toss Throw Speed")},WHITE,util::random(2*PI),0.f));
|
||||
|
||||
if(m.I(A::STONE_TOSS_COUNT)<=0){
|
||||
m.phase=STONE_RAIN;
|
||||
SETPHASE(STONE_RAIN);
|
||||
m.F(A::BREAK_TIME)=ConfigFloat("Stone Rain.Stone Golem Wait Time");
|
||||
m.PerformAnimation("CAST");
|
||||
|
||||
@ -338,7 +338,7 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
|
||||
case STONE_RAIN:{
|
||||
m.F(A::BREAK_TIME)-=fElapsedTime;
|
||||
if(m.F(A::BREAK_TIME)<=0.f){
|
||||
m.phase=STANDARD;
|
||||
SETPHASE(STANDARD);
|
||||
}
|
||||
}break;
|
||||
}
|
||||
|
@ -72,15 +72,15 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
|
||||
};
|
||||
|
||||
auto ReturnToWaitingPhase=[&](){
|
||||
m.phase=WAITING;
|
||||
SETPHASE(WAITING);
|
||||
m.PerformIdleAnimation();
|
||||
m.F(A::ATTACK_COOLDOWN)=0.f;
|
||||
};
|
||||
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case INITIALIZE:{
|
||||
m.F(A::ATTACK_COOLDOWN)=util::random(ConfigFloat("Attack Wait Time")/1.5f);
|
||||
m.phase=WAITING;
|
||||
SETPHASE(WAITING);
|
||||
}break;
|
||||
case WAITING:{
|
||||
m.F(A::ATTACK_COOLDOWN)+=fElapsedTime;
|
||||
@ -95,7 +95,7 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
|
||||
case 0:{
|
||||
m.PerformAnimation("STONE PILLAR CAST");
|
||||
m.SIZET(A::LOOPING_SOUND_ID)=SoundEffect::PlayLoopingSFX("Rock Toss Cast",m.GetPos());
|
||||
m.phase=STONE_PILLAR_CAST;
|
||||
SETPHASE(STONE_PILLAR_CAST);
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Stone Pillar Cast Time");
|
||||
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos();
|
||||
game->AddEffect(std::make_unique<SpellCircle>(m.V(A::LOCKON_POS),ConfigFloat("Stone Pillar Cast Time"),"range_indicator.png","spell_insignia.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Pillar").GetSizeMult()/12.f)*1.25f,0.3f,vf2d{},ConfigPixel("Stone Pillar Spell Circle Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Pillar Spell Circle Rotation Spd")),false,vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Pillar").GetSizeMult()/12.f)*0.9f,0.3f,vf2d{},ConfigPixel("Stone Pillar Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Pillar Spell Insignia Rotation Spd"))),false);
|
||||
@ -103,7 +103,7 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
|
||||
case 1:{
|
||||
m.PerformAnimation("ROCK TOSS CAST");
|
||||
m.SIZET(A::LOOPING_SOUND_ID)=SoundEffect::PlayLoopingSFX("Rock Toss Cast",m.GetPos());
|
||||
m.phase=SHOOT_STONE_CAST;
|
||||
SETPHASE(SHOOT_STONE_CAST);
|
||||
m.B(A::PLAYED_FLAG)=false;
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Rock Toss Track Time")+ConfigFloat("Rock Toss Wait Time");
|
||||
CreateBullet(LevitatingRock)(m,game->GetPlayer()->GetPos(),1.f,0.f,ConfigPixels("Rock Toss Max Spawn Distance"),ConfigFloat("Rock Toss Track Time"),ConfigFloat("Rock Toss Wait Time"),ConfigFloat("Rock Toss Bullet Speed"),ConfigFloat("Rock Radius"),std::max(1,ConfigInt("Rock Toss Damage")/5),m.OnUpperLevel(),false,WHITE,vf2d{1,1})EndBullet;
|
||||
@ -121,7 +121,7 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
|
||||
case 2:{
|
||||
SoundEffect::PlaySFX("Dig",m.GetPos());
|
||||
m.PerformAnimation("BURROW UNDERGROUND");
|
||||
m.phase=DIVE_UNDERGROUND_DIG;
|
||||
SETPHASE(DIVE_UNDERGROUND_DIG);
|
||||
m.F(A::CASTING_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
}break;
|
||||
}
|
||||
@ -167,7 +167,7 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
|
||||
case DIVE_UNDERGROUND_DIG:{
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0.f){
|
||||
m.phase=DIVE_UNDERGROUND_MOVE;
|
||||
SETPHASE(DIVE_UNDERGROUND_MOVE);
|
||||
float randomAngle=util::random(2*PI);
|
||||
|
||||
const float minDist=ConfigPixelsArr("Burrow Teleport Distance",0);
|
||||
@ -198,7 +198,7 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
|
||||
m.B(A::IGNORE_DEFAULT_ANIMATIONS)=false;
|
||||
SoundEffect::PlaySFX("Rise",m.GetPos());
|
||||
m.PerformAnimation("RISE FROM UNDERGROUND");
|
||||
m.phase=DIVE_UNDERGROUND_SURFACE;
|
||||
SETPHASE(DIVE_UNDERGROUND_SURFACE);
|
||||
m.targetAcquireTimer=0;
|
||||
m.F(A::CASTING_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration();
|
||||
|
||||
|
@ -53,9 +53,9 @@ INCLUDE_DATA
|
||||
using A=Attribute;
|
||||
|
||||
void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy){
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case 0:{
|
||||
m.phase=ConfigInt("StartPhase");
|
||||
SETPHASE(ConfigInt("StartPhase"));
|
||||
m.overlaySprite=ConfigString("Overlay Sprite");
|
||||
m.overlaySpriteTransparency=0U;
|
||||
|
||||
@ -94,7 +94,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
|
||||
}break;
|
||||
case 1:{ //Run bear strategy in phase 1.
|
||||
auto TransitionToPhase2=[&](){
|
||||
m.phase=2;
|
||||
SETPHASE(2);
|
||||
m.PerformAnimation("SIT");
|
||||
m.AddBuff(BARRIER_DAMAGE_REDUCTION,INFINITE,ConfigFloat("Phase 2.Barrier Damage Reduction")/100.f);
|
||||
m.I(A::PHASE_REPEAT_COUNT)=ConfigInt("Phase 2.Wisp Pattern Spawn Count");
|
||||
@ -106,7 +106,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
|
||||
if(m.GetHealthRatio()<=ConfigFloat("Phase 2.Change")/100.f){
|
||||
//before moving to Phase 2, we need to make sure we're in Phase 0 of the bear AI.
|
||||
if(m.overlaySpriteTransparency<210U){
|
||||
if(m.I(A::PHASE)!=0)goto bear;
|
||||
if(m.GetPhase("Bear")!=0)goto bear;
|
||||
else{
|
||||
if(m.F(A::RUN_AWAY_TIMER)==0.f)m.F(A::RUN_AWAY_TIMER)=ConfigFloat("Phase 1.Fur Change Color Time");
|
||||
else{
|
||||
@ -142,9 +142,9 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
|
||||
|
||||
if(m.I(A::BEAR_STOMP_COUNT)%(ConfigInt("Phase 1.Stomp Count")+1)==ConfigInt("Phase 1.Stomp Count")){
|
||||
m.F(A::RUN_AWAY_TIMER)=ConfigFloat("Phase 1.Run Time");
|
||||
m.I(A::PREVIOUS_PHASE)=m.phase;
|
||||
m.I(A::PREVIOUS_PHASE)=PHASE();
|
||||
m.AddBuff(SPEEDBOOST,10.f,ConfigFloat("Phase 1.Run Speed Boost")/100.f);
|
||||
m.phase=6;
|
||||
SETPHASE(6);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -219,7 +219,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
|
||||
}else{
|
||||
|
||||
auto TransitionToPhase3=[&](){
|
||||
m.phase=3;
|
||||
SETPHASE(3);
|
||||
game->SetWorldColor(ConfigPixel("Phase 3.Environment Fade-in Color"));
|
||||
m.F(A::ENVIRONMENT_TIMER)=ConfigFloat("Phase 3.Environment Fade-in Time");
|
||||
m.I(A::ENVIRONMENT_PHASE)=0;
|
||||
@ -266,11 +266,11 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
|
||||
float distToPlayer=geom2d::line<float>(game->GetPlayer()->GetPos(),m.GetPos()).length();
|
||||
m.F(A::CHARGE_COOLDOWN)=std::max(0.f,m.F(A::CHARGE_COOLDOWN)-fElapsedTime);
|
||||
|
||||
if(m.I(A::PHASE)!=0)goto bear2; //Prevent doing anything else if a part of bear AI is still running.
|
||||
if(m.GetPhase("Bear")!=0)goto bear2; //Prevent doing anything else if a part of bear AI is still running.
|
||||
|
||||
if(m.GetHealthRatio()<=ConfigFloat("Phase 4.Change")/100.f){
|
||||
auto TransitionToPhase5=[&](){
|
||||
m.phase=5;
|
||||
SETPHASE(5);
|
||||
m.PerformAnimation("SIT");
|
||||
SoundEffect::PlaySFX("Ursule Phase Transition",SoundEffect::CENTERED);
|
||||
m.F(A::ENVIRONMENT_TIMER)=ConfigFloat("Phase 4.Environment Fade-out Time");
|
||||
@ -311,7 +311,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
|
||||
if(m.F(A::CASTING_TIMER)>0.f){
|
||||
m.F(A::CASTING_TIMER)=std::max(0.f,m.F(A::CASTING_TIMER)-fElapsedTime);
|
||||
if(m.F(A::CASTING_TIMER)==0.f){
|
||||
m.phase=4;
|
||||
SETPHASE(4);
|
||||
m.AddBuff(SPEEDBOOST,10.f,ConfigFloat("Phase 3.Charge Speed Boost")/100.f);
|
||||
m.AddBuff(FIXED_COLLISION_DMG,10.f,ConfigFloat("Phase 3.Charge Attack Damage"));
|
||||
m.AddBuff(COLLISION_KNOCKBACK_STRENGTH,10.f,ConfigFloat("Phase 3.Charge Attack Knockback Strength"));
|
||||
@ -332,8 +332,8 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
|
||||
if(m.I(A::BEAR_STOMP_COUNT)%(ConfigInt("Phase 3.Stomp Count")+1)==ConfigInt("Phase 3.Stomp Count")){
|
||||
m.F(A::RUN_AWAY_TIMER)=ConfigFloat("Phase 3.Run Time");
|
||||
m.AddBuff(SPEEDBOOST,10.f,ConfigFloat("Phase 3.Run Speed Boost")/100.f);
|
||||
m.I(A::PREVIOUS_PHASE)=m.phase;
|
||||
m.phase=6;
|
||||
m.I(A::PREVIOUS_PHASE)=PHASE();
|
||||
SETPHASE(6);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -352,7 +352,7 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
|
||||
float distToTarget=geom2d::line<float>(m.target,m.GetPos()).length();
|
||||
|
||||
if(distToTarget<=4.f||m.F(A::TARGET_TIMER)==0.f||m.B(A::COLLIDED_WITH_PLAYER)){
|
||||
m.phase=3;
|
||||
SETPHASE(3);
|
||||
m.RemoveBuff(SPEEDBOOST);
|
||||
m.RemoveBuff(FIXED_COLLISION_DMG);
|
||||
m.RemoveBuff(COLLISION_KNOCKBACK_STRENGTH);
|
||||
@ -429,13 +429,13 @@ void Monster::STRATEGY::URSULE(Monster&m,float fElapsedTime,std::string strategy
|
||||
m.target=game->GetPlayer()->GetPos();
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
if(m.F(A::RUN_AWAY_TIMER)==0.f){
|
||||
m.phase=m.I(A::PREVIOUS_PHASE);
|
||||
SETPHASE(m.I(A::PREVIOUS_PHASE));
|
||||
m.RemoveBuff(SPEEDBOOST);
|
||||
m.I(A::BEAR_STOMP_COUNT)=0;
|
||||
}
|
||||
}break;
|
||||
default:{
|
||||
ERR(std::format("WARNING! Unknown phase {} for {} reached!",m.phase,m.GetName()));
|
||||
ERR(std::format("WARNING! Unknown phase {} for {} reached!",PHASE(),m.GetName()));
|
||||
}
|
||||
}
|
||||
}
|
@ -39,7 +39,7 @@ All rights reserved.
|
||||
#define VERSION_MAJOR 1
|
||||
#define VERSION_MINOR 3
|
||||
#define VERSION_PATCH 0
|
||||
#define VERSION_BUILD 11657
|
||||
#define VERSION_BUILD 11666
|
||||
|
||||
#define stringify(a) stringify_(a)
|
||||
#define stringify_(a) #a
|
||||
|
@ -49,11 +49,11 @@ INCLUDE_BULLET_LIST
|
||||
using A=Attribute;
|
||||
|
||||
void Monster::STRATEGY::WOLF(Monster&m,float fElapsedTime,std::string strategy){
|
||||
switch(m.I(A::PHASE)){
|
||||
switch(PHASE()){
|
||||
case 0:{ //Run towards the player.
|
||||
float distToPlayer=geom2d::line<float>(game->GetPlayer()->GetPos(),m.GetPos()).length();
|
||||
if(distToPlayer<=ConfigFloat("Lockon Range")/100*24.f){
|
||||
m.I(A::PHASE)=1;
|
||||
SETPHASE(1);
|
||||
m.V(A::LOCKON_POS)=geom2d::line<float>(m.GetPos(),game->GetPlayer()->GetPos()).upoint(1.2f);
|
||||
m.AddBuff(BuffType::LOCKON_SPEEDBOOST,INFINITE,ConfigFloat("Lockon Speed Boost")/100);
|
||||
m.F(A::TARGET_TIMER)=5.0f;
|
||||
@ -65,7 +65,7 @@ void Monster::STRATEGY::WOLF(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.F(A::TARGET_TIMER)=std::max(0.f,m.F(A::TARGET_TIMER)-fElapsedTime);
|
||||
float distToTarget=geom2d::line<float>(m.GetPos(),m.V(A::LOCKON_POS)).length();
|
||||
if(distToTarget<=12.f||m.F(A::TARGET_TIMER)==0.f){
|
||||
m.I(A::PHASE)=2;
|
||||
SETPHASE(2);
|
||||
m.F(A::RECOVERY_TIME)=ConfigFloat("Charge Recovery Time");
|
||||
m.PerformIdleAnimation();
|
||||
}else{
|
||||
@ -97,7 +97,7 @@ void Monster::STRATEGY::WOLF(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.I(A::PATH_DIR)=util::random()%2;
|
||||
if(m.I(A::PATH_DIR)==0)m.I(A::PATH_DIR)=-1;
|
||||
m.pathIndex=util::random()%disengagePoints.size();
|
||||
m.I(A::PHASE)=3;
|
||||
SETPHASE(3);
|
||||
m.F(A::RUN_AWAY_TIMER)=ConfigFloat("Disengage Duration");
|
||||
m.PerformJumpAnimation();
|
||||
}
|
||||
@ -110,7 +110,7 @@ void Monster::STRATEGY::WOLF(Monster&m,float fElapsedTime,std::string strategy){
|
||||
m.pathIndex=m.pathIndex+m.path.points.size();
|
||||
}
|
||||
if(m.F(A::RUN_AWAY_TIMER)==0.f){
|
||||
m.I(A::PHASE)=0;
|
||||
SETPHASE(0);
|
||||
}
|
||||
m.target=m.path.GetSplinePoint(m.pathIndex).pos;
|
||||
geom2d::line<float>moveTowardsLine=geom2d::line(m.pos,m.path.GetSplinePoint(m.pathIndex).pos);
|
||||
|
@ -71,7 +71,7 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
|
||||
LEFT
|
||||
};
|
||||
|
||||
if(m.phase!=HALFHEALTH_PHASE)m.F(A::SPAWNER_TIMER)-=fElapsedTime;
|
||||
if(PHASE()!=HALFHEALTH_PHASE)m.F(A::SPAWNER_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::SPAWNER_TIMER)<=0.f){
|
||||
const float randomDir=util::random(2*PI);
|
||||
game->SpawnMonster(m.GetPos()+vf2d{ConfigFloat("Basic Hawk Spawn Radius"),randomDir}.cart(),MONSTER_DATA.at("Hawk_NOXP"),m.OnUpperLevel(),true);
|
||||
@ -85,10 +85,10 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
|
||||
if(m.F(A::FLYING_HEIGHT)<m.F(A::TARGET_FLYING_HEIGHT))m.F(A::FLYING_HEIGHT)=std::min(m.F(A::TARGET_FLYING_HEIGHT),m.F(A::FLYING_HEIGHT)+ConfigPixels("Fly Rise/Fall Speed")*fElapsedTime);
|
||||
else if(m.F(A::FLYING_HEIGHT)>-ConfigFloat("Flight Oscillation Amount"))m.F(A::FLYING_HEIGHT)=std::max(-ConfigFloat("Flight Oscillation Amount"),m.F(A::FLYING_HEIGHT)-ConfigPixels("Fly Rise/Fall Speed")*fElapsedTime);
|
||||
|
||||
switch(m.phase){
|
||||
switch(PHASE()){
|
||||
case INITIALIZE:{
|
||||
m.F(A::SPAWNER_TIMER)=ConfigFloat("Basic Hawk Spawn Time");
|
||||
m.phase=IDLE;
|
||||
SETPHASE(IDLE);
|
||||
game->SetOverlay(ConfigString("Wind Attack.Wind Overlay Sprite"),ConfigPixel("Wind Attack.Wind Overlay Color"));
|
||||
game->GetOverlay().Disable();
|
||||
|
||||
@ -110,9 +110,9 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
|
||||
}break;
|
||||
case IDLE:{
|
||||
#pragma region Mid Phase Check
|
||||
if(m.GetHealthRatio()<=ConfigFloat("Mid Phase Health Transition %")/100.f&&!m.B(A::PHASE)){
|
||||
m.B(A::PHASE)=true;
|
||||
m.phase=HALFHEALTH_PREPARE_PHASE;
|
||||
if(m.GetHealthRatio()<=ConfigFloat("Mid Phase Health Transition %")/100.f&&!m.B(A::MID_PHASE)){
|
||||
m.B(A::MID_PHASE)=true;
|
||||
SETPHASE(HALFHEALTH_PREPARE_PHASE);
|
||||
m.F(A::TARGET_FLYING_HEIGHT)=50.f;
|
||||
m.target=ConfigVec("Mid Phase.Pillar Position");
|
||||
for(int i=0;i<ConfigInt("Mid Phase.Basic Hawk Spawn Count");i++){
|
||||
@ -150,14 +150,14 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
|
||||
|
||||
if(RightDirectionChosen)m.target=ConfigVec("Fly Across Attack.Right Edge Start Pos");
|
||||
else m.target=ConfigVec("Fly Across Attack.Left Edge Start Pos");
|
||||
m.phase=FLY_ACROSS_PREPARE;
|
||||
SETPHASE(FLY_ACROSS_PREPARE);
|
||||
}break;
|
||||
case 1:{
|
||||
m.phase=TORNADO_ATTACK_PREPARE;
|
||||
SETPHASE(TORNADO_ATTACK_PREPARE);
|
||||
m.target=ConfigVec("Tornado Attack.Landing Area");
|
||||
}break;
|
||||
case 2:{
|
||||
m.phase=WIND_ATTACK_FLY;
|
||||
SETPHASE(WIND_ATTACK_FLY);
|
||||
m.F(A::TARGET_FLYING_HEIGHT)=ConfigPixels("Wind Attack.Fly Up Height");
|
||||
const bool LeftLandingSite=m.I(A::ATTACK_CHOICE)=util::random()%2;
|
||||
if(LeftLandingSite)m.target=ConfigVec("Wind Attack.Left Landing Site");
|
||||
@ -174,7 +174,7 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
|
||||
//We're choosing the opposite side of the field to direct the boss towards for this attack.
|
||||
if(RightDirectionChosen)m.target=ConfigVec("Fly Across Attack.Left Edge Start Pos");
|
||||
else m.target=ConfigVec("Fly Across Attack.Right Edge Start Pos");
|
||||
m.phase=FLY_ACROSS;
|
||||
SETPHASE(FLY_ACROSS);
|
||||
|
||||
m.AddBuff(BuffType::SPEEDBOOST,INFINITY,ConfigFloat("Fly Across Attack.Move Speed Multiplier")-1.f);
|
||||
}
|
||||
@ -199,7 +199,7 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
|
||||
m.F(A::SHOOT_TIMER)=ConfigFloat("Fly Across Attack.Attack Frequency");
|
||||
}
|
||||
if(m.ReachedTargetPos()){
|
||||
m.phase=IDLE;
|
||||
SETPHASE(IDLE);
|
||||
m.RemoveBuff(BuffType::SPEEDBOOST);
|
||||
m.targetAcquireTimer=0.f;
|
||||
}
|
||||
@ -208,7 +208,7 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
|
||||
m.targetAcquireTimer=20.f;
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
if(m.ReachedTargetPos()){
|
||||
m.phase=TORNADO_ATTACK;
|
||||
SETPHASE(TORNADO_ATTACK);
|
||||
m.PerformAnimation("ATTACK",Direction::SOUTH);
|
||||
m.targetAcquireTimer=0.f;
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Tornado Attack.Attack Duration");
|
||||
@ -235,19 +235,19 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
|
||||
}break;
|
||||
case TORNADO_ATTACK:{
|
||||
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
||||
if(m.F(A::CASTING_TIMER)<=0.f)m.phase=IDLE;
|
||||
if(m.F(A::CASTING_TIMER)<=0.f)SETPHASE(IDLE);
|
||||
}break;
|
||||
case WIND_ATTACK_FLY:{
|
||||
m.targetAcquireTimer=20.f;
|
||||
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
||||
if(m.ReachedTargetPos()){
|
||||
m.phase=WIND_ATTACK_LAND;
|
||||
SETPHASE(WIND_ATTACK_LAND);
|
||||
m.F(A::TARGET_FLYING_HEIGHT)=0.f;
|
||||
}
|
||||
}break;
|
||||
case WIND_ATTACK_LAND:{
|
||||
if(m.GetZ()==0.f){
|
||||
m.phase=WIND_ATTACK;
|
||||
SETPHASE(WIND_ATTACK);
|
||||
game->GetOverlay().Enable();
|
||||
m.F(A::CASTING_TIMER)=ConfigFloat("Wind Attack.Wind Duration");
|
||||
m.F(A::WIND_STRENGTH)=ConfigFloat("Wind Attack.Wind Starting Strength")/100.f;
|
||||
@ -310,7 +310,7 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
|
||||
#pragma endregion
|
||||
|
||||
if(m.F(A::CASTING_TIMER)<=0.f){
|
||||
m.phase=IDLE;
|
||||
SETPHASE(IDLE);
|
||||
game->GetOverlay().Disable();
|
||||
game->SetWindSpeed({});
|
||||
}
|
||||
@ -337,7 +337,7 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
|
||||
const bool HasLandedOnGround=m.GetZ()==0.f;
|
||||
|
||||
if(HasLandedOnGround){
|
||||
m.phase=HALFHEALTH_PHASE;
|
||||
SETPHASE(HALFHEALTH_PHASE);
|
||||
CreateBullet(LargeTornado)(ConfigVec("Mid Phase.Large Tornado Position"),ConfigPixels("Mid Phase.Large Tornado Suction"),ConfigFloat("Mid Phase.Large Tornado Knockup Duration"),ConfigFloat("Mid Phase.Large Tornado Knockback Amount"),ConfigInt("Mid Phase.Large Tornado Damage"),ConfigFloat("Mid Phase.Large Tornado Radius"),INFINITY,m.OnUpperLevel())EndBullet;
|
||||
BULLET_LIST.back()->SetFadeinTime(1.0f);
|
||||
m.F(A::SHOOT_TIMER)=2.f;
|
||||
@ -350,7 +350,7 @@ void Monster::STRATEGY::ZEPHY(Monster&m,float fElapsedTime,std::string strategy)
|
||||
m.UpdateFacingDirection(Direction::SOUTH);
|
||||
m.PerformAnimation("ATTACK");
|
||||
if(game->BossEncounterMobCount()==1){
|
||||
m.phase=IDLE;
|
||||
SETPHASE(IDLE);
|
||||
std::for_each(BULLET_LIST.begin(),BULLET_LIST.end(),[](std::unique_ptr<IBullet>&bullet){
|
||||
if(bullet->GetBulletType()==BulletType::LARGE_TORNADO){
|
||||
bullet->fadeOutTime=1.f;
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="335" height="165" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="16">
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="335" height="165" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="17">
|
||||
<properties>
|
||||
<property name="Background Music" propertytype="BGM" value="beach"/>
|
||||
<property name="Level Type" type="int" propertytype="LevelType" value="0"/>
|
||||
@ -870,5 +870,11 @@
|
||||
<property name="spawner" type="object" value="8"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="16" name="Waterfall" type="AudioEnvironmentalSound" x="5352" y="192">
|
||||
<properties>
|
||||
<property name="Sound Name" propertytype="EnvironmentalSounds" value="Waterfall"/>
|
||||
</properties>
|
||||
<point/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
</map>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="288" height="287" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="3">
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="288" height="287" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="4">
|
||||
<properties>
|
||||
<property name="Background Music" propertytype="BGM" value="beach"/>
|
||||
<property name="Level Type" type="int" propertytype="LevelType" value="0"/>
|
||||
@ -1471,5 +1471,11 @@
|
||||
</properties>
|
||||
</object>
|
||||
<object id="2" name="Player Spawn" type="PlayerSpawnLocation" x="531.333" y="246" width="24" height="24"/>
|
||||
<object id="3" name="Waterfall" type="AudioEnvironmentalSound" x="4680" y="5112">
|
||||
<properties>
|
||||
<property name="Sound Name" propertytype="EnvironmentalSounds" value="Waterfall"/>
|
||||
</properties>
|
||||
<point/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
</map>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="339" height="165" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="3">
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="339" height="165" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="4">
|
||||
<properties>
|
||||
<property name="Background Music" propertytype="BGM" value="beach"/>
|
||||
<property name="Level Type" type="int" propertytype="LevelType" value="0"/>
|
||||
@ -861,5 +861,11 @@
|
||||
</properties>
|
||||
</object>
|
||||
<object id="2" name="Player Spawn" type="PlayerSpawnLocation" x="282" y="504" width="24" height="24"/>
|
||||
<object id="3" name="Waterfall" type="AudioEnvironmentalSound" x="4152" y="3144">
|
||||
<properties>
|
||||
<property name="Sound Name" propertytype="EnvironmentalSounds" value="Waterfall"/>
|
||||
</properties>
|
||||
<point/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
</map>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="288" height="208" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="3">
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="288" height="208" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="4">
|
||||
<properties>
|
||||
<property name="Background Music" propertytype="BGM" value="beach"/>
|
||||
<property name="Level Type" type="int" propertytype="LevelType" value="0"/>
|
||||
@ -1076,5 +1076,11 @@
|
||||
<property name="Upper?" type="bool" value="false"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="3" name="Waterfall" type="AudioEnvironmentalSound" x="3216" y="1488">
|
||||
<properties>
|
||||
<property name="Sound Name" propertytype="EnvironmentalSounds" value="Waterfall"/>
|
||||
</properties>
|
||||
<point/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
</map>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="356" height="357" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="3">
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="356" height="357" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="4">
|
||||
<properties>
|
||||
<property name="Background Music" propertytype="BGM" value="beach"/>
|
||||
<property name="Level Type" type="int" propertytype="LevelType" value="0"/>
|
||||
@ -1821,5 +1821,11 @@
|
||||
<property name="Upper?" type="bool" value="false"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="3" name="Waterfall" type="AudioEnvironmentalSound" x="4896" y="5112">
|
||||
<properties>
|
||||
<property name="Sound Name" propertytype="EnvironmentalSounds" value="Waterfall"/>
|
||||
</properties>
|
||||
<point/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
</map>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="254" height="370" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="3">
|
||||
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="254" height="370" tilewidth="24" tileheight="24" infinite="0" nextlayerid="8" nextobjectid="4">
|
||||
<properties>
|
||||
<property name="Background Music" propertytype="BGM" value="beach"/>
|
||||
<property name="Level Type" type="int" propertytype="LevelType" value="0"/>
|
||||
@ -1886,5 +1886,11 @@
|
||||
<property name="Upper?" type="bool" value="false"/>
|
||||
</properties>
|
||||
</object>
|
||||
<object id="3" name="Waterfall" type="AudioEnvironmentalSound" x="2088" y="8064">
|
||||
<properties>
|
||||
<property name="Sound Name" propertytype="EnvironmentalSounds" value="Waterfall"/>
|
||||
</properties>
|
||||
<point/>
|
||||
</object>
|
||||
</objectgroup>
|
||||
</map>
|
||||
|
@ -1148,8 +1148,8 @@ MonsterStrategy
|
||||
# Amount of time parrot remains stunned on death.
|
||||
Stun Time = 5s
|
||||
|
||||
Fly Away Speed = 100
|
||||
Fly Away Z Speed = 10
|
||||
Fly Away Speed = 140
|
||||
Fly Away Z Speed = 30
|
||||
}
|
||||
Crab
|
||||
{
|
||||
|
@ -1263,10 +1263,10 @@ Monsters
|
||||
Dagger Slash Distance = 12
|
||||
|
||||
# Slash Attack windup time
|
||||
Slash Windup Time = 0.2s
|
||||
Slash Windup Time = 0.4s
|
||||
|
||||
# Stab Attack windup time
|
||||
Stab Windup Time = 0.2s
|
||||
Stab Windup Time = 0.4s
|
||||
|
||||
# Amount of time where nothing happens after an attack.
|
||||
Attack Recovery Time = 0.6s
|
||||
@ -1343,7 +1343,7 @@ Monsters
|
||||
Attack Spacing = 100
|
||||
|
||||
# Slash Attack windup time
|
||||
Slash Windup Time = 0.2s
|
||||
Slash Windup Time = 0.4s
|
||||
|
||||
Dagger Slash Image = "pirate_slash.png"
|
||||
|
||||
@ -1415,10 +1415,10 @@ Monsters
|
||||
Dagger Slash Distance = 12
|
||||
|
||||
# Slash Attack windup time
|
||||
Slash Windup Time = 0.2s
|
||||
Slash Windup Time = 0.4s
|
||||
|
||||
# Stab Attack windup time
|
||||
Stab Windup Time = 0.2s
|
||||
Stab Windup Time = 0.4s
|
||||
|
||||
# Amount of time where nothing happens after an attack.
|
||||
Attack Recovery Time = 0.6s
|
||||
@ -1434,9 +1434,9 @@ Monsters
|
||||
|
||||
# Offset for the dagger stab effect per direction from the monster's center.
|
||||
Dagger Up Offset = -6,-5.5
|
||||
Dagger Down Offset = -5,-1
|
||||
Dagger Right Offset = 9,0
|
||||
Dagger Left Offset = -8,-2
|
||||
Dagger Down Offset = -7,-1
|
||||
Dagger Right Offset = 10,1
|
||||
Dagger Left Offset = -8,0
|
||||
|
||||
########
|
||||
|
||||
@ -1455,7 +1455,7 @@ Monsters
|
||||
SHOOTING = 3, 0.2, OneShot
|
||||
SHOOT = 1, 0.1, OneShot
|
||||
# Drink is approximately 2 seconds long.
|
||||
DRINK = 3, 0.65, PingPong
|
||||
DRINK = 2, 0.65, PingPong
|
||||
}
|
||||
|
||||
# Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity
|
||||
@ -1602,6 +1602,8 @@ Monsters
|
||||
|
||||
Strategy = Seagull
|
||||
|
||||
Ignore Collisions = True
|
||||
|
||||
#Size of each animation frame
|
||||
SheetFrameSize = 48,48
|
||||
|
||||
@ -1666,7 +1668,7 @@ Monsters
|
||||
|
||||
Hurt Sound = Monster Hurt
|
||||
Death Sound = Slime Dead
|
||||
Walk Sound = Slime Walk
|
||||
Walk Sound = Wing Flap
|
||||
}
|
||||
Parrot
|
||||
{
|
||||
@ -1676,12 +1678,14 @@ Monsters
|
||||
CollisionDmg = 22
|
||||
|
||||
MoveSpd = 180%
|
||||
Size = 50%
|
||||
Size = 100%
|
||||
|
||||
XP = 0
|
||||
|
||||
Collision Radius = 7
|
||||
|
||||
Ignore Collisions = True
|
||||
|
||||
Strategy = Parrot
|
||||
|
||||
# Instead of the monster dying, it gets knocked unconscious
|
||||
@ -1698,10 +1702,10 @@ Monsters
|
||||
# Frame Count, Frame Speed (s), Frame Cycling (Repeat,OneShot,PingPong,Reverse,ReverseOneShot)
|
||||
# Animations must be defined in the same order as they are in their sprite sheets
|
||||
# The First Four animations must represent a standing, walking, attack, and death animation. Their names are up to the creator.
|
||||
IDLE = 4, 0.2, Repeat
|
||||
IDLE = 1, 0.2, Repeat
|
||||
FLYING = 4, 0.15, Repeat
|
||||
ATTACKING = 2, 0.3, Repeat
|
||||
DEATH = 4, 0.15, OneShot
|
||||
DEATH = 1, 0.15, OneShot
|
||||
ATTACK = 2, 0.3, Repeat
|
||||
}
|
||||
|
||||
@ -1710,6 +1714,6 @@ Monsters
|
||||
|
||||
Hurt Sound = Monster Hurt
|
||||
Death Sound = Slime Dead
|
||||
Walk Sound = Slime Walk
|
||||
Walk Sound = Wing Flap
|
||||
}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user