Update of /cvsroot/wpdev/wolfpack/ai
In directory sc8-pr-cvs1:/tmp/cvs-serv8134/ai
Added Files:
ai.cpp ai.h ai_animals.cpp ai_humans.cpp ai_monsters.cpp
Log Message:
door generation command ( reference )
fixed some of the copyright notices
minor improvements.
Moving AI to it's own folder
--- NEW FILE: ai.cpp ---
//==================================================================================
//
// Wolfpack Emu (WP)
// UO Server Emulation Program
//
// Copyright 2001-2003 by holders identified in authors.txt
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Palace - Suite 330, Boston, MA 02111-1307, USA.
//
// * In addition to that license, if you are running this program or modified
// * versions of it on a public system you HAVE TO make the complete source of
// * the version used by you available or provide people with a location to
// * download it.
//
//
//
// Wolfpack Homepage: http://wpdev.sf.net/
//==================================================================================
#include "python/utilities.h"
#include "ai.h"
#include "npc.h"
#include "sectors.h"
#include "player.h"
#include "srvparams.h"
#include "globals.h"
#include "basics.h"
#include "walking.h"
#include "itemid.h"
#include "items.h"
#include "console.h"
#include "world.h"
// library includes
#include <math.h>
#include <vector>
void cAIFactory::checkScriptAI( const QStringList &oldSections, const QStringList &newSections )
{
QStringList::const_iterator aiit = oldSections.begin();
while( aiit != oldSections.end() )
{
// We must reset all existing and scripted AI objects, so changes can take effect.
if( !newSections.contains( *aiit ) )
{
cCharIterator iter;
for( P_CHAR pChar = iter.first(); pChar; pChar = iter.next() )
{
P_NPC pNPC = dynamic_cast< P_NPC >(pChar);
if( pNPC )
{
ScriptAI* ai = dynamic_cast< ScriptAI* >(pNPC->ai());
if( ai && ai->name() == (*aiit) )
{
pNPC->setAI( *aiit );
}
}
}
}
else
{
cCharIterator iter;
for( P_CHAR pChar = iter.first(); pChar; pChar = iter.next() )
{
P_NPC pNPC = dynamic_cast< P_NPC >(pChar);
if( pNPC )
{
ScriptAI* ai = dynamic_cast< ScriptAI* >(pNPC->ai());
if( ai && ai->name() == (*aiit) )
{
delete ai;
pNPC->setAI( NULL );
}
}
}
unregisterType( *aiit );
}
++aiit;
}
aiit = newSections.begin();
while( aiit != newSections.end() )
{
if( !oldSections.contains( *aiit ) )
{
ScriptAI::registerInFactory( *aiit );
}
++aiit;
}
}
struct stActionNode
{
stActionNode( float fz, AbstractAction* ac ) : fuzzy( fz ), action( ac ) {}
float fuzzy;
AbstractAction* action;
};
struct ActionNodeComparePredicate : public std::binary_function<stActionNode, stActionNode, bool>
{
bool operator()(const stActionNode &a, const stActionNode &b)
{
return a.fuzzy > b.fuzzy;
}
};
void AbstractAI::check()
{
// If we have no current action or our action cant be executed, we must get a new one
if( !m_currentAction || ( m_currentAction && m_currentAction->preCondition() <= 0.0f ) )
{
std::vector< stActionNode > actions;
std::vector< stActionNode >::iterator it;
AbstractAction* action = NULL;
for( action = m_actions.first(); action; action = m_actions.next() )
{
actions.push_back( stActionNode( action->preCondition(), action ) );
}
std::sort( actions.begin(), actions.end(), ActionNodeComparePredicate() );
it = actions.begin();
while( it != actions.end() && !m_currentAction )
{
if( (*it).fuzzy > 0.0f )
m_currentAction = (*it).action;
++it;
}
}
// Now we should have a current action set, else do nothing!
if( m_currentAction )
{
m_currentAction->execute();
// We must check the postcondition now and set the current action to NULL
// if the action is finished (when it returns >= 1.0f)!
float rnd = RandomNum( 0, 1000 ) / 1000.0f;
if( m_currentAction->postCondition() >= rnd )
m_currentAction = NULL;
}
}
static AbstractAI* productCreator_SCP()
{
return new ScriptAI( NULL );
}
void ScriptAI::registerInFactory( const QString &name )
{
AIFactory::instance()->registerType(name, productCreator_SCP);
}
void ScriptAI::processNode( const cElement *Tag )
{
QString TagName = Tag->name();
// <action precondition="scriptfunction" postcondition="scriptfunction" execute="scriptfunction" onspeech="scriptfunction" />
if( TagName == "action" )
{
if( Tag->hasAttribute( "precondition" ) &&
Tag->hasAttribute( "postcondition" ) &&
Tag->hasAttribute( "execute" ) )
{
ScriptAction* action = new ScriptAction( m_npc, this );
action->setPreCondFunction( Tag->getAttribute( "precondition" ) );
action->setPostCondFunction( Tag->getAttribute( "postcondition" ) );
action->setExecuteFunction( Tag->getAttribute( "execute" ) );
m_actions.append( action );
}
else
Console::instance()->send( "Action tag in ai definition must contain attributes for pre-,postcondition and execute at least\n" );
}
else if( TagName == "onspeech" )
{
setOnSpeechFunction( Tag->getValue() );
}
}
void ScriptAI::init( P_NPC npc )
{
const cElement* node = DefManager->getDefinition( WPDT_AI, m_name );
if( node )
applyDefinition( node );
AbstractAI::init( npc );
}
void ScriptAI::onSpeechInput( P_PLAYER pTalker, const QString &comm )
{
if( !onspeech.isNull() )
{
// Try to call the python function
// Get everything before the last dot
if( onspeech.contains( "." ) )
{
// Find the last dot
INT32 position = onspeech.findRev( "." );
QString sModule = onspeech.left( position );
QString sFunction = onspeech.right( onspeech.length() - (position+1) );
PyObject *pModule = PyImport_ImportModule( const_cast< char* >( sModule.latin1() ) );
if( pModule )
{
PyObject *pFunc = PyObject_GetAttrString( pModule, const_cast< char* >( sFunction.latin1() ) );
if( pFunc && PyCallable_Check( pFunc ) )
{
// Create our Argument list
PyObject *p_args = PyTuple_New( 3 );
PyTuple_SetItem( p_args, 0, PyGetCharObject( m_npc ) );
PyTuple_SetItem( p_args, 1, PyGetCharObject( pTalker ) );
PyTuple_SetItem( p_args, 2, PyString_FromString( comm.latin1() ) );
Py_XDECREF( PyEval_CallObject( pFunc, p_args ) );
Py_XDECREF( p_args );
reportPythonError( sModule );
}
Py_XDECREF( pFunc );
}
Py_XDECREF( pModule );
}
}
}
float ScriptAction::preCondition()
{
if( !precond.isNull() )
{
// Try to call the python function
// Get everything before the last dot
if( precond.contains( "." ) )
{
// Find the last dot
INT32 position = precond.findRev( "." );
QString sModule = precond.left( position );
QString sFunction = precond.right( precond.length() - (position+1) );
PyObject *pModule = PyImport_ImportModule( const_cast< char* >( sModule.latin1() ) );
if( pModule )
{
PyObject *pFunc = PyObject_GetAttrString( pModule, const_cast< char* >( sFunction.latin1() ) );
if( pFunc && PyCallable_Check( pFunc ) )
{
// Create our Argument list
PyObject *p_args = PyTuple_New( 3 );
PyTuple_SetItem( p_args, 0, PyGetCharObject( m_npc ) );
PyTuple_SetItem( p_args, 1, Py_None );
PyTuple_SetItem( p_args, 2, Py_None );
PyObject *returnValue = PyObject_CallObject( pFunc, p_args );
Py_XDECREF( p_args );
reportPythonError( sModule );
if( returnValue == NULL || !PyFloat_Check( returnValue ) )
{
Py_XDECREF( returnValue );
return 1.0f;
}
else
{
double result = PyFloat_AsDouble( returnValue );
Py_XDECREF( returnValue );
return result;
}
}
Py_XDECREF( pFunc );
}
Py_XDECREF( pModule );
}
}
return 0.0f;
}
float ScriptAction::postCondition()
{
if( !postcond.isNull() )
{
// Try to call the python function
// Get everything before the last dot
if( postcond.contains( "." ) )
{
// Find the last dot
INT32 position = postcond.findRev( "." );
QString sModule = postcond.left( position );
QString sFunction = postcond.right( postcond.length() - (position+1) );
PyObject *pModule = PyImport_ImportModule( const_cast< char* >( sModule.latin1() ) );
if( pModule )
{
PyObject *pFunc = PyObject_GetAttrString( pModule, const_cast< char* >( sFunction.latin1() ) );
if( pFunc && PyCallable_Check( pFunc ) )
{
// Create our Argument list
PyObject *p_args = PyTuple_New( 3 );
PyTuple_SetItem( p_args, 0, PyGetCharObject( m_npc ) );
PyTuple_SetItem( p_args, 1, Py_None );
PyTuple_SetItem( p_args, 2, Py_None );
PyObject *returnValue = PyObject_CallObject( pFunc, p_args );
Py_XDECREF( p_args );
reportPythonError( sModule );
if( returnValue == NULL || !PyFloat_Check( returnValue ) )
{
Py_XDECREF( returnValue );
return 1.0f;
}
else
{
double result = PyFloat_AsDouble( returnValue );
Py_XDECREF( returnValue );
return result;
}
}
Py_XDECREF( pFunc );
}
Py_XDECREF( pModule );
}
}
return 1.0f;
}
void ScriptAction::execute()
{
if( !exec.isNull() )
{
// Try to call the python function
// Get everything before the last dot
if( exec.contains( "." ) )
{
// Find the last dot
INT32 position = exec.findRev( "." );
QString sModule = exec.left( position );
QString sFunction = exec.right( exec.length() - (position+1) );
PyObject *pModule = PyImport_ImportModule( const_cast< char* >( sModule.latin1() ) );
if( pModule )
{
PyObject *pFunc = PyObject_GetAttrString( pModule, const_cast< char* >( sFunction.latin1() ) );
if( pFunc && PyCallable_Check( pFunc ) )
{
// Create our Argument list
PyObject *p_args = PyTuple_New( 3 );
PyTuple_SetItem( p_args, 0, PyGetCharObject( m_npc ) );
PyTuple_SetItem( p_args, 1, Py_None );
PyTuple_SetItem( p_args, 2, Py_None );
Py_XDECREF( PyEval_CallObject( pFunc, p_args ) );
Py_XDECREF( p_args );
reportPythonError( sModule );
}
Py_XDECREF( pFunc );
}
Py_XDECREF( pModule );
}
}
}
float Action_Wander::preCondition()
{
/*
* Wandering has the following preconditions:
* - There is no character attacking us.
* - The wander type is not enHalt.
* - We are wandering towards a destination and aren't there yet.
* - We are following a char and aren't in follow range yet.
*/
if( m_npc->attackerSerial() != INVALID_SERIAL )
return 0.0f;
if( m_npc->wanderType() == enHalt )
return 0.0f;
if( m_npc->wanderType() == enDestination && m_npc->wanderDestination() == m_npc->pos() )
return 0.0f;
if( m_npc->wanderType() == enFollowTarget && !m_npc->inRange( m_npc->wanderFollowTarget(), SrvParams->pathfindFollowRadius() ) )
return 0.0f;
return 1.0f;
}
float Action_Wander::postCondition()
{
/*
* Wandering has the following postconditions:
* - The NPC is wandering freely/in a circle/in a rectangle,
* then we can abort after each step!
* - The NPC has reached its destination.
* - The NPC is within follow range.
*/
switch( m_npc->wanderType() )
{
case enFreely:
case enCircle:
case enRectangle:
return 1.0f;
case enDestination:
if( m_npc->wanderDestination() == m_npc->pos() )
return 1.0f;
break;
case enFollowTarget:
if( m_npc->inRange( m_npc->wanderFollowTarget(), SrvParams->pathfindFollowRadius() ) )
return 1.0f;
break;
};
return 0.0f;
}
void Action_Wander::execute()
{
if( m_npc->isAtWar() )
m_npc->toggleCombat();
switch( m_npc->wanderType() )
{
case enFreely:
{
UINT8 dir = m_npc->direction();
if( RandomNum(0, 100) < 20 )
dir = RandomNum( 0, 7 );
m_npc->setDirection( dir );
Movement::instance()->Walking( m_npc, dir, 0xFF );
break;
}
case enRectangle:
{
// get any point out of the rectangle and calculate the direction to it
UINT16 rndx = RandomNum( m_npc->wanderX1(), m_npc->wanderX2() );
UINT16 rndy = RandomNum( m_npc->wanderY1(), m_npc->wanderY2() );
UINT8 dir = m_npc->pos().direction( Coord_cl( rndx, rndy ) );
m_npc->setDirection( dir );
Movement::instance()->Walking( m_npc, dir, 0xFF );
break;
}
case enCircle:
{
Coord_cl pos = m_npc->pos();
pos.x = m_npc->wanderX1();
pos.y = m_npc->wanderY1();
// get any point within the circle and calculate the direction to it
// first a random distance which can be max. the length of the radius
float rnddist = (float)RandomNum( 1, m_npc->wanderRadius() );
// now get a point on this circle around the m_npc
float rndphi = (float)RandomNum( 0, 100 ) / 100.0f * 2.0f * 3.14159265358979323846f;
pos.x = pos.x + (INT16)floor( cos( rndphi ) * rnddist );
pos.y = pos.y + (INT16)floor( sin( rndphi ) * rnddist );
UINT8 dir = m_npc->pos().direction( pos );
m_npc->setDirection( dir );
Movement::instance()->Walking( m_npc, dir, 0xFF );
break;
}
case enFollowTarget:
{
if( SrvParams->pathfind4Follow() )
{
P_CHAR pTarget = m_npc->wanderFollowTarget();
if( pTarget )
{
movePath( pTarget->pos() );
}
if( pTarget->dist( m_npc ) > 3 )
m_npc->setAICheckTime( uiCurrentTime + (float)m_npc->aiCheckInterval() * 0.0005f * MY_CLOCKS_PER_SEC );
}
else
{
P_CHAR pTarget = m_npc->wanderFollowTarget();
if( pTarget )
{
moveTo( pTarget->pos() );
}
}
break;
}
case enDestination:
{
movePath( m_npc->wanderDestination() );
break;
}
}
}
void Action_Wander::moveTo( const Coord_cl &pos )
{
// simply move towards the target
UINT8 dir = m_npc->pos().direction( pos );
Coord_cl newPos = Movement::instance()->calcCoordFromDir( dir, m_npc->pos() );
if( !mayWalk( m_npc, newPos ) )
{
if( dir == 7 )
dir = 0;
else
dir++;
newPos = Movement::instance()->calcCoordFromDir( dir, m_npc->pos() );
if( !mayWalk( m_npc, newPos ) )
{
if( dir == 0 )
dir = 6;
else if( dir == 1 )
dir = 7;
else
dir = dir - 2;
newPos = Movement::instance()->calcCoordFromDir( dir, m_npc->pos() );
if( !mayWalk( m_npc, newPos ) )
{
return;
}
}
}
m_npc->setDirection( dir );
Movement::instance()->Walking( m_npc, dir, 0xFF );
}
void Action_Wander::movePath( const Coord_cl &pos )
{
if( ( waitForPathCalculation <= 0 && !m_npc->hasPath() ) || pos != m_npc->pathDestination() )
{
UINT8 range = 1;
if( m_npc->rightHandItem() && IsBowType( m_npc->rightHandItem()->id() ) )
range = ARCHERY_RANGE;
m_npc->findPath( pos, range == 1 ? 1.5f : (float)range );
// dont return here!
}
else if( !m_npc->hasPath() )
{
waitForPathCalculation--;
moveTo( pos );
return;
}
if( m_npc->hasPath() )
{
waitForPathCalculation = 0;
Coord_cl nextmove = m_npc->nextMove();
UINT8 dir = m_npc->pos().direction( nextmove );
m_npc->setDirection( dir );
Movement::instance()->Walking( m_npc, dir, 0xFF );
m_npc->popMove();
return;
}
else
{
waitForPathCalculation = 3;
moveTo( pos );
return;
}
}
void Action_Flee::execute()
{
if( !m_npc->hasPath() )
{
Coord_cl newPos = m_npc->pos();
Coord_cl fleePos = pFleeFrom->pos();
// find a valid spot in a circle of flee_radius fields to move to
float rnddist = (float)RandomNum( 1, SrvParams->pathfindFleeRadius() );
if( newPos != fleePos )
{
int v1 = newPos.x - fleePos.x;
int v2 = newPos.y - fleePos.y;
float v_norm = sqrt( (double)( v1 * v1 + v2 * v2 ) );
newPos.x = newPos.x + (INT16)floor( rnddist * v1 / v_norm );
newPos.y = newPos.y + (INT16)floor( rnddist * v2 / v_norm );
}
else
{
float rndphi = (float)RandomNum( 0, 100 ) / 100.0f * 2 * 3.14;
newPos.x = newPos.x + (INT16)floor( sin( rndphi ) * rnddist );
newPos.y = newPos.y + (INT16)floor( cos( rndphi) * rnddist );
}
// we use pathfinding for fleeing
movePath( newPos );
}
else
movePath( m_npc->pathDestination() );
}
float Action_FleeAttacker::preCondition()
{
/*
* Fleeing from an attacker has the following preconditions:
* - There is a character attacking us.
* - The attacking character has not died.
* - The attacking character is within flee range.
* - The hitpoints are below the critical value.
*
* Fuzzy: The nearer we get to the critical health, the chance
* increases to flee.
*/
P_CHAR pAttacker = World::instance()->findChar( m_npc->attackerSerial() );
if( !pAttacker || pAttacker->isDead() || !m_npc->inRange( pAttacker, SrvParams->pathfindFleeRadius() ) )
return 0.0f;
if( m_npc->hitpoints() < m_npc->criticalHealth() )
return 0.0f;
pFleeFrom = pAttacker;
float healthmod = (float)(m_npc->maxHitpoints() - m_npc->hitpoints()) /
(float)(m_npc->maxHitpoints() - m_npc->criticalHealth());
return healthmod;
}
float Action_FleeAttacker::postCondition()
{
/*
* Fleeing from an attacker has the following postconditions:
* - The character isn't attacking us anymore.
* - The attacker has died.
* - The attacker is not within flee range.
* - The hitpoints are restored.
*
* Fuzzy: The farer we are from the critical health line,
* the higher is the chance to end the flee action.
*/
P_CHAR pAttacker = World::instance()->findChar( m_npc->attackerSerial() );
if( !pAttacker || pAttacker->isDead() || !m_npc->inRange( pAttacker, SrvParams->pathfindFleeRadius() ) )
return 1.0f;
float healthmod = (float)(m_npc->hitpoints() - m_npc->criticalHealth()) /
(float)(m_npc->maxHitpoints() - m_npc->criticalHealth());
return healthmod;
}
float Action_Defend::preCondition()
{
/*
* Defending has the following preconditions:
* - There is a character attacking us.
* - The attacking character has not died.
* - The attacking character is within combat range.
* - The hitpoints are above the critical value.
*
* Fuzzy: The nearer we get to the critical health, the chance
* increases to flee.
*/
P_CHAR pAttacker = World::instance()->findChar( m_npc->attackerSerial() );
if( !pAttacker || pAttacker->isDead() )
return 0.0f;
if( m_npc->hitpoints() < m_npc->criticalHealth() )
return 0.0f;
UINT8 range = 1;
if( m_npc->rightHandItem() && IsBowType( m_npc->rightHandItem()->id() ) )
range = ARCHERY_RANGE;
if( !m_npc->inRange( pAttacker, range ) )
return 0.0f;
float healthmod = (float)m_npc->hitpoints() / ((float)m_npc->criticalHealth()/100.0f * (float)m_npc->maxHitpoints());
return healthmod;
}
float Action_Defend::postCondition()
{
/*
* Defending has the following postconditions:
* - The character isn't attacking us anymore.
* - The attacker has died.
* - The attacker is not within combat range.
* - Health is critical.
*
* Fuzzy: The nearer we get to the critical health line,
* the higher is the chance to end the defend action.
*/
P_CHAR pAttacker = World::instance()->findChar( m_npc->attackerSerial() );
if( !pAttacker || pAttacker->isDead() )
return 1.0f;
UINT8 range = 1;
if( m_npc->rightHandItem() && IsBowType( m_npc->rightHandItem()->id() ) )
range = ARCHERY_RANGE;
if( !m_npc->inRange( pAttacker, range ) )
return 1.0f;
float healthmod = (float)(m_npc->maxHitpoints() - m_npc->hitpoints()) /
(float)(m_npc->maxHitpoints() - ((float)m_npc->criticalHealth()/100.0f * (float)m_npc->maxHitpoints()));
return healthmod;
}
void Action_Defend::execute()
{
// combat is handled somewhere else
}
--- NEW FILE: ai.h ---
//==================================================================================
//
// Wolfpack Emu (WP)
// UO Server Emulation Program
//
// Copyright 2001-2004 by holders identified in authors.txt
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Palace - Suite 330, Boston, MA 02111-1307, USA.
//
// * In addition to that license, if you are running this program or modified
// * versions of it on a public system you HAVE TO make the complete source of
// * the version used by you available or provide people with a location to
// * download it.
//
//
//
// Wolfpack Homepage: http://wpdev.sf.net/
//==================================================================================
#ifndef AI_H_HEADER_INCLUDED
#define AI_H_HEADER_INCLUDED
// platform includes
#include "../platform.h"
// wolfpack includes
#include "../factory.h"
#include "../definable.h"
#include "../singleton.h"
#include "../typedefs.h"
// library includes
#include <qptrlist.h>
#include <qstring.h>
#include <qstringlist.h>
// forward declarations
class Coord_cl;
class AbstractAI;
class AbstractAction
{
protected:
AbstractAction() : m_npc( NULL ),m_ai( NULL ) {}
public:
AbstractAction( P_NPC npc, AbstractAI* ai ) : m_npc( npc ), m_ai( ai ) {}
~AbstractAction() {}
// executes the action
virtual void execute() = 0;
/* preCondition
*
* @return A value between 0 and 1 which indicates the
* propability of the action to be executed successfully.
* (So the method will return 0 if the action cannot be executed)
* This gives us the possibility of fuzzy logic.
*/
virtual float preCondition() = 0;
/* postCondition
*
* @return A value between 0 and 1 which indicates the
* propability that this action will lead to a result
* in proper time.
* (So the method will return 1 if the action is finished)
* This gives us the possibility of fuzzy logic.
*/
virtual float postCondition() = 0;
P_NPC npc() const { return m_npc; }
void setNPC( P_NPC npc ) { m_npc = npc; }
protected:
P_NPC m_npc;
AbstractAI* m_ai;
};
class AbstractAI
{
protected:
AbstractAI() : m_npc( NULL ), m_currentAction( NULL ), notorityOverride_( 0 ) { m_actions.setAutoDelete( true ); }
public:
AbstractAI( P_NPC npc ) : m_npc( npc ), m_currentAction( NULL ), notorityOverride_( 0 ) { m_actions.setAutoDelete( true ); }
virtual ~AbstractAI() {} // virtual destructor.
// some events that can be triggered from outside
virtual void onSpeechInput( P_PLAYER pTalker, const QString &comm ) { Q_UNUSED(pTalker); Q_UNUSED(comm); }
// this method is called, when the npc ai should be checked
virtual void check();
virtual QString name() = 0;
P_NPC npc() const { return m_npc; }
void setNPC( P_NPC npc ) { m_npc = npc; }
unsigned char notorityOverride() const { return notorityOverride_; }
void setNotorityOverride( unsigned char value ) { notorityOverride_ = value; }
// This is for creating AI interfaces through the AIFactory
virtual void init( P_NPC npc )
{
m_npc = npc;
AbstractAction* action = NULL;
for( action = m_actions.first(); action ; action = m_actions.next() )
{
action->setNPC( npc );
}
}
protected:
P_NPC m_npc;
AbstractAction* m_currentAction;
QPtrList< AbstractAction > m_actions;
unsigned char notorityOverride_;
};
class cAIFactory : public Factory< AbstractAI, QString >
{
public:
void checkScriptAI( const QStringList &oldSections, const QStringList &newSections );
};
typedef SingletonHolder< cAIFactory > AIFactory;
class Action_Wander : public AbstractAction
{
protected:
Action_Wander() : AbstractAction(), waitForPathCalculation( 0 ) {}
public:
Action_Wander( P_NPC npc, AbstractAI* ai ) : AbstractAction( npc, ai ), waitForPathCalculation( 0 ) {}
virtual void execute();
virtual float preCondition();
virtual float postCondition();
protected:
void moveTo( const Coord_cl& pos );
void movePath( const Coord_cl& pos );
int waitForPathCalculation;
};
class Action_Flee : public Action_Wander
{
protected:
Action_Flee() : Action_Wander(), pFleeFrom( NULL ) {}
public:
Action_Flee( P_NPC npc, AbstractAI* ai ) : Action_Wander( npc, ai ), pFleeFrom( NULL ) {}
virtual void execute();
protected:
P_CHAR pFleeFrom;
};
class Action_FleeAttacker : public Action_Flee
{
protected:
Action_FleeAttacker() : Action_Flee() {}
public:
Action_FleeAttacker( P_NPC npc, AbstractAI* ai ) : Action_Flee( npc, ai ) {}
virtual float preCondition();
virtual float postCondition();
};
class Action_Defend : public AbstractAction
{
protected:
Action_Defend() : AbstractAction() {}
public:
Action_Defend( P_NPC npc, AbstractAI* ai ) : AbstractAction( npc, ai ) {}
virtual void execute();
virtual float preCondition();
virtual float postCondition();
};
class Monster_Aggr_Wander : public Action_Wander
{
protected:
Monster_Aggr_Wander() : Action_Wander() {}
public:
Monster_Aggr_Wander( P_NPC npc, AbstractAI* ai ) : Action_Wander( npc, ai ) {}
virtual float preCondition();
};
class Monster_Aggr_MoveToTarget : public Action_Wander
{
protected:
Monster_Aggr_MoveToTarget() : Action_Wander() {}
public:
Monster_Aggr_MoveToTarget( P_NPC npc, AbstractAI* ai ) : Action_Wander( npc, ai ) {}
virtual void execute();
virtual float preCondition();
virtual float postCondition();
};
class Monster_Aggr_Fight : public AbstractAction
{
protected:
Monster_Aggr_Fight() : AbstractAction() {}
public:
Monster_Aggr_Fight( P_NPC npc, AbstractAI* ai ) : AbstractAction( npc, ai ) {}
virtual void execute();
virtual float preCondition();
virtual float postCondition();
};
class Monster_Aggressive : public AbstractAI
{
protected:
Monster_Aggressive() : AbstractAI(), m_currentVictim( NULL )
{
notorityOverride_ = 6;
}
public:
Monster_Aggressive( P_NPC npc ) : AbstractAI( npc ), m_currentVictim( NULL )
{
notorityOverride_ = 6;
}
virtual void check();
P_CHAR currentVictim() const { return m_currentVictim; }
protected:
virtual void selectVictim() = 0;
P_CHAR m_currentVictim;
};
class Monster_Aggressive_L0 : public Monster_Aggressive
{
protected:
Monster_Aggressive_L0() : Monster_Aggressive() {}
public:
Monster_Aggressive_L0( P_NPC npc ) : Monster_Aggressive( npc )
{
m_actions.append( new Monster_Aggr_Wander( npc, this ) );
m_actions.append( new Monster_Aggr_MoveToTarget( npc, this ) );
m_actions.append( new Monster_Aggr_Fight( npc, this ) );
}
static void registerInFactory();
virtual QString name() { return "Monster_Aggressive_L0"; }
protected:
virtual void selectVictim();
};
class Monster_Aggressive_L1 : public Monster_Aggressive
{
protected:
Monster_Aggressive_L1() : Monster_Aggressive() {}
public:
Monster_Aggressive_L1( P_NPC npc ) : Monster_Aggressive( npc )
{
m_actions.append( new Monster_Aggr_Wander( npc, this ) );
m_actions.append( new Action_FleeAttacker( npc, this ) );
m_actions.append( new Monster_Aggr_MoveToTarget( npc, this ) );
m_actions.append( new Monster_Aggr_Fight( npc, this ) );
}
static void registerInFactory();
virtual QString name() { return "Monster_Aggressive_L1"; }
protected:
virtual void selectVictim();
};
class Human_Vendor : public AbstractAI
{
protected:
Human_Vendor() : AbstractAI()
{
notorityOverride_ = 1;
}
public:
Human_Vendor( P_NPC npc ) : AbstractAI( npc )
{
notorityOverride_ = 1;
m_actions.append( new Action_Wander( npc, this ) );
m_actions.append( new Action_FleeAttacker( npc, this ) );
}
virtual void onSpeechInput( P_PLAYER pTalker, const QString &comm );
static void registerInFactory();
virtual QString name() { return "Human_Vendor"; }
};
class cUORxTarget;
class Human_Stablemaster : public AbstractAI
{
protected:
Human_Stablemaster() : AbstractAI()
{
notorityOverride_ = 1;
}
public:
Human_Stablemaster( P_NPC npc );
virtual void init( P_NPC npc );
virtual void onSpeechInput( P_PLAYER pTalker, const QString &comm );
static void registerInFactory();
virtual QString name() { return "Human_Stablemaster"; }
void refreshStock();
void handleTargetInput( P_PLAYER player, cUORxTarget* target );
};
class Animal_Wild_Flee : public Action_Flee
{
protected:
Animal_Wild_Flee() : Action_Flee() {}
public:
Animal_Wild_Flee( P_NPC npc, AbstractAI* ai ) : Action_Flee( npc, ai ) {}
virtual float preCondition();
virtual float postCondition();
};
class AnimalAI : public AbstractAI
{
protected:
AnimalAI() : AbstractAI() {}
public:
AnimalAI( P_NPC npc ) : AbstractAI( npc ) {}
virtual void onSpeechInput( P_PLAYER pTalker, const QString &comm );
};
class Animal_Wild : public AnimalAI
{
protected:
Animal_Wild() : AnimalAI() {}
public:
Animal_Wild( P_NPC npc ) : AnimalAI( npc )
{
m_actions.append( new Action_Wander( npc, this ) );
m_actions.append( new Action_FleeAttacker( npc, this ) );
m_actions.append( new Action_Defend( npc, this ) );
m_actions.append( new Animal_Wild_Flee( npc, this ) );
}
static void registerInFactory();
virtual QString name() { return "Animal_Wild"; }
};
class Animal_Domestic : public AnimalAI
{
protected:
Animal_Domestic() : AnimalAI() {}
public:
Animal_Domestic( P_NPC npc ) : AnimalAI( npc )
{
m_actions.append( new Action_Wander( npc, this ) );
m_actions.append( new Action_FleeAttacker( npc, this ) );
m_actions.append( new Action_Defend( npc, this ) );
}
static void registerInFactory();
virtual QString name() { return "Animal_Domestic"; }
};
class ScriptAction : public AbstractAction
{
protected:
ScriptAction() : AbstractAction(), exec( (char*)0 ),
precond( (char*)0 ), postcond( (char*)0 ) {}
public:
ScriptAction( P_NPC npc, AbstractAI* ai ) : AbstractAction( npc, ai ), exec( (char*)0 ),
precond( (char*)0 ), postcond( (char*)0 ) {}
virtual void execute();
virtual float preCondition();
virtual float postCondition();
void setExecuteFunction( const QString &data ) { exec = data; }
void setPreCondFunction( const QString &data ) { precond = data; }
void setPostCondFunction( const QString &data ) { postcond = data; }
protected:
QString exec;
QString precond;
QString postcond;
};
class ScriptAI : public AbstractAI, public cDefinable
{
protected:
ScriptAI() : AbstractAI(), onspeech( (char*)0 ) {}
virtual void processNode( const cElement *Tag );
public:
ScriptAI( P_NPC npc ) : AbstractAI( npc ), onspeech( (char*)0 ) {}
static void registerInFactory( const QString &name );
virtual QString name() { return m_name; }
void setName( const QString &d ) { m_name = d; }
virtual void init( P_NPC npc );
virtual void onSpeechInput( P_PLAYER pTalker, const QString &comm );
void setOnSpeechFunction( const QString &data ) { onspeech = data; }
protected:
QString m_name;
QString onspeech;
};
class Human_Guard_Called_Fight : public AbstractAction
{
protected:
Human_Guard_Called_Fight() : AbstractAction() {}
public:
Human_Guard_Called_Fight( P_NPC npc, AbstractAI* ai ) : AbstractAction( npc, ai ) {}
virtual void execute();
virtual float preCondition();
virtual float postCondition();
};
class Human_Guard_Called_TeleToTarget : public AbstractAction
{
protected:
Human_Guard_Called_TeleToTarget() : AbstractAction() {}
public:
Human_Guard_Called_TeleToTarget( P_NPC npc, AbstractAI* ai ) : AbstractAction( npc, ai ) {}
virtual void execute();
virtual float preCondition();
virtual float postCondition();
};
class Human_Guard_Called_Disappear : public AbstractAction
{
protected:
Human_Guard_Called_Disappear() : AbstractAction() {}
public:
Human_Guard_Called_Disappear( P_NPC npc, AbstractAI* ai ) : AbstractAction( npc, ai ) {}
virtual void execute();
virtual float preCondition();
virtual float postCondition();
};
class cTerritory;
class Human_Guard_Called : public AbstractAI
{
protected:
Human_Guard_Called() : AbstractAI()
{
notorityOverride_ = 1;
}
cTerritory* region_;
public:
Human_Guard_Called( P_NPC npc );
virtual void init( P_NPC npc );
static void registerInFactory();
virtual QString name() { return "Human_Guard_Called"; }
};
class Human_Guard_Wander : public Action_Wander
{
protected:
Human_Guard_Wander() : Action_Wander() {}
public:
Human_Guard_Wander( P_NPC npc, AbstractAI* ai ) : Action_Wander( npc, ai ) {}
virtual float preCondition();
virtual float postCondition();
};
class Human_Guard_MoveToTarget : public Action_Wander
{
protected:
Human_Guard_MoveToTarget() : Action_Wander() {}
public:
Human_Guard_MoveToTarget( P_NPC npc, AbstractAI* ai ) : Action_Wander( npc, ai ) {}
virtual void execute();
virtual float preCondition();
virtual float postCondition();
};
class Human_Guard_Fight : public AbstractAction
{
protected:
Human_Guard_Fight() : AbstractAction() {}
public:
Human_Guard_Fight( P_NPC npc, AbstractAI* ai ) : AbstractAction( npc, ai ) {}
virtual void execute();
virtual float preCondition();
virtual float postCondition();
};
class Human_Guard : public AbstractAI
{
protected:
Human_Guard() : AbstractAI(), m_currentVictim( NULL )
{
notorityOverride_ = 1;
}
public:
Human_Guard( P_NPC npc );
static void registerInFactory();
virtual QString name() { return "Human_Guard"; }
virtual void check();
P_CHAR currentVictim() const { return m_currentVictim; }
protected:
virtual void selectVictim();
P_CHAR m_currentVictim;
};
#endif /* AI_H_HEADER_INCLUDED */
--- NEW FILE: ai_animals.cpp ---
//==================================================================================
//
// Wolfpack Emu (WP)
// UO Server Emulation Program
//
// Copyright 2001-2003 by holders identified in authors.txt
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Palace - Suite 330, Boston, MA 02111-1307, USA.
//
// * In addition to that license, if you are running this program or modified
// * versions of it on a public system you HAVE TO make the complete source of
// * the version used by you available or provide people with a location to
// * download it.
//
//
//
// Wolfpack Homepage: http://wpdev.sf.net/
//==================================================================================
#include "ai.h"
#include "npc.h"
#include "player.h"
#include "srvparams.h"
#include "globals.h"
#include "sectors.h"
#include "basics.h"
#include "targetrequests.h"
#include "chars.h"
// library includes
#include <math.h>
static AbstractAI* productCreator_AW()
{
return new Animal_Wild( NULL );
}
void Animal_Wild::registerInFactory()
{
AIFactory::instance()->registerType("Animal_Wild", productCreator_AW);
}
static AbstractAI* productCreator_AD()
{
return new Animal_Domestic( NULL );
}
void Animal_Domestic::registerInFactory()
{
AIFactory::instance()->registerType("Animal_Domestic", productCreator_AD);
}
void AnimalAI::onSpeechInput( P_PLAYER pTalker, const QString &comm )
{
if( m_npc->owner() != pTalker && !pTalker->isGM() )
return;
// too far away to hear us
if( pTalker->dist( m_npc ) > 7 )
return;
if( comm.contains( " FOLLOW" ) )
{
if( comm.contains( " ME" ) )
{
m_npc->setWanderFollowTarget( pTalker );
m_npc->setWanderType( enFollowTarget );
m_npc->bark( cBaseChar::Bark_Attacking );
}
else
{
pTalker->socket()->attachTarget( new cFollowTarget( m_npc ) );
// LEGACY: target( s, 0, 1, 0, 117, "Click on the target to follow." );
}
}
else if( ( comm.contains( " KILL" ) ) || ( comm.contains( " ATTACK" ) ) )
{
if( m_npc->inGuardedArea() ) // Ripper..No pet attacking in town.
{
pTalker->message( tr( "You can't have pets attack in town!" ) );
}
//pPlayer->setGuarded( false );
// >> LEGACY
//addx[s]=pPet->serial();
//target(s, 0, 1, 0, 118, "Select the target to attack.");//AntiChrist
}
else if( ( comm.contains( " FETCH" ) ) || ( comm.contains( " GET" ) ) )
{
//pPlayer->setGuarded(false);
// >> LEGACY
//addx[s]=pPet->serial();
//target(s, 0, 1, 0, 124, "Click on the object to fetch.");
}
else if( comm.contains( " COME" ) )
{
//pPlayer->setGuarded( false );
// pPet->setWanderFollowTarget( pPlayer->serial() );
// pPet->setWanderType( enFollowTarget );
// m_npc->setNextMoveTime();
// pTalker->message( tr( "Your pet begins following you." ) );
}
else if( comm.contains( " GUARD" ) )
{
// LEGACY
/*addx[s] = pPet->serial(); // the pet's serial
addy[s] = 0;
if( comm.find( " ME" ) != string::npos )
addy[s]=1; // indicates we already know whom to guard (for future use)
// for now they still must click on themselves (Duke)
target(s, 0, 1, 0, 120, "Click on the char to guard.");*/
}
else if( ( comm.contains( " STOP" ) ) || ( comm.contains(" STAY") ) )
{
m_npc->setCombatTarget( INVALID_SERIAL );
if (m_npc->isAtWar())
m_npc->toggleCombat();
m_npc->setWanderType( enHalt );
}
else if( comm.contains( " TRANSFER" ) )
{
//pPlayer->setGuarded( false );
// >> LEGACY
/*addx[s]=pPet->serial();
target(s, 0, 1, 0, 119, "Select character to transfer your pet to.");*/
}
else if( comm.contains( " RELEASE" ) )
{
// Has it been summoned ? Let's dispel it
if( m_npc->summonTime() > uiCurrentTime )
m_npc->setSummonTime( uiCurrentTime );
m_npc->setWanderType( enFreely );
m_npc->setOwner( NULL );
m_npc->setTamed( false );
m_npc->emote( tr( "%1 appears to have decided that it is better off without a master" ).arg( m_npc->name() ) );
if( SrvParams->tamedDisappear() )
{
m_npc->soundEffect( 0x01FE );
cCharStuff::DeleteChar( m_npc );
}
}
}
float Animal_Wild_Flee::preCondition()
{
/*
* Fleeing from an approaching player has the following preconditions:
* - There is a player within flight range.
* - There is no character attacking us.
* - Our owner is not in range.
*
*/
if( m_npc->attackerSerial() != INVALID_SERIAL )
return 0.0f;
RegionIterator4Chars ri( m_npc->pos(), SrvParams->animalWildFleeRange() );
for(ri.Begin(); !ri.atEnd(); ri++)
{
P_PLAYER pPlayer = dynamic_cast<P_PLAYER>(ri.GetData());
if( pPlayer && !pPlayer->free && !pPlayer->isGMorCounselor() && !pPlayer->isHidden() && !pPlayer->isInvisible() )
{
pFleeFrom = pPlayer;
}
if( pPlayer && m_npc->owner() == pPlayer )
return 0.0f;
}
if( pFleeFrom )
return 1.0f;
return 0.0f;
}
float Animal_Wild_Flee::postCondition()
{
/*
* Fleeing from an approaching player has the following postconditions:
* - There is no character in flight range.
* - There is an character attacking us.
* - Our owner has come in range.
*
*/
if( m_npc->attackerSerial() != INVALID_SERIAL )
return 1.0f;
RegionIterator4Chars ri( m_npc->pos(), SrvParams->animalWildFleeRange() );
bool found = false;
for(ri.Begin(); !ri.atEnd(); ri++)
{
P_PLAYER pPlayer = dynamic_cast<P_PLAYER>(ri.GetData());
if( pPlayer && !pPlayer->free && !pPlayer->isGMorCounselor() && !pPlayer->isHidden() && !pPlayer->isInvisible() )
found = true;
if( pPlayer && m_npc->owner() == pPlayer )
return 1.0f;
}
if( found )
return 0.0f;
return 1.0f;
}
--- NEW FILE: ai_humans.cpp ---
//==================================================================================
//
// Wolfpack Emu (WP)
// UO Server Emulation Program
//
// Copyright 2001-2003 by holders identified in authors.txt
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Palace - Suite 330, Boston, MA 02111-1307, USA.
//
// * In addition to that license, if you are running this program or modified
// * versions of it on a public system you HAVE TO make the complete source of
// * the version used by you available or provide people with a location to
// * download it.
//
//
//
// Wolfpack Homepage: http://wpdev.sf.net/
//==================================================================================
#include "ai.h"
#include "npc.h"
#include "player.h"
#include "network/uosocket.h"
#include "speech.h"
#include "targetrequests.h"
#include "TmpEff.h"
#include "srvparams.h"
#include "globals.h"
#include "sectors.h"
#include "world.h"
#include "basics.h"
// library includes
#include <math.h>
static AbstractAI* productCreator_HV()
{
return new Human_Vendor( NULL );
}
void Human_Vendor::registerInFactory()
{
AIFactory::instance()->registerType("Human_Vendor", productCreator_HV);
}
void Human_Vendor::onSpeechInput( P_PLAYER pTalker, const QString &comm )
{
if( !pTalker->socket() )
return;
if( m_npc->inRange( pTalker, 4 ) && VendorChkName( m_npc, comm ) )
{
if( comm.contains( tr(" BUY") ) )
{
P_ITEM pContA = m_npc->GetItemOnLayer( cBaseChar::BuyRestockContainer );
P_ITEM pContB = m_npc->GetItemOnLayer( cBaseChar::BuyNoRestockContainer );
m_npc->turnTo( pTalker );
if( !pContA && !pContB )
{
m_npc->talk( tr( "Sorry but i have no goods to sell" ) );
return;
}
m_npc->talk( tr( "Take a look at my wares!" ) );
pTalker->socket()->sendBuyWindow( m_npc );
}
else if( comm.contains( tr(" SELL") ) )
{
P_ITEM pContC = m_npc->GetItemOnLayer( cBaseChar::SellContainer );
m_npc->turnTo( pTalker );
if( !pContC )
{
m_npc->talk( tr( "Sorry, I cannot use any of your wares!" ) );
return;
}
m_npc->talk( tr( "This could be of interest!" ) );
pTalker->socket()->sendSellWindow( m_npc, pTalker );
}
}
}
Human_Stablemaster::Human_Stablemaster( P_NPC npc ) : AbstractAI( npc )
{
notorityOverride_ = 1;
m_actions.append( new Action_Wander( npc, this ) );
m_actions.append( new Action_FleeAttacker( npc, this ) );
}
void Human_Stablemaster::init( P_NPC npc )
{
AbstractAI::init( npc );
}
static AbstractAI* productCreator_HS()
{
return new Human_Stablemaster( NULL );
}
void Human_Stablemaster::registerInFactory()
{
AIFactory::instance()->registerType("Human_Stablemaster", productCreator_HS);
}
void Human_Stablemaster::onSpeechInput( P_PLAYER pTalker, const QString &message )
{
if( !pTalker->socket() )
return;
if( m_npc->inRange( pTalker, 4 ) && ( VendorChkName( m_npc, message ) || message.contains( tr("STABLEMASTER") ) ) )
{
if( message.contains( tr(" STABLE") ) )
{
m_npc->talk( tr("Which pet do you want me to stable?") );
pTalker->socket()->attachTarget( new cStableTarget( m_npc ) );
}
else if( message.contains( tr(" RELEASE") ) )
{
int gold = pTalker->CountBankGold() + pTalker->CountGold();
P_ITEM pPack = m_npc->getBackpack();
cItem::ContainerContent stableitems;
if( pPack )
{
cItem::ContainerContent content = pPack->content();
cItem::ContainerContent::const_iterator it( content.begin() );
while( it != content.end() )
{
if( !(*it)->hasTag( "player" ) || !(*it)->hasTag( "pet" ) )
continue;
if( (*it) && (*it)->id() == 0x1ea7 && (*it)->getTag( "player" ).toInt() == pTalker->serial() )
stableitems.push_back( (*it) );
++it;
}
}
if( !stableitems.empty() )
{
cItem::ContainerContent::const_iterator it( stableitems.begin() );
while( it != stableitems.end() )
{
if( (*it) )
{
P_NPC pPet = dynamic_cast<P_NPC>(World::instance()->findChar( (*it)->getTag( "pet" ).toInt() ));
if( pPet )
{
pPet->free = false;
pPet->moveTo( m_npc->pos() );
pPet->resend();
}
(*it)->remove();
}
++it;
}
pPack->update();
m_npc->talk( tr("Here's your pet back!") );
}
}
}
}
void Human_Stablemaster::refreshStock()
{
}
void Human_Stablemaster::handleTargetInput( P_PLAYER player, cUORxTarget *target )
{
if( !player )
return;
P_ITEM pPack = m_npc->getBackpack();
if( !pPack )
return;
P_NPC pPet = dynamic_cast< P_NPC >(World::instance()->findChar( target->serial() ));
if( !pPet )
{
m_npc->talk( tr("I cannot stable that!" ) );
return;
}
if( pPet->owner() != player )
{
m_npc->talk( tr("This does not belong to you!" ) );
return;
}
// we spawn a worldgem in the stablemasters backpack for the pet
// it does only hold the serial of it, the serial of the owner and the
// number of refresh signals since begin of stabling
// the pet becomes "free", which means, that it isnt in the world
// but will still be saved.
P_ITEM pGem = new cItem();
pGem->Init( false );
pGem->setTag( "player", cVariant( player->serial() ) );
pGem->setTag( "pet", cVariant( pPet->serial() ) );
pGem->setId( 0x1ea7 );
pGem->setName( tr("petitem: %1").arg(pPet->name()) );
pGem->setVisible( 2 ); // gm visible
pPack->addItem( pGem );
pGem->update();
pPet->free = true;
MapObjects::instance()->remove( pPet );
pPet->removeFromView();
m_npc->talk( tr("Say release to get your pet back!") );
}
static AbstractAI* productCreator_HGC()
{
return new Human_Guard_Called( NULL );
}
void Human_Guard_Called::registerInFactory()
{
AIFactory::instance()->registerType("Human_Guard_Called", productCreator_HGC);
}
Human_Guard_Called::Human_Guard_Called( P_NPC npc ) : AbstractAI( npc )
{
notorityOverride_ = 1;
m_actions.append( new Human_Guard_Called_Fight( npc, this ) );
m_actions.append( new Human_Guard_Called_TeleToTarget( npc, this ) );
m_actions.append( new Human_Guard_Called_Disappear( npc, this ) );
}
void Human_Guard_Called::init( P_NPC npc )
{
npc->setSummonTime( uiCurrentTime + MY_CLOCKS_PER_SEC * SrvParams->guardDispelTime() );
AbstractAI::init( npc );
}
void Human_Guard_Called_Fight::execute()
{
// talk only in about every 10th check
switch( RandomNum( 0, 20 ) )
{
case 0: m_npc->talk( tr( "Thou shalt regret thine actions, swine!" ), -1, 0, true ); break;
case 1: m_npc->talk( tr( "Death to all Evil!" ), -1, 0, true ); break;
}
m_npc->setSummonTime( uiCurrentTime + MY_CLOCKS_PER_SEC * SrvParams->guardDispelTime() );
// Fighting is handled within combat..
}
float Human_Guard_Called_Fight::preCondition()
{
if( m_npc->combatTarget() == INVALID_SERIAL )
return 0.0f;
P_CHAR pTarget = World::instance()->findChar( m_npc->combatTarget() );
if( !pTarget || pTarget->isDead() || pTarget->isInnocent() || pTarget->region() != m_npc->region() )
return 0.0f;
if( pTarget && m_npc->dist( pTarget ) < 2 )
return 1.0f;
else
return 0.0f;
}
float Human_Guard_Called_Fight::postCondition()
{
return 1.0f - preCondition();
}
void Human_Guard_Called_TeleToTarget::execute()
{
m_npc->setSummonTime( uiCurrentTime + MY_CLOCKS_PER_SEC * SrvParams->guardDispelTime() );
// Teleports the guard towards the target
P_CHAR pTarget = World::instance()->findChar( m_npc->combatTarget() );
if( pTarget )
{
m_npc->moveTo( pTarget->pos() );
m_npc->soundEffect( 0x1FE );
m_npc->effect( 0x372A, 0x09, 0x06 );
m_npc->resend( false );
}
}
float Human_Guard_Called_TeleToTarget::preCondition()
{
if( m_npc->combatTarget() == INVALID_SERIAL )
return 0.0f;
P_CHAR pTarget = World::instance()->findChar( m_npc->combatTarget() );
if( !pTarget || pTarget->isDead() || pTarget->isInnocent() || pTarget->region() != m_npc->region() )
return 0.0f;
if( pTarget && m_npc->dist( pTarget ) >= 2 )
return 1.0f;
else
return 0.0f;
}
float Human_Guard_Called_TeleToTarget::postCondition()
{
return 1.0f - preCondition();
}
void Human_Guard_Called_Disappear::execute()
{
// nothing to do
}
float Human_Guard_Called_Disappear::preCondition()
{
if( m_npc->combatTarget() == INVALID_SERIAL )
return 1.0f;
P_CHAR pTarget = World::instance()->findChar( m_npc->combatTarget() );
if( !pTarget || pTarget->isDead() || pTarget->isInnocent() || pTarget->region() != m_npc->region() )
return 1.0f;
return 0.0f;
}
float Human_Guard_Called_Disappear::postCondition()
{
return 1.0f - preCondition();
}
static AbstractAI* productCreator_HG()
{
return new Human_Guard( NULL );
}
void Human_Guard::registerInFactory()
{
AIFactory::instance()->registerType("Human_Guard", productCreator_HG);
}
Human_Guard::Human_Guard( P_NPC npc ) : AbstractAI( npc ), m_currentVictim( NULL )
{
notorityOverride_ = 1;
m_actions.append( new Human_Guard_Wander( npc, this ) );
m_actions.append( new Human_Guard_MoveToTarget( npc, this ) );
m_actions.append( new Human_Guard_Fight( npc, this ) );
}
void Human_Guard::check()
{
selectVictim();
AbstractAI::check();
}
void Human_Guard::selectVictim()
{
if( m_currentVictim )
{
// Check if the current target is valid, including:
// - Target not dead.
// - Target in attack range.
// - Target not innocent.
if( m_currentVictim->isDead() || m_currentVictim->isInnocent() )
m_currentVictim = NULL;
else if( !m_npc->inRange( m_currentVictim, SrvParams->attack_distance() ) )
m_currentVictim = NULL;
}
if( !m_currentVictim )
{
// Get a criminal or murderer in range to attack it
RegionIterator4Chars ri( m_npc->pos(), VISRANGE );
for( ri.Begin(); !ri.atEnd(); ri++ )
{
P_CHAR pChar = ri.GetData();
if( pChar && !pChar->free && pChar != m_npc && !pChar->isInvulnerable() && !pChar->isHidden() && !pChar->isInvisible() && !pChar->isDead() && !pChar->isInnocent() )
{
P_PLAYER pPlayer = dynamic_cast<P_PLAYER>(pChar);
if( pPlayer && pPlayer->isGMorCounselor() )
continue;
m_currentVictim = pChar;
break;
}
}
// If we found a new target, let us attack it
if( m_currentVictim )
m_npc->fight( m_currentVictim );
}
}
void Human_Guard_Fight::execute()
{
// talk only in about every 10th check
switch( RandomNum( 0, 20 ) )
{
case 0: m_npc->talk( tr( "Thou shalt regret thine actions, swine!" ), -1, 0, true ); break;
case 1: m_npc->talk( tr( "Death to all Evil!" ), -1, 0, true ); break;
}
if( !m_npc->isAtWar() )
m_npc->toggleCombat();
// Fighting is handled within combat..
}
float Human_Guard_Fight::preCondition()
{
Human_Guard* pAI = dynamic_cast< Human_Guard* >(m_ai);
P_CHAR pTarget = ( pAI ? pAI->currentVictim() : NULL );
if( !pTarget || pTarget->isDead() || pTarget->isInnocent() )
return 0.0f;
if( pTarget && m_npc->dist( pTarget ) < 2 )
return 1.0f;
else
return 0.0f;
}
float Human_Guard_Fight::postCondition()
{
return 1.0f - preCondition();
}
void Human_Guard_MoveToTarget::execute()
{
if( !m_npc->isAtWar() )
m_npc->toggleCombat();
Human_Guard* pAI = dynamic_cast< Human_Guard* >(m_ai);
P_CHAR pTarget = ( pAI ? pAI->currentVictim() : NULL );
if( !pTarget )
return;
if( SrvParams->pathfind4Combat() )
movePath( pTarget->pos() );
else
moveTo( pTarget->pos() );
}
float Human_Guard_MoveToTarget::preCondition()
{
Human_Guard* pAI = dynamic_cast< Human_Guard* >(m_ai);
P_CHAR pTarget = ( pAI ? pAI->currentVictim() : NULL );
if( !pTarget || pTarget->isDead() || pTarget->isInnocent() )
return 0.0f;
if( pTarget && m_npc->dist( pTarget ) >= 2 )
return 1.0f;
else
return 0.0f;
}
float Human_Guard_MoveToTarget::postCondition()
{
return 1.0f - preCondition();
}
float Human_Guard_Wander::preCondition()
{
Human_Guard* pAI = dynamic_cast< Human_Guard* >(m_ai);
P_CHAR pTarget = ( pAI ? pAI->currentVictim() : NULL );
if( !pTarget || pTarget->isDead() || pTarget->isInnocent() )
return 1.0f;
return 0.0f;
}
float Human_Guard_Wander::postCondition()
{
return 1.0f - preCondition();
}
--- NEW FILE: ai_monsters.cpp ---
//==================================================================================
//
// Wolfpack Emu (WP)
// UO Server Emulation Program
//
// Copyright 2001-2003 by holders identified in authors.txt
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Palace - Suite 330, Boston, MA 02111-1307, USA.
//
// * In addition to that license, if you are running this program or modified
// * versions of it on a public system you HAVE TO make the complete source of
// * the version used by you available or provide people with a location to
// * download it.
//
//
//
// Wolfpack Homepage: http://wpdev.sf.net/
//==================================================================================
#include "ai.h"
#include "npc.h"
#include "factory.h"
#include "sectors.h"
#include "player.h"
#include "srvparams.h"
#include "globals.h"
#include "basics.h"
#include "itemid.h"
#include "items.h"
// library includes
#include <math.h>
void Monster_Aggressive::check()
{
selectVictim();
AbstractAI::check();
}
static AbstractAI* productCreator_MAL0()
{
return new Monster_Aggressive_L0( NULL );
}
void Monster_Aggressive_L0::registerInFactory()
{
AIFactory::instance()->registerType("Monster_Aggressive_L0", productCreator_MAL0);
}
void Monster_Aggressive_L0::selectVictim()
{
// We must make sure, that the npc has a correct target.
// If he already has one, we must check if it is still correct.
// Else we search a new one.
// If the npc is tamed, it cant attack
if( m_npc->isTamed() )
{
m_currentVictim = NULL;
return;
}
if( m_currentVictim )
{
// Check if the current target is valid, including:
// - Target not dead.
// - Target in attack range.
if( m_currentVictim->isDead() )
m_currentVictim = NULL;
else if( !m_npc->inRange( m_currentVictim, SrvParams->attack_distance() ) )
m_currentVictim = NULL;
}
if( !m_currentVictim )
{
// Get the first best character and attack it
RegionIterator4Chars ri( m_npc->pos(), VISRANGE );
for( ri.Begin(); !ri.atEnd(); ri++ )
{
P_CHAR pChar = ri.GetData();
if( pChar && !pChar->free && pChar != m_npc && !pChar->isInvulnerable() && !pChar->isHidden() && !pChar->isInvisible() && !pChar->isDead() )
{
P_PLAYER pPlayer = dynamic_cast<P_PLAYER>(pChar);
if( pPlayer && pPlayer->isGMorCounselor() )
continue;
m_currentVictim = pChar;
break;
}
}
// If we found a new target, let us attack it
if( m_currentVictim )
m_npc->fight( m_currentVictim );
}
}
static AbstractAI* productCreator_MAL1()
{
return new Monster_Aggressive_L1( NULL );
}
void Monster_Aggressive_L1::registerInFactory()
{
AIFactory::instance()->registerType("Monster_Aggressive_L1", productCreator_MAL1);
}
void Monster_Aggressive_L1::selectVictim()
{
// We must make sure, that the npc has a correct target.
// If he already has one, we must check if it is still correct.
// Else we search a new one.
// If the npc is tamed, it cant attack
if( m_npc->isTamed() )
{
m_currentVictim = NULL;
return;
}
if( m_currentVictim )
{
// Check if the current target is valid, including:
// - Target not dead.
// - Target in attack range.
if( m_currentVictim->isDead() )
m_currentVictim = NULL;
else if( !m_npc->inRange( m_currentVictim, SrvParams->attack_distance() ) )
m_currentVictim = NULL;
}
int currentPriority = -0x00DDDDDD;
P_CHAR newVictim = m_currentVictim;
// we divide the negative priority of the current victim by two,
// so it cant be overwritten that easy.
// This shall reduce "target hopping".
if( m_currentVictim )
currentPriority = (0 - m_currentVictim->dist( m_npc ) - m_currentVictim->hitpoints() - m_currentVictim->calcDefense(ALLBODYPARTS)) / 2;
// Get the attackable char which has the highest priority.
// The priority is calculated by *distance* and *strength*.
RegionIterator4Chars ri( m_npc->pos(), VISRANGE );
for( ri.Begin(); !ri.atEnd(); ri++ )
{
P_CHAR pChar = ri.GetData();
if( pChar && !pChar->free && pChar != m_npc && !pChar->isInvulnerable() && !pChar->isHidden() && !pChar->isInvisible() && !pChar->isDead() )
{
P_PLAYER pPlayer = dynamic_cast<P_PLAYER>(pChar);
int priority = 0 - pChar->dist( m_npc ) - pChar->hitpoints() - pChar->calcDefense( ALLBODYPARTS );
if( priority > currentPriority )
{
newVictim = pChar;
currentPriority = priority;
}
}
}
// If we found a new target, let us attack it
if( (!m_currentVictim && newVictim) || (m_currentVictim && m_currentVictim != newVictim) )
{
m_currentVictim = newVictim;
m_npc->fight( newVictim );
}
}
float Monster_Aggr_Wander::preCondition()
{
/*
* Wandering has the following preconditions:
* - Same as Action_Wander
* - No target has been set.
*/
Monster_Aggressive* pAI = dynamic_cast< Monster_Aggressive* >( m_ai );
if( pAI && pAI->currentVictim() )
return 0.0f;
return Action_Wander::preCondition();
}
float Monster_Aggr_MoveToTarget::preCondition()
{
/*
* Moving to the target has the following preconditions:
* - A target has been set.
* - The NPC is not in combat range.
*
* Here we take the fuzzy logic into account.
* If the npc is injured, the chance of fighting will decrease.
*/
Monster_Aggressive* pAI = dynamic_cast< Monster_Aggressive* >( m_ai );
if( !pAI || !pAI->currentVictim() )
return 0.0f;
UINT8 range = 1;
if( m_npc->rightHandItem() && IsBowType( m_npc->rightHandItem()->id() ) )
range = ARCHERY_RANGE;
if( m_npc->inRange( pAI->currentVictim(), range ) )
return 0.0f;
float healthmod = (float)m_npc->hitpoints() / ((float)m_npc->criticalHealth()/100.0f * (float)m_npc->maxHitpoints());
return healthmod;
}
float Monster_Aggr_MoveToTarget::postCondition()
{
/*
* Moving to the target has the following postconditions:
* - The target is not set anymore.
* - The NPC is within fight range.
* - The NPC is not injured above the critical line.
*/
Monster_Aggressive* pAI = dynamic_cast< Monster_Aggressive* >( m_ai );
if( !pAI || !pAI->currentVictim() )
return 1.0f;
UINT8 range = 1;
if( m_npc->rightHandItem() && IsBowType( m_npc->rightHandItem()->id() ) )
range = ARCHERY_RANGE;
if( m_npc->inRange( pAI->currentVictim(), range ) )
return 1.0f;
float healthmod = (float)(m_npc->maxHitpoints() - m_npc->hitpoints()) /
(float)(m_npc->maxHitpoints() - ((float)m_npc->criticalHealth()/100.0f * (float)m_npc->maxHitpoints()));
return healthmod;
}
void Monster_Aggr_MoveToTarget::execute()
{
if( !m_npc->isAtWar() )
m_npc->toggleCombat();
Monster_Aggressive* pAI = dynamic_cast< Monster_Aggressive* >( m_ai );
if( !pAI || !pAI->currentVictim() )
return;
if( SrvParams->pathfind4Combat() )
movePath( pAI->currentVictim()->pos() );
else
moveTo( pAI->currentVictim()->pos() );
}
float Monster_Aggr_Fight::preCondition()
{
/*
* Fighting the target has the following preconditions:
* - A target has been set.
* - The target is not dead.
* - The NPC is in combat range.
*
* Here we take the fuzzy logic into account.
* If the npc is injured, the chance of fighting will decrease.
*/
Monster_Aggressive* pAI = dynamic_cast< Monster_Aggressive* >( m_ai );
if( !pAI || !pAI->currentVictim() || pAI->currentVictim()->isDead() )
return 0.0f;
UINT8 range = 1;
if( m_npc->rightHandItem() && IsBowType( m_npc->rightHandItem()->id() ) )
range = ARCHERY_RANGE;
if( !m_npc->inRange( pAI->currentVictim(), range ) )
return 0.0f;
float healthmod = (float)m_npc->hitpoints() / ((float)m_npc->criticalHealth()/100.0f * (float)m_npc->maxHitpoints());
return he...
[truncated message content] |