Created
January 14, 2020 10:12
-
-
Save tehsausage/0a1b881e43dc292f95853bb9acc69cef to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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); | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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(); | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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(); | |
} | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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(); | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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(); | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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; | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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(); | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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; | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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(); | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* $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