// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
// Nel Misc
#include "nel/misc/command.h"
#include "nel/misc/path.h"
#include "nel/net/message.h"
#include "nel/net/unified_network.h"
#include "nel/misc/aabbox.h"
//#include "combat_interface.h"
// Game share
#include "game_share/tick_event_handler.h"
#include "game_share/synchronised_message.h"
//#include "game_share/msg_brick_service.h"
// Local includes
#include "actor.h"
#include "actor_manager.h"
#include "move_manager.h"
#include "sheets.h"
#include "mirrors.h"
#include "actor_group.h"
using namespace NLMISC;
using namespace NLNET;
using namespace NLPACS;
using namespace std;
namespace AGS_TEST
{
#define DT_TEMP 200
//----------------------------------------------------------------------------------
CActor::CActor(const std::string &type, const std::string &name, const CEntityId &id, bool &success)
{
_sheetId=type;
// if (CSheets::lookup(_sheetId)->Name.empty())
_visualName=name;
// else
// _visualName=CSheets::lookup(_sheetId)->Name;
_name=name;
_id=id;
_activity=SQUARE; //NOTHING;
_DefaultActivity=UNKNOWN;
_target=0;
//_positionChanged=false;
if ( ! CMirrors::Mirror.createAndDeclareEntity(_id) )
{
success = false;
return;
}
_MoveContainer = NULL;
_MovePrimitive = NULL;
_Continent = 0xFF;
// _mode is setup outside the constructor :o(
_x.init( TheDataset, id, "X" );
_y.init( TheDataset, id, "Y" );
_z.init( TheDataset, id, "Z" );
_angle.init( TheDataset, id, "Theta" );
_vpa.init( TheDataset, id, "VisualPropertyA" );
_whoseesme.init( TheDataset, id, "WhoSeesMe" );
_behaviour.init( TheDataset, id, "Behaviour" );
_mode.init( TheDataset, id, "Mode" );
_AttackDistance = 5.0f;
_PrimZone = NULL;
SAltLookProp vpa;
vpa.Element.Seed = 1;
_vpa = vpa.Summary;
_whoseesme = (sint64)(-1);
_Group = NULL;
_ChatSet = -1;
_RespawnCounter = -1;
_AlreadyAdded = false;
_CyclesState = STATE_WANDER;
_Timer.set( DT_TEMP );
success = true;
}
//
CActor::~CActor()
{
CMirrors::Mirror.removeEntity(_id);
}
void CActor::removeRefs(CActor *actor)
{
if (this==0) return;
if (_target==actor)
_target=0;
}
//----------------------------------------------------------------------------------
// set the actor's current group
void CActor::setGroup(CActorGroup *group)
{
if (this==0) return;
if (_Group != NULL)
{
_Group->removeActor(this);
}
if (group != NULL)
{
group->addActor(this);
}
_Group = group;
}
//----------------------------------------------------------------------------------
// displaying the actor's current state
void CActor::display(NLMISC::CLog *log)
{
if (this==0) return;
const char *angleNames[]= {"E", "ENE", "NE", "NNE", "N","NNW","NW","WNW","W","WSW","SW","SSW","S","SSE","SE","ESE"};
log->displayNL("Actor id=%s sheetId='%s'(%08x) name='%s'",
_id.toString().c_str(),
_sheetId.toString().c_str(),
_sheetId.asInt(),
_name.c_str() );
log->displayNL("\tpos=(%d,%d,%d) Orientation=%s(%d)",
_x(),_y(),_z(),
angleNames[unsigned((_angle()<0.0?-0.5:0.5)+_angle()*8.0/3.14159265359)&15],
int(_angle()*180.0/3.14159265359));
log->displayNL("\t attackDist=%f dialogue=%d activity=%s Magnet(%.1f,%.1f,%.1f) MagnetRange=%.1f MagnetDecay=%.1f",
_AttackDistance,
_ChatSet,
getActivityName(),
_Magnet.x,_Magnet.y,_Magnet.z,
_MagnetDistance,
_MagnetDecayDistance);
}
//----------------------------------------------------------------------------------
// script management
void CActor::setActivity(EActivity activity)
{
if ( this == 0 )
return;
if ( _DefaultActivity == CActor::UNKNOWN )
setDefaultActivity( activity );
switch( activity )
{
case NOTHING:
doNothing();
break;
case WANDER:
doWander();
break;
case SQUARE:
doSquare();
break;
case FAUNA_EAT:
doEat();
break;
case FAUNA_SLEEP:
doSleep();
break;
case FIGHT:
nlwarning("CActor::setActivity(): can't set activty to FIGHT");
break;
case ATTACK:
doAttack();
break;
case FLEE:
doFlee();
break;
default:
nlwarning("CActor::setActivity(): unknown activity");
}
}
void CActor::doNothing()
{
if ( this == 0 )
return;
if ( _activity == FIGHT )
stopFight();
_activity = NOTHING;
}
void CActor::doSquare()
{
if ( this == 0 )
return;
if ( _activity == FIGHT )
stopFight();
_activity = SQUARE;
}
void CActor::doWander()
{
if ( this == 0 )
return;
if ( _activity == FIGHT )
stopFight();
_activity = WANDER;
}
void CActor::doEat()
{
if ( this == 0 )
return;
if ( _activity == FIGHT )
stopFight();
_activity = FAUNA_EAT;
}
void CActor::doSleep()
{
if ( this == 0 )
return;
if ( _activity == FIGHT )
stopFight();
_activity = FAUNA_SLEEP;
}
void CActor::doFight(CActorGroup *target)
{
if ( this == 0 )
return;
if ( target->actorCount() == 0 )
return;
unsigned i,j=0;
double mindist= calcDist(*(*target)[0]);
for (i=1;iactorCount();i++)
if( calcDist(*(*target)[i]) < mindist)
{
mindist= calcDist(*(*target)[i]);
j=i;
}
doFight((*target)[j]);
}
void CActor::doFight(CActor *target)
{
if ( this == 0 || target == 0 )
return;
doFight( target->_id );
}
void CActor::doFight(const NLMISC::CEntityId &id)
{
if ( this == 0 )
return;
// if (_activity==FIGHT) stopFight();
// turn to face the target
_activity=FIGHT;
_target=CActorManager::getActor(id); // returns 0 if the target not managed here
nlassert(false);
// send the message to the rules service
// CCombatInterface::attack(_id,id,0,true);
// make sure the target stands still
if (_target!=0 && !_target->fighting())
_target->doAttacked(this);
}
void CActor::doAttack()
{
if ( this == 0 )
return;
/*
if ( _activity == FIGHT )
stopFight();
*/
_activity = ATTACK;
}
void CActor::doFlee()
{
if ( this == 0 )
return;
if ( _activity == FIGHT )
stopFight();
_activity = FLEE;
}
void CActor::doAttacked(CActor *target)
{
if ( this == 0 )
return;
if (_activity!=FIGHT)
doNothing();
}
void CActor::stopFight()
{
if ( this == 0 )
return;
if (_activity!=FIGHT)
return;
_activity=NOTHING;
nlassert(false);
// send a 'engage in combat' message to the brick service
// CCombatInterface::disengage(_id);
}
bool CActor::update()
{
if (this==0)
return false;
// generate a position in meters (floating point)
CVectorD pos(_x*0.001, _y*0.001, 0.0);
checkRespawn();
checkTimers();
switch (_activity)
{
case NOTHING:
break;
case FAUNA_WAIT:
if ( checkAttack( pos ) )
break;
if ( _CyclesState == STATE_WANDER )
{
setActivity( FAUNA_WANDER );
break;
}
else if ( _CyclesState == STATE_VERY_TIRED || _CyclesState == STATE_EXHAUSTED )
{
setActivity( FAUNA_SLEEP );
break;
}
else if ( _CyclesState == STATE_VERY_HUNGRY || _CyclesState == STATE_STARVING )
{
setActivity( FAUNA_EAT );
break;
}
break;
case SQUARE:
if ( checkAttack( pos ) )
break;
processSquare();
break;
case FIGHT:
processFight();
break;
case WANDER:
if ( ! computeMove( pos ) )
return false;
break;
case FAUNA_WANDER:
if ( checkAttack( pos ) )
break;
if ( ! computeMove( pos ) )
return false;
if ( _CyclesState == STATE_VERY_TIRED || _CyclesState == STATE_EXHAUSTED )
{
setActivity( FAUNA_SLEEP );
break;
}
else if ( _CyclesState == STATE_VERY_HUNGRY || _CyclesState == STATE_STARVING )
{
setActivity( FAUNA_EAT );
break;
}
break;
case FAUNA_SLEEP:
if ( _CyclesState == STATE_SHAKING )
{
setActivity( FAUNA_WAIT );
break;
}
processSleep();
break;
case FAUNA_EAT:
if ( checkAttack( pos ) )
break;
if ( _CyclesState == STATE_DIGESTING )
{
setActivity( FAUNA_WAIT );
break;
}
processEat();
break;
}
return true;
}
//----------------------------------------------------------------------------------
// code requiring network comms
// connections to the GPMS
void CActor::addToOtherServices()
{
if (this==0) return;
/*
if (!CMirrors::exists(_id))
CMirrors::Mirror.createAndDeclareEntity( _id );
*/
// add the actor name to the IOS
{
CMessage msgout("CHARACTER_NAME");
TDataSetRow raw = TheDataset.getDataSetRow( _id );
msgout.serial( raw );
ucstring uname( _visualName );
msgout.serial( uname );
sendMessageViaMirror( "IOS", msgout );
}
_AlreadyAdded = true;
}
void CActor::removeFromOtherServices()
{
if (this==0) return;
CMirrors::Mirror.removeEntity( _id );
_AlreadyAdded = false;
}
// pos management
void CActor::initMovePrimitive(sint32 x, sint32 y, sint32 z)
{
if (this==0) return;
// added by ben
sint continent = CMoveManager::Continents.findContinent(CVectorD(x*0.001, y*0.001, z*0.001));
if (continent == -1)
{
nlwarning("Unable to spawn %s in any continent! Position (%.1f,%.1f) not valid!", _id.toString().c_str(), x*0.001, y*0.001);
continent = 0;
}
// if a primitive already exists
if (_MovePrimitive != NULL)
{
// previous move container must not be null
nlassert(_Continent != 0xFF);
// check the previous move container is different (otherwise does not create a primitive)
if (continent != _Continent)
{
// depending on the type of the previous primitive, creates a copy of it
UMovePrimitive *primitive = CMoveManager::Continents.getMoveContainer(continent)->addNonCollisionablePrimitive(_MovePrimitive);
nlassert(primitive != NULL);
// removes previous primitive
CMoveManager::Continents.getMoveContainer(_Continent)->removePrimitive(_MovePrimitive);
// and set new one as entity primitive
_MovePrimitive = primitive;
_Continent = continent;
_MoveContainer = CMoveManager::Continents.getMoveContainer(_Continent);
}
}
else
{
const CSheets::CSheet *sheet = CSheets::lookup(_sheetId);
float primRadius = 0.5f;
float primHeight = 2.0f;
if (sheet != NULL)
{
primRadius = sheet->Radius;
primHeight = sheet->Height;
}
_Continent = continent;
_MoveContainer = CMoveManager::Continents.getMoveContainer(_Continent);
// init pacs primitive
_MovePrimitive = _MoveContainer->addNonCollisionablePrimitive();
nlassert( _MovePrimitive != 0 );
_MovePrimitive->UserData = (uint64) this;
_MovePrimitive->setPrimitiveType( UMovePrimitive::_2DOrientedCylinder );
_MovePrimitive->setReactionType( UMovePrimitive::Slide );
_MovePrimitive->setTriggerType( UMovePrimitive::NotATrigger );
_MovePrimitive->setCollisionMask( 0x00000000 );
_MovePrimitive->setOcclusionMask( 0xffffffff );
_MovePrimitive->setObstacle( false );
_MovePrimitive->setAbsorbtion( 0 );
_MovePrimitive->setHeight( primHeight );
_MovePrimitive->setRadius( primRadius );
// insert actor in grid as an obstacle
CObstacle obstacle;
obstacle.Id = _id;
obstacle.Position = CVector(x*0.001f, y*0.001f, 0.0f);
obstacle.Radius = primRadius;
_GridIterator = CMoveManager::Grid.insert(obstacle, obstacle.Position);
}
setPos(x, y, z);
_Magnet = CVector(x*0.001f, y*0.001f, 0.0f);
_MagnetDistance = 50.0f;
_MagnetDecayDistance = 10.0f;
}
// simple pos setup, pacs jump to position
void CActor::setPos(sint32 x, sint32 y, sint32 z)
{
if (this==0) return;
if (_MovePrimitive == NULL || _MoveContainer == NULL)
initMovePrimitive(x, y, z);
CVectorD pos(x*0.001, y*0.001, z*0.001);
_MovePrimitive->setGlobalPosition( pos, 0 );
_MovePrimitive->setOrientation( _angle , 0 );
_MoveContainer->evalCollision(1, 0); // world image 0 for all
pos = _MovePrimitive->getFinalPosition( 0 );
(*_GridIterator).Position = CVector(pos);
CMoveManager::Grid.move(_GridIterator, (*_GridIterator).Position);
_x = (sint32)(1000*pos.x);
_y = (sint32)(1000*pos.y);
_z = (sint32)(1000*pos.z);
//_positionChanged=true;
}
// some complex movement - involving GPMS update
void CActor::moveBy(sint32 x, sint32 y, sint32 z)
{
if (this==0) return;
if ((x|y|z)==0) return;
// modif by ben
//setPos(_x+x, _y+y, _z+z);
//_positionChanged=true;
if (_MovePrimitive == NULL || _MoveContainer == NULL)
{
nlwarning("Can't moveBy(%d,%d,%d) actor %s, _MovePrimitive or _MoveContainer not set", x, y, z, _id.toString().c_str());
return;
}
_MovePrimitive->move(CVectorD(x*0.001, y*0.001, 0.0), 0);
_MoveContainer->evalNCPrimitiveCollision(1.0, _MovePrimitive, 0);
CVectorD pos = _MovePrimitive->getFinalPosition( 0 );
(*_GridIterator).Position = CVector(pos);
CMoveManager::Grid.move(_GridIterator, (*_GridIterator).Position);
_x = (sint32)(1000*pos.x);
_y = (sint32)(1000*pos.y);
_z = (sint32)(1000*pos.z);
//_positionChanged=true;
}
/*
void CActor::addPositionChangesToMessage(CMessage &msg)
{
if (this==0) return;
if (!_positionChanged) return;
msg.serial( _id );
msg.serial( _x );
msg.serial( _y );
msg.serial( _z );
msg.serial( _angle );
NLMISC::TGameCycle tick = CTickEventHandler::getGameCycle()-1;
msg.serial( tick );
_positionChanged=false;
}
*/
void CActor::teleportTo(sint32 x, sint32 y, sint32 z)
{
if (this==0) return;
setPos(x, y, z);
/*
// added/modified by ben
initMovePrimitive(x, y, z);
// displaced by ben
CMessage msgout("ENTITY_TELEPORTATION");
msgout.serial( _id );
msgout.serial( _x );
msgout.serial( _y );
msgout.serial( _z );
msgout.serial( _angle );
NLMISC::TGameCycle tick = CTickEventHandler::getGameCycle()-1;
msgout.serial( tick );
sendMessageViaMirror( "GPMS", msgout );
//_positionChanged=false;
*/
}
//----------------------------------------------------------------------------------
// setting the actor behaviour
void CActor::setBehaviour(std::string behaviourName)
{
if (this==0) return;
if (behaviourName.empty()) return;
/*
MBEHAV::EBehaviour behaviour=MBEHAV::stringToBehaviour(behaviourName);
CMessage msgout("SET_BEHAVIOUR");
msgout.serial( _id );
MBEHAV::CBehaviour b = behaviour;
msgout.serial( b );
sendMessageViaMirror( "EGS", msgout );
*/
MBEHAV::EBehaviour behaviour=MBEHAV::stringToBehaviour(behaviourName);
MBEHAV::CBehaviour b = behaviour;
_behaviour = (uint64)b;
}
/*
// setting the actor mode
void CActor::setMode(std::string modeName)
{
if ( this == 0 )
return;
if ( modeName.empty() )
return;
MBEHAV::EMode mode = MBEHAV::stringToMode( modeName );
CMessage msgout("SET_MODE");
msgout.serial( _id );
MBEHAV::EMode b = mode;
msgout.serialEnum( b );
sendMessageViaMirror( "EGS", msgout );
}
void CActor::setMode(MBEHAV::EMode mode)
{
if ( this == 0 )
return;
CMessage msgout("SET_MODE");
msgout.serial( _id );
msgout.serialEnum( mode );
sendMessageViaMirror( "EGS", msgout );
}
*/
//----------------------------------------------------------------------------------
// setting the objects carried in the actor's hands
void CActor::setVPA(const SAltLookProp &vpa)
{
_vpa = vpa.Summary;
}
void CActor::setLeftHand(NLMISC::CSheetId sheetId)
{
if (this==0) return;
}
void CActor::setRightHand(NLMISC::CSheetId sheetId)
{
if (this==0) return;
}
void CActor::checkRespawn()
{
// if actor doesn't exist in mirror and has not yet been added to gpms, spawn in and set as wandering
if (!CMirrors::exists(_id) && !_AlreadyAdded)
{
setPos((sint32)(_Magnet.x*1000), (sint32)(_Magnet.y*1000), (sint32)(_Magnet.z*1000));
addToOtherServices();
//setMode( MBEHAV::NORMAL );
setActivity(_DefaultActivity); // doWander();
}
// if actor is dead and was on gpms previously then:
// 1. initialise counter so that actor will respawn when counter reaches 0
// 2. remove actor from gpms, and lock respawn counter to 0
if (CMirrors::mode(_id) == MBEHAV::DEATH && _AlreadyAdded)
{
if (_RespawnCounter<0)
{
_RespawnCounter = (int)((_MinSpawnDelay + (_MaxSpawnDelay-_MinSpawnDelay) * (float)(((double)rand()-RAND_MAX/2)/(float)(RAND_MAX/2)))*10.0f);
nlinfo("Actor dies %s, will be respawned soon...", _id.toString().c_str());
doNothing();
}
else if (_RespawnCounter == 1)
{
nlinfo("Dead Actor %s removed, will be respawned soon...", _id.toString().c_str());
// remove agent from gpms
removeFromOtherServices();
--_RespawnCounter;
}
else if (_RespawnCounter > 0)
{
--_RespawnCounter;
}
}
else
{
_RespawnCounter = -1;
}
}
bool CActor::processSquare()
{
/* if (_mode().Mode!=MBEHAV::NORMAL)
return false;
*/
sint32 speed=sint32(100.0*CSheets::lookup(_sheetId)->WalkSpeed);
sint32 key= (sint32)CTickEventHandler::getGameCycle()-1;
key>>=5;
// key+=(sint32)_sid.Id; // this line makes the actors move out of sync with eachother
moveBy((key&1)*((key&2)-1)*speed,(1-(key&1))*((key&2)-1)*speed,0); // this is a grid
//sint32 keyx=key>>4;
//sint32 keyy=keyx+2;
//moveBy( (keyx&4?speed:-speed)*(keyx&3==3?0:1), (keyy&4?speed:-speed)*(keyy&3==3?0:1), 0 ); // this is an octagon
_angle = (float)::atan2((1-(key&1))*((key&2)-1)*speed, (key&1)*((key&2)-1)*speed);
return true;
}
bool CActor::processFight()
{
/* if (_mode().Mode!=MBEHAV::COMBAT)
return false;
{
sint32 key= (sint32)CTickEventHandler::getGameCycle()-1;
key%=200;
if (!key && _target)
{
nlassert(false);
// CCombatInterface::attack(_id,_target->_id,1,false);
}
// if we have position info for the target then setup orientation info
if (_target)
{
CMoveManager::TObstacleMap::iterator it;
it = CMoveManager::ObstacleMap.find(_target->_id);
if (it != CMoveManager::ObstacleMap.end())
{
NLMISC::CVectorD pos(float(_x)/1000.0f,float(_y)/1000.0f,float(_z)/1000.0f);
pos -= (*((*it).second)).Position;
setAngle((float)atan2(pos.y,pos.x));
}
}
}
*/
//todo:
// check hitpoints in georges
// check regen rate in george (should be: rate=1000 value=0)
// subscribe to hitpoint mirror (sint16)
// add group management to command to engage in combat
// if target dead go to idle
// from time to time insert a 'coup en force' attack
// CBSExecuteMsg msg;
// msg.PlayerId=_sid;
// msg.TargetId=_target;
// msg.Bricks.push_back(CSheetId("coup_en_force.brick_base"));
// msg.send("BS");
return true;
}
bool CActor::processEat()
{
/*
if ( getMode() != MBEHAV::EAT )
setMode( MBEHAV::EAT );
*/ return true;
}
bool CActor::processSleep()
{
/* if ( getMode() != MBEHAV::REST )
setMode( MBEHAV::REST );
*/ return true;
}
bool CActor::checkAttack( CVectorD &pos )
{
/*
if we're close to an actor then attack them
*/
CAABBox box;
box.setCenter(CVector(_x*0.001f, _y*0.001f, 0.0f));
box.setHalfSize(CVector(5.0f, 5.0f, 1.0f));
CMoveManager::Grid.clearSelection();
CMoveManager::Grid.select(box);
CMoveManager::TObstacleGrid::CIterator it;
for (it=CMoveManager::Grid.begin(); it!=CMoveManager::Grid.end(); ++it)
{
CObstacle &other = (*it);
// attacks player if close to me
CVector d = pos-other.Position;
d.z = 0.0f;
if (other.Id.getType() == RYZOMID::player && d.norm() <= _AttackDistance)
{
nlinfo("Actor <%s> attacks player because _AttackDistance = %f and separation = %f", _name.c_str(), _AttackDistance, d.norm());
doFight(other.Id);
return true;
}
}
return false;
}
// State machine defining transitions between cycle states
void CActor::updateCycles()
{
_CyclesState = (ECycleState) ( ( (int)_CyclesState ) + 1 );
if ( _CyclesState == STATE_LAST )
_CyclesState = STATE_WANDER;
}
/*
void CActor::updateCycles()
{
switch( _CyclesState )
{
case STATE_WANDER:
_CyclesState = STATE_HUNGRY;
setActivity( WANDER );
break;
case STATE_HUNGRY:
_CyclesState = STATE_VERY_HUNGRY;
break;
case STATE_VERY_HUNGRY:
_CyclesState = STATE_STARVING;
break;
case STATE_STARVING:
_CyclesState = STATE_EATING;
break;
case STATE_EATING:
{
_CyclesState = STATE_HUNGRY;
setActivity( EAT );
}
break;
case STATE_DIGESTING:
_CyclesState = STATE_TIRED;
setActivity( WANDER );
break;
case STATE_TIRED:
_CyclesState = STATE_VERY_TIRED;
break;
case STATE_VERY_TIRED:
_CyclesState = STATE_EXHAUSTED;
break;
case STATE_EXHAUSTED:
_CyclesState = STATE_SLEEPING;
break;
case STATE_SLEEPING:
{
_CyclesState = STATE_HUNGRY;
setActivity( SLEEP );
}
break;
case STATE_SHAKING:
{
_CyclesState = STATE_SHAKING;
setActivity( WANDER );
}
break;
}
}
*/
void CActor::checkTimers()
{
if ( _Timer.test() )
{
updateCycles();
_Timer.add( DT_TEMP );
}
}
bool CActor::computeMove( NLMISC::CVectorD &pos)
{
// use the current tick and the entity's id to decide when to move and when to stand still
// the objective is to move in short bursts, standing still between times
sint32 key= (sint32)CTickEventHandler::getGameCycle()-1;
key>>=2;
key>>=3;
key+=(sint32)_id.getShortId(); // this line makes the actors move out of sync with eachother
if (!(key & 3))
{
// we're not moving but we can turn round
sint32 lastkey= (sint32)CTickEventHandler::getGameCycle()-2;
lastkey>>=2;
lastkey+=(sint32)_id.getShortId(); // this line makes the actors move out of sync with eachother
lastkey>>=3;
if (!(lastkey & 1))
_angle = _angle() + (float)(((double)rand()-RAND_MAX/2)/(float)(RAND_MAX/2)*3.14159265359f/2.0f);
return true;
}
// lookup my walk and run speeds and so on from my sheetid
const CSheets::CSheet *sheet = CSheets::lookup(_sheetId);
if (!sheet)
{
// nlwarning("Unknown sheet id for entity %s",_Id.toString().c_str());
return false;
}
float walkSpeed = sheet->WalkSpeed;
float runSpeed = sheet->RunSpeed;
float radius = sheet->Radius;
double dt = (double)(CTickEventHandler::getGameTimeStep());
CVectorD move = CVectorD(0.0, 0.0, 0.0);
// randomly change direction
key= (sint32)CTickEventHandler::getGameCycle()-1;
key+=(sint32)_id.getShortId();
if ((key & 3) == 3)
{
// _angle += (float)((3.14159265359f/8.0f)*sin((2.0f*3.14159265359f/360.0f)*(float)((CTickEventHandler::getGameCycle()+7*_id.getShortId())%360)));
_angle = _angle() + (float)(((double)rand()-RAND_MAX/2)/(RAND_MAX/2)*5.7*dt);
while (_angle() > +3.14159265359f) _angle = _angle() - 2.0f*3.14159265359f;
while (_angle() < -3.14159265359f) _angle = _angle() + 2.0f*3.14159265359f;
}
move.x = walkSpeed*cos(_angle)*dt;
move.y = walkSpeed*sin(_angle)*dt;
// some attraction by the magnet
CVectorD dmagnet = _Magnet-pos;
dmagnet.z = 0.0;
double nmagnet = dmagnet.norm();
if (nmagnet > _MagnetDistance)
{
if (_MagnetDecayDistance<1) _MagnetDecayDistance=1; // big bad anti-bug
move += dmagnet.normed()*walkSpeed*dt*(nmagnet-_MagnetDistance)/_MagnetDecayDistance;
}
// avoid obstacles
CVectorD avoid = CVectorD::Null;
CMoveManager::TObstacleGrid::CIterator it;
for (it=CMoveManager::Grid.begin(); it!=CMoveManager::Grid.end(); ++it)
{
CObstacle &other = (*it);
// don't avoid self
if (other.Id == _id)
continue;
CVectorD d = other.Position-pos;
d.z = 0.0;
double dist = d.norm()+0.001;
const float EntitiesRepulsSpacing = 1.0f;
float EntitiesRepulsDistance = EntitiesRepulsSpacing+radius+other.Radius;
const float EntitiesRepulsStrength = 0.5f;
if (dist < EntitiesRepulsDistance)
{
double coef = 1.0f - (float)(pow(EntitiesRepulsDistance, EntitiesRepulsStrength)/pow(dist, EntitiesRepulsStrength));
avoid += d*(walkSpeed*dt*coef/dist);
}
}
move += avoid;
CVectorD front = move.normed();
CVectorD lateral(front.y, -front.x, 0.0);
CVectorD normal;
// avoid walls
if (!_MoveContainer->testMove(_MovePrimitive, front, 1, 0, &normal))
move += normal*walkSpeed*0.3*dt;
if (!_MoveContainer->testMove(_MovePrimitive, (front+lateral)*0.5, 1, 0, &normal))
move += normal*walkSpeed*0.3*dt;
if (!_MoveContainer->testMove(_MovePrimitive, (front+lateral)*0.5, 1, 0, &normal))
move += normal*walkSpeed*0.3*dt;
// avoid patat borders
if (!testPositionInPatat(pos+front))
{
uint j, n=4;
CVector newPos;
for (j=1; j dt*runSpeed)
move *= dt*runSpeed/movenorm;
//setup the angle field
_angle = (float)::atan2(move.y, move.x);
moveBy((sint32)(move.x*1000.0), (sint32)(move.y*1000.0), 0);
return true;
}
} // end of namespace AGS_TEST
/*
NLMISC::CVectorD posE = e->getExtrapoledPosition();
agressivite = 0.0;
instinct = 0.0;
TCarrieerCombat carriere = getProfile(c);
r = getEnnemiFar(posE, c->getCoordinate());
double fameCoputed = (double)f;
if(fameCoputed >= 0)
return NOCombat;
agressivite = -fameCoputed;
double l = getLevelPoint(c) - getLevelPoint(e->getClass());
agressivite += (l>0.0) ? l*20 : 0.0;
agressivite += getProfileAvantage(carriere, e);
if ( r < 3) agressivite += 20;
instinct += (l<0.0) ? -l*20 : 0.0;
double l100 = getNormedLifePoint(c);
if( l100 < 0.5 )
{
if( l100 < 0.25 )
instinct += 60;
else
instinct += 40;
}
double seuil = agressivite - instinct;
if(seuil >= getSeuilBelliqueux(c))
{
if(r < _REngage) return Attack;
else
{
return Poursuivre;
}
}
else
{
double fuite = getSeuilFuite(c);
if(instinct >= fuite)
return Fuite;
else
return NOCombat;
}
return NOCombat;
*/