Add Pct-based pillar respawning attack for Stone Golem. Add "Breaking Stone Pillar" versions that will automatically shake and crumble. Release Build 9951.

This commit is contained in:
sigonasr2 2024-07-11 14:59:35 -05:00
parent 3560ddc31e
commit 784aa022e2
8 changed files with 160 additions and 3 deletions

View File

@ -43,6 +43,11 @@ All rights reserved.
using A=Attribute; using A=Attribute;
void Monster::STRATEGY::BREAKING_PILLAR(Monster&m,float fElapsedTime,std::string strategy){ void Monster::STRATEGY::BREAKING_PILLAR(Monster&m,float fElapsedTime,std::string strategy){
enum Phase{
INITIALIZE,
RUN,
};
if(m.GetHealthRatio()>=ConfigFloat("Break Phase 1 HP % Threshold")/100.f){ if(m.GetHealthRatio()>=ConfigFloat("Break Phase 1 HP % Threshold")/100.f){
m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Unbroken Animation Name")); m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Unbroken Animation Name"));
}else }else
@ -50,6 +55,30 @@ void Monster::STRATEGY::BREAKING_PILLAR(Monster&m,float fElapsedTime,std::string
m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Break Phase 1 Animation Name")); 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")); }else m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Break Phase 2 Animation Name"));
switch(m.phase){
case INITIALIZE:{
m.F(A::BREAK_TIME)=ConfigFloat("Break Time");
m.F(A::SHAKE_TIMER)=0.2f;
m.phase=RUN;
}break;
case RUN:{
m.F(A::BREAK_TIME)-=fElapsedTime;
if(m.F(A::BREAK_TIME)<=2.f){
if(m.F(A::BREAK_TIME)<=0.f){
m._DealTrueDamage(m.GetHealth()); //Kill the pillar, making it crumble.
break;
}
m.F(A::SHAKE_TIMER)-=fElapsedTime;
if(m.F(A::SHAKE_TIMER)<=0.f){
m.F(A::SHAKE_TIMER)+=0.2f;
if(m.B(A::SHAKE_DIR))m.SetX(m.GetPos().x-2);
else m.SetX(m.GetPos().x+2);
m.B(A::SHAKE_DIR)=!m.B(A::SHAKE_DIR);
}
}
}break;
}
m.SetStrategyDeathFunction([&](GameEvent&deathEvent,Monster&m,const std::string&strategy){ m.SetStrategyDeathFunction([&](GameEvent&deathEvent,Monster&m,const std::string&strategy){
m.lifetime=0.01f; m.lifetime=0.01f;
m.B(A::MARKED_DEAD)=true; m.B(A::MARKED_DEAD)=true;

View File

@ -126,4 +126,9 @@ enum class Attribute{
PLAYED_FLAG, PLAYED_FLAG,
HEALTH_PCT_PHASE, //Used for tracking the percentage a mechanic was done in Stone Golem Fight. HEALTH_PCT_PHASE, //Used for tracking the percentage a mechanic was done in Stone Golem Fight.
STAGE_POLYGONS, STAGE_POLYGONS,
NEXT_HEALTH_PCT_PILLAR_PHASE,
BAD_PILLAR_SPAWN_COUNT,
BREAK_TIME,
SHAKE_TIMER,
SHAKE_DIR,
}; };

View File

@ -55,7 +55,9 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
enum PhaseName{ enum PhaseName{
INITIALIZE, INITIALIZE,
SPAWN_PILLAR_PREPARE, SPAWN_PILLAR_PREPARE,
RESPAWN_PILLAR_PREPARE,
SPAWN_PILLAR_CAST, SPAWN_PILLAR_CAST,
RESPAWN_PILLAR_CAST,
STANDARD, STANDARD,
STONE_THROW_CAST, STONE_THROW_CAST,
STONE_THROW_FINISH_ANIMATION, STONE_THROW_FINISH_ANIMATION,
@ -114,7 +116,10 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
m.F(A::RECOVERY_TIME)=ConfigFloat("Beginning Phase.Pillar Cast Delay Time"); m.F(A::RECOVERY_TIME)=ConfigFloat("Beginning Phase.Pillar Cast Delay Time");
m.I(A::PATTERN_REPEAT_COUNT)=ConfigInt("Beginning Phase.Repeat Count"); m.I(A::PATTERN_REPEAT_COUNT)=ConfigInt("Beginning Phase.Repeat Count");
m.F(A::HEALTH_PCT_PHASE)=1.f; 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.phase=SPAWN_PILLAR_PREPARE; m.phase=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; }break;
case SPAWN_PILLAR_PREPARE:{ case SPAWN_PILLAR_PREPARE:{
m.F(A::RECOVERY_TIME)-=fElapsedTime; m.F(A::RECOVERY_TIME)-=fElapsedTime;
@ -127,6 +132,17 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
m.phase=SPAWN_PILLAR_CAST; m.phase=SPAWN_PILLAR_CAST;
} }
}break; }break;
case RESPAWN_PILLAR_PREPARE:{
m.F(A::RECOVERY_TIME)-=fElapsedTime;
if(m.F(A::RECOVERY_TIME)<=0.f){
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos();
m.SIZET(A::LOOPING_SOUND_ID)=SoundEffect::PlayLoopingSFX("Rock Toss Cast",m.GetPos());
m.PerformAnimation("CAST",m.GetFacingDirectionToTarget(m.V(A::LOCKON_POS)));
game->AddEffect(std::make_unique<SpellCircle>(m.V(A::LOCKON_POS),ConfigFloat("Pillar Respawns.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("Pillar Respawns.Spell Circle Color"),util::random(2*PI),util::degToRad(ConfigFloat("Pillar Respawns.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("Pillar Respawns.Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Pillar Respawns.Spell Insignia Rotation Spd"))),true);
m.F(A::CASTING_TIMER)=ConfigFloat("Pillar Respawns.Cast Time");
m.phase=RESPAWN_PILLAR_CAST;
}
}break;
case SPAWN_PILLAR_CAST:{ case SPAWN_PILLAR_CAST:{
m.F(A::CASTING_TIMER)-=fElapsedTime; m.F(A::CASTING_TIMER)-=fElapsedTime;
if(m.F(A::CASTING_TIMER)<=0.f){ if(m.F(A::CASTING_TIMER)<=0.f){
@ -144,18 +160,49 @@ void Monster::STRATEGY::STONE_GOLEM(Monster&m,float fElapsedTime,std::string str
} }
} }
}break; }break;
case RESPAWN_PILLAR_CAST:{
m.F(A::CASTING_TIMER)-=fElapsedTime;
if(m.F(A::CASTING_TIMER)<=0.f){
SoundEffect::StopLoopingSFX(m.SIZET(A::LOOPING_SOUND_ID));
SoundEffect::PlaySFX("Pillar Rise",m.V(A::LOCKON_POS));
if(m.I(A::BAD_PILLAR_SPAWN_COUNT)>0&&
(util::random_range(0,1)<0.5f||m.I(A::PATTERN_REPEAT_COUNT)<=m.I(A::BAD_PILLAR_SPAWN_COUNT))){
Monster&badPillar{game->SpawnMonster(m.V(A::LOCKON_POS),MONSTER_DATA.at("Breaking Stone Golem Pillar"),m.OnUpperLevel())};
badPillar._DealTrueDamage(badPillar.GetMaxHealth()-1U); //Force the health to be 1.
m.I(A::BAD_PILLAR_SPAWN_COUNT)--;
}else game->SpawnMonster(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Golem Pillar"),m.OnUpperLevel());
m.I(A::PATTERN_REPEAT_COUNT)--;
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;
}else{
m.PerformIdleAnimation(m.GetFacingDirectionToTarget(game->GetPlayer()->GetPos()));
m.F(A::RECOVERY_TIME)=ConfigFloat("Pillar Respawns.Cast Delay Time");
m.phase=RESPAWN_PILLAR_PREPARE;
}
}
}break;
case STANDARD:{ case STANDARD:{
BEAR(m,fElapsedTime,"Bear"); BEAR(m,fElapsedTime,"Bear");
//Extending the bear script's variables to read the state of it... //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); const bool SlamHasFinished=m.I(A::ATTACK_COUNT)!=m.I(A::BEAR_STOMP_COUNT);
if(SlamHasFinished){ if(SlamHasFinished){
if(m.F(A::HEALTH_PCT_PHASE)-m.GetHealthRatio()>=0.1f){ if(m.F(A::HEALTH_PCT_PHASE)-m.GetHealthRatio()>=ConfigFloat("Shockwave.Repeating Threshold")/100.f){
m.F(A::HEALTH_PCT_PHASE)-=0.1f; m.F(A::HEALTH_PCT_PHASE)-=ConfigFloat("Shockwave.Repeating Threshold")/100.f;
m.F(A::CASTING_TIMER)=ConfigFloat("Shockwave.Cast Time"); m.F(A::CASTING_TIMER)=ConfigFloat("Shockwave.Cast Time");
PrepareSafeAreas(); PrepareSafeAreas();
m.phase=SHOCKWAVE; m.phase=SHOCKWAVE;
break; break;
} }
if(m.F(A::NEXT_HEALTH_PCT_PILLAR_PHASE)>=m.GetHealthRatio()){
m.F(A::NEXT_HEALTH_PCT_PILLAR_PHASE)-=ConfigFloat("Pillar Respawns.Repeating Threshold")/100.f;
m.F(A::RECOVERY_TIME)=ConfigFloat("Pillar Respawns.Cast Delay Time");
m.I(A::PATTERN_REPEAT_COUNT)=ConfigIntArr("Pillar Respawns.Respawn Count",0);
m.I(A::BAD_PILLAR_SPAWN_COUNT)=ConfigIntArr("Pillar Respawns.Respawn Count",1);
m.phase=RESPAWN_PILLAR_PREPARE;
break;
}
const bool StoneThrowRollSucceeds=util::random(100.f)<=ConfigFloat("Standard Attack.Stone Throw Chance"); const bool StoneThrowRollSucceeds=util::random(100.f)<=ConfigFloat("Standard Attack.Stone Throw Chance");
m.I(A::ATTACK_COUNT)=m.I(A::BEAR_STOMP_COUNT); //Make sure the slams are now reset if necessary. m.I(A::ATTACK_COUNT)=m.I(A::BEAR_STOMP_COUNT); //Make sure the slams are now reset if necessary.

View File

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1 #define VERSION_MAJOR 1
#define VERSION_MINOR 2 #define VERSION_MINOR 2
#define VERSION_PATCH 3 #define VERSION_PATCH 3
#define VERSION_BUILD 9948 #define VERSION_BUILD 9951
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

View File

@ -898,6 +898,25 @@ MonsterStrategy
} }
Stone Golem Stone Golem
{ {
Pillar Respawns
{
Start HP Threshold = 75%
# How much HP Pct separates each triple pillar respawn.
Repeating Threshold = 10%
# How many pillars to spawn each time. Optionally, the second argument indicates how many spawn damaged.
Respawn Count = 3,2 damaged
Cast Time = 2s
Cast Delay Time = 0.5s
Spell Circle Color = 40, 40, 40, 80
Spell Insignia Color = 144, 137, 160, 255
# Degrees/sec. Positive is CW, Negative is CCW.
Spell Circle Rotation Spd = -30
# Degrees/sec. Positive is CW, Negative is CCW.
Spell Insignia Rotation Spd = 50
}
Beginning Phase Beginning Phase
{ {
# Number of pillars to spawn. # Number of pillars to spawn.
@ -933,6 +952,10 @@ MonsterStrategy
} }
Shockwave Shockwave
{ {
Start HP Threshold = 90%
# How much HP Pct separates each shockwave attack.
Repeating Threshold = 10%
Cast Time = 3s Cast Time = 3s
Damage = 60 Damage = 60
Pillar Damage = 1 Pillar Damage = 1
@ -953,6 +976,9 @@ MonsterStrategy
Break Phase 2 HP % Threshold = 34% or below Break Phase 2 HP % Threshold = 34% or below
Break Phase 2 Animation Name = BREAK2 Break Phase 2 Animation Name = BREAK2
# How long until the pillar breaks. When <2 seconds remains, it starts visually shaking to indicate it is about to disappear.
Break Time = 999999s
Death Ring Bullet Count = 24 Death Ring Bullet Count = 24
Death Ring Bullet Speed = 140 Death Ring Bullet Speed = 140
Death Ring Bullet Damage = 15 Death Ring Bullet Damage = 15

View File

@ -1096,6 +1096,56 @@ Monsters
# Death Sound = Slime Dead # Death Sound = Slime Dead
# Walk Sound = Slime Walk # Walk Sound = Slime Walk
# Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity
# DROP[0] = Ring of the Bear,100%,1,1
}
Breaking Stone Golem Pillar
{ # Has three lives and breaks once all three health is lost. Changes sprite based on health.
Health = 3
Attack = 40
CollisionDmg = 0
Immovable = True
Invulnerable = True
MoveSpd = 0%
# The Pillar is supposed to be 350 radius.
Size = 600%
Collision Radius = 7
# If provided, constructs a rectangular collision instance for this enemy.
# Args: Pos X,Pos Y,Width,Height.
# NOTE: Position coordinates are relative.
Rectangle Collision = -4,-4,8,8
XP = 0
Strategy = Breaking Pillar
## Breaking Pillar override
Break Time = 3s
#Size of each animation frame
SheetFrameSize = 24,96
# Setting this to true means every four rows indicates one animation, the ordering of the directions is: NORTH, EAST, SOUTH, WEST
4-Way Spritesheet = False
Animations
{
# Frame Count, Frame Speed (s), Frame Cycling (Repeat,OneShot,PingPong,Reverse)
# 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.
NORMAL = 4, 0.4, OneShot
BREAK1 = 4, 0.4, OneShot
BREAK2 = 4, 0.4, OneShot
CRUMBLE = 6, 0.3, OneShot
}
Hurt Sound = Warrior Ground Slam
# Death Sound = Slime Dead
# Walk Sound = Slime Walk
# Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity # Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity
# DROP[0] = Ring of the Bear,100%,1,1 # DROP[0] = Ring of the Bear,100%,1,1
} }