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.
149 lines
8.4 KiB
149 lines
8.4 KiB
#pragma region License
|
|
/*
|
|
License (OLC-3)
|
|
~~~~~~~~~~~~~~~
|
|
|
|
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions or derivations of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions or derivative works in binary form must reproduce the above
|
|
copyright notice. This list of conditions and the following disclaimer must be
|
|
reproduced in the documentation and/or other materials provided with the distribution.
|
|
|
|
3. Neither the name of the copyright holder nor the names of its contributors may
|
|
be used to endorse or promote products derived from this software without specific
|
|
prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
SUCH DAMAGE.
|
|
|
|
Portions of this software are copyright © 2024 The FreeType
|
|
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
|
All rights reserved.
|
|
*/
|
|
#pragma endregion
|
|
#include "AdventuresInLestoria.h"
|
|
#include "DEFINES.h"
|
|
#include "Monster.h"
|
|
#include "MonsterStrategyHelpers.h"
|
|
#include "BulletTypes.h"
|
|
#include "util.h"
|
|
|
|
INCLUDE_game
|
|
INCLUDE_BULLET_LIST
|
|
|
|
using A=Attribute;
|
|
|
|
/*
|
|
* Every 2 seconds after completing an attack do one of the following attacks:
|
|
Casts a Stone Pillar on the location of the Player: radius 350. Hits with 3 seconds delay. Becomes an solid object (Player colision) and decays after 5 seconds again.
|
|
If the Stone pillar would hit the Stone Elemental it will move out of its range.
|
|
Shoot a Stone (bullet) in the direction of the target.
|
|
The Stone gets created in form of a Cone and tracks the player movement without moving for the first second.
|
|
Once it locks it doesnt move for another second. then it starts flying towards the player with 3x the normal Bullet speed.
|
|
Dive under earth and show up 1 second later in a random location 400-700 away from player. while emerging shoot a ring of bullets (0.5x Attack)
|
|
(maybe emerge with 200 away from anything with colision to avoid the Elemental getting stuck? Other solutions for that are welcome aswell)
|
|
if range to player > 1200 always use 3rd attack
|
|
*/
|
|
|
|
void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string strategy){
|
|
enum PhaseName{
|
|
WAITING,
|
|
STONE_PILLAR_CAST,
|
|
SHOOT_STONE_CAST,
|
|
DIVE_UNDERGROUND_DIG,
|
|
DIVE_UNDERGROUND_SURFACE,
|
|
};
|
|
|
|
switch(m.phase){
|
|
case WAITING:{
|
|
m.F(A::ATTACK_COOLDOWN)+=fElapsedTime;
|
|
|
|
if(m.F(A::ATTACK_COOLDOWN)>=ConfigFloat("Attack Wait Time")){
|
|
int randomAttackChoice=util::random()%2;
|
|
|
|
float distToPlayer=util::distance(m.GetPos(),game->GetPlayer()->GetPos());
|
|
//if(distToPlayer>=ConfigPixels("Auto Dive Range"))randomAttackChoice=2; //Force dig attack if too far away.
|
|
|
|
switch(randomAttackChoice){
|
|
case 0:{
|
|
m.PerformAnimation("STONE PILLAR CAST");
|
|
m.phase=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<Effect>(m.V(A::LOCKON_POS),ConfigFloat("Stone Pillar Cast Time"),"range_indicator.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()/12.f),0.3f,vf2d{},ConfigPixel("Stone Pillar Spell Circle Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Pillar Spell Circle Rotation Spd"))),true);
|
|
game->AddEffect(std::make_unique<Effect>(m.V(A::LOCKON_POS),ConfigFloat("Stone Pillar Cast Time"),"spell_insignia.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()/12.f)*0.75f,0.3f,vf2d{},ConfigPixel("Stone Pillar Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Pillar Spell Insignia Rotation Spd"))),true);
|
|
}break;
|
|
case 1:{
|
|
m.PerformAnimation("ROCK TOSS CAST");
|
|
m.phase=SHOOT_STONE_CAST;
|
|
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;
|
|
const int masterRockInd=BULLET_LIST.size()-1;
|
|
CreateBullet(LevitatingRock)(m,game->GetPlayer()->GetPos(),1.f,util::degToRad(-20.f),ConfigPixels("Rock Toss Max Spawn Distance")*0.75f,ConfigFloat("Rock Toss Track Time"),ConfigFloat("Rock Toss Wait Time"),ConfigFloat("Rock Toss Bullet Speed"),ConfigFloat("Rock Radius")*0.6f,std::max(1,ConfigInt("Rock Toss Damage")/5),m.OnUpperLevel(),false,WHITE,vf2d{0.6f,0.6f})EndBullet;
|
|
CreateBullet(LevitatingRock)(m,game->GetPlayer()->GetPos(),1.f,util::degToRad(-40.f),ConfigPixels("Rock Toss Max Spawn Distance")*0.5f,ConfigFloat("Rock Toss Track Time"),ConfigFloat("Rock Toss Wait Time"),ConfigFloat("Rock Toss Bullet Speed"),ConfigFloat("Rock Radius")*0.6f,std::max(1,ConfigInt("Rock Toss Damage")/5),m.OnUpperLevel(),false,WHITE,vf2d{0.6f,0.6f})EndBullet;
|
|
CreateBullet(LevitatingRock)(m,game->GetPlayer()->GetPos(),1.f,util::degToRad(20.f),ConfigPixels("Rock Toss Max Spawn Distance")*0.75f,ConfigFloat("Rock Toss Track Time"),ConfigFloat("Rock Toss Wait Time"),ConfigFloat("Rock Toss Bullet Speed"),ConfigFloat("Rock Radius")*0.6f,std::max(1,ConfigInt("Rock Toss Damage")/5),m.OnUpperLevel(),false,WHITE,vf2d{0.6f,0.6f})EndBullet;
|
|
CreateBullet(LevitatingRock)(m,game->GetPlayer()->GetPos(),1.f,util::degToRad(40.f),ConfigPixels("Rock Toss Max Spawn Distance")*0.5f,ConfigFloat("Rock Toss Track Time"),ConfigFloat("Rock Toss Wait Time"),ConfigFloat("Rock Toss Bullet Speed"),ConfigFloat("Rock Radius")*0.6f,std::max(1,ConfigInt("Rock Toss Damage")/5),m.OnUpperLevel(),false,WHITE,vf2d{0.6f,0.6f})EndBullet;
|
|
auto bulletListEndIter{BULLET_LIST.end()};
|
|
for(int i=0;i<4;i++){ //This is going to assign the last four stones we created as slaves to the first rock so that their directions can all be changed together.
|
|
bulletListEndIter--;
|
|
((LevitatingRock*)(*bulletListEndIter).get())->AssignMaster((LevitatingRock*)BULLET_LIST[masterRockInd].get());
|
|
}
|
|
}break;
|
|
case 2:{
|
|
m.PerformAnimation("BURROW UNDEGROUND");
|
|
m.phase=DIVE_UNDERGROUND_DIG;
|
|
m.F(A::CASTING_TIMER)=ConfigFloat("Burrow Wait Time");
|
|
}break;
|
|
}
|
|
}
|
|
}break;
|
|
case STONE_PILLAR_CAST:{
|
|
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
|
if(m.F(A::CASTING_TIMER)<=0.f){
|
|
m.phase=WAITING;
|
|
game->SpawnMonster(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar"),m.OnUpperLevel());
|
|
m.PerformIdleAnimation();
|
|
m.F(A::ATTACK_COOLDOWN)=0.f;
|
|
game->Hurt(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar").GetCollisionRadius(),m.GetAttack(),m.OnUpperLevel(),0.f,HurtType::PLAYER);
|
|
}
|
|
if(geom2d::overlaps(geom2d::circle<float>{m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()},geom2d::circle<float>{m.GetPos(),m.GetCollisionRadius()})){
|
|
geom2d::line<float>stonePillarCastLine{m.V(A::LOCKON_POS),m.GetPos()};
|
|
const vf2d targetWalkPos=stonePillarCastLine.rpoint(stonePillarCastLine.length()+48.f);
|
|
m.target=targetWalkPos;
|
|
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
|
|
m.PerformAnimation("ROCK TOSS CAST");
|
|
}
|
|
}break;
|
|
case SHOOT_STONE_CAST:{
|
|
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
|
if(m.F(A::CASTING_TIMER)<=0.f){
|
|
m.phase=WAITING;
|
|
m.PerformIdleAnimation();
|
|
m.F(A::ATTACK_COOLDOWN)=0.f;
|
|
}
|
|
}break;
|
|
case DIVE_UNDERGROUND_DIG:{
|
|
m.F(A::CASTING_TIMER)-=fElapsedTime;
|
|
if(m.F(A::CASTING_TIMER)<=0.f){
|
|
m.phase=DIVE_UNDERGROUND_SURFACE;
|
|
}
|
|
}break;
|
|
case DIVE_UNDERGROUND_SURFACE:{
|
|
|
|
}break;
|
|
}
|
|
} |