Skip to content

Instantly share code, notes, and snippets.

@tehsausage
Created January 14, 2020 10:12
Show Gist options
  • Save tehsausage/0a1b881e43dc292f95853bb9acc69cef to your computer and use it in GitHub Desktop.
Save tehsausage/0a1b881e43dc292f95853bb9acc69cef to your computer and use it in GitHub Desktop.
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#include "npc_ai_hw2016_apozen.hpp"
#include "../character.hpp"
#include "../config.hpp"
#include "../map.hpp"
#include "../npc.hpp"
#include "../timer.hpp"
#include "../world.hpp"
#include "npc_ai_hw2016_apozenskull.hpp"
#include "../console.hpp"
#include "../util.hpp"
NPC_AI_HW2016_Apozen::NPC_AI_HW2016_Apozen(NPC* npc)
: NPC_AI_Standard(npc)
, skull{}
, num_skulls(0)
, charging(-6)
, chase(0)
{ }
NPC_AI_HW2016_Apozen::~NPC_AI_HW2016_Apozen()
{ }
bool NPC_AI_HW2016_Apozen::IsInRange(int x, int y, int range) const
{
return util::path_length(this->npc->x, this->npc->y, x, y) <= range;
}
void NPC_AI_HW2016_Apozen::Spawn()
{
}
bool NPC_AI_HW2016_Apozen::Dying()
{
if (this->npc->map->world->hw2016_dyingapozen)
return true;
for (int i = 0; i < 4; ++i)
{
NPC* skull = this->skull[i];
if (skull)
{
NPC_AI_HW2016_ApozenSkull* skull_ai = static_cast<NPC_AI_HW2016_ApozenSkull*>(skull->ai.get());
if (skull_ai)
{
skull_ai->apozen = nullptr;
}
}
}
if (!really_die)
{
this->npc->map->world->hw2016_dyingapozen = this->npc;
this->npc->Say("NEXT TIME, MORTALS");
}
return !really_die;
}
void NPC_AI_HW2016_Apozen::Act()
{
this->npc->hw2016_aposhield = (this->npc->map->npcs.size() > (this->num_skulls + 1));
++this->charging;
if (this->charging >= 200)
{
if (this->charging == 201)
this->npc->Say("RUN FOOLS");
if (this->charging == 220)
this->charging = -15;
}
else if (this->charging >= 0)
{
switch (util::rand(0,4))
{
case 0: // summon imps
if (!this->npc->hw2016_aposhield && --this->spawn_cooldown <= 0)
{
this->npc->Say("ARISE");
this->npc->map->world->hw2016_apospawn = 1;
this->charging = -15;
this->spawn_cooldown = 3;
}
break;
case 1: // summon demons
if (!this->npc->hw2016_aposhield && --this->spawn_cooldown <= 0)
{
this->npc->Say("ARISE");
this->npc->map->world->hw2016_apospawn = 2;
this->charging = -15;
this->spawn_cooldown = 3;
}
break;
case 2: // teleport
this->chase = 0;
this->npc->Effect(3);
this->npc->x = util::rand(10,17);
this->npc->y = util::rand(7,17) - 1;
for (int i = 0; i < 4; ++i)
{
if (this->skull[i])
{
int xoff, yoff;
switch (i)
{
case 0: xoff = 2; yoff = 2; break;
case 1: xoff = 2; yoff = -2; break;
case 2: xoff = -2; yoff = 2; break;
case 3: xoff = -2; yoff = -2; break;
}
this->skull[i]->Effect(3);
this->skull[i]->x = this->npc->x + xoff;
this->skull[i]->y = this->npc->y + yoff;
}
}
for (Character* c : this->npc->map->characters)
c->Refresh();
this->npc->Effect(2);
for (int i = 0; i < 4; ++i)
{
if (this->skull[i])
this->skull[i]->Effect(3);
}
// no break!
case 3: // aoe attack special!
if (this->num_skulls > this->npc->hw2016_apoweak)
{
this->charging = 200;
for (int i = 0; i < 4; ++i)
{
if (this->skull[i])
{
NPC_AI_HW2016_ApozenSkull* ai = static_cast<NPC_AI_HW2016_ApozenSkull*>(skull[i]->ai.get());
if (ai)
ai->charging = 200;
}
}
}
break;
case 4:
chase = 0;
this->charging = -10;
}
}
else
{
Character* attacker = this->PickTargetRandomMD();
if (attacker)
{
this->target = attacker;
chase = 0;
}
else
{
++chase;
this->target = this->PickTargetRandom();
if (chase < 10)
{
if (this->target)
this->SmartChase(this->target);
{
NPC* skull = this->skull[util::rand(0,3)];
if (!skull) return;
NPC_AI_HW2016_ApozenSkull* ai = static_cast<NPC_AI_HW2016_ApozenSkull*>(skull->ai.get());
if (!ai) return;
if (ai->charging < -1)
{
ai->charging = -1;
this->charging++;
}
}
}
}
if (this->target)
{
if (util::rand(0, 3) < num_skulls)
this->npc->Attack(this->target);
}
else
{
NPC* skull = this->skull[util::rand(0,3)];
if (!skull) return;
NPC_AI_HW2016_ApozenSkull* ai = static_cast<NPC_AI_HW2016_ApozenSkull*>(skull->ai.get());
if (!ai) return;
if (ai->charging < -1)
{
ai->charging = -1;
this->charging++;
}
}
}
}
void NPC_AI_HW2016_Apozen::SmartChase(Character* attacker, int range)
{
int ax = this->npc->x;
int ay = this->npc->y;
int tx = attacker->x;
int ty = attacker->y;
int x = tx;
int y = ty;
if (range > 0)
{
int rx[2] = {
util::clamp(ax, tx - range, tx + range),
tx
};
int ry[2] = {
ty,
util::clamp(ay, ty - range, ty + range)
};
if (util::path_length(ax, ay, rx[0], ry[0]) <= util::path_length(ax, ay, rx[1], ry[1]))
{
x = rx[0];
y = ry[0];
}
else
{
x = rx[1];
y = ry[1];
}
}
auto remove_from_damagelist = [this, attacker]()
{
// Will call our Unregister function too
this->npc->UnregisterCharacter(attacker);
};
auto recalculate_path = [this, &remove_from_damagelist, x, y]()
{
this->path = this->npc->map->world->astar.FindPath({this->npc->x, this->npc->y}, {x, y}, 15,
[this, x, y](int cx, int cy)
{
return (cx == x && cy == y)
|| (this->npc->map->Walkable(cx, cy, true) && !this->npc->map->Occupied(cx, cy, Map::PlayerAndNPC));
}
);
// OLD: Can't find a path to target, remove them from the damage list and do nothing this action
// NEW: Can't find a path to target, use dumbwalk instead!
if (this->path.size() < 2)
{
this->DumbChase(x, y);
//remove_from_damagelist();
return false;
}
this->path.pop_front();
return true;
};
if (this->path.size() == 0)
{
if (!recalculate_path())
return;
}
std::pair<int, int> target = this->path.back();
// Target has moved away from the end of the pathfinding path
if (util::path_length(target.first, target.second, x, y) > 1)
{
if (!recalculate_path())
return;
}
std::pair<int, int> next_step = this->path.front();
int direction_no = (next_step.first - this->npc->x) + 2 * (next_step.second - this->npc->y);
Direction direction;
switch (direction_no)
{
case -2: direction = DIRECTION_UP; break;
case -1: direction = DIRECTION_LEFT; break;
case 1: direction = DIRECTION_RIGHT; break;
case 2: direction = DIRECTION_DOWN; break;
default:
Console::Wrn("NPC path-finding state is corrupt!");
recalculate_path();
return;
}
if (!this->npc->Walk(direction, true))
{
recalculate_path();
}
else
{
for (int i = 0; i < 4; ++i)
{
if (this->skull[i])
this->skull[i]->Walk(direction, true);
}
this->path.pop_front();
}
}
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#ifndef NPC_AI_HW2016_APOZEN_HPP_INCLUDED
#define NPC_AI_HW2016_APOZEN_HPP_INCLUDED
#include "../fwd/npc.hpp"
#include "../npc_ai.hpp"
#include <memory>
class NPC_AI_HW2016_ApozenSkull;
class NPC_AI_HW2016_Apozen : public NPC_AI_Standard
{
public:
int num_skulls;
NPC* skull[4];
bool really_die = false;
protected:
int charging;
int chase;
int spawn_cooldown = 1;
bool IsInRange(int x, int y, int range) const;
public:
NPC_AI_HW2016_Apozen(NPC* npc);
~NPC_AI_HW2016_Apozen();
void SmartChase(Character* attacker, int range = 0);
virtual void Spawn();
virtual bool Dying();
virtual void Act();
friend class NPC_AI_HW2016_ApozenSkull;
};
#endif // NPC_AI_HW2016_APOZEN_HPP_INCLUDED
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#include "npc_ai_hw2016_apozenskull.hpp"
#include "../character.hpp"
#include "../config.hpp"
#include "../map.hpp"
#include "../npc.hpp"
#include "../timer.hpp"
#include "../world.hpp"
#include "npc_ai_hw2016_apozen.hpp"
#include "../console.hpp"
#include "../util.hpp"
NPC_AI_HW2016_ApozenSkull::NPC_AI_HW2016_ApozenSkull(NPC* npc, NPC* apozen)
: NPC_AI_Standard(npc)
, charging(-4)
, apozen(apozen)
{ }
NPC_AI_HW2016_ApozenSkull::~NPC_AI_HW2016_ApozenSkull()
{ }
bool NPC_AI_HW2016_ApozenSkull::IsInRange(int x, int y, int range) const
{
return util::path_length(this->npc->x, this->npc->y, x, y) <= range;
}
NPC* NPC_AI_HW2016_ApozenSkull::PickHealTarget(int range) const
{
NPC* buddy = nullptr;
int buddy_hp = 0;
int ax = this->npc->x;
int ay = this->npc->y;
auto eval_npc = [&](NPC* npc)
{
int tx = npc->x;
int ty = npc->y;
int distance = util::path_length(ax, ay, tx, ty);
if ((distance < range) && npc->hp < buddy_hp)
{
buddy = npc;
buddy_hp = npc->hp;
}
};
for (NPC* npc : this->npc->map->NPCsInRange(ax, ay, 8))
{
eval_npc(npc);
}
return buddy;
}
bool NPC_AI_HW2016_ApozenSkull::Dying()
{
this->npc->Effect(16);
if (this->apozen)
{
int weak = 0;
for (int i = 0; i < 4; ++i)
{
NPC_AI_HW2016_Apozen* ai = static_cast<NPC_AI_HW2016_Apozen*>(this->apozen->ai.get());
if (ai && ai->skull[i] == this->npc)
ai->skull[i] = nullptr;
if (ai && ai->skull[i] == nullptr)
++weak;
}
this->apozen->hw2016_apoweak = weak;
this->npc->map->world->hw2016_dyingskull.push_back({this->npc->x, this->npc->y});
}
return false;
}
void NPC_AI_HW2016_ApozenSkull::Act()
{
if (!this->apozen)
return;
this->npc->hw2016_aposhield = (apozen->hw2016_aposhield);
if (true)
{
this->target = this->PickTargetRandom();
NPC_AI_HW2016_Apozen* apozen_ai = static_cast<NPC_AI_HW2016_Apozen*>(this->apozen->ai.get());
if (apozen_ai)
{
for (int i = 0; i < apozen_ai->num_skulls; ++i)
{
if (apozen_ai->skull[i])
{
NPC_AI_HW2016_ApozenSkull* skull_ai = static_cast<NPC_AI_HW2016_ApozenSkull*>(apozen_ai->skull[i]->ai.get());
if (skull_ai)
{
if (skull_ai != this && skull_ai->target == this->target)
this->target = nullptr;
}
}
}
}
if (this->charging < 200)
{
if (!this->target)
return;
}
++this->charging;
if (this->charging >= 204)
{
std::list<Character*> burned;
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
if ((x != 0 || y != 0))
{
for (Character* c : this->npc->map->CharactersInRange(this->npc->x + x, this->npc->y + y, 0))
burned.push_back(c);
this->npc->map->SpellEffect(this->npc->x + x, this->npc->y + y, 0);
}
}
}
for (Character* c : burned)
{
int hit = c->maxhp * 0.2;
if (hit > c->hp)
hit = c->hp - 1;
if (hit > 0)
c->SpikeDamage(hit);
}
if (this->charging >= 206)
this->charging = -5;
}
else if (charging >= 200 && charging % 2 == 0)
{
this->npc->Effect(17);
}
else if (this->charging == 103)
{
this->npc->map->SpellAttackNPC(this->npc, this->target, 10);
this->charging = -5;
}
else if (this->charging >= 0 && this->charging < 100)
{
int r = util::rand(0,3);
switch (r)
{
case 0:
this->npc->Effect(29);
this->charging = 100;
break;
case 1:
this->npc->Effect(29);
//this->charging = 200;
this->charging = 100;
break;
case 2:
case 3:
this->charging = -4;
this->target = this->PickTargetRandomMD();
if (this->target && this->IsNextTo(this->target->x, this->target->y))
this->npc->Attack(this->target);
break;
}
}
else if (this->charging < 0)
{
this->target = this->PickTargetRandomMD();
if (this->target && this->IsNextTo(this->target->x, this->target->y))
this->npc->Attack(this->target);
}
}
else
{
this->target = this->PickTargetRandomMD();
if (this->target && this->IsNextTo(this->target->x, this->target->y))
this->npc->Attack(this->target);
}
}
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#ifndef NPC_AI_HW2016_APOZENSKULL_HPP_INCLUDED
#define NPC_AI_HW2016_APOZENSKULL_HPP_INCLUDED
#include "../fwd/npc.hpp"
#include "../npc_ai.hpp"
class NPC_AI_HW2016_ApozenSkull : public NPC_AI_Standard
{
protected:
int charging;
public:
NPC* apozen;
bool really_die = false;
protected:
bool IsInRange(int x, int y, int range) const;
NPC* PickHealTarget(int range) const;
public:
NPC_AI_HW2016_ApozenSkull(NPC* npc, NPC* apozen);
~NPC_AI_HW2016_ApozenSkull();
virtual bool Dying();
virtual void Act();
friend class NPC_AI_HW2016_Apozen;
};
#endif // NPC_AI_HW2016_APOZENSKULL_HPP_INCLUDED
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#include "npc_ai_hw2016_banshee.hpp"
#include "../character.hpp"
#include "../config.hpp"
#include "../map.hpp"
#include "../npc.hpp"
#include "../timer.hpp"
#include "../world.hpp"
#include "../util.hpp"
NPC_AI_HW2016_Banshee::NPC_AI_HW2016_Banshee(NPC* npc)
: NPC_AI_Standard(npc)
, charging(-1)
{ }
NPC_AI_HW2016_Banshee::~NPC_AI_HW2016_Banshee()
{ }
bool NPC_AI_HW2016_Banshee::IsInRange(int x, int y, int range) const
{
return util::path_length(this->npc->x, this->npc->y, x, y) <= range;
}
NPC* NPC_AI_HW2016_Banshee::PickHealTarget(int range) const
{
NPC* buddy = nullptr;
int buddy_hp = 0;
int ax = this->npc->x;
int ay = this->npc->y;
auto eval_npc = [&](NPC* npc)
{
int tx = npc->x;
int ty = npc->y;
int distance = util::path_length(ax, ay, tx, ty);
if ((distance < range) && npc->hp < buddy_hp)
{
buddy = npc;
buddy_hp = npc->hp;
}
};
for (NPC* npc : this->npc->map->NPCsInRange(ax, ay, 8))
{
eval_npc(npc);
}
return buddy;
}
void NPC_AI_HW2016_Banshee::Act()
{
Character* attacker = this->PickTarget();
if (this->target != attacker)
{
this->target = attacker;
this->path.clear();
}
if (this->target)
{
if (this->IsInRange(this->target->x, this->target->y, 8))
{
++this->charging;
if (this->charging == 102)
{
this->npc->map->SpellAttackNPC(this->npc, this->target, 23);
this->charging = -3;
}
else if (this->charging == 202)
{
/*NPC* buddy = this->PickHealTarget(8);
if (buddy)
this->npc->map->SpellAttackNPC(this->npc, buddy, 28);*/
this->charging = -3;
}
else if (this->charging == 302)
{
/*NPC* buddy = this->PickHealTarget();
if (buddy)
this->npc->map->SpellAttackNPC(this->npc, buddy, 11);*/
this->charging = -3;
}
else if (this->charging == 0)
{
this->path.clear();
switch (util::rand(0,3))
{
case 0:
case 1:
case 2:
this->npc->Effect(29);
this->charging = 100;
break;
/*
case 3:
case 4:
this->npc->Effect(29);
this->charging = 200;
break;
case 5:
this->npc->Effect(29);
this->charging = 300;
break;
*/
case 3:
this->charging = -1;
if (this->IsNextTo(this->target->x, this->target->y))
this->npc->Attack(this->target);
else
this->SmartChase(this->target);
break;
}
}
else if (this->charging < 0)
{
if (this->IsNextTo(this->target->x, this->target->y))
this->npc->Attack(this->target);
else
this->SmartChase(this->target);
}
}
else
{
this->charging = -1;
this->SmartChase(this->target);
}
}
else
{
this->charging = -1;
if (this->npc->ENF().type == ENF::Aggressive)
{
// return to spawn point
if (util::path_length(this->npc->x, this->npc->y, this->npc->spawn_x, this->npc->spawn_y) > 3)
this->DumbChase(this->npc->spawn_x, this->npc->spawn_y);
}
else
{
this->RandomWalk();
}
}
}
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#ifndef NPC_AI_HW2016_BANSHEE_HPP_INCLUDED
#define NPC_AI_HW2016_BANSHEE_HPP_INCLUDED
#include "../fwd/npc.hpp"
#include "../npc_ai.hpp"
class NPC_AI_HW2016_Banshee : public NPC_AI_Standard
{
protected:
int charging;
bool IsInRange(int x, int y, int range) const;
NPC* PickHealTarget(int range) const;
public:
NPC_AI_HW2016_Banshee(NPC* npc);
~NPC_AI_HW2016_Banshee();
virtual void Act();
};
#endif // NPC_AI_HW2016_BANSHEE_HPP_INCLUDED
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#include "npc_ai_hw2016_crane.hpp"
#include "../character.hpp"
#include "../config.hpp"
#include "../map.hpp"
#include "../npc.hpp"
#include "../timer.hpp"
#include "../world.hpp"
#include "../util.hpp"
NPC_AI_HW2016_Crane::NPC_AI_HW2016_Crane(NPC* npc)
: NPC_AI_Standard(npc)
{ }
NPC_AI_HW2016_Crane::~NPC_AI_HW2016_Crane()
{ }
bool NPC_AI_HW2016_Crane::Dying()
{
return false;
}
void NPC_AI_HW2016_Crane::Act()
{
Character *attacker = this->PickTarget(true);
if (attacker && IsNextTo(attacker->x, attacker->y))
{
this->npc->Attack(attacker);
}
else
{
if (this->npc->map->world->hw2016_hallway <= 40)
{
if (this->npc->y < 86 - this->npc->map->world->hw2016_hallway)
{
if (!this->npc->Walk(DIRECTION_DOWN))
this->RandomWalk();
}
else if (this->npc->y > 90 - this->npc->map->world->hw2016_hallway)
{
if (!this->npc->Walk(DIRECTION_UP))
this->RandomWalk();
}
else
{
this->RandomWalk();
}
}
else
{
if (this->npc->y > 40)
{
if (!this->npc->Walk(DIRECTION_UP))
this->RandomWalk();
}
else
{
this->RandomWalk();
}
}
}
}
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#ifndef NPC_AI_HW2016_CRANE_HPP_INCLUDED
#define NPC_AI_HW2016_CRANE_HPP_INCLUDED
#include "../fwd/npc.hpp"
#include "../npc_ai.hpp"
class NPC_AI_HW2016_Crane : public NPC_AI_Standard
{
public:
NPC_AI_HW2016_Crane(NPC* npc);
~NPC_AI_HW2016_Crane();
virtual bool Dying();
virtual void Act();
};
#endif // NPC_AI_HW2016_CRANE_HPP_INCLUDED
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#include "npc_ai_hw2016_cursed_mask.hpp"
#include "../character.hpp"
#include "../config.hpp"
#include "../map.hpp"
#include "../npc.hpp"
#include "../timer.hpp"
#include "../world.hpp"
#include "../util.hpp"
NPC_AI_HW2016_Cursed_Mask::NPC_AI_HW2016_Cursed_Mask(NPC* npc)
: NPC_AI_Standard(npc)
, charging(-4)
{ }
NPC_AI_HW2016_Cursed_Mask::~NPC_AI_HW2016_Cursed_Mask()
{ }
bool NPC_AI_HW2016_Cursed_Mask::IsInRange(int x, int y, int range) const
{
return util::path_length(this->npc->x, this->npc->y, x, y) <= range;
}
void NPC_AI_HW2016_Cursed_Mask::Act()
{
Character* attacker = this->PickTarget();
if (this->target != attacker)
{
this->target = attacker;
this->path.clear();
}
if (this->target)
{
if (this->IsInRange(this->target->x, this->target->y, 8))
{
++this->charging;
if (this->charging == 102)
{
this->npc->map->SpellAttackNPC(this->npc, this->target, 29);
this->charging = -3;
}
else if (this->charging == 0)
{
this->path.clear();
switch (util::rand(0,2))
{
case 0:
case 1:
this->npc->Effect(29);
this->charging = 100;
break;
case 2:
this->charging = -2;
if (this->IsNextTo(this->target->x, this->target->y))
this->npc->Attack(this->target);
else
this->SmartChase(this->target);
break;
}
}
else if (this->charging < 0)
{
if (this->IsNextTo(this->target->x, this->target->y))
this->npc->Attack(this->target);
else
this->SmartChase(this->target);
}
}
else
{
this->charging = -1;
this->SmartChase(this->target);
}
}
else
{
this->charging = -1;
if (this->npc->ENF().type == ENF::Aggressive)
{
// return to spawn point
if (util::path_length(this->npc->x, this->npc->y, this->npc->spawn_x, this->npc->spawn_y) > 3)
this->DumbChase(this->npc->spawn_x, this->npc->spawn_y);
}
else
{
this->RandomWalk();
}
}
}
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#ifndef NPC_AI_HW2016_CURSED_MASK_HPP_INCLUDED
#define NPC_AI_HW2016_CURSED_MASK_HPP_INCLUDED
#include "../fwd/npc.hpp"
#include "../npc_ai.hpp"
class NPC_AI_HW2016_Cursed_Mask : public NPC_AI_Standard
{
protected:
int charging;
bool IsInRange(int x, int y, int range) const;
public:
NPC_AI_HW2016_Cursed_Mask(NPC* npc);
~NPC_AI_HW2016_Cursed_Mask();
virtual void Act();
};
#endif // NPC_AI_HW2016_CURSED_MASK_HPP_INCLUDED
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#include "npc_ai_hw2016_dark_magician.hpp"
#include "../character.hpp"
#include "../config.hpp"
#include "../map.hpp"
#include "../npc.hpp"
#include "../timer.hpp"
#include "../world.hpp"
#include "../util.hpp"
NPC_AI_HW2016_Dark_Magician::NPC_AI_HW2016_Dark_Magician(NPC* npc)
: NPC_AI_Standard(npc)
, charging(-1)
{ }
NPC_AI_HW2016_Dark_Magician::~NPC_AI_HW2016_Dark_Magician()
{ }
bool NPC_AI_HW2016_Dark_Magician::IsInRange(int x, int y, int range) const
{
return util::path_length(this->npc->x, this->npc->y, x, y) <= range;
}
void NPC_AI_HW2016_Dark_Magician::Act()
{
for (Character* c : this->npc->map->characters)
{
if (c->y < this->npc->y - 1)
{
if (util::rand(0,2) == 2)
this->npc->Say("Get over here!");
c->Warp(this->npc->map->id, this->npc->x, this->npc->y + 1, WARP_ANIMATION_ADMIN);
//this->npc->Attack(c, false);
this->npc->map->SpellAttackNPC(this->npc, c, 23);
break;
}
}
Character* attacker = this->PickTarget();
if (this->target != attacker)
{
this->target = attacker;
this->path.clear();
}
if (this->target)
{
if (this->IsInRange(this->target->x, this->target->y, 8))
{
++this->charging;
if (this->charging == 102)
{
this->npc->map->SpellAttackNPC(this->npc, this->target, 23);
this->charging = -3;
}
else if (this->charging == 0)
{
this->path.clear();
switch (util::rand(0,2))
{
case 0:
case 1:
this->npc->Effect(29);
this->charging = 100;
break;
case 2:
this->charging = -2;
if (this->IsNextTo(this->target->x, this->target->y))
this->npc->Attack(this->target);
else
this->SmartChase(this->target);
break;
}
}
else if (this->charging < 0)
{
if (this->IsNextTo(this->target->x, this->target->y))
this->npc->Attack(this->target);
else
this->SmartChase(this->target);
}
}
else
{
this->charging = -1;
this->SmartChase(this->target);
}
}
else
{
this->charging = -1;
if (this->npc->ENF().type == ENF::Aggressive)
{
// return to spawn point
if (util::path_length(this->npc->x, this->npc->y, this->npc->spawn_x, this->npc->spawn_y) > 3)
this->DumbChase(this->npc->spawn_x, this->npc->spawn_y);
}
else
{
this->RandomWalk();
}
}
}
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#ifndef NPC_AI_HW2016_DARK_MAGICIAN_HPP_INCLUDED
#define NPC_AI_HW2016_DARK_MAGICIAN_HPP_INCLUDED
#include "../fwd/npc.hpp"
#include "../npc_ai.hpp"
class NPC_AI_HW2016_Dark_Magician : public NPC_AI_Standard
{
protected:
int charging;
bool IsInRange(int x, int y, int range) const;
public:
NPC_AI_HW2016_Dark_Magician(NPC* npc);
~NPC_AI_HW2016_Dark_Magician();
virtual void Act();
};
#endif // NPC_AI_HW2016_DARK_MAGICIAN_HPP_INCLUDED
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#include "npc_ai_hw2016_inferno_grenade.hpp"
#include "../character.hpp"
#include "../config.hpp"
#include "../map.hpp"
#include "../npc.hpp"
#include "../timer.hpp"
#include "../world.hpp"
#include "../util.hpp"
#include <list>
NPC_AI_HW2016_Inferno_Grenade::NPC_AI_HW2016_Inferno_Grenade(NPC* npc)
: NPC_AI_Standard(npc)
, charging(-20)
{ }
NPC_AI_HW2016_Inferno_Grenade::~NPC_AI_HW2016_Inferno_Grenade()
{ }
bool NPC_AI_HW2016_Inferno_Grenade::IsInRange(int x, int y, int range) const
{
return util::path_length(this->npc->x, this->npc->y, x, y) <= range;
}
bool NPC_AI_HW2016_Inferno_Grenade::Dying()
{
if (this->charging < 0)
this->charging = 0;
this->npc->hp = this->npc->ENF().hp;
return true;
}
void NPC_AI_HW2016_Inferno_Grenade::Act()
{
++charging;
if (charging > 6)
{
std::list<Character*> burned;
for (int x = -2; x <= 2; ++x)
{
for (int y = -2; y <= 2; ++y)
{
if ((x != 0 || y != 0) && (std::abs(x) + std::abs(y)) < 4)
{
for (Character* c : this->npc->map->CharactersInRange(this->npc->x + x, this->npc->y + y, 0))
burned.push_back(c);
this->npc->map->SpellEffect(this->npc->x + x, this->npc->y + y, 0);
}
}
}
for (Character* c : burned)
{
int hit = c->maxhp * 0.3;
if (hit > c->hp)
hit = c->hp - 1;
if (hit > 0)
c->SpikeDamage(hit);
}
this->npc->Die();
}
else if (charging >= 0 && charging % 2 == 0)
{
this->npc->Effect(17);
this->npc->hp = this->npc->ENF().hp;
}
else if (charging < 0)
{
Character* attacker = this->PickTarget();
if (this->target != attacker)
{
this->target = attacker;
this->path.clear();
}
if (this->target)
{
this->SmartChase(this->target);
if (this->IsNextTo(this->target->x, this->target->y))
charging = 0;
}
}
}
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#ifndef NPC_AI_HW2016_INFERNO_GRENADE_HPP_INCLUDED
#define NPC_AI_HW2016_INFERNO_GRENADE_HPP_INCLUDED
#include "../fwd/npc.hpp"
#include "../npc_ai.hpp"
class NPC_AI_HW2016_Inferno_Grenade : public NPC_AI_Standard
{
protected:
int charging;
bool IsInRange(int x, int y, int range) const;
public:
NPC_AI_HW2016_Inferno_Grenade(NPC* npc);
~NPC_AI_HW2016_Inferno_Grenade();
virtual bool Dying();
virtual void Act();
};
#endif // NPC_AI_HW2016_INFERNO_GRENADE_HPP_INCLUDED
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#include "npc_ai_hw2016_skeleton_warlock.hpp"
#include "../character.hpp"
#include "../config.hpp"
#include "../map.hpp"
#include "../npc.hpp"
#include "../timer.hpp"
#include "../world.hpp"
#include "../util.hpp"
NPC_AI_HW2016_Skeleton_Warlock::NPC_AI_HW2016_Skeleton_Warlock(NPC* npc)
: NPC_AI_Standard(npc)
, charging(-4)
{ }
NPC_AI_HW2016_Skeleton_Warlock::~NPC_AI_HW2016_Skeleton_Warlock()
{ }
bool NPC_AI_HW2016_Skeleton_Warlock::IsInRange(int x, int y, int range) const
{
return util::path_length(this->npc->x, this->npc->y, x, y) <= range;
}
void NPC_AI_HW2016_Skeleton_Warlock::Act()
{
Character* attacker = this->PickTarget();
if (this->target != attacker)
{
this->target = attacker;
this->path.clear();
}
if (this->target)
{
if (this->IsInRange(this->target->x, this->target->y, 8))
{
++this->charging;
if (this->charging == 102)
{
this->npc->map->SpellAttackNPC(this->npc, this->target, 4);
this->charging = -3;
}
else if (this->charging == 202)
{
this->npc->map->SpellAttackNPC(this->npc, this->target, 5);
this->charging = -3;
}
else if (this->charging == 0)
{
this->path.clear();
int r = util::rand(0,3);
switch (r)
{
case 0:
this->npc->Effect(29);
this->charging = 100;
break;
case 1:
this->npc->Effect(29);
this->charging = 200;
break;
case 2:
case 3:
this->charging = -4;
if (this->IsNextTo(this->target->x, this->target->y))
this->npc->Attack(this->target);
else
this->SmartChase(this->target);
break;
}
}
else if (this->charging < 0)
{
if (this->IsNextTo(this->target->x, this->target->y))
this->npc->Attack(this->target);
else
this->SmartChase(this->target);
}
}
else
{
this->charging = -1;
this->SmartChase(this->target);
}
}
else
{
this->charging = -1;
if (this->npc->ENF().type == ENF::Aggressive)
{
// return to spawn point
if (util::path_length(this->npc->x, this->npc->y, this->npc->spawn_x, this->npc->spawn_y) > 3)
this->DumbChase(this->npc->spawn_x, this->npc->spawn_y);
}
else
{
this->RandomWalk();
}
}
}
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#ifndef NPC_AI_HW2016_SKELETON_WARLOCK_HPP_INCLUDED
#define NPC_AI_HW2016_SKELETON_WARLOCK_HPP_INCLUDED
#include "../fwd/npc.hpp"
#include "../npc_ai.hpp"
class NPC_AI_HW2016_Skeleton_Warlock : public NPC_AI_Standard
{
protected:
int charging;
bool IsInRange(int x, int y, int range) const;
public:
NPC_AI_HW2016_Skeleton_Warlock(NPC* npc);
~NPC_AI_HW2016_Skeleton_Warlock();
virtual void Act();
};
#endif // NPC_AI_HW2016_SKELETON_WARLOCK_HPP_INCLUDED
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#include "npc_ai_hw2016_tentacle.hpp"
#include "../character.hpp"
#include "../config.hpp"
#include "../map.hpp"
#include "../npc.hpp"
#include "../timer.hpp"
#include "../world.hpp"
#include "../util.hpp"
NPC_AI_HW2016_Tentacle::NPC_AI_HW2016_Tentacle(NPC* npc)
: NPC_AI_Standard(npc)
, charging(-1)
{ }
NPC_AI_HW2016_Tentacle::~NPC_AI_HW2016_Tentacle()
{ }
bool NPC_AI_HW2016_Tentacle::IsInRange(int x, int y, int range) const
{
return util::path_length(this->npc->x, this->npc->y, x, y) <= range;
}
void NPC_AI_HW2016_Tentacle::Act()
{
Character* attacker = this->PickTargetRandomMD();
if (!attacker)
attacker = this->PickTargetRandomRange();
if (this->target != attacker)
{
this->target = attacker;
}
if (this->target)
{
if (this->IsNextTo(this->target->x, this->target->y))
{
this->charging = -1;
this->npc->Attack(this->target);
}
else if (this->IsInRange(this->target->x, this->target->y, 8))
{
++this->charging;
if (this->charging == 102)
{
this->npc->map->SpellAttackNPC(this->npc, this->target, 26);
this->charging = -1;
}
else if (this->charging == 0)
{
this->charging = 100;
if (util::rand(0, 300) == 69)
this->npc->Say("I know you've seen enough hentai to know where this is going");
}
}
else
{
this->charging = -1;
}
}
}
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#ifndef NPC_AI_HW2016_TENTACLE_HPP_INCLUDED
#define NPC_AI_HW2016_TENTACLE_HPP_INCLUDED
#include "../fwd/npc.hpp"
#include "../npc_ai.hpp"
class NPC_AI_HW2016_Tentacle : public NPC_AI_Standard
{
protected:
int charging;
bool IsInRange(int x, int y, int range) const;
public:
NPC_AI_HW2016_Tentacle(NPC* npc);
~NPC_AI_HW2016_Tentacle();
virtual void Act();
};
#endif // NPC_AI_HW2016_TENTACLE_HPP_INCLUDED
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#include "npc_ai_magic.hpp"
#include "../character.hpp"
#include "../config.hpp"
#include "../map.hpp"
#include "../npc.hpp"
#include "../timer.hpp"
#include "../world.hpp"
#include "../util.hpp"
NPC_AI_Magic::NPC_AI_Magic(NPC* npc, int charge_effect_id, int spell_id)
: NPC_AI_Standard(npc)
, charge_effect_id(charge_effect_id)
, spell_id(spell_id)
, charging(-1)
{ }
NPC_AI_Magic::~NPC_AI_Magic()
{ }
bool NPC_AI_Magic::IsInRange(int x, int y, int range) const
{
return util::path_length(this->npc->x, this->npc->y, x, y) <= range;
}
void NPC_AI_Magic::Act()
{
Character* attacker = this->PickTarget();
if (this->target != attacker)
{
this->target = attacker;
this->path.clear();
}
if (this->target)
{
if (this->IsInRange(this->target->x, this->target->y, 8))
{
++this->charging;
if (this->charging == 2)
{
this->npc->map->SpellAttackNPC(this->npc, this->target, spell_id);
this->charging = -3;
}
else if (this->charging == 0)
{
this->path.clear();
this->npc->Effect(charge_effect_id);
//this->npc->Say("tentacles");
}
else if (this->charging < 0)
{
if (this->IsNextTo(this->target->x, this->target->y))
this->npc->Attack(this->target);
else
this->SmartChase(this->target);
}
}
else
{
this->charging = -1;
this->SmartChase(this->target);
}
}
else
{
this->charging = -1;
if (this->npc->ENF().type == ENF::Aggressive)
{
// return to spawn point
if (util::path_length(this->npc->x, this->npc->y, this->npc->spawn_x, this->npc->spawn_y) > 3)
this->DumbChase(this->npc->spawn_x, this->npc->spawn_y);
}
else
{
this->RandomWalk();
}
}
}
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#ifndef NPC_AI_MAGIC_HPP_INCLUDED
#define NPC_AI_MAGIC_HPP_INCLUDED
#include "../fwd/npc.hpp"
#include "../npc_ai.hpp"
class NPC_AI_Magic : public NPC_AI_Standard
{
protected:
int charge_effect_id;
int spell_id;
int charging;
bool IsInRange(int x, int y, int range) const;
public:
NPC_AI_Magic(NPC* npc, int charge_effect_id, int spell_id);
~NPC_AI_Magic();
virtual void Act();
};
#endif // NPC_AI_MAGIC_HPP_INCLUDED
/* $Id$
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#include "npc_ai_ranged.hpp"
#include "../character.hpp"
#include "../config.hpp"
#include "../map.hpp"
#include "../npc.hpp"
#include "../timer.hpp"
#include "../world.hpp"
#include "../util.hpp"
NPC_AI_Ranged::NPC_AI_Ranged(NPC* npc)
: NPC_AI_Standard(npc)
{ }
NPC_AI_Ranged::~NPC_AI_Ranged()
{ }
Character* NPC_AI_Ranged::PickTargetRanged(int range, bool legacy) const
{
Character *attacker = nullptr;
unsigned char attacker_distance = static_cast<int>(this->npc->map->world->config["NPCChaseDistance"]);
unsigned short attacker_damage = 0;
int ax = this->npc->x;
int ay = this->npc->y;
auto eval_opponent = [&](const std::unique_ptr<NPC_Opponent>& opponent)
{
int tx = opponent->attacker->x;
int ty = opponent->attacker->y;
int x[2] = {
util::clamp(ax, tx - range, tx + range),
tx
};
int y[2] = {
ty,
util::clamp(ay, ty - range, ty + range)
};
for (int i = 0; i < 2; ++i)
{
int distance = util::path_length(x[i], y[i], ax, ay);
if (distance == 0)
distance = 1;
if ((distance < attacker_distance) || (distance == attacker_distance && opponent->damage > attacker_damage))
{
attacker = opponent->attacker;
attacker_damage = opponent->damage;
attacker_distance = distance;
}
}
};
if (this->npc->ENF().type == ENF::Passive || this->npc->ENF().type == ENF::Aggressive)
{
UTIL_FOREACH_CREF(this->npc->damagelist, opponent)
{
if (opponent->attacker->map != this->npc->map || opponent->attacker->nowhere || opponent->last_hit < Timer::GetTime() - static_cast<double>(this->npc->map->world->config["NPCBoredTimer"]))
continue;
eval_opponent(opponent);
}
if (this->npc->parent)
{
UTIL_FOREACH_CREF(this->npc->parent->damagelist, opponent)
{
if (opponent->attacker->map != this->npc->map || opponent->attacker->nowhere || opponent->last_hit < Timer::GetTime() - static_cast<double>(this->npc->map->world->config["NPCBoredTimer"]))
continue;
eval_opponent(opponent);
}
}
}
if (((legacy || !attacker) && this->npc->ENF().type == ENF::Aggressive) || (this->npc->parent && attacker))
{
Character *closest = nullptr;
unsigned char closest_distance = static_cast<int>(this->npc->map->world->config["NPCChaseDistance"]);
if (attacker)
{
closest = attacker;
closest_distance = std::min(closest_distance, attacker_distance);
}
UTIL_FOREACH(this->npc->map->characters, character)
{
if (character->IsHideNpc() || !character->CanInteractCombat())
continue;
int distance = util::path_length(character->x, character->y, this->npc->x, this->npc->y);
if (distance == 0)
distance = 1;
if (distance < closest_distance)
{
closest = character;
closest_distance = distance;
}
}
if (closest)
{
attacker = closest;
}
}
return attacker;
}
bool NPC_AI_Ranged::IsInStraightRange(int x, int y, int range) const
{
if (util::path_length(this->npc->x, this->npc->y, x, y) <= range)
{
if (this->npc->x == x)
{
int from = std::min<int>(y, this->npc->y);
int to = std::max<int>(y, this->npc->y);
for (int i = from + 1; i < to; ++i)
{
if (!this->npc->map->Walkable(x, i))
return false;
}
}
else if (this->npc->y == y)
{
int from = std::min<int>(x, this->npc->x);
int to = std::max<int>(x, this->npc->x);
for (int i = from + 1; i < to; ++i)
{
if (!this->npc->map->Walkable(i, y))
return false;
}
}
else
{
return false;
}
return true;
}
return false;
}
void NPC_AI_Ranged::Act()
{
Character* attacker = this->PickTargetRanged(6);
if (this->target != attacker)
{
this->target = attacker;
this->path.clear();
}
if (this->target)
{
if (this->IsInStraightRange(this->target->x, this->target->y, 5))
{
this->npc->Attack(this->target);
}
else
{
// Range target seeking isn't good without wall checks!
//this->SmartChase(this->target, 7);
this->SmartChase(this->target);
}
}
else
{
if (this->npc->ENF().type == ENF::Aggressive)
{
// return to spawn point
if (util::path_length(this->npc->x, this->npc->y, this->npc->spawn_x, this->npc->spawn_y) > 3)
this->DumbChase(this->npc->spawn_x, this->npc->spawn_y);
}
else
{
this->RandomWalk();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment