From: <ma...@us...> - 2010-04-04 01:20:11
|
Revision: 5763 http://planeshift.svn.sourceforge.net/planeshift/?rev=5763&view=rev Author: magodra Date: 2010-04-04 01:19:59 +0000 (Sun, 04 Apr 2010) Log Message: ----------- - Added doxy comments for call/function/vars for the BehaviorSet class. - Added min and max parameters for behaviors to prevent the need from running away if needed. - Moved operations around to sort then mostly alphabetically to make it easier to find and keeping documentation of use up to date. Modified Paths: -------------- trunk/src/npcclient/npc.cpp trunk/src/npcclient/npcbehave.cpp trunk/src/npcclient/npcbehave.h trunk/src/npcclient/npcoperations.cpp trunk/src/npcclient/npcoperations.h trunk/src/npcclient/perceptions.cpp Modified: trunk/src/npcclient/npc.cpp =================================================================== --- trunk/src/npcclient/npc.cpp 2010-04-03 19:26:22 UTC (rev 5762) +++ trunk/src/npcclient/npc.cpp 2010-04-04 01:19:59 UTC (rev 5763) @@ -269,7 +269,7 @@ { if (last_update && !disabled) { - brain->Advance(when-last_update,this); + brain->Advance(when-last_update, this); } last_update = when; @@ -279,7 +279,7 @@ { if (!disabled) { - brain->ResumeScript(this,which); + brain->ResumeScript(this, which); } } @@ -288,7 +288,7 @@ if (!disabled) { Printf(15,"Got event %s",pcpt->ToString().GetData() ); - brain->FirePerception(this,pcpt); + brain->FirePerception(this, pcpt); } } Modified: trunk/src/npcclient/npcbehave.cpp =================================================================== --- trunk/src/npcclient/npcbehave.cpp 2010-04-03 19:26:22 UTC (rev 5762) +++ trunk/src/npcclient/npcbehave.cpp 2010-04-04 01:19:59 UTC (rev 5763) @@ -259,11 +259,16 @@ } //--------------------------------------------------------------------------- +BehaviorSet::BehaviorSet(EventManager *eventmanager) +{ + active=NULL; + eventmgr = eventmanager; +} void BehaviorSet::ClearState(NPC *npc) { - // Ensure any existing script is ended correctly. - Interrupt(npc); + // Ensure any existing script is ended correctly. + Interrupt(npc); for (size_t i = 0; i<behaviors.GetSize(); i++) { behaviors[i]->ResetNeed(); @@ -273,17 +278,19 @@ active = NULL; } -bool BehaviorSet::Add(Behavior *b) +bool BehaviorSet::Add(Behavior *behavior) { + // Search for dublicates for (size_t i=0; i<behaviors.GetSize(); i++) { - if (!strcmp(behaviors[i]->GetName(),b->GetName())) + if (!strcmp(behaviors[i]->GetName(),behavior->GetName())) { - behaviors[i] = b; // substitute + behaviors[i] = behavior; // substitute return false; } } - behaviors.Push(b); + // Insert as new behavior + behaviors.Push(behavior); return true; } @@ -291,7 +298,7 @@ { while (true) { - max_need = -999; + int max_need = -999; bool behaviours_changed = false; // Go through and update needs based on time @@ -377,7 +384,7 @@ return active; } -void BehaviorSet::ResumeScript(NPC *npc,Behavior *which) +void BehaviorSet::ResumeScript(NPC *npc, Behavior *which) { if (which == active && which->ApplicableToNPCState(npc)) { @@ -437,49 +444,64 @@ Behavior::Behavior() { - loop = false; - is_active = false; - need_decay_rate = 0; - need_growth_rate = 0; - completion_decay = 0; - new_need=-999; - interrupted = false; - resume_after_interrupt = false; - current_step = 0; - init_need = 0; + name = ""; + loop = false; + is_active = false; + is_applicable_when_dead = false; + need_decay_rate = 0; + need_growth_rate = 0; + completion_decay = 0; + init_need = 0; + resume_after_interrupt = false; current_need = init_need; + new_need = -999; + interrupted = false; + current_step = 0; + minLimitValid = false; + minLimit = 0.0; + maxLimitValid = false; + maxLimit = 0.0; } Behavior::Behavior(const char *n) { - loop = false; - is_active = false; - need_decay_rate = 0; - need_growth_rate = 0; - completion_decay = 0; - new_need=-999; - interrupted = false; - resume_after_interrupt = false; - current_step = 0; - name = n; - init_need = 0; + name = n; + loop = false; + is_active = false; + is_applicable_when_dead = false; + need_decay_rate = 0; + need_growth_rate = 0; + completion_decay = 0; + init_need = 0; + resume_after_interrupt = false; current_need = init_need; + new_need =-999; + interrupted = false; + current_step = 0; + minLimitValid = false; + minLimit = 0.0; + maxLimitValid = false; + maxLimit = 0.0; } void Behavior::DeepCopy(Behavior& other) { + name = other.name; loop = other.loop; is_active = other.is_active; + is_applicable_when_dead = other.is_applicable_when_dead; need_decay_rate = other.need_decay_rate; // need lessens while performing behavior need_growth_rate = other.need_growth_rate; // need grows while not performing behavior completion_decay = other.completion_decay; - new_need = -999; - name = other.name; init_need = other.init_need; + resume_after_interrupt = other.resume_after_interrupt; current_need = other.current_need; - last_check = other.last_check; - is_applicable_when_dead = other.is_applicable_when_dead; - resume_after_interrupt = other.resume_after_interrupt; + new_need = -999; + interrupted = false; + minLimitValid = other.minLimitValid; + minLimit = other.minLimit; + maxLimitValid = other.maxLimitValid; + maxLimit = other.maxLimit; for (size_t x=0; x<other.sequence.GetSize(); x++) { @@ -488,7 +510,6 @@ // Instance local variables. No need to copy. current_step = 0; - interrupted = false; } bool Behavior::Load(iDocumentNode *node) @@ -508,6 +529,24 @@ init_need = node->GetAttributeValueAsFloat("initial"); is_applicable_when_dead = node->GetAttributeValueAsBool("when_dead"); resume_after_interrupt = node->GetAttributeValueAsBool("resume",false); + if (node->GetAttributeValue("min")) + { + minLimitValid = true; + minLimit = node->GetAttributeValueAsFloat("min"); + } else + { + minLimitValid = false; + } + if (node->GetAttributeValue("max")) + { + maxLimitValid = true; + maxLimit = node->GetAttributeValueAsFloat("max"); + } else + { + maxLimitValid = false; + } + + current_need = init_need; return LoadScript(node,true); @@ -553,14 +592,14 @@ { op = new DigOperation; } + else if ( strcmp( node->GetValue(), "drop" ) == 0 ) + { + op = new DropOperation; + } else if ( strcmp( node->GetValue(), "eat" ) == 0 ) { op = new EatOperation; } - else if ( strcmp( node->GetValue(), "drop" ) == 0 ) - { - op = new DropOperation; - } else if ( strcmp( node->GetValue(), "equip" ) == 0 ) { op = new EquipOperation; @@ -587,22 +626,18 @@ { op = new MemorizeOperation; } - else if ( strcmp( node->GetValue(), "share_memories" ) == 0 ) - { - op = new ShareMemoriesOperation; - } else if ( strcmp( node->GetValue(), "move" ) == 0 ) { op = new MoveOperation; } + else if ( strcmp( node->GetValue(), "movepath" ) == 0 ) + { + op = new MovePathOperation; + } else if ( strcmp( node->GetValue(), "moveto" ) == 0 ) { op = new MoveToOperation; } - else if ( strcmp( node->GetValue(), "movepath" ) == 0 ) - { - op = new MovePathOperation; - } else if ( strcmp( node->GetValue(), "navigate" ) == 0 ) { op = new NavigateOperation; @@ -631,6 +666,10 @@ { op = new SequenceOperation; } + else if ( strcmp( node->GetValue(), "share_memories" ) == 0 ) + { + op = new ShareMemoriesOperation; + } else if ( strcmp( node->GetValue(), "talk" ) == 0 ) { op = new TalkOperation; @@ -692,8 +731,9 @@ return true; // success } -void Behavior::Advance(csTicks delta,NPC *npc,EventManager *eventmgr) +void Behavior::Advance(csTicks delta, NPC *npc, EventManager *eventmgr) { + // Initialize new_need if not updated before. if (new_need == -999) { new_need = current_need; @@ -703,7 +743,9 @@ if (is_active) { - new_need = new_need - (d * need_decay_rate); + // Apply delta to need, will check for limits as well + ApplyNeedDelta(npc, -d * need_decay_rate ); + if (current_step < sequence.GetSize()) { npc->Printf(10,"%s - Advance active delta: %.3f Need: %.2f Decay Rate: %.2f", @@ -717,12 +759,78 @@ } else { - new_need = new_need + (d * need_growth_rate); + // Apply delta to need, will check for limits as well + ApplyNeedDelta(npc, d * need_growth_rate ); + npc->Printf(10,"%s - Advance none active delta: %.3f Need: %.2f Growth Rate: %.2f", name.GetData(),d,new_need,need_growth_rate); } } + +void Behavior::CommitAdvance() +{ + // Only update the current_need if new_need has been initialized. + if (new_need!=-999) + { + current_need = new_need; + } +} + +void Behavior::ApplyNeedDelta(NPC *npc, float deltaDesire) +{ + // Initialize new_need if not updated before. + if (new_need==-999) + { + new_need = current_need; + } + + // Apply the delta to new_need + new_need += deltaDesire; + + // Handle min desire limit + if (minLimitValid && (new_need < minLimit)) + { + npc->Printf(5,"%s - ApplyNeedDelta limited new_need of %.3f to min value %.3f.", + name.GetData(),new_need,minLimit); + + new_need = minLimit; + } + + // Handle max desire limit + if (maxLimitValid && (new_need > maxLimit)) + { + npc->Printf(5,"%s - ApplyNeedDelta limited new_need of %.3f to max value %.3f.", + name.GetData(),new_need,maxLimit); + + new_need = maxLimit; + } +} + +void Behavior::ApplyNeedAbsolute(NPC *npc, float absoluteDesire) +{ + new_need = absoluteDesire; + + // Handle min desire limit + if (minLimitValid && (new_need < minLimit)) + { + npc->Printf(5,"%s - ApplyNeedAbsolute limited new_need of %.3f to min value %.3f.", + name.GetData(),new_need,minLimit); + + new_need = minLimit; + } + + // Handle max desire limit + if (maxLimitValid && (new_need > maxLimit)) + { + npc->Printf(5,"%s - ApplyNeedAbsolute limited new_need of %.3f to max value %.3f.", + name.GetData(),new_need,maxLimit); + + new_need = maxLimit; + } +} + + bool Behavior::ApplicableToNPCState(NPC *npc) { return npc->IsAlive() || (!npc->IsAlive() && is_applicable_when_dead); @@ -746,12 +854,6 @@ } } -Behavior* BehaviorSet::Find(Behavior *key) -{ - size_t found = behaviors.Find(key); - return (found = SIZET_NOT_FOUND) ? NULL : behaviors[found]; -} - bool Behavior::RunScript(NPC *npc, EventManager *eventmgr, bool interrupted) { size_t start_step = current_step; Modified: trunk/src/npcclient/npcbehave.h =================================================================== --- trunk/src/npcclient/npcbehave.h 2010-04-03 19:26:22 UTC (rev 5762) +++ trunk/src/npcclient/npcbehave.h 2010-04-04 01:19:59 UTC (rev 5763) @@ -55,45 +55,113 @@ class Waypoint; class psResumeScriptEvent; -/** -* This is just a collection of behaviors for a particular -* type of NPC. -*/ +/** This is the set of Behaviors available for an NPC + * + * This is a collection of behaviors for a particular + * type of NPC. They represents the behaviors that + * this NPC is capable of. + * + */ class BehaviorSet { protected: - // BinaryTree<Behavior> behaviors; - csPDelArray<Behavior> behaviors; - Behavior *active; - float max_need; - EventManager *eventmgr; + csPDelArray<Behavior> behaviors; ///< The set of behavoirs for this NPCType. + Behavior* active; ///< Points to the current active behavior. + + EventManager* eventmgr; ///< Cached pointer to the event manger. public: - BehaviorSet(EventManager *eventmanager) { active=NULL; eventmgr = eventmanager; } + /** Constructor + */ + BehaviorSet(EventManager *eventmanager); - /// Returns true if the behavior didn't already exist. Otherwise returns false and removes the existing duplicate behavior. - bool Add(Behavior *b); - Behavior *Find(Behavior *key); - + /** Add a behavior to the brain + * + * Add a new behavior to the set. If a behavior with the same name exists, + * than the new behavior will substitude the existing behavior. + * This to support the nested structure of the behavoir scripts. + * + * @param behavior The new behavior to be added to this BehaviorSet. + * + * @return Returns true if the behavior didn't already exist. Otherwise returns false and substitude the existing duplicate behavior. + */ + bool Add(Behavior *behavior); + + /** Find a behavior in the set. + * + * Search the set for a behavior maching the given name. + */ Behavior *Find(const char *name); + + /** Do a deap copy + * + * Will do a deap copy of the BehaviorSet. The NPCType will contain a complete + * BehaviorSet for each type. When a new NPC is instantiated a deap copy of the + * set will be done before assigning it to the new NPC. + * + * @param other The source to be copied into this set. + */ void DeepCopy(BehaviorSet& other); + + /** Prepare the set after use + * + * Called when the npc actor is deleted. Should reset all session + * depended stats. So that a new instance of the actor for the + * npc can start with a fresh brain. + * + * @param npc The NPC that own this BehaviorSet. + */ void ClearState(NPC *npc); - /// Advances the behaviors and returns the active one. + /** Advances the behaviors and returns the active one. + * + * Will calculate the need and select the active behavior. + * + * @param delta The numbers of ticks to advance this set. + * @param npc The NPC that own this BehaviorSet. + * + * @return The current active behavior after advancing. + */ Behavior* Advance(csTicks delta,NPC *npc); + + /** Interrupt the current active behavior. + * + * If there is an active behavior in this set that + * behavior will be interrupted by calling this function. + * + * @param npc The NPC that own this BehaviorSet. + */ void Interrupt(NPC *npc); - void ResumeScript(NPC *npc,Behavior *which); + + /** Resume the current active behavior. + * + * Resume the given behavior. This is witch is equal to + * the current active behavior and witch is + * applicable to the current npc state. + * + * @param npc The NPC that own this BehaviorSet. + * @param witch The behavior to resume. + */ + void ResumeScript(NPC *npc, Behavior *which); + + /** Return current active behavior + */ Behavior *GetCurrentBehavior() { return active; } + + /** Dump the behavior list for debug. + * + * @param npc The NPC that own this BehaviorSet. + */ void DumpBehaviorList(NPC *npc); }; -/** -* A collection of behaviors and reactions will represent a type of -* npc. Each npc will be assigned one of these types. This lets us -* reuse the same script for many mobs at once--each one keeping its -* own state information. -*/ +/** A collection of behaviors and reactions will represent a type of npc. + * + * Each npc will be assigned one of these types. This lets us + * reuse the same script for many mobs at once--each one keeping its + * own state information. + */ class NPCType { enum VelSource { @@ -104,11 +172,16 @@ }; protected: - csArray<Reaction*> reactions; - BehaviorSet behaviors; - csString name; - float ang_vel,vel; - VelSource velSource; + csString name; ///< The name of this NPC type. + csArray<Reaction*> reactions; ///< The reactions available for this NPCType. + BehaviorSet behaviors; ///< The set of behaviors available for this NPCType. + float ang_vel; ///< Default ang_vel for this NPCType. + ///< Will be used for all behaviors unless overriden + ///< by each behavior. + float vel; ///< Default vel for this NPCType. + ///< Will be used for all behaviors unless overriden + ///< by each behavior. + VelSource velSource; ///< public: NPCType(psNPCClient* npcclient, EventManager* eventmanager); @@ -142,32 +215,40 @@ }; -/** -* A Behavior is a general activity of an NPC. Guarding, -* smithing, talking to a player, fighting, fleeing, eating, -* sleeping are all behaviors I expect to be scripted -* for our npcs. -*/ +/** A set of operations building a generic behavior for a NPC. + * + * A Behavior is a general activity of an NPC. Guarding, + * smithing, talking to a player, fighting, fleeing, eating, + * sleeping are all behaviors. They consists of a set of + * ScriptOperations loaded from file. + * + */ class Behavior { protected: - csArray<ScriptOperation*> sequence; /// Sequence of ScriptOperations. - size_t current_step; /// The ScriptOperation in the sequence - /// that is currently executed. - bool loop,is_active,is_applicable_when_dead; - float need_decay_rate; // need lessens while performing behavior - float need_growth_rate; // need grows while not performing behavior - float completion_decay; // need lessens AFTER behavior script is run once - // Use -1 to remove all need - float init_need; // starting need, also used in ClearState resets - csString name; - bool resume_after_interrupt; // Resume at active step after interrupt. + csString name; ///< The name of this behavior + + csArray<ScriptOperation*> sequence; ///< Sequence of ScriptOperations. + size_t current_step; ///< The ScriptOperation in the sequence that is currently executed. + bool loop; ///< True if this behavior should start over when completed all operations. + bool is_active; + bool is_applicable_when_dead; + float need_decay_rate; ///< need lessens while performing behavior + float need_growth_rate; ///< need grows while not performing behavior + float completion_decay; ///< need lessens AFTER behavior script is complete. Use -1 to remove all need + float init_need; ///< starting need, also used in ClearState resets + bool resume_after_interrupt; ///< Resume at active step after interrupt. - float current_need; - float new_need; - csTicks last_check; - bool interrupted; + float current_need; ///< The current need of this behavior after last advance + float new_need; ///< The accumulated change to the need after last advance + bool interrupted; ///< Set to true if this behavior is interruped by another in a BehaviorSet + + bool minLimitValid; ///< True if a minimum limit for the need for this bahavior has been set + float minLimit; ///< The minimum value to limit the need if minLimitValid has been set true. + bool maxLimitValid; ///< True if a maximum limit for the need for this bahavior has been set + float maxLimit; ///< The maximum value to limit the need if maxLimitValid has been set true. + public: Behavior(); Behavior(const char *n); @@ -183,33 +264,48 @@ bool LoadScript(iDocumentNode *node,bool top_level=true); void Advance(csTicks delta,NPC *npc,EventManager *eventmgr); - float CurrentNeed() - { return current_need; } - float NewNeed() - { return new_need; } - void CommitAdvance() - { - if (new_need!=-999) - { - current_need = new_need; - } - } - void ApplyNeedDelta(float delta_desire) - { - if (new_need==-999) - { - new_need = current_need; - } - new_need += delta_desire; - } - void ApplyNeedAbsolute(float absolute_desire) - { - new_need = absolute_desire; - } - void SetActive(bool flag) - { is_active = flag; } - bool GetActive() - { return is_active; } + float CurrentNeed() { return current_need; } + float NewNeed() { return new_need; } + + /** Update the current_need of the behavour after it has been advanced. + * + * If the new_need has been updating during advance make the new_need the + * current need of this behavior. The current_need will allways be the + * value held right after last advance of the behavior. During advance + * and between advance the new_need will be updated by the advance function + * and reactions based on matched perceptions. + * + */ + void CommitAdvance(); + + /** Update the new need of the behavior with a delta desire. + * + * The need is adjusted by the given delta value. + * + * @param delta_desire The delta in desire for this behavior + * + * \note Will limit the new_need if limits has been given for the + * need of this behavior. + */ + void ApplyNeedDelta(NPC *npc, float deltaDesire); + + + /** Set the new need of the behavior to the given desire. + * + * The need is set to the given desire. Ignoring whatever + * value the new need of the behavoir might have had previous. + * + * @param absolute_desire The absolute desire to be used for this behavior + * + * \note Will limit the new_need if limits has been given for the + * need of this behavior. + */ + void ApplyNeedAbsolute(NPC *npc, float absoluteDesire); + + void SetActive(bool flag) { is_active = flag; } + + bool GetActive(){ return is_active; } + void SetCurrentStep(int step) { current_step = step; } size_t GetCurrentStep(){ return current_step; } void ResetNeed() { current_need = new_need = init_need; } Modified: trunk/src/npcclient/npcoperations.cpp =================================================================== --- trunk/src/npcclient/npcoperations.cpp 2010-04-03 19:26:22 UTC (rev 5762) +++ trunk/src/npcclient/npcoperations.cpp 2010-04-04 01:19:59 UTC (rev 5763) @@ -425,67 +425,352 @@ //--------------------------------------------------------------------------- // Following section contain specefix NPC operations. +// Ordered alphabeticaly //--------------------------------------------------------------------------- -bool MoveOperation::Load(iDocumentNode *node) +//--------------------------------------------------------------------------- + +const char * ChaseOperation::typeStr[]={"nearest","owner","target"}; + +bool ChaseOperation::Load(iDocumentNode *node) { + action = node->GetAttributeValue("anim"); + csString typestr = node->GetAttributeValue("type"); + if (typestr.CompareNoCase("nearest")) + { + type = NEAREST; + } + else if (typestr.CompareNoCase("boss") || typestr.CompareNoCase("owner")) + { + type = OWNER; + } + else if (typestr.CompareNoCase("target")) + { + type = TARGET; + } + else if (!typestr.IsEmpty()) + { + Error2("Loading Chase Operation can't mach type of '%s'",typestr.GetDataSafe()); + return false; + } + else + { + type = NEAREST; + } + + + if (node->GetAttributeValue("range")) + { + searchRange = node->GetAttributeValueAsFloat("range"); + } + else + { + searchRange = 2.0f; + } + + if (node->GetAttributeValue("chase_range")) + { + chaseRange = node->GetAttributeValueAsFloat("chase_range"); + } + else + { + chaseRange = -1.0f; // Disable max chase range + } + + if ( node->GetAttributeValue("offset") ) + { + offset = node->GetAttributeValueAsFloat("offset"); + } + else + offset = 0.5f; + LoadVelocity(node); - action = node->GetAttributeValue("anim"); - duration = node->GetAttributeValueAsFloat("duration"); + LoadCheckMoveOk(node); ang_vel = node->GetAttributeValueAsFloat("ang_vel"); + return true; } -ScriptOperation *MoveOperation::MakeCopy() +ScriptOperation *ChaseOperation::MakeCopy() { - MoveOperation *op = new MoveOperation; + ChaseOperation *op = new ChaseOperation; + op->action = action; + op->type = type; + op->searchRange = searchRange; + op->chaseRange = chaseRange; op->velSource = velSource; op->vel = vel; - op->action = action; - op->duration = duration; - op->ang_vel = ang_vel; - op->angle = angle; + op->ang_vel= ang_vel; + op->offset = offset; + return op; } -bool MoveOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) +bool ChaseOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) { - // Get Going at the right velocity - csVector3 velvec(0,0,-GetVelocity(npc) ); - npc->GetLinMove()->SetVelocity(velvec); - npc->GetLinMove()->SetAngularVelocity(angle<0?-ang_vel:ang_vel); + float targetRot; + iSector* targetSector; + csVector3 targetPos; + + float myRot; + iSector* mySector; + csVector3 myPos; - // SetAction animation for the mesh also, so it looks right - SetAnimation(npc, action); + csVector3 dest; + csString name; + gemNPCObject *entity = NULL; + target_id = EID(0); + + switch (type) + { + case NEAREST: + target_id = npc->GetNearestEntity(dest, name, searchRange); + npc->Printf(6, "Targeting nearest entity (%s) at (%1.2f,%1.2f,%1.2f) for chase ...", + (const char *)name, dest.x, dest.y, dest.z); + if (target_id.IsValid()) + { + entity = npcclient->FindEntityID(target_id); + } + break; + case OWNER: + { + + gemNPCObject * owner = npc->GetOwner(); + if (owner) + { + entity = owner; + } + if (entity) + { + target_id = entity->GetEID(); + psGameObject::GetPosition(entity, dest, targetRot, targetSector); + npc->Printf(6, "Targeting owner (%s) at (%1.2f,%1.2f,%1.2f) for chase ...", + entity->GetName(),dest.x,dest.y,dest.z ); - //now persist - npcclient->GetNetworkMgr()->QueueDRData(npc); + } + } + break; + case TARGET: + { + gemNPCObject * target = npc->GetTarget(); + if (target) + { + entity = target; + } + if (entity) + { + target_id = entity->GetEID(); + psGameObject::GetPosition(entity, dest, targetRot,targetSector); + npc->Printf(6, "Targeting current target (%s) at (%1.2f,%1.2f,%1.2f) for chase ...", + entity->GetName(), dest.x,dest.y, dest.z ); + } + } + break; + } - // Note no "wake me up when over" event here. - // Move just keeps moving the same direction until pre-empted by something else. - consec_collisions = 0; + if (target_id.IsValid() && entity) + { + psGameObject::GetPosition(npc->GetActor(),myPos, myRot, mySector); + + psGameObject::GetPosition(entity, targetPos, targetRot, targetSector); - if (!interrupted) + npc->Printf(5, "Chasing enemy <%s, %s> at %s", entity->GetName(), ShowID(entity->GetEID()), + toString(targetPos,targetSector).GetDataSafe()); + + // We need to work in the target sector space + if (!npcclient->GetWorld()->WarpSpace(targetSector, mySector, targetPos)) + { + npc->Printf("ChaseOperation: target's sector is not connected to ours!"); + return true; // This operation is complete + } + if ( Calc2DDistance( myPos, targetPos ) < offset ) + { + return true; // This operation is complete + } + + // This prevents NPCs from wanting to occupy the same physical space as something else + csVector3 displacement = targetPos - myPos; + displacement.y = 0; + float factor = offset / displacement.Norm(); + csVector3 destPos = myPos + (1 - factor) * displacement; + destPos.y = targetPos.y; + + path.SetMaps(npcclient->GetMaps()); + path.SetDest(destPos); + path.CalcLocalDest(myPos, mySector, localDest); + + + if ( GetAngularVelocity(npc) > 0 || GetVelocity(npc) > 0 ) + { + StartMoveTo(npc, eventmgr, localDest, targetSector, GetVelocity(npc), action, false); + return false; + } + else + { + return true; // This operation is complete + } + } + else { - remaining = duration; + npc->Printf(5, "No one found to chase!"); + return true; // This operation is complete } - if(remaining > 0) + return true; // This operation is complete +} + +void ChaseOperation::Advance(float timedelta, NPC *npc, EventManager *eventmgr) +{ + + csVector3 myPos,myNewPos,targetPos; + float myRot,dummyrot; + InstanceID myInstance, targetInstance; + iSector * mySector, *myNewSector, *targetSector; + csVector3 forward; + + npc->GetLinMove()->GetLastPosition(myPos, myRot, mySector); + myInstance = npc->GetActor()->GetInstance(); + + // Now turn towards entity being chased again + csString name; + + if (type == NEAREST) { - Resume((int)(remaining*1000.0),npc,eventmgr); + // Switch target if a new entity is withing search range. + EID newTargetEID = npc->GetNearestEntity(targetPos, name, searchRange); + if (newTargetEID.IsValid()) + { + target_id = newTargetEID; + } } + + gemNPCActor *target_entity = NULL; + gemNPCActor * targetActor = dynamic_cast<gemNPCActor*>(npcclient->FindEntityID(target_id)); + if (targetActor) + { + target_entity = targetActor; + } - return false; + if (!targetActor || !target_entity) // no entity close to us + { + npc->Printf(5, "ChaseOp has no target now!"); + npc->ResumeScript(npc->GetBrain()->GetCurrentBehavior() ); + return; + } + + if(name.IsEmpty()) + { + name = target_entity->GetName(); + } + + psGameObject::GetPosition(target_entity,targetPos,dummyrot,targetSector); + targetInstance = targetActor->GetInstance(); + + // We work in our sector's space + if (!npcclient->GetWorld()->WarpSpace(targetSector, mySector, targetPos)) + { + npc->Printf("ChaseOperation: target's sector is not connected to ours!"); + npc->ResumeScript(npc->GetBrain()->GetCurrentBehavior() ); + return; + } + + // This prevents NPCs from wanting to occupy the same physical space as something else + csVector3 displacement = targetPos - myPos; + + displacement.y = 0; + float distance = displacement.Norm(); + + if ( (chaseRange > 0 && distance > chaseRange) || (targetInstance != myInstance) ) + { + npc->Printf(5, "Target out of chase range -> we are done.."); + csString str; + str.Append(typeStr[type]); + str.Append(" out of chase"); + Perception range(str); + npc->TriggerEvent(&range); + npc->ResumeScript(npc->GetBrain()->GetCurrentBehavior() ); + return; + } + + + float factor = offset / distance; + targetPos = myPos + (1 - factor) * displacement; + targetPos.y = myPos.y; + + npc->Printf(10, "Still chasing %s at %s...",(const char *)name,toString(targetPos,targetSector).GetDataSafe()); + + float angleToTarget = psGameObject::CalculateIncidentAngle(myPos, targetPos); + csVector3 pathDest = path.GetDest(); + float angleToPath = psGameObject::CalculateIncidentAngle(myPos, pathDest); + + + // if the target diverged from the end of our path, we must calculate it again + if ( fabs( AngleDiff(angleToTarget, angleToPath) ) > EPSILON ) + { + npc->Printf(8, "turn to target.."); + path.SetDest(targetPos); + path.CalcLocalDest(myPos, mySector, localDest); + StartMoveTo(npc,eventmgr,localDest, mySector, GetVelocity(npc), action, false); + } + + + float close = GetVelocity(npc)*timedelta; // Add 10 % to the distance moved in one tick. + + if (Calc2DDistance(localDest, myPos) <= 0.5f) + { + npc->GetLinMove()->SetPosition(myPos,myRot,mySector); + npc->Printf(5,"Set position %g %g %g, sector %s\n", myPos.x, myPos.y, myPos.z, mySector->QueryObject()->GetName()); + + if (Calc2DDistance(myPos,targetPos) <= 0.5f) + { + npc->Printf(5, "We are done.."); + npc->ResumeScript(npc->GetBrain()->GetCurrentBehavior() ); + return; + } + else + { + npc->Printf(6, "We are at localDest.."); + path.SetDest(targetPos); + path.CalcLocalDest(myPos, mySector, localDest); + StartMoveTo(npc, eventmgr, localDest, mySector, GetVelocity(npc), action, false); + } + } + else + { + TurnTo(npc, localDest, mySector, forward); + } + // Limit time extrapolation so we arrive near the correct place. + if(Calc2DDistance(localDest, myPos) <= close) + timedelta = Calc2DDistance(localDest, myPos) / GetVelocity(npc); + + npc->Printf(8, "advance: pos=(%f.2,%f.2,%f.2) rot=%.2f %s localDest=(%f.2,%f.2,%f.2) dist=%f", + myPos.x,myPos.y,myPos.z, myRot, mySector->QueryObject()->GetName(), + localDest.x,localDest.y,localDest.z, + Calc2DDistance(localDest, myPos)); + + { + ScopedTimer st(250, "chase extrapolate %.2f time for %s", timedelta, ShowID(npc->GetActor()->GetEID())); + npc->GetLinMove()->ExtrapolatePosition(timedelta); + } + bool on_ground; + float speed,ang_vel; + csVector3 bodyVel,worldVel; + + npc->GetLinMove()->GetDRData(on_ground,speed,myNewPos,myRot,myNewSector,bodyVel,worldVel,ang_vel); + + npc->Printf(8,"World position bodyVel=%s worldVel=%s",toString(bodyVel).GetDataSafe(),toString(worldVel).GetDataSafe()); + + CheckMovedOk(npc, eventmgr, myPos, mySector, myNewPos, myNewSector, timedelta); } -void MoveOperation::InterruptOperation(NPC *npc,EventManager *eventmgr) +void ChaseOperation::InterruptOperation(NPC *npc,EventManager *eventmgr) { ScriptOperation::InterruptOperation(npc,eventmgr); - + StopMovement(npc); } -bool MoveOperation::CompleteOperation(NPC *npc,EventManager *eventmgr) +bool ChaseOperation::CompleteOperation(NPC *npc,EventManager *eventmgr) { StopMovement(npc); @@ -494,26 +779,6 @@ return true; // Script can keep going } -void MoveOperation::Advance(float timedelta, NPC *npc, EventManager *eventmgr) -{ - remaining -= timedelta; - - // This updates the position of the entity every 1/2 second so that - // range and distance calculations will work when interrupted. - - csVector3 oldPos,newPos; - float oldRot,newRot; - iSector *oldSector,*newSector; - - npc->GetLinMove()->GetLastPosition(oldPos, oldRot, oldSector); - npc->GetLinMove()->ExtrapolatePosition(timedelta); - npc->GetLinMove()->GetLastPosition(newPos, newRot, newSector); - - psGameObject::SetPosition(npc->GetActor(), newPos, newSector); - - CheckMovedOk(npc, eventmgr, oldPos, oldSector, newPos, newSector, timedelta); -} - //--------------------------------------------------------------------------- bool CircleOperation::Load(iDocumentNode *node) @@ -566,13 +831,7 @@ if (duration == 0) { - if (angle != 0) - { - duration = fabs(angle)*radius/GetVelocity(npc); - } else - { - duration = 2*PI*radius/GetVelocity(npc); - } + duration = fabs(angle)*radius/GetVelocity(npc); } if (ang_vel == 0) @@ -585,416 +844,257 @@ //--------------------------------------------------------------------------- -bool MoveToOperation::Load(iDocumentNode *node) +bool DebugOperation::Load(iDocumentNode *node) { - dest.x = node->GetAttributeValueAsFloat("x"); - dest.y = node->GetAttributeValueAsFloat("y"); - dest.z = node->GetAttributeValueAsFloat("z"); - - LoadVelocity(node); - action = node->GetAttributeValue("anim"); - + exclusive = node->GetAttributeValue("exclusive"); + level = node->GetAttributeValueAsInt("level"); return true; } -ScriptOperation *MoveToOperation::MakeCopy() +ScriptOperation *DebugOperation::MakeCopy() { - MoveToOperation *op = new MoveToOperation; - op->velSource = velSource; - op->vel = vel; - op->dest = dest; - op->action = action; + DebugOperation *op = new DebugOperation; + op->exclusive = exclusive; + op->level = level; return op; } -bool MoveToOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) +bool DebugOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) { - npc->Printf(5,"MoveToOp Start dest=(%1.2f,%1.2f,%1.2f) at %1.2f m/sec.", - dest.x,dest.y,dest.z,GetVelocity(npc)); - - csVector3 pos, forward, up; - float rot; - iSector *sector; - - psGameObject::GetPosition(npc->GetActor(),pos,rot,sector); - - path.SetMaps(npcclient->GetMaps()); - path.SetDest(dest); - path.CalcLocalDest(pos, sector, localDest); - - // Using "true" teleports to dest location after proper time has - // elapsed and is therefore more tolerant of CD errors. - StartMoveTo(npc, eventmgr, localDest, sector,vel,action, true); - return false; -} - -void MoveToOperation::Advance(float timedelta,NPC *npc,EventManager *eventmgr) -{ - csVector3 pos,pos2; - float rot; - iSector * sector; - csVector3 forward; - - npc->GetLinMove()->GetLastPosition(pos,rot,sector); - - npc->Printf(10,"advance: pos=(%1.2f,%1.2f,%1.2f) rot=%.2f localDest=(%.2f,%.2f,%.2f) dest=(%.2f,%.2f,%.2f) dist=%f", - pos.x,pos.y,pos.z, rot, - localDest.x,localDest.y,localDest.z, - dest.x,dest.y,dest.z, - Calc2DDistance(localDest, pos)); - - TurnTo(npc, localDest, sector, forward); - - //tolerance must be according to step size - //we must ignore y - if (Calc2DDistance(localDest, pos) <= 0.5) + if (exclusive.Length()) { - pos.x = localDest.x; - pos.z = localDest.z; - npc->GetLinMove()->SetPosition(pos,rot,sector); + static bool debug_exclusive = false; - if (Calc2DDistance(localDest,dest) <= 0.5) //tolerance must be according to step size, ignore y + if (level && debug_exclusive) { - npc->Printf(8,"MoveTo within minimum acceptable range...Stopping him now."); - // npc->ResumeScript(eventmgr, npc->GetBrain()->GetCurrentBehavior() ); - CompleteOperation(npc, eventmgr); + // Can't turn on when exclusive is set. + return true; } + if (level) + { + debug_exclusive = true; + } else { - npc->Printf(8,"we are at localDest... WHAT DOES THIS MEAN?"); - path.CalcLocalDest(pos, sector, localDest); - StartMoveTo(npc,eventmgr,localDest, sector, vel,action, false); + debug_exclusive = false; } } - else + + + if (!level) // Print before when turning off { - npc->GetLinMove()->ExtrapolatePosition(timedelta); - npc->GetLinMove()->GetLastPosition(pos2,rot,sector); + npc->Printf(1, "DebugOp Set debug %d",level); + } - if ((pos-pos2).SquaredNorm() < SMALL_EPSILON) // then stopped dead, presumably by collision - { - Perception collision("collision"); - npc->TriggerEvent(&collision); - } + npc->SetDebugging(level); + + if (level) // Print after when turning on + { + npc->Printf(1, "DebugOp Set debug %d",level); } -} -bool MoveToOperation::CompleteOperation(NPC *npc,EventManager *eventmgr) -{ - // Get the rot and sector here so they don't change in the SetPosition call - float rot; - iSector *sector; - csVector3 pos; - psGameObject::GetPosition(npc->GetActor(),pos,rot,sector); - - // Set position to where it is supposed to go - npc->GetLinMove()->SetPosition(dest,rot,sector); - - // Stop the movement - StopMovement(npc); - - npc->Printf(5,"MoveToOp - Completed. pos=(%1.2f,%1.2f,%1.2f) rot=%.2f dest set=(%1.2f,%1.2f,%1.2f)", - pos.x,pos.y,pos.z, rot, - dest.x,dest.y,dest.z); - completed = true; - - return true; // Script can keep going + return true; // We are done } //--------------------------------------------------------------------------- -bool RotateOperation::Load(iDocumentNode *node) +bool DequipOperation::Load(iDocumentNode *node) { - csString type = node->GetAttributeValue("type"); - ang_vel = node->GetAttributeValueAsFloat("ang_vel")*TWO_PI/360.0f; - action = node->GetAttributeValue("anim"); + slot = node->GetAttributeValue("slot"); - if (type == "inregion") + if (slot.IsEmpty()) { - op_type = ROT_REGION; - min_range = node->GetAttributeValueAsFloat("min")*TWO_PI/360.0f; - max_range = node->GetAttributeValueAsFloat("max")*TWO_PI/360.0f; - return true; + Error1("No slot defined for Dequip operation"); + return false; } - else if (type == "random") - { - op_type = ROT_RANDOM; - min_range = node->GetAttributeValueAsFloat("min")*TWO_PI/360.0f; - max_range = node->GetAttributeValueAsFloat("max")*TWO_PI/360.0f; - return true; - } - else if (type == "absolute") - { - op_type = ROT_ABSOLUTE; - target_angle = node->GetAttributeValueAsFloat("value")*TWO_PI/360.0f; - return true; - } - else if (type == "relative") - { - op_type = ROT_RELATIVE; - delta_angle = node->GetAttributeValueAsFloat("value")*TWO_PI/360.0f; - return true; - } - else if (type == "locatedest") - { - op_type = this->ROT_LOCATEDEST; - return true; - } - else if (type == "target") - { - op_type = this->ROT_TARGET; - return true; - } - else - { - Error1("Rotate Op type must be 'random', 'absolute', 'relative', " - "'target' or 'locatedest' right now."); - } - return false; + + return true; } -ScriptOperation *RotateOperation::MakeCopy() +ScriptOperation *DequipOperation::MakeCopy() { - RotateOperation *op = new RotateOperation; - op->action = action; - op->op_type = op_type; - op->max_range = max_range; - op->min_range = min_range; - op->ang_vel = ang_vel; - op->delta_angle = delta_angle; - op->target_angle = target_angle; - + DequipOperation *op = new DequipOperation; + op->slot = slot; return op; } -bool RotateOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) +bool DequipOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) { - float rot; - csVector3 pos; - iSector* sector; - psGameObject::GetPosition(npc->GetActor(),pos,rot,sector); + npc->Printf(5, " Who: %s Where: %s", + npc->GetName(), slot.GetData()); - float ang_vel = GetAngularVelocity(npc); + npcclient->GetNetworkMgr()->QueueDequipCommand(npc->GetActor(), slot ); - if (interrupted) + return true; +} + +//--------------------------------------------------------------------------- + +bool DigOperation::Load(iDocumentNode *node) +{ + resource = node->GetAttributeValue("resource"); + if (resource.IsEmpty()) { - npc->Printf(5,"Interrupted rotation to %.2f deg",target_angle*180.0f/PI); - - angle_delta = target_angle - rot; + Error1("No resource defined for Dig operation"); + return false; } - else if (op_type == ROT_RANDOM || op_type == ROT_REGION) - { - LocationType *rgn = npc->GetRegion(); + + return true; +} - // Start the turn - bool verified = false; - int count=0; - float rot_angle=0; - while (rgn && op_type == ROT_REGION && !verified && count<10) - { - // Find range of allowable angles to turn inwards to region again - float min_angle = TWO_PI, max_angle = -TWO_PI; +ScriptOperation *DigOperation::MakeCopy() +{ + DigOperation *op = new DigOperation; - for (size_t i=0; i<rgn->locs.GetSize(); i++) - { - rot_angle = psGameObject::CalculateIncidentAngle(pos,rgn->locs[i]->pos); - if (min_angle > rot_angle) - min_angle = rot_angle; - if (max_angle < rot_angle) - max_angle = rot_angle; - } + op->resource = resource; - if (max_angle-min_angle > PI ) - { - float temp=max_angle; - max_angle=min_angle+TWO_PI; - min_angle=temp; - } + return op; +} - // Pick an angle in that range - target_angle = SeekAngle(npc, psGetRandom() * (max_angle-min_angle) + min_angle); - - - verified = true; - } - if (!rgn || op_type == ROT_RANDOM || !verified) - { - target_angle = SeekAngle(npc, psGetRandom() * (max_range - min_range)+min_range - PI); - } - - // Save target angle so we can jam that in on Rotate completion. - angle_delta = target_angle - rot; - } - else if (op_type == ROT_LOCATEDEST) +bool DigOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) +{ + if (resource == "tribe:wealth") { - csVector3 dest; - float dest_rot; - iSector *dest_sector; - - npc->GetActiveLocate(dest,dest_sector,dest_rot); - - if(pos == dest && sector == dest_sector && rot == dest_rot) + if (npc->GetTribe()) { - npc->Printf(5,"At located destination, end rotation."); - return true; + npcclient->GetNetworkMgr()->QueueDigCommand(npc->GetActor(), npc->GetTribe()->GetNeededResourceNick()); } - - target_angle = psGameObject::CalculateIncidentAngle(pos,dest); - - angle_delta = target_angle-rot; - - // If the angle is close enough don't worry about it and just go to next command. - if (fabs(angle_delta) < TWO_PI/60.0) - { - npc->Printf(5, "Rotation at destination angle. Ending rotation."); - return true; - } } - else if (op_type == ROT_ABSOLUTE) - { - npc->Printf(5, "Absolute rotation to %.2f deg",target_angle*180.0f/PI); - - angle_delta = target_angle - rot; - } - else if (op_type == ROT_RELATIVE) - { - npc->Printf(5, "Relative rotation by %.2f deg",delta_angle*180.0f/PI); - - angle_delta = delta_angle; - target_angle = rot + angle_delta; - } else { - Error1("ERROR: No known rotation type defined"); - return true; + npcclient->GetNetworkMgr()->QueueDigCommand(npc->GetActor(), resource ); } + - psGameObject::NormalizeRadians(angle_delta); // -PI to PI - psGameObject::ClampRadians(target_angle); // 0 to 2*PI + return true; +} - npc->GetLinMove()->SetAngularVelocity( csVector3(0,(angle_delta>0)?-ang_vel:ang_vel,0) ); - SetAnimation(npc, action); +//--------------------------------------------------------------------------- - //now persist - npcclient->GetNetworkMgr()->QueueDRData(npc); - - // wake me up when it's over - int msec = (int)fabs(1000.0*angle_delta/ang_vel); - Resume(msec,npc,eventmgr); - - npc->Printf(5,"Rotating %1.2f deg from %1.2f to %1.2f at %1.2f deg/sec in %.3f sec.", - angle_delta*180/PI,rot*180.0f/PI,target_angle*180.0f/PI,ang_vel*180.0f/PI,msec/1000.0f); - - return false; +bool DropOperation::Load(iDocumentNode *node) +{ + slot = node->GetAttributeValue("slot"); + if (slot.IsEmpty()) + { + Error1("Drop operation must have a slot defined"); + return false; + } + + return true; } -void RotateOperation::Advance(float timedelta, NPC *npc, EventManager *eventmgr) +ScriptOperation *DropOperation::MakeCopy() { - npc->GetLinMove()->ExtrapolatePosition(timedelta); + DropOperation *op = new DropOperation; + op->slot = slot; + return op; } -float RotateOperation::SeekAngle(NPC* npc, float targetYRot) +bool DropOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) { - // Try to avoid big ugly stuff in our path - float rot; - iSector *sector; - csVector3 pos; - psGameObject::GetPosition(npc->GetActor(),pos,rot,sector); + npcclient->GetNetworkMgr()->QueueDropCommand(npc->GetActor(), slot ); - csVector3 isect,start,end,dummy,box,legs; + return true; +} - // Construct the feeling broom +//--------------------------------------------------------------------------- - // Calculate the start and end poses - start = pos; - npc->GetLinMove()->GetCDDimensions(box,legs,dummy); +bool EatOperation::Load(iDocumentNode *node) +{ + resource = node->GetAttributeValue("resource"); + if (resource.IsEmpty()) return false; + return true; +} - // We can walk over some stuff - start += csVector3(0,0.6f,0); - end = start + csVector3(sinf(targetYRot), 0, cosf(targetYRot)) * -2; +ScriptOperation *EatOperation::MakeCopy() +{ + EatOperation *op = new EatOperation; + op->resource = resource; + return op; +} - // Feel - csIntersectingTriangle closest_tri; - iMeshWrapper* sel = 0; - float dist = csColliderHelper::TraceBeam (npcclient->GetCollDetSys(), sector, - start, end, true, closest_tri, isect, &sel); +bool EatOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) +{ + csString res = resource; + + if (resource == "tribe:wealth") + { + if (npc->GetTribe()) + { + res = npc->GetTribe()->GetNeededResourceNick(); + } + } - - if(dist > 0) + gemNPCActor *ent = npc->GetNearestDeadActor(1.0); + if (ent) { - const float begin = (PI/6); // The lowest turning constant - const float length = 2; - - float left,right,turn = 0; - for(int i = 1; i <= 3;i++) + // Take a bite :) + if (npc->GetTribe()) { - csVector3 broomStart[2],broomEnd[2]; + npc->GetTribe()->AddResource(res,1); + } + } - // Left and right - left = targetYRot - (begin * float(i)); - right = targetYRot + (begin * float(i)); + return true; +} - // Construct the testing brooms - broomStart[0] = start; - broomEnd[0] = start + csVector3(sinf(left),0,cosf(left)) * -length; +//--------------------------------------------------------------------------- - broomStart[1] = start; - broomEnd[1] = start + csVector3(sinf(right),0,cosf(right)) * -length; +bool EquipOperation::Load(iDocumentNode *node) +{ + item = node->GetAttributeValue("item"); + if (item.IsEmpty()) + { + Error1("Equip operation needs an item"); + return false; + } + slot = node->GetAttributeValue("slot"); + if (slot.IsEmpty()) + { + Error1("Equip operation needs a slot"); + return false; + } + count = node->GetAttributeValueAsInt("count"); + if (count <= 0) count = 1; // Allways equip at least one. + return true; +} - // The broom is already 0.6 over the ground, so we need to cut that out - //broomStart[0].y += legs.y + box.y - 0.6; - //broomEnd[1].y += legs.y + box.y - 0.6; +ScriptOperation *EquipOperation::MakeCopy() +{ + EquipOperation *op = new EquipOperation; + op->item = item; + op->slot = slot; + op->count = count; + return op; +} - // Check if we can get the broom through where we want to go - float dist = csColliderHelper::TraceBeam (npcclient->GetCollDetSys(), sector, - broomStart[0], broomEnd[0], true, closest_tri, isect, &sel); +bool EquipOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) +{ + npc->Printf(5, " Who: %s What: %s Where: %s Count: %d", + npc->GetName(),item.GetData(), slot.GetData(), count); - if(dist < 0) - { - npc->Printf(6,"Turning left!"); - turn = left; - break; - } + npcclient->GetNetworkMgr()->QueueEquipCommand(npc->GetActor(), item, slot, count); - // Do again for the other side - dist = csColliderHelper::TraceBeam (npcclient->GetCollDetSys(), sector, - broomStart[1], broomEnd[1], true, closest_tri, isect, &sel); + return true; +} - if(dist < 0) - { - npc->Printf(6,"Turning right!"); - turn = right; - break; - } +//--------------------------------------------------------------------------- - - } - if (turn==0.0) - { - npc->Printf(5,"Possible ERROR: turn value was 0"); - } - // Apply turn - targetYRot = turn; - } - return targetYRot; +bool InvisibleOperation::Load(iDocumentNode *node) +{ + return true; } -void RotateOperation::InterruptOperation(NPC *npc, EventManager *eventmgr) +ScriptOperation *InvisibleOperation::MakeCopy() { - ScriptOperation::InterruptOperation(npc,eventmgr); - - StopMovement(npc); + InvisibleOperation *op = new InvisibleOperation; + return op; } -bool RotateOperation::CompleteOperation(NPC *npc, EventManager *eventmgr) +bool InvisibleOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) { - // Set target angle and stop the turn - psGameObject::SetRotationAngle(npc->GetActor(),target_angle); - StopMovement(npc); - - completed = true; - - return true; // Script can keep going + npcclient->GetNetworkMgr()->QueueVisibilityCommand(npc->GetActor(), false); + return true; } //--------------------------------------------------------------------------- @@ -1003,6 +1103,12 @@ { object = node->GetAttributeValue("obj"); + if (object.IsEmpty()) + { + Error1("Locate operation need obj paramter"); + return false; + } + static_loc = node->GetAttributeValueAsBool("static",false); if (node->GetAttribute("range")) { @@ -1372,6 +1478,510 @@ //--------------------------------------------------------------------------- +bool MoveOperation::Load(iDocumentNode *node) +{ + LoadVelocity(node); + action = node->GetAttributeValue("anim"); + duration = node->GetAttributeValueAsFloat("duration"); + ang_vel = node->GetAttributeValueAsFloat("ang_vel"); + return true; +} + +ScriptOperation *MoveOperation::MakeCopy() +{ + MoveOperation *op = new MoveOperation; + op->velSource = velSource; + op->vel = vel; + op->action = action; + op->duration = duration; + op->ang_vel = ang_vel; + op->angle = angle; + return op; +} + +bool MoveOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) +{ + // Get Going at the right velocity + csVector3 velvec(0,0,-GetVelocity(npc) ); + npc->GetLinMove()->SetVelocity(velvec); + npc->GetLinMove()->SetAngularVelocity(angle<0?-ang_vel:ang_vel); + + // SetAction animation for the mesh also, so it looks right + SetAnimation(npc, action); + + //now persist + npcclient->GetNetworkMgr()->QueueDRData(npc); + + // Note no "wake me up when over" event here. + // Move just keeps moving the same direction until pre-empted by something else. + consec_collisions = 0; + + if (!interrupted) + { + remaining = duration; + } + + if(remaining > 0) + { + Resume((int)(remaining*1000.0),npc,eventmgr); + } + + return false; +} + +void MoveOperation::InterruptOperation(NPC *npc,EventManager *eventmgr) +{ + ScriptOperation::InterruptOperation(npc,eventmgr); + + StopMovement(npc); +} + +bool MoveOperation::CompleteOperation(NPC *npc,EventManager *eventmgr) +{ + StopMovement(npc); + + completed = true; + + return true; // Script can keep going +} + +void MoveOperation::Advance(float timedelta, NPC *npc, EventManager *eventmgr) +{ + remaining -= timedelta; + + // This updates the position of the entity every 1/2 second so that + // range and distance calculations will work when interrupted. + + csVector3 oldPos,newPos; + float oldRot,newRot; + iSector *oldSector,*newSector; + + npc->GetLinMove()->GetLastPosition(oldPos, oldRot, oldSector); + npc->GetLinMove()->ExtrapolatePosition(timedelta); + npc->GetLinMove()->GetLastPosition(newPos, newRot, newSector); + + psGameObject::SetPosition(npc->GetActor(), newPos, newSector); + + CheckMovedOk(npc, eventmgr, oldPos, oldSector, newPos, newSector, timedelta); +} + + +//--------------------------------------------------------------------------- + +bool MoveToOperation::Load(iDocumentNode *node) +{ + dest.x = node->GetAttributeValueAsFloat("x"); + dest.y = node->GetAttributeValueAsFloat("y"); + dest.z = node->GetAttributeValueAsFloat("z"); + + LoadVelocity(node); + action = node->GetAttributeValue("anim"); + + return true; +} + +ScriptOperation *MoveToOperation::MakeCopy() +{ + MoveToOperation *op = new MoveToOperation; + op->velSource = velSource; + op->vel = vel; + op->dest = dest; + op->action = action; + return op; +} + +bool MoveToOperation::Run(NPC *npc, EventManager *eventmgr, bool interrupted) +{ + npc->Printf(5,"MoveToOp Start dest=(%1.2f,%1.2f,%1.2f) at %1.2f m/sec.", + dest.x,dest.y,dest.z,GetVelocity(npc)); + + csVector3 pos, forward, up; + float rot; + iSector *sector; + + psGameObject::GetPosition(npc->GetActor(),pos,rot,sector); + + path.SetMaps(npcclient->GetMaps()); + path.SetDest(dest); + path.CalcLocalDest(pos, sector, localDest); + + // Using "true" teleports to dest location after proper time has + // elapsed and is therefore more tolerant of CD errors. + StartMoveTo(npc, eventmgr, localDest, sector,vel,action, true); + return false; +} + +void MoveToOperation::Advance(float timedelta,NPC *npc,EventManager *eventmgr) +{ + csVector3 pos,pos2; + float rot; + iSector * sector; + csVector3 forward; + + npc->GetLinMove()->GetLastPosition(pos,rot,sec... [truncated message content] |