Update of /cvsroot/wpdev/wolfpack/ai
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv3668/ai
Modified Files:
ai.cpp ai.h ai_monsters.cpp
Added Files:
ai_mage.cpp ai_mage.h
Log Message:
npc mages
--- NEW FILE: ai_mage.h ---
#if !defined(__AI_MAGE_H__)
class Monster_Mage: public Monster_Aggressive {
protected:
Monster_Mage() : Monster_Aggressive() {
}
public:
Monster_Mage(P_NPC npc);
static AbstractAI *create() {
return new Monster_Mage(0);
}
static void registerInFactory() {
AIFactory::instance()->registerType( "Monster_Mage", create );
}
virtual QString name() {
return "Monster_Mage";
}
protected:
virtual void selectVictim();
};
#endif
--- NEW FILE: ai_mage.cpp ---
#include "ai.h"
#include "ai_mage.h"
#include "../npc.h"
#include "../combat.h"
#include "../serverconfig.h"
/*
The additional mage code does:
- Meditate while wandering to get more mana back.
*/
class Monster_Mage_Wander : public Monster_Aggr_Wander {
protected:
Monster_Mage_Wander() : Monster_Aggr_Wander() {}
public:
Monster_Mage_Wander(P_NPC npc, AbstractAI* ai) : Monster_Aggr_Wander(npc, ai) {}
/*
This is a slightly changed version of the combined super class methods.
We still execute the wander action even if we dont really move.
*/
virtual float preCondition() {
Monster_Aggressive* pAI = dynamic_cast<Monster_Aggressive*>( m_ai );
if ( pAI && pAI->currentVictim() )
return 0.0f;
if ( m_npc->attackTarget() )
return 0.0f;
return 1.0f;
}
virtual void execute() {
// Since we removed this from the precondition method,
// we need check for it here.
if ( m_npc->wanderType() != enHalt
&& ( m_npc->wanderType() != enDestination || m_npc->wanderDestination() != m_npc->pos() )
&& ( m_npc->wanderType() != enFollowTarget || m_npc->inRange( m_npc->wanderFollowTarget(), Config::instance()->pathfindFollowRadius() ) )
)
{
Monster_Aggr_Wander::execute();
}
// Should we heal ourself?
if (m_npc->hitpoints() < m_npc->maxHitpoints() // Only try to heal if we're not at full health
&& !m_npc->summoned() // Summoned creatures dont heal
&& (m_npc->skillValue(MAGERY) / 100) > RandomNum(1, 100) // We dont heal with every step
) {
int toheal = m_npc->maxHitpoints() - m_npc->hitpoints(); // How many hitpoints did we loose?
if (toheal >= 50 && m_npc->mana() >= 11) { // Is it worth the effort? Do we have enough mana?
// Try a greater heal
if (m_npc->canHandleEvent(EVENT_CASTSPELL)) {
PyObject* args = Py_BuildValue( "(Nii[]N)", m_npc->getPyObject(), 29, 0, m_npc->getPyObject() );
bool result = m_npc->callEventHandler( EVENT_CASTSPELL, args );
Py_DECREF( args );
}
} else if (m_npc->mana() >= 4) { // Only bother if we have enough mana
// Try a normal heal instead
if (m_npc->canHandleEvent(EVENT_CASTSPELL)) {
PyObject* args = Py_BuildValue( "(Nii[]N)", m_npc->getPyObject(), 4, 0, m_npc->getPyObject() );
bool result = m_npc->callEventHandler( EVENT_CASTSPELL, args );
Py_DECREF( args );
}
}
}
}
virtual const char* name() {
return "Monster_Mage_Wander";
}
};
/*
The additional mage code does:
*/
class Monster_Mage_Fight : public Monster_Aggr_Fight {
protected:
Monster_Mage_Fight() : Monster_Aggr_Fight() {}
public:
Monster_Mage_Fight(P_NPC npc, AbstractAI* ai) : Monster_Aggr_Fight(npc, ai) {}
virtual float preCondition() {
return Monster_Aggr_Fight::preCondition(); // Casting has precende if possible
}
virtual const char* name() {
return "Monster_Mage_Fight";
}
};
class Monster_Mage_Cast : public AbstractAction {
protected:
Monster_Mage_Cast() : AbstractAction() {}
unsigned int nextSpellTime;
int spell;
cUObject *objTarget;
Coord_cl posTarget;
public:
Monster_Mage_Cast(P_NPC npc, AbstractAI *ai) : AbstractAction(npc, ai) {
nextSpellTime = 0;
spell = -1; // Current Spell
objTarget = 0;
posTarget = Coord_cl::null;
}
// Is the target dispellable?
bool canDispel(P_CHAR target) {
P_NPC npc = dynamic_cast<P_NPC>(target);
return npc && npc->summoned() && m_npc->inRange(npc, 12);
}
/*
Find something to dispel we are fighting.
*/
P_NPC findDispelOpponent() {
if (m_npc->intelligence() < 95 || m_npc->summoned()) {
return 0; // No dispelling below 95 int or if the NPC is summoned itself.
}
QPtrList<cFightInfo> &fights = m_npc->fights();
Monster_Aggressive *ai = static_cast<Monster_Aggressive*>(m_ai);
P_NPC currentTarget = 0;
unsigned int currentPriority = 0;
/*
Check our current attack target. It has the highest priority of all since
we are fighting it anyway.
*/
if (!invalidTarget(m_npc, ai->currentVictim()) && canDispel(ai->currentVictim())) {
currentTarget = dynamic_cast<P_NPC>(ai->currentVictim());
currentPriority = m_npc->dist(ai->currentVictim());
if (currentPriority <= 2) {
return currentTarget; // We found a threat in our range, so dispel it now.
}
}
/*
Now check everyone who is fighting us
*/
for (cFightInfo *info = fights.first(); info; info = fights.next()) {
P_NPC checkTarget;
if (info->victim() == m_npc) {
checkTarget = dynamic_cast<P_NPC>(info->attacker());
} else {
checkTarget = dynamic_cast<P_NPC>(info->victim());
}
// They have to be fighting us or they are uninteresting for this check
if (!checkTarget || checkTarget->attackTarget() != m_npc) {
continue;
}
if (!invalidTarget(m_npc, checkTarget) && canDispel(checkTarget)) {
unsigned int newPriority = m_npc->dist(checkTarget);
if (!currentTarget || currentPriority > newPriority) {
currentTarget = dynamic_cast<P_NPC>(ai->currentVictim());
currentPriority = m_npc->dist(ai->currentVictim());
if (currentPriority <= 2) {
return currentTarget; // We found a threat in our range, so dispel it now.
}
}
}
}
return currentTarget;
}
static const mageryPerCircle = (1000.0 / 7.0);
/*
Get the id of a random damage spell
*/
int getRandomHarmfulSpell() {
unsigned char maxCircle = QMIN(8, QMAX(1, (unsigned char)((m_npc->skillValue(MAGERY) + 200) / mageryPerCircle)));
int selected = RandomNum(1, maxCircle * 2) - 1; // Select a random spell
// 5: Magic Arrow
// 12: Harm
// 18: Fireball
// 30: Lightning
// 37: Mind Blast
// 42: Energy Bolt
// 43: Explosion
// 51: FlameStrike (Default)
static int const spells[16] = { 5, 5, 12, 12, 18, 18, 30, 30, 37, 37, 42, 43, 51, };
return spells[selected];
}
/*
Chose a random spell
*/
void chooseSpell(int &spell, cUObject *&objTarget, Coord_cl &posTarget, P_CHAR currentVictim) {
// If we are not summoned, try healing
if (m_npc->hitpoints() < m_npc->maxHitpoints() // Only try to heal if we're not at full health
&& !m_npc->summoned() // Summoned creatures dont heal
&& (m_npc->skillValue(MAGERY) / 100) > RandomNum(1, 100) // We dont heal with every step
) {
int toheal = m_npc->maxHitpoints() - m_npc->hitpoints(); // How many hitpoints did we loose?
if (toheal >= 50 && m_npc->mana() >= 11) { // Is it worth the effort? Do we have enough mana?
spell = 29; // Greater Heal
objTarget = m_npc;
return;
} else if (toheal >= 10 && m_npc->mana() >= 4) { // Only bother if we have enough mana
spell = 4; // Heal
objTarget = m_npc;
return;
}
}
spell = getRandomHarmfulSpell();
objTarget = currentVictim;
}
virtual float preCondition() {
// Can we cast a spell?
if (nextSpellTime > Server::instance()->time()) {
return 0.0f;
}
Monster_Aggressive *ai = static_cast<Monster_Aggressive*>(m_ai);
// If we are already casting a spell, cancel this
if (m_npc->hasScript("magic")) {
return 0.0f;
}
// Reinitialize to "zero"
spell = -1;
objTarget = 0;
posTarget = Coord_cl::null;
// We dont have a spell ready, are ready to cast.
P_NPC dispelTarget = findDispelOpponent();
if (m_npc->poison() != -1) { // Always try to cure if poisoned
spell = 11; // Cure
objTarget = m_npc;
} else if (dispelTarget) { // We have something to dispel that is attacking us. Easily dispatch threat.
spell = 41; // Dispel
objTarget = dispelTarget;
} else {
P_CHAR currentVictim = ai->currentVictim();
if (currentVictim && m_npc->inRange(currentVictim, 12)) {
chooseSpell(spell, objTarget, posTarget, currentVictim); // Choose a spell
}
}
if (spell == -1) {
return 0.0f; // We couldn't find a spell to cast
}
return 2.0f; // Give us priority in the fuzzy logic
}
virtual float postCondition() {
return 1.0f; // One time action
}
virtual void execute() {
if (spell == -1) {
return; // Shouldn't happen
}
//m_npc->talk(tr("CASTING SPELL %1").arg(spell));
if (m_npc->canHandleEvent(EVENT_CASTSPELL)) {
PyObject *target = PyGetObjectObject(objTarget);
PyObject* args = Py_BuildValue( "(Nii[]N)", m_npc->getPyObject(), spell, 0, target );
bool result = m_npc->callEventHandler( EVENT_CASTSPELL, args );
Py_DECREF( args );
}
nextSpellTime = Server::instance()->time() + 5000; // Cast all 5 seconds
}
virtual const char* name() {
return "Monster_Mage_Cast";
}
virtual bool isPassive() {
return false;
}
};
/*
The additional mage code does:
- Run away from the current target if we are casting a spell.
*/
class Monster_Mage_MoveToTarget : public Monster_Aggr_Wander {
protected:
Monster_Mage_MoveToTarget() : Monster_Aggr_Wander() {}
public:
Monster_Mage_MoveToTarget(P_NPC npc, AbstractAI* ai) : Monster_Aggr_Wander(npc, ai) {}
virtual float preCondition() {
return Monster_Aggr_Wander::preCondition();
}
virtual void execute() {
Monster_Aggr_Wander::execute();
}
virtual const char* name() {
return "Monster_Mage_MoveToTarget";
}
};
Monster_Mage::Monster_Mage( P_NPC npc ) : Monster_Aggressive( npc ) {
m_actions.append( new Monster_Mage_Wander( npc, this ) );
m_actions.append( new Action_FleeAttacker( npc, this ) );
m_actions.append( new Monster_Mage_MoveToTarget( npc, this ) );
m_actions.append( new Monster_Mage_Fight( npc, this ) );
m_actions.append( new Monster_Mage_Cast( npc, this ) );
}
void Monster_Mage::selectVictim() {
}
Index: ai.h
===================================================================
RCS file: /cvsroot/wpdev/wolfpack/ai/ai.h,v
retrieving revision 1.20
retrieving revision 1.21
diff -C2 -d -r1.20 -r1.21
*** ai.h 16 Oct 2004 04:53:23 -0000 1.20
--- ai.h 16 Oct 2004 18:19:41 -0000 1.21
***************
*** 52,55 ****
--- 52,59 ----
// #define AIDEBUG
+ // Export this for other AI functions
+ bool invalidTarget(P_NPC npc, P_CHAR victim, int dist = -1);
+ bool validTarget(P_NPC npc, P_CHAR victim, int dist = -1);
+
class AbstractAction
{
Index: ai.cpp
===================================================================
RCS file: /cvsroot/wpdev/wolfpack/ai/ai.cpp,v
retrieving revision 1.39
retrieving revision 1.40
diff -C2 -d -r1.39 -r1.40
*** ai.cpp 16 Oct 2004 04:53:23 -0000 1.39
--- ai.cpp 16 Oct 2004 18:19:40 -0000 1.40
***************
*** 531,534 ****
--- 531,535 ----
case enRectangle:
case enWanderSpawnregion:
+ case enHalt:
return 1.0f;
Index: ai_monsters.cpp
===================================================================
RCS file: /cvsroot/wpdev/wolfpack/ai/ai_monsters.cpp,v
retrieving revision 1.27
retrieving revision 1.28
diff -C2 -d -r1.27 -r1.28
*** ai_monsters.cpp 16 Oct 2004 04:53:23 -0000 1.27
--- ai_monsters.cpp 16 Oct 2004 18:19:41 -0000 1.28
***************
*** 40,44 ****
// Is this an invalid target?
! bool invalidTarget(P_NPC npc, P_CHAR victim, int dist = -1) {
if (victim->isInvulnerable() || victim->isDead()) {
return true;
--- 40,48 ----
// Is this an invalid target?
! bool invalidTarget(P_NPC npc, P_CHAR victim, int dist) {
! if (!victim) {
! return true;
! }
!
if (victim->isInvulnerable() || victim->isDead()) {
return true;
***************
*** 65,69 ****
// Is this a valid target?
! bool validTarget(P_NPC npc, P_CHAR victim, int dist = -1) {
if (invalidTarget(npc, victim, dist)) {
return false;
--- 69,73 ----
// Is this a valid target?
! bool validTarget(P_NPC npc, P_CHAR victim, int dist) {
if (invalidTarget(npc, victim, dist)) {
return false;
***************
*** 328,334 ****
}
if ( Config::instance()->pathfind4Combat() && m_npc->dist(currentVictim) < 5 )
{
! if (!movePath( currentVictim->pos() )) {
nextTry = Server::instance()->time() + RandomNum(1250, 2250);
}
--- 332,340 ----
}
+ bool run = m_npc->dist(currentVictim) > 3;
+
if ( Config::instance()->pathfind4Combat() && m_npc->dist(currentVictim) < 5 )
{
! if (!movePath( currentVictim->pos(), run )) {
nextTry = Server::instance()->time() + RandomNum(1250, 2250);
}
***************
*** 336,340 ****
else
{
! moveTo( currentVictim->pos() );
}
}
--- 342,346 ----
else
{
! moveTo( currentVictim->pos(), run );
}
}
|