Revision: 8414
http://planeshift.svn.sourceforge.net/planeshift/?rev=8414&view=rev
Author: magodra
Date: 2012-08-04 12:01:36 +0000 (Sat, 04 Aug 2012)
Log Message:
-----------
- New admin client command /debugnpc <debug_level>. Allow clients to view npc debug output.
- New NPC Operation <control /> and <relase_control /> to take control of a player.
- The player position will be updated according to the npc position until control
is released.
- Added target to NPC locations located by the locate operation
- Added attribute only and ignore to the <copy_locate /> npc operation. The flags
are: pos,angle,sector,wp,radius,target to allow partial copy of locate information.
- Extended replace npc variables to be able to replace $perception_type that
will be the last perception type received. Will typically be used in locate.
- Added replace of locations as well. $LOCATION[<name>.<attribute>].
- Renamed the QueueInfoReplyCommand to QueueServerInfoCommand to use same name
as in the server. Added use of args to that function for printf like behavior.
- Added reload sub command to the /changenpcbrain client admin function.
This will reload behaviors from db and reassign the old brain to the selected npcs.
Note: All other NPCs will keep its old brain, only selected npcs that will be updated.
- Changed CheckMoveOk to use the return from the Extrapolate function in lin move
instead of trying to figure out collitons etc. The circle command didn't work
with the current estimate of movement and checks.
- Added a few new locate object types: dead, actor, player
- Fixed locate region to search outside +/- 5 if no ground point is found.
- Updated help file from /changenpctype and /debugnpc.
Modified Paths:
--------------
trunk/data/help.xml
trunk/src/client/pscelclient.cpp
trunk/src/client/pscelclient.h
trunk/src/client/psclientdr.cpp
trunk/src/client/zonehandler.cpp
trunk/src/client/zonehandler.h
trunk/src/common/engine/linmove.cpp
trunk/src/common/engine/linmove.h
trunk/src/common/net/message.h
trunk/src/common/net/messages.cpp
trunk/src/common/net/messages.h
trunk/src/common/net/netbase.cpp
trunk/src/common/net/npcmessages.cpp
trunk/src/common/net/npcmessages.h
trunk/src/npcclient/networkmgr.cpp
trunk/src/npcclient/networkmgr.h
trunk/src/npcclient/npc.cpp
trunk/src/npcclient/npc.h
trunk/src/npcclient/npcbehave.cpp
trunk/src/npcclient/npcclient.cpp
trunk/src/npcclient/npcoperations.cpp
trunk/src/npcclient/npcoperations.h
trunk/src/npcclient/perceptions.h
trunk/src/server/adminmanager.cpp
trunk/src/server/adminmanager.h
trunk/src/server/database/mysql/command_access.sql
trunk/src/server/database/mysql/npc_responses.sql
trunk/src/server/database/mysql/npc_triggers.sql
trunk/src/server/database/mysql/sc_npctypes.sql
trunk/src/server/gem.cpp
trunk/src/server/gem.h
trunk/src/server/npcmanager.cpp
trunk/src/server/npcmanager.h
Modified: trunk/data/help.xml
===================================================================
--- trunk/data/help.xml 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/data/help.xml 2012-08-04 12:01:36 UTC (rev 8414)
@@ -2754,6 +2754,13 @@
Changes the player's name. If force is given, the lastname doesn't have to be unique. If forceall is given name and lastname doesn't have to be unique. If the lastname is not given the lastname will be preseved. If no is given as lastname, the lastname will be removed.</content>
</Contents>
</topic>
+ <topic name="/changenpctype">
+ <Contents>
+ <content type="text">/changenpctype [me/target/eid/pid/area/old name] [<brain>|reload]
+
+Changes the brain of the selected npcs. Use reload to reload the npctype db and set the new brain for the npc. Only the selected npc will have the reloaded brain set.</content>
+ </Contents>
+ </topic>
<topic name="/charlist">
<Contents>
<content type="text">/charlist [me/target/eid/pid/area/name]
@@ -2783,6 +2790,13 @@
Kills something.</content>
</Contents>
</topic>
+ <topic name="/debugnpc">
+ <Contents>
+ <content type="text">/debugnpc [target/eid/pid/area/name] [<debug level>]
+
+Changes the debug level of the selected npcs.</content>
+ </Contents>
+ </topic>
<topic name="/deletechar">
<Contents>
<content type="text">/deletechar [character name] [petitioner name]
Modified: trunk/src/client/pscelclient.cpp
===================================================================
--- trunk/src/client/pscelclient.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/client/pscelclient.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -1781,6 +1781,18 @@
linmove->SetPosition(pos, rot, sector);
}
+void GEMClientActor::SetVelocity(const csVector3& vel)
+{
+ linmove->SetVelocity(vel);
+}
+
+void GEMClientActor::SetYRotation(const float yrot)
+{
+ linmove->SetYRotation(yrot);
+}
+
+
+
void GEMClientActor::InitCharData(const char* traits, const char* equipment)
{
this->traits = traits;
@@ -2003,7 +2015,7 @@
if(newmode == movementMode)
return;
- Debug4(LOG_CELPERSIST, 0, "Setting New mode: %d for %s eid: %d", newmode, GetName(), this->GetEID().Unbox());
+ Debug4(LOG_CELPERSIST, 0, "Setting New mode: %zu for %s eid: %d", newmode, GetName(), this->GetEID().Unbox());
movementMode = newmode;
Modified: trunk/src/client/pscelclient.h
===================================================================
--- trunk/src/client/pscelclient.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/client/pscelclient.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -490,6 +490,14 @@
virtual void SetPosition(const csVector3& pos, float rot, iSector* sector);
+ /** Set the velocity of the actor.
+ */
+ void SetVelocity(const csVector3& vel);
+
+ /** Set the rotation of the actor.
+ */
+ void SetYRotation(const float yrot);
+
void SetAlive( bool aliveFlag, bool newactor );
virtual bool IsAlive() { return alive; }
virtual int GetMasqueradeType() { return masqueradeType; }
Modified: trunk/src/client/psclientdr.cpp
===================================================================
--- trunk/src/client/psclientdr.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/client/psclientdr.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -255,7 +255,8 @@
else
{
psengine->GetZoneHandler()->HandleDelayAndAnim(msg.loadTime, msg.start, msg.dest, msg.backgroundname, msg.loadWidget);
- psengine->GetZoneHandler()->LoadZone(msg.pos, msg.sectorName, true);
+ psengine->GetZoneHandler()->LoadZone(msg.pos, msg.sectorName, msg.vel, true);
+ celclient->GetMainPlayer()->SetYRotation(msg.yrot);
}
}
Modified: trunk/src/client/zonehandler.cpp
===================================================================
--- trunk/src/client/zonehandler.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/client/zonehandler.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -195,10 +195,10 @@
Notify3(LOG_LOAD, "Crossed from sector %s to sector %s.", msg.oldSector.GetData(), msg.newSector.GetData());
- LoadZone(msg.pos, msg.newSector);
+ LoadZone(msg.pos, msg.newSector, 0);
}
-void ZoneHandler::LoadZone(csVector3 pos, const char* sector, bool force)
+void ZoneHandler::LoadZone(csVector3 pos, const char* sector, float vel, bool force)
{
if((loading || !strcmp(sector, LOADING_SECTOR)) && !force)
return;
@@ -215,7 +215,7 @@
}
// Move player to the loading sector.
- MovePlayerTo(csVector3(0.0f), LOADING_SECTOR);
+ MovePlayerTo(csVector3(0.0f), LOADING_SECTOR, vel);
// load target location
if(!psengine->BackgroundWorldLoading())
@@ -285,10 +285,10 @@
// do move the player in *any* case to make sure we won't end up looping to death
// move player to new pos
- MovePlayerTo(newPos, sectorToLoad);
+ MovePlayerTo(newPos, sectorToLoad, vel);
}
-void ZoneHandler::MovePlayerTo(const csVector3 & newPos, const csString & newSector)
+void ZoneHandler::MovePlayerTo(const csVector3 & newPos, const csString & newSector, float newVel)
{
if(!celclient->IsReady())
return;
@@ -304,6 +304,7 @@
{
Notify5(LOG_LOAD, "Setting position of player %f %f %f in sector '%s'", newPos.x, newPos.y, newPos.z, newSector.GetData());
celclient->GetMainPlayer()->SetPosition(newPos, yrot, sector); // set new position
+ celclient->GetMainPlayer()->SetVelocity(csVector3(0.0,0.0,newVel));
}
else
{
@@ -318,7 +319,7 @@
if(psengine->GetLoader()->GetLoadingCount() == 0 && csGetTicks() >= forcedLoadingEndTime)
{
// move player to new pos
- MovePlayerTo(newPos, sectorToLoad);
+ MovePlayerTo(newPos, sectorToLoad, 0.0);
if(psengine->HasLoadedMap())
loadWindow->Hide();
Modified: trunk/src/client/zonehandler.h
===================================================================
--- trunk/src/client/zonehandler.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/client/zonehandler.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -136,9 +136,10 @@
*
* @param pos The target position in the new zone
* @param sector The name of the target zone
+ * @param vel Target velocity
* @param force Whether to force the loading of the target zone
*/
- void LoadZone(csVector3 pos, const char* sector, bool force = false);
+ void LoadZone(csVector3 pos, const char* sector, float vel, bool force = false);
/** @brief Called after drawing on screen has finished.
*
@@ -151,8 +152,9 @@
*
* @param newPos Target position to move to
* @param newSector Target sector to move to
+ * @param newVel Target velocity
*/
- void MovePlayerTo(const csVector3 & newPos, const csString & newSector);
+ void MovePlayerTo(const csVector3 & newPos, const csString & newSector, float newVel);
/** @brief Handles delay and dot animation
*
Modified: trunk/src/common/engine/linmove.cpp
===================================================================
--- trunk/src/common/engine/linmove.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/common/engine/linmove.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -916,6 +916,17 @@
->GetTransform ().GetT2O ();
return Matrix2YRot (transf);
}
+
+void psLinearMovement::SetYRotation( float yrot )
+{
+ // Rotation
+ csMatrix3 matrix = (csMatrix3) csYRotMatrix3 (yrot);
+ mesh->GetMovable ()->GetTransform ().SetO2T (matrix);
+
+ mesh->GetMovable ()->UpdateMove ();
+}
+
+
const csVector3 psLinearMovement::GetPosition () const
{
// user will get a warning and a nothing if theres no mesh
@@ -1017,14 +1028,14 @@
}
- // Position and Sector
- mesh->GetMovable ()->SetPosition ((iSector *)sector,pos);
+ // Position and Sector
+ mesh->GetMovable ()->SetPosition ((iSector *)sector,pos);
- // Rotation
- csMatrix3 matrix = (csMatrix3) csYRotMatrix3 (yrot);
- mesh->GetMovable ()->GetTransform ().SetO2T (matrix);
+ // Rotation
+ csMatrix3 matrix = (csMatrix3) csYRotMatrix3 (yrot);
+ mesh->GetMovable ()->GetTransform ().SetO2T (matrix);
- mesh->GetMovable ()->UpdateMove ();
+ mesh->GetMovable ()->UpdateMove ();
}
void psLinearMovement::SetPosition (const char* center_name, float yrot,
@@ -1104,7 +1115,7 @@
void psLinearMovement::AddVelocity (const csVector3& vel)
{
- // -107 is terminal velocity
+ // -107 is terminal velocity
if (vel.Norm() > 110)
printf("Garbage data in AddVel!\n");
Modified: trunk/src/common/engine/linmove.h
===================================================================
--- trunk/src/common/engine/linmove.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/common/engine/linmove.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -102,6 +102,7 @@
virtual void SetSpeed (float speedz);
virtual float GetYRotation () const;
+ virtual void SetYRotation(float yrot);
const csVector3 GetPosition () const;
const csVector3 GetFullPosition () const;
@@ -111,7 +112,7 @@
virtual void SetFullPosition (const csVector3& pos, float yrot, const iSector* sector);
virtual void SetPosition (const char* center_name, float yrot, iSector* sector);
virtual void SetFullPosition (const char* center_name, float yrot, iSector* sector);
- virtual void GetLastClientPosition (csVector3& pos, float& yrot, iSector*& sector);
+ virtual void GetLastClientPosition (csVector3& pos, float& yrot, iSector*& sector);
virtual bool IsOnGround () const;
Modified: trunk/src/common/net/message.h
===================================================================
--- trunk/src/common/net/message.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/common/net/message.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -124,6 +124,11 @@
*/
const unsigned int MAX_MESSAGE_SIZE = 65535 - sizeof(psMessageBytes) - 1; // Current max, -1 for safety
+#define MSG_SIZEOF_VECTOR2 (2*sizeof(uint32))
+#define MSG_SIZEOF_VECTOR3 (3*sizeof(uint32))
+#define MSG_SIZEOF_VECTOR4 (4*sizeof(uint32))
+#define MSG_SIZEOF_SECTOR 100 // Sector can be a string in the message!!!
+#define MSG_SIZEOF_FLOAT sizeof(uint32)
//-----------------------------------------------------------------------------
Modified: trunk/src/common/net/messages.cpp
===================================================================
--- trunk/src/common/net/messages.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/common/net/messages.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -4681,6 +4681,7 @@
void psDRMessage::ReadDRInfo(MsgEntry* me, NetBase::AccessPointers* accessPointers)
{
entityid = me->GetUInt32();
+ filterNumber = entityid.Unbox(); // Set the filter number to be used when filtering this in console output
counter = me->GetUInt8();
// Find out what's packed here
@@ -4756,15 +4757,15 @@
PSF_IMPLEMENT_MSG_FACTORY_ACCESS_POINTER(psForcePositionMessage, MSGTYPE_FORCE_POSITION);
psForcePositionMessage::psForcePositionMessage(uint32_t client, uint8_t sequenceNumber,
- const csVector3 & pos, float yRot, iSector *sector,
- csStringSet *msgstrings, uint32_t time, csString loadBackground,
- csVector2 start, csVector2 dest, csString loadWidget)
+ const csVector3 & pos, float yRot, iSector *sector, float vel,
+ csStringSet *msgstrings, uint32_t time, csString loadBackground,
+ csVector2 start, csVector2 dest, csString loadWidget)
{
CS_ASSERT(sector);
csString sectorName = sector->QueryObject()->GetName();
csStringID sectorNameStrId = msgstrings ? msgstrings->Request(sectorName) : csInvalidStringID;
- msg.AttachNew(new MsgEntry(sizeof(float)*8 + sizeof(uint8_t) + sizeof(uint32_t) + (sectorNameStrId == csInvalidStringID ? sectorName.Length() + 1 : 0) + sizeof(uint32_t) + loadBackground.Length() + 1 + loadWidget.Length() + 1, PRIORITY_HIGH, sequenceNumber));
+ msg.AttachNew(new MsgEntry(MSG_SIZEOF_FLOAT*9 + sizeof(uint8_t) + sizeof(uint32_t) + (sectorNameStrId == csInvalidStringID ? sectorName.Length() + 1 : 0) + sizeof(uint32_t) + loadBackground.Length() + 1 + loadWidget.Length() + 1, PRIORITY_HIGH, sequenceNumber));
msg->SetType(MSGTYPE_FORCE_POSITION);
msg->clientnum = client;
@@ -4787,6 +4788,8 @@
//widget which replaces the standard load one
msg->Add(loadWidget);
+ msg->Add(vel);
+
// Sets valid flag based on message overrun state
valid=!(msg->overrun);
}
@@ -4809,6 +4812,7 @@
dest.y = me->GetFloat();
loadWidget = me->GetStr();
+ vel = me->GetFloat();
valid = !(me->overrun);
}
@@ -4818,13 +4822,14 @@
pos = other.pos;
yrot = other.yrot;
sector = other.sector;
+ vel = other.vel;
}
csString psForcePositionMessage::ToString(NetBase::AccessPointers * /*accessPointers*/)
{
csString msgtext;
msgtext.AppendFmt("Sector: %s ", sectorName.GetDataSafe());
- msgtext.AppendFmt("Pos(%.2f,%.2f,%.2f), yRot: %.2f", pos.x, pos.y, pos.z, yrot);
+ msgtext.AppendFmt("Pos(%.2f,%.2f,%.2f), yRot: %.2f vel: %.2f", pos.x, pos.y, pos.z, yrot, vel);
return msgtext;
}
@@ -5227,7 +5232,7 @@
psPersistItem::psPersistItem( MsgEntry* me, NetBase::AccessPointers * accessPointers )
- :flags(0),tribeID(0)
+ :tribeID(0),flags(0)
{
eid = EID(me->GetUInt32());
type = me->GetUInt32();
@@ -7156,7 +7161,6 @@
psPlaySongMessage::psPlaySongMessage(uint32_t client, uint32_t songID, bool toPlayer,
float minimumDuration, const char* instrName, uint32_t scoreSize, const char* musicalScore)
{
- size_t size = sizeof(uint32_t) + sizeof(bool) + sizeof(float) + strlen(instrName) + 1 + sizeof(uint32_t) + scoreSize + 1;
msg.AttachNew(new MsgEntry(sizeof(uint32_t) + sizeof(bool) + sizeof(float) + strlen(instrName) + 1 + sizeof(uint32_t) + scoreSize + 1));
msg->SetType(MSGTYPE_PLAY_SONG);
@@ -7640,9 +7644,8 @@
return psfMsgTypeName(msgType);
}
-csString GetDecodedMessage(MsgEntry* me, NetBase::AccessPointers* accessPointers, bool filterhex)
+void DecodeMessage(MsgEntry* me, NetBase::AccessPointers* accessPointers, bool filterhex, csString& msgtext, int& filterNumber)
{
- csString msgtext;
MsgEntry msg(me); // Take a copy to make sure we dont destroy the message.
// Can't do this const since current pointers are modified
// when parsing messages.
@@ -7677,10 +7680,10 @@
msgtext.Append(" > ");
msgtext.Append(cracker->ToString(accessPointers));
+ filterNumber = cracker->filterNumber;
+
delete cracker;
}
-
- return msgtext;
}
typedef struct
Modified: trunk/src/common/net/messages.h
===================================================================
--- trunk/src/common/net/messages.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/common/net/messages.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -50,7 +50,7 @@
// NPC Networking version is separate so we don't have to break compatibility
// with clients to enhance the superclients. Made it a large number to ensure
// no inadvertent overlaps.
-#define PS_NPCNETVERSION 0x1029
+#define PS_NPCNETVERSION 0x102A
enum Slot_Containers
{
@@ -328,6 +328,7 @@
csRef<MsgEntry> msg;
bool valid;
+ int filterNumber;
psMessageCracker()
: msg(NULL),valid(true)
@@ -383,7 +384,7 @@
typedef psMessageCracker* (*psfMsgFactoryFunc)(MsgEntry* me, NetBase::AccessPointers* accessPointers);
csString GetMsgTypeName(int msgType);
-csString GetDecodedMessage(MsgEntry* me, NetBase::AccessPointers* accessPointers, bool filterhex);
+void DecodeMessage(MsgEntry* me, NetBase::AccessPointers* accessPointers, bool filterhex, csString& msgText, int& filterNumber);
void psfRegisterMsgFactoryFunction(psfMsgFactoryFunc factoryfunc, int msgtype, const char* msgtypename);
psMessageCracker* psfCreateMsg(int msgtype,
@@ -3057,10 +3058,11 @@
csVector2 start; ///<Start point of anmiation
csVector2 dest;///<Destination point of animation
csString loadWidget; ///< The widget to replace the load window with.
+ float vel; ///< The velocity of the actor
psForcePositionMessage() { }
psForcePositionMessage(uint32_t client, uint8_t sequence,
- const csVector3 &pos, float yRot, iSector* sector,
+ const csVector3 &pos, float yRot, iSector* sector, float vel,
csStringSet* msgstrings, uint32_t time = 0, csString loadBackground = "", csVector2 start = 0, csVector2 dest = 0, csString loadWidget = "");
psForcePositionMessage(MsgEntry* me, NetBase::AccessPointers* accessPointers);
Modified: trunk/src/common/net/netbase.cpp
===================================================================
--- trunk/src/common/net/netbase.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/common/net/netbase.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -934,8 +934,13 @@
void NetBase::LogMessages(char dir,MsgEntry* me)
{
if (DoLogDebug(LOG_MESSAGES) && !FilterLogMessage(me->bytes->type,dir))
- Debug3(LOG_MESSAGES,0,"%c: %s\n",dir,
- GetDecodedMessage(me, &accessPointers, logmsgfiltersetting.filterhex).GetData());
+ {
+ csString msgtext;
+ int filterNumber;
+ DecodeMessage(me, &accessPointers, logmsgfiltersetting.filterhex, msgtext, filterNumber);
+ Debug3(LOG_MESSAGES,filterNumber,"%c: %s\n",dir,msgtext.GetDataSafe());
+
+ }
}
Modified: trunk/src/common/net/npcmessages.cpp
===================================================================
--- trunk/src/common/net/npcmessages.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/common/net/npcmessages.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -568,6 +568,39 @@
break;
}
+ case psNPCCommandsMessage::CMD_CONTROL:
+ {
+ msgtext.Append("CMD_CONTROL: ");
+
+ // Extract the data
+ EID controllingEntity = EID(msg->GetUInt32());
+ // Extract the DR data
+ uint32_t len = 0;
+ void *data = msg->GetBufferPointerUnsafe(len);
+
+ // Make sure we haven't run past the end of the buffer
+ if (msg->overrun)
+ {
+ Debug2(LOG_SUPERCLIENT,msg->clientnum,"Received incomplete CMD_CONTROL from NPC client %u.\n",msg->clientnum);
+ break;
+ }
+
+ msgtext.AppendFmt("Controlling EID: %u ", controllingEntity.Unbox());
+
+ if ( (accessPointers->msgstrings || accessPointers->msgstringshash) && accessPointers->engine)
+ {
+ psDRMessage drmsg(data, len, accessPointers); // alternate method of cracking
+
+ msgtext.Append(drmsg.ToString(accessPointers));
+ }
+ else
+ {
+ msgtext.Append("Missing msg strings to decode");
+ }
+
+ break;
+ }
+
case psNPCCommandsMessage::CMD_DROP:
{
msgtext.Append("CMD_DROP: ");
@@ -726,6 +759,18 @@
msgtext.AppendFmt("Target: %u Attacker: %u ", target.Unbox(), attacker.Unbox());
break;
}
+ case psNPCCommandsMessage::PCPT_DEBUG_NPC:
+ {
+ msgtext.Append("PCPT_DEBUG_NPC: ");
+
+ // Extract the data
+ EID npc_eid = EID(msg->GetUInt32());
+ uint32_t clientnum = msg->GetUInt32();
+ uint8_t debugLevel = msg->GetUInt8();
+
+ msgtext.AppendFmt("NPC: %s Client: %ul debugLevel: %d", ShowID(npc_eid), clientnum, debugLevel);
+ break;
+ }
case psNPCCommandsMessage::PCPT_GROUPATTACK:
{
msgtext.Append("PCPT_GROUPATTACK: ");
@@ -1624,6 +1669,10 @@
case LOCATION_RADIUS:
radius = msgEntry->GetFloat();
break;
+ case LOCATION_SET_FLAG:
+ flags = msgEntry->GetStr();
+ enable = msgEntry->GetBool();
+ break;
case LOCATION_RENAME:
name = msgEntry->GetStr();
break;
@@ -1661,6 +1710,9 @@
case LOCATION_RENAME:
str.AppendFmt("Cmd: LOCATION_RENAME ID: %d Name: %s", id, name.GetDataSafe());
break;
+ case LOCATION_SET_FLAG:
+ str.AppendFmt("Cmd: LOCATION_SET_FLAG ID: %d Flag: %s Enable: %s", id, flags.GetDataSafe(),enable?"true":"false");
+ break;
case LOCATION_TYPE_ADD:
str.AppendFmt("Cmd: LOCATION_TYPE_ADD ID: %d Name: %s", id, typeName.GetDataSafe());
break;
Modified: trunk/src/common/net/npcmessages.h
===================================================================
--- trunk/src/common/net/npcmessages.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/common/net/npcmessages.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -202,7 +202,8 @@
enum PerceptionType
{
- CMD_TERMINATOR, // cmds go from superclient to server
+ // Commands go from superclient to server
+ CMD_TERMINATOR,
CMD_ASSESS,
CMD_ATTACK,
CMD_BUSY,
@@ -219,21 +220,24 @@
CMD_SEQUENCE,
CMD_SIT,
CMD_SPAWN,
- CMD_SPAWN_BUILDING, // used for superclient to request a new building
+ CMD_SPAWN_BUILDING, // used for superclient to request a new building
CMD_TALK,
CMD_TRANSFER,
CMD_VISIBILITY,
CMD_WORK,
- PCPT_ANYRANGEPLAYER, // perceptions go from server to superclient
+ CMD_CONTROL,
+ // Perceptions go from server to superclient
+ PCPT_ANYRANGEPLAYER,
PCPT_ASSESS,
PCPT_ATTACK,
- PCPT_CHANGE_BRAIN, //Command to superclient used to change the brain of an npc.
+ PCPT_CHANGE_BRAIN, // Command to superclient used to change the brain of an npc.
PCPT_DEATH,
+ PCPT_DEBUG_NPC, // Command the superclient to change the debug level of an npc.
PCPT_DMG,
PCPT_FAILED_TO_ATTACK,
PCPT_FLAG,
PCPT_GROUPATTACK,
- PCPT_INFO_REQUEST, //Command to superclient, not a perception
+ PCPT_INFO_REQUEST, // Command to superclient, not a perception
PCPT_INVENTORY,
PCPT_LONGRANGEPLAYER,
PCPT_NPCCMD,
Modified: trunk/src/npcclient/networkmgr.cpp
===================================================================
--- trunk/src/npcclient/networkmgr.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/npcclient/networkmgr.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -933,14 +933,17 @@
bool NetworkManager::ReceiveNPCList(MsgEntry *msg)
{
- uint32_t length, pid, eid;
+ uint32_t length;
+ PID pid;
+ EID eid;
length = msg->GetUInt32();
CPrintf(CON_WARNING, "Received list of %i NPCs.\n", length);
for (unsigned int x=0; x<length; x++)
{
- pid = msg->GetUInt32();
- eid = msg->GetUInt32();
+ pid = PID(msg->GetUInt32());
+ eid = EID(msg->GetUInt32());
+ CPrintf(CON_WARNING, "%s %s\n", ShowID(pid),ShowID(eid));
}
return true;
@@ -1089,6 +1092,26 @@
npc->TriggerEvent(&attack);
break;
}
+ case psNPCCommandsMessage::PCPT_DEBUG_NPC:
+ {
+ EID npc_eid = EID(list.msg->GetUInt32());
+ uint32_t clientNum = list.msg->GetUInt32();
+ uint8_t debugLevel = list.msg->GetUInt8();
+
+ NPC *npc = npcclient->FindNPC(npc_eid);
+
+ if (!npc)
+ {
+ QueueSystemInfoCommand(clientNum,"NPCClient: No NPC found");
+ break;
+ }
+
+ npc->AddDebugClient(clientNum);
+ npc->SetDebugging(debugLevel);
+
+ QueueSystemInfoCommand(clientNum,"NPCCleint: Debug level set to %d for %s(%s)",debugLevel,npc->GetName(),ShowID(npc->GetEID()));
+ break;
+ }
case psNPCCommandsMessage::PCPT_GROUPATTACK:
{
EID targetEID = EID(list.msg->GetUInt32());
@@ -1518,15 +1541,11 @@
csString reply("NPCClient: ");
reply.Append(npc->Info());
- QueueInfoReplyCommand(clientNum,reply);
+ QueueSystemInfoCommand(clientNum,reply);
- reply = "";
- reply.AppendFmt(" Behaviors: %s",npc->GetBrain()->InfoBehaviors(npc).GetDataSafe());
- QueueInfoReplyCommand(clientNum,reply);
+ QueueSystemInfoCommand(clientNum," Behaviors: %s",npc->GetBrain()->InfoBehaviors(npc).GetDataSafe());
- reply = "";
- reply.AppendFmt(" Reactions: %s",npc->GetBrain()->InfoReactions(npc).GetDataSafe());
- QueueInfoReplyCommand(clientNum,reply);
+ QueueSystemInfoCommand(clientNum," Reactions: %s",npc->GetBrain()->InfoReactions(npc).GetDataSafe());
break;
@@ -1541,21 +1560,35 @@
NPC *npc = npcclient->FindNPC(npc_eid);
if (!npc)
+ {
+ QueueSystemInfoCommand(clientNum,"NPC not found");
break;
+ }
- npc->Printf("Got change brain request to %s.", brainType.GetDataSafe());
+ npc->Printf("Got change brain request to %s from client %u.", brainType.GetDataSafe(), clientNum);
+ if (brainType == "reload")
+ {
+ brainType = npc->GetBrain()->GetName();
+ if (!npcclient->LoadNPCTypes())
+ {
+ QueueSystemInfoCommand(clientNum,"Failed to reload npctypes.");
+ break;
+ }
+ }
+
//search for the requested npc type
-
NPCType* type = npcclient->FindNPCType(brainType.GetDataSafe());
if (!type)
{
Error2("NPC type '%s' is not found",(const char *)brainType);
+ QueueSystemInfoCommand(clientNum,"Brain not found");
break;
}
//if found set it
npc->SetBrain(type, npcclient->GetEventManager());
+ QueueSystemInfoCommand(clientNum,"Brain changed");
break;
}
@@ -1998,17 +2031,26 @@
cmd_count++;
}
-void NetworkManager::QueueInfoReplyCommand(uint32_t clientNum,const char* reply)
+void NetworkManager::QueueSystemInfoCommand(uint32_t clientNum, const char* reply, ...)
{
- CheckCommandsOverrun(sizeof(int8_t)+sizeof(uint32_t)+(strlen(reply)+1));
+ // Format the reply
+ va_list args;
+ va_start(args, reply);
+ char str[1024];
+ vsprintf(str, reply, args);
+ va_end(args);
+
+ // Queue the System Info
+ CheckCommandsOverrun(sizeof(int8_t)+sizeof(uint32_t)+(strlen(str)+1));
+
outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_INFO_REPLY);
outbound->msg->Add( clientNum );
- outbound->msg->Add( reply );
+ outbound->msg->Add( str );
if ( outbound->msg->overrun )
{
- CS_ASSERT(!"NetworkManager::QueueInfoReplyCommand put message in overrun state!\n");
+ CS_ASSERT(!"NetworkManager::QueueSystemInfoCommand put message in overrun state!\n");
}
cmd_count++;
@@ -2069,7 +2111,25 @@
cmd_count++;
}
+void NetworkManager::QueueControlCommand(gemNPCActor* controllingEntity, gemNPCActor* controlledEntity)
+{
+ CheckCommandsOverrun(sizeof(int8_t)+sizeof(uint32_t)+100);
+ outbound->msg->Add( (int8_t) psNPCCommandsMessage::CMD_CONTROL);
+ outbound->msg->Add( controllingEntity->GetEID().Unbox() );
+
+ psDRMessage drmsg(0,controlledEntity->GetEID(),0,connection->GetAccessPointers(),controlledEntity->pcmove);
+ outbound->msg->Add( drmsg.msg->bytes->payload,(uint32_t)drmsg.msg->bytes->GetTotalSize() );
+
+ if ( outbound->msg->overrun )
+ {
+ CS_ASSERT(!"NetworkManager::QueueControlCommand put message in overrun state!\n");
+ }
+
+ cmd_count++;
+}
+
+
void NetworkManager::SendAllCommands(bool final)
{
// If this is the final send all for a tick we need to check if any NPCs has been queued for sending of DR data.
Modified: trunk/src/npcclient/networkmgr.h
===================================================================
--- trunk/src/npcclient/networkmgr.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/npcclient/networkmgr.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -187,11 +187,17 @@
void QueueResurrectCommand(csVector3 where, float rot, iSector* sector, PID character_id);
void QueueSequenceCommand(csString name, int cmd, int count);
void QueueImperviousCommand(gemNPCActor * entity, bool impervious);
- void QueueInfoReplyCommand(uint32_t clientNum,const char* reply);
+
+ /** Send an info info command to the client.
+ */
+ void QueueSystemInfoCommand(uint32_t clientNum, const char* reply, ...);
+
void QueueAssessCommand(gemNPCActor* entity, gemNPCObject* target, const csString& physicalAssessmentPerception,
const csString& magicalAssessmentPerception, const csString& overallAssessmentPerception);
void QueueCastCommand(gemNPCActor* entity, gemNPCObject* target, const csString& spell, float kFactor);
void QueueBusyCommand(gemNPCActor* entity, bool busy );
+
+ void QueueControlCommand(gemNPCActor* controllingEntity, gemNPCActor* controlledEntity );
void SendAllCommands(bool final = false);
Modified: trunk/src/npcclient/npc.cpp
===================================================================
--- trunk/src/npcclient/npc.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/npcclient/npc.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -254,6 +254,17 @@
{
CPrintf(CON_CMDOUTPUT, "Empty\n");
}
+
+ csString controlled;
+ for (size_t i = 0; i < controlledActors.GetSize(); i++)
+ {
+ if (controlledActors[i].IsValid())
+ {
+ controlled.AppendFmt(" %s",controlledActors[i]->GetName());
+ }
+ }
+ CPrintf(CON_CMDOUTPUT, "Controlled: %s\n",controlled.GetDataSafe());
+
}
EID NPC::GetEID()
@@ -413,6 +424,8 @@
if (last_update && !disabled)
{
brain->Advance(when-last_update, this);
+
+ UpdateControlled();
}
last_update = when;
@@ -559,9 +572,9 @@
return activeLocate->radius;
}
-bool NPC::CopyLocate(csString source, csString destination)
+bool NPC::CopyLocate(csString source, csString destination, unsigned int flags)
{
- Printf(5,"Copy locate from %s to %s",source.GetDataSafe(),destination.GetDataSafe());
+ Printf(5,"Copy locate from %s to %s (%X)",source.GetDataSafe(),destination.GetDataSafe(),flags);
Locate* sourceLocate = storedLocates.Get(source,NULL);
if (!sourceLocate)
@@ -577,11 +590,34 @@
storedLocates.PutUnique(destination,destinationLocation);
}
- destinationLocation->pos = sourceLocate->pos;
- destinationLocation->sector = sourceLocate->sector;
- destinationLocation->angle = sourceLocate->angle;
- destinationLocation->wp = sourceLocate->wp;
- destinationLocation->radius = sourceLocate->radius;
+ if (flags & LOCATION_POS)
+ {
+ destinationLocation->pos = sourceLocate->pos;
+ }
+ if (flags & LOCATION_SECTOR)
+ {
+ destinationLocation->sector = sourceLocate->sector;
+ }
+ if (flags & LOCATION_ANGLE)
+ {
+ destinationLocation->angle = sourceLocate->angle;
+ }
+ if (flags & LOCATION_WP)
+ {
+ destinationLocation->wp = sourceLocate->wp;
+ }
+ if (flags & LOCATION_RADIUS)
+ {
+ destinationLocation->radius = sourceLocate->radius;
+ }
+ if (flags & LOCATION_TARGET)
+ {
+ destinationLocation->target = sourceLocate->target;
+ if (destination == "Active")
+ {
+ SetTarget(destinationLocation->target);
+ }
+ }
return true;
}
@@ -675,6 +711,12 @@
CPrintf(CON_CMDOUTPUT, "Rotation: %.2f\n",rot);
CPrintf(CON_CMDOUTPUT, "Instance: %d\n",instance);
CPrintf(CON_CMDOUTPUT, "Debugging: %d\n",debugging);
+ csString clients;
+ for (size_t i = 0; i < debugClients.GetSize(); i++)
+ {
+ clients.AppendFmt(" %u",debugClients[i]);
+ }
+ CPrintf(CON_CMDOUTPUT, "Debug clients: %s\n",clients.GetDataSafe());
CPrintf(CON_CMDOUTPUT, "DR Counter: %d\n",DRcounter);
CPrintf(CON_CMDOUTPUT, "Alive: %s\n",alive?"True":"False");
CPrintf(CON_CMDOUTPUT, "Disabled: %s\n",disabled?"True":"False");
@@ -1012,9 +1054,10 @@
void NPC::Printf(const char *msg,...)
{
va_list args;
- if(!IsDebugging())
- return;
+ if(!IsDebugging())
+ return;
+
va_start(args, msg);
VPrintf(5,msg,args);
va_end(args);
@@ -1022,32 +1065,22 @@
void NPC::Printf(int debug, const char *msg,...)
{
- char str[1024];
va_list args;
- if(!IsDebugging())
- return;
+ if(!IsDebugging())
+ return;
va_start(args, msg);
- vsprintf(str, msg, args);
+ VPrintf(debug,msg,args);
va_end(args);
-
- // Add string to the internal log buffer
- debugLog[nextDebugLogEntry] = str;
- nextDebugLogEntry = (nextDebugLogEntry+1)%debugLog.GetSize();
-
- if (!IsDebugging(debug))
- return;
-
- CPrintf(CON_CMDOUTPUT, "%s (%s)> %s\n", GetName(), ShowID(pid), str);
}
void NPC::VPrintf(int debug, const char *msg, va_list args)
{
char str[1024];
- if(!IsDebugging())
- return;
+ if(!IsDebugging())
+ return;
vsprintf(str, msg, args);
@@ -1059,6 +1092,11 @@
return;
CPrintf(CON_CMDOUTPUT, "%s (%s)> %s\n", GetName(), ShowID(pid), str);
+
+ for (size_t i = 0; i < debugClients.GetSize(); i++)
+ {
+ networkmanager->QueueSystemInfoCommand(debugClients[i],"%s (%s)> %s", GetName(), ShowID(pid), str);
+ }
}
gemNPCObject* NPC::GetTarget()
@@ -1168,7 +1206,49 @@
}
+void NPC::TakeControl(gemNPCActor* actor)
+{
+ controlledActors.PushSmart(csWeakRef<gemNPCActor>(actor));
+}
+void NPC::ReleaseControl(gemNPCActor* actor)
+{
+ controlledActors.Delete(csWeakRef<gemNPCActor>(actor));
+}
+
+void NPC::UpdateControlled()
+{
+ if (controlledActors.IsEmpty())
+ {
+ return;
+ }
+
+ csVector3 pos,vel;
+ float yrot;
+ iSector* sector;
+
+ psGameObject::GetPosition(GetActor(), pos, yrot, sector);
+ vel = GetLinMove()->GetVelocity();
+
+ for (size_t i = 0; i < controlledActors.GetSize(); i++)
+ {
+ if (controlledActors[i].IsValid())
+ {
+ gemNPCActor* controlled = controlledActors[i];
+
+ // TODO: Calculate some offset from controlled to controlling
+
+ // For now use controlling position for controlled
+ psGameObject::SetPosition(controlled, pos, sector);
+ psGameObject::SetRotationAngle(controlled, yrot);
+ controlled->pcmove->SetVelocity(vel);
+
+ // Queue the new controlled position to the server.
+ networkmanager->QueueControlCommand(GetActor(), controlled );
+ }
+ }
+}
+
void NPC::CheckPosition()
{
// We only need to check the position once
@@ -1287,6 +1367,9 @@
void NPC::ReplaceBuffers(csString& result)
{
+ // Only replace if there is something to replace
+ if (result.Find("$NBUFFER[") == ((size_t)-1) ) return;
+
BufferHash::GlobalIterator iter = npcBuffer.GetIterator();
while (iter.HasNext())
{
@@ -1300,8 +1383,83 @@
}
}
+void NPC::ReplaceLocations(csString& result)
+{
+ size_t startPos,endPos;
+
+ // Only replace if there is something to replace
+ startPos = result.Find("$LOCATION[");
+ while (startPos != ((size_t)-1))
+ {
+ endPos = result.FindFirst(']',startPos);
+ if (endPos == ((size_t)-1)) return; // There should always be a ] after $LOCATION
+ csString locationString = result.Slice(startPos+10,endPos-(startPos+10));
+ csArray<csString> strArr = psSplit(locationString,'.');
+ if (strArr.GetSize() != 2) return; // Should always be a location and an attribute.
+
+ NPC::Locate* location = storedLocates.Get(strArr[0],NULL);
+ if (!location)
+ {
+ Error4("NPC %s(%s) Failed to find location %s in replace locations",
+ GetName(),ShowID(GetEID()),strArr[0].GetDataSafe());
+ return; // Failed to find location
+ }
+
+ csString replace;
+
+
+ if (strArr[1].CompareNoCase("targetEID"))
+ {
+ if (location->target.IsValid())
+ {
+ replace = ShowID(location->target->GetEID());
+ }
+ }
+ else if (strArr[1].CompareNoCase("targetName"))
+ {
+ if (location->target.IsValid())
+ {
+ replace = location->target->GetName();
+ }
+ } else
+ {
+ Error5("NPC %s(%s) Failed to find find attribute %s for location %s in replace locations",
+ GetName(),ShowID(GetEID()),strArr[1].GetDataSafe(),strArr[0].GetDataSafe());
+ return; // Not implemented or unkown attribute.
+ }
+
+ result.DeleteAt(startPos,endPos-startPos+1);
+ result.Insert(startPos,replace);
+
+ startPos = result.Find("$LOCATION[");
+ }
+}
+
+bool NPC::SwitchDebugging()
+{
+ debugging = !debugging;
+ return IsDebugging();
+}
+
+void NPC::SetDebugging(int debug)
+{
+ debugging = debug;
+}
+
+void NPC::AddDebugClient(uint clientnum)
+{
+ debugClients.PushSmart(clientnum);
+}
+
+void NPC::RemoveDebugClient(uint clientnum)
+{
+ debugClients.Delete(clientnum);
+}
+
+
+
//-----------------------------------------------------------------------------
void HateList::AddHate(EID entity_id, float delta)
Modified: trunk/src/npcclient/npc.h
===================================================================
--- trunk/src/npcclient/npc.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/npcclient/npc.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -37,6 +37,7 @@
//=============================================================================
//#include "npcbehave.h"
#include "tribe.h"
+#include "gem.h"
class Behavior;
class EventManager;
@@ -106,14 +107,27 @@
/** Structure to hold located positions */
typedef struct
{
- csVector3 pos; ///< The position of the located object
- iSector* sector; ///< The sector for the located object
- float angle; ///< The angle of the located object
- Waypoint* wp; ///< The nearest waypoint to the located object
- float radius; ///< The radius of the located object
+ csVector3 pos; ///< The position of the located object
+ iSector* sector; ///< The sector for the located object
+ float angle; ///< The angle of the located object
+ Waypoint* wp; ///< The nearest waypoint to the located object
+ float radius; ///< The radius of the located object
+ csWeakRef<gemNPCObject> target; ///< The located target
} Locate;
typedef csHash<Locate*,csString> LocateHash;
+ enum
+ {
+ LOCATION_NONE = 0,
+ LOCATION_POS = 1,
+ LOCATION_SECTOR = 2,
+ LOCATION_ANGLE = 4,
+ LOCATION_WP = 8,
+ LOCATION_RADIUS = 16,
+ LOCATION_TARGET = 32,
+ LOCATION_ALL = -1
+ };
+
protected:
typedef csHash<csString,csString> BufferHash;
@@ -151,6 +165,8 @@
RaceInfo_t *raceInfo;
+ csArray< csWeakRef<gemNPCActor> > controlledActors; ///< Actors that are dragged/pushed around by this NPC.
+
// Initial position checks
csVector3 checkedPos;
iSector* checkedSector;
@@ -330,19 +346,30 @@
*
* Typically used to take backups of "Active" locate.
*/
- bool CopyLocate(csString source, csString destination);
+ bool CopyLocate(csString source, csString destination, unsigned int flags);
+
+ /** Replace $LOCATION[<location>.<attribute>]
+ */
+ void ReplaceLocations(csString& result);
+
+ /** Switch the debuging state of this NPC.
+ */
+ bool SwitchDebugging();
+ /** Set a new debug level for this NPC.
+ * @param debug New debug level, 0 is no debugging
+ */
+ void SetDebugging(int debug);
- bool SwitchDebugging()
- {
- debugging = !debugging;
- return IsDebugging();
- }
+ /** Add a client to receive debug information
+ * @param clentnum The client to add.
+ */
+ void AddDebugClient(uint clientnum);
- void SetDebugging(int debug)
- {
- debugging = debug;
- }
+ /** Remove client from list of debug receivers.
+ * @param clentnum The client to remove.
+ */
+ void RemoveDebugClient(uint clientnum);
float GetAngularVelocity();
float GetVelocity();
@@ -391,8 +418,9 @@
*/
gemNPCActor* GetNearestPlayer(float range, csVector3 &destPosition, iSector* &destSector, float &destRange);
- gemNPCActor * GetNearestVisibleFriend(float range);
- gemNPCActor * GetNearestDeadActor(float range);
+ gemNPCActor* GetNearestVisibleFriend(float range);
+
+ gemNPCActor* GetNearestDeadActor(float range);
void Printf(const char *msg,...);
void Printf(int debug, const char *msg,...);
@@ -438,10 +466,22 @@
*/
void SetInsideTribeHome(bool inside) { insideTribeHome = inside; }
-
+ /** Get the npc race info
+ */
RaceInfo_t * GetRaceInfo();
+ /** Take control of another entity.
+ */
+ void TakeControl(gemNPCActor* actor);
+ /** Release control of another controlled entity.
+ */
+ void ReleaseControl(gemNPCActor* actor);
+
+ /**
+ */
+ void UpdateControlled();
+
bool IsDebugging() { return (debugging > 0);};
bool IsDebugging(int debug) { return (debugging > 0 && debug <= debugging);};
@@ -518,7 +558,8 @@
friend class psNPCTick;
csArray<csString> debugLog; ///< Local debug log of last n print statments for this NPC.
- int nextDebugLogEntry; ///< The next entry to use.
+ int nextDebugLogEntry; ///< The next entry to use.
+ csArray<uint> debugClients; ///< List of clients doing debugging
};
// The event that makes the NPC brain go TICK.
Modified: trunk/src/npcclient/npcbehave.cpp
===================================================================
--- trunk/src/npcclient/npcbehave.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/npcclient/npcbehave.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -1066,6 +1066,10 @@
{
op = new CopyLocateOperation;
}
+ else if ( strcmp( node->GetValue(), "control" ) == 0 )
+ {
+ op = new ControlOperation(true);
+ }
else if ( strcmp( node->GetValue(), "circle" ) == 0 )
{
op = new CircleOperation;
@@ -1148,6 +1152,10 @@
{
op = new PickupOperation;
}
+ else if ( strcmp( node->GetValue(), "release_control" ) == 0 )
+ {
+ op = new ControlOperation(false);
+ }
else if ( strcmp( node->GetValue(), "reproduce" ) == 0 )
{
op = new ReproduceOperation;
@@ -1721,10 +1729,19 @@
csString psGameObject::ReplaceNPCVariables(NPC* npc, const csString& object)
{
- csString result(object);
+ // Check if there are any $ sign in the string. If not there is no more to do
+ if (object.FindFirst('$') == ((size_t)-1)) return object;
+ // Now check if any of the $ signs is something to replace
+ csString result(object); // Result will hold the string with all variables replaced
+
// First replace buffers, this so that keywords can be put into buffers as well.
npc->ReplaceBuffers(result);
+
+ // Replace locations
+ npc->ReplaceLocations(result);
+
+ // Replace tribe stuff
if (npc->GetTribe())
{
npc->GetTribe()->ReplaceBuffers(result);
@@ -1749,7 +1766,13 @@
result.ReplaceAll("$target",npc->GetTarget()->GetName());
}
+ if (npc->GetLastPerception())
+ {
+ result.ReplaceAll("$perception_type",npc->GetLastPerception()->GetType());
+ }
+ npc->Printf(10,"Replaced variables in '%s' to get '%s'",object.GetDataSafe(),result.GetDataSafe());
+
return result;
}
Modified: trunk/src/npcclient/npcclient.cpp
===================================================================
--- trunk/src/npcclient/npcclient.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/npcclient/npcclient.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -459,6 +459,7 @@
bool psNPCClient::LoadNPCTypes(iDocumentNode* root)
{
+
csRef<iDocumentNode> topNode = root->GetNode("npctypes");
if(!topNode)
{
@@ -495,6 +496,13 @@
bool psNPCClient::LoadNPCTypes()
{
+ // First clear the npctypes in case load npc types is used as a reload.
+ csHash<NPCType*, const char*>::GlobalIterator npcTypeIter(npctypes.GetIterator());
+ while (npcTypeIter.HasNext())
+ delete npcTypeIter.Next();
+ npctypes.Empty();
+
+
csArray<unsigned long> postponedNPCTypeID;
Result rs(db->Select("SELECT * from sc_npctypes ORDER BY id"));
Modified: trunk/src/npcclient/npcoperations.cpp
===================================================================
--- trunk/src/npcclient/npcoperations.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/npcclient/npcoperations.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -252,24 +252,19 @@
}
}
-bool ScriptOperation::CheckMoveOk(NPC *npc, csVector3 oldPos, iSector* oldSector, const csVector3 & newPos, iSector* newSector, float timedelta)
+bool ScriptOperation::CheckMoveOk(NPC *npc, csVector3 oldPos, iSector* oldSector, const csVector3 & newPos, iSector* newSector, int resultFromExtrapolate )
{
- npcMesh* pcmesh = npc->GetActor()->pcmesh;
-
+ // Check if sectors where crossed correctly
if (!npcclient->GetWorld()->WarpSpace(oldSector, newSector, oldPos))
{
npc->Printf("CheckMoveOk: new and old sectors are not connected by a portal!");
return false;
}
- float velocity = npc->GetLinMove()->GetVelocity().Norm();
- float moveLimit = 0.5*velocity*timedelta; // 1/2 the distance that should have been travelled.
- float movedDistance = (oldPos - newPos).Norm();
-
- if (movedDistance < moveLimit) // then stopped dead, presumably by collision
+ // Check result from extrapolation
+ if (resultFromExtrapolate == PS_MOVE_FAIL || resultFromExtrapolate == PS_MOVE_DONTMOVE)
{
- npc->Printf(5,"Moved %.3f m at %0.3f m/s in %0.3f s limit: %.3f m -> presumably collided",
- movedDistance, velocity, timedelta, moveLimit);
+ npc->Printf(5, "Moved failed -> presumably collided");
// We collided. Now stop the movment and inform the server.
StopMovement(npc);
@@ -278,23 +273,39 @@
SendCollitionPerception(npc);
return false;
+
+ } else if (resultFromExtrapolate == PS_MOVE_PARTIAL)
+ {
+ consecCollisions++;
+ npc->Printf(10, "Bang. %d consec collisions...", consecCollisions);
+
+ if (consecCollisions > 8) // allow for hitting trees but not walls
+ {
+
+ // Now we collided so may times, that we stop
+ StopMovement(npc);
+
+ // After a couple seconds of sliding against something
+ // the npc should give up and react to the obstacle.
+ // Send perception for collision
+ SendCollitionPerception(npc);
+
+ return false;
+ }
+
+ // We have not ended up in the expected positon. Send updated position to server.
+ npcclient->GetNetworkMgr()->QueueDRData(npc);
}
else
{
- csVector3 velvec(0,0,-GetVelocity(npc) );
- // Check for non-stationary collisions
- csReversibleTransform rt = pcmesh->GetMesh()->GetMovable()->GetFullTransform();
- csMatrix3 mat = rt.GetT2O();
- csVector3 expected_pos;
- expected_pos = mat*(velvec*timedelta) + oldPos;
+ consecCollisions = 0;
+ }
+
- float diffExpectedX = fabs(newPos.x - expected_pos.x);
- float diffExpectedZ = fabs(newPos.z - expected_pos.z);
-
-
+ // Check if this NPC is falling.
+ {
float diffPositionY = fabs(newPos.y - oldPos.y);
- // Check if this NPC is falling.
if (fabs(diffPositionY) > 800.0 || fabs(newPos.y) > 800.0 )
{
@@ -316,34 +327,10 @@
return false;
}
-
- if (diffExpectedX > EPSILON || diffExpectedZ > EPSILON)
- {
- consecCollisions++;
- npc->Printf(10,"Bang. %d consec collisions last with diffs (%1.2f,%1.2f)...",
- consecCollisions,diffExpectedX,diffExpectedZ);
- if (consecCollisions > 8) // allow for hitting trees but not walls
- {
-
- // Now we collided so may times, that we stop
- StopMovement(npc);
-
- // After a couple seconds of sliding against something
- // the npc should give up and react to the obstacle.
- // Send perception for collision
- SendCollitionPerception(npc);
-
- return false;
- }
-
- // We have not ended up in the expected positon. Send updated position to server.
- npcclient->GetNetworkMgr()->QueueDRData(npc);
- }
- else
- {
- consecCollisions = 0;
- }
-
+ }
+
+ // Check regions
+ {
LocationType *rgn = npc->GetRegion();
if (rgn)
@@ -384,7 +371,10 @@
{
npc->Printf(10, "No region to check for bounds.");
}
-
+ }
+
+ // Check tribe home
+ {
// If tribehome checking has been enabled and npc is in a tribe
if (checkTribeHome && npc->GetTribe())
@@ -859,10 +849,10 @@
npc->Printf(8, "advance: pos=%s rot=%.2f dest=%s dist=%.3f timedelta=%.3f",
toString(myPos,mySector).GetDataSafe(), myRot,
dest->GetPosition().Description().GetData(),distance,timedelta);
-
+ int ret;
{
ScopedTimer st(250, "Movement extrapolate %.2f time for %s", timedelta, ShowID(npc->GetActor()->GetEID()));
- npc->GetLinMove()->ExtrapolatePosition(timedelta);
+ ret = npc->GetLinMove()->ExtrapolatePosition(timedelta);
}
{
@@ -875,7 +865,7 @@
npc->Printf(8,"World position bodyVel=%s worldVel=%s",
toString(bodyVel).GetDataSafe(),toString(worldVel).GetDataSafe());
- CheckMoveOk(npc, myPos, mySector, myNewPos, myNewSector, timedelta);
+ CheckMoveOk(npc, myPos, mySector, myNewPos, myNewSector, ret);
}
return OPERATION_NOT_COMPLETED;
@@ -1441,6 +1431,82 @@
return false;
}
+ flags = NPC::LOCATION_ALL;
+
+ if (node->GetAttribute("only"))
+ {
+ csArray<csString> strArr = psSplit(node->GetAttributeValue("only"),',');
+ flags = NPC::LOCATION_NONE;
+ for (size_t i = 0; i < strArr.GetSize(); i++)
+ {
+ if (strArr[i].CompareNoCase("pos"))
+ {
+ flags |= NPC::LOCATION_POS;
+ }
+ else if (strArr[i].CompareNoCase("sector"))
+ {
+ flags |= NPC::LOCATION_SECTOR;
+ }
+ else if (strArr[i].CompareNoCase("angle"))
+ {
+ flags |= NPC::LOCATION_ANGLE;
+ }
+ else if (strArr[i].CompareNoCase("WP"))
+ {
+ flags |= NPC::LOCATION_WP;
+ }
+ else if (strArr[i].CompareNoCase("radius"))
+ {
+ flags |= NPC::LOCATION_RADIUS;
+ }
+ else if (strArr[i].CompareNoCase("target"))
+ {
+ flags |= NPC::LOCATION_TARGET;
+ }
+ else
+ {
+ Error2("Unknown flag '%s' in only attribute in copy_location operation",strArr[i].GetDataSafe());
+ return false;
+ }
+ }
+ }
+ if (node->GetAttribute("ignore"))
+ {
+ csArray<csString> strArr = psSplit(node->GetAttributeValue("ignore"),',');
+ for (size_t i = 0; i < strArr.GetSize(); i++)
+ {
+ if (strArr[i].CompareNoCase("pos"))
+ {
+ flags &= ~NPC::LOCATION_POS;
+ }
+ else if (strArr[i].CompareNoCase("sector"))
+ {
+ flags &= ~NPC::LOCATION_SECTOR;
+ }
+ else if (strArr[i].CompareNoCase("angle"))
+ {
+ flags &= ~NPC::LOCATION_ANGLE;
+ }
+ else if (strArr[i].CompareNoCase("WP"))
+ {
+ flags &= ~NPC::LOCATION_WP;
+ }
+ else if (strArr[i].CompareNoCase("radius"))
+ {
+ flags &= ~NPC::LOCATION_RADIUS;
+ }
+ else if (strArr[i].CompareNoCase("target"))
+ {
+ flags &= ~NPC::LOCATION_TARGET;
+ }
+ else
+ {
+ Error2("Unknown flag '%s' in only attribute in copy_location operation",strArr[i].GetDataSafe());
+ return false;
+ }
+ }
+ }
+
return true;
}
@@ -1449,6 +1515,7 @@
CopyLocateOperation *op = new CopyLocateOperation;
op->source = source;
op->destination = destination;
+ op->flags = flags;
return op;
}
@@ -1457,7 +1524,7 @@
csString sourceVariablesReplaced = psGameObject::ReplaceNPCVariables(npc,source);
csString destinationVariablesReplaced = psGameObject::ReplaceNPCVariables(npc, destination);
- if (!npc->CopyLocate(sourceVariablesReplaced, destinationVariablesReplaced))
+ if (!npc->CopyLocate(sourceVariablesReplaced, destinationVariablesReplaced, flags))
{
return OPERATION_FAILED;
}
@@ -1530,6 +1597,44 @@
//---------------------------------------------------------------------------
+bool ControlOperation::Load(iDocumentNode *node)
+{
+ return true;
+}
+
+ScriptOperation *ControlOperation::MakeCopy()
+{
+ ControlOperation *op = new ControlOperation(control);
+
+ return op;
+}
+
+ScriptOperation::OperationResult ControlOperation::Run(NPC *npc, bool interrupted)
+{
+ gemNPCActor* target = dynamic_cast<gemNPCActor*>(npc->GetTarget());
+
+ if (!target)
+ {
+ npc->Printf(5, " Nothing to Control");
+ return OPERATION_FAILED;
+ }
+
+ if (control)
+ {
+ npc->Printf(5, " Take Control %s", target->GetName());
+ npc->TakeControl(target);
+ }
+ else
+ {
+ npc->Printf(5, " Release Control %s", target->GetName());
+ npc->ReleaseControl(target);
+ }
+
+ return OPERATION_COMPLETED; // Nothing more to do for this op.
+}
+
+//---------------------------------------------------------------------------
+
bool DebugOperation::Load(iDocumentNode *node)
{
exclusive = node->GetAttributeValue("exclusive");
@@ -1880,10 +1985,18 @@
// Some more validation checks on obj.
csArray<csString> split_obj = psSplit(object,':');
- if (split_obj[0] == "building_spot")
+ if (split_obj[0] == "actor")
{
return true;
}
+ else if (split_obj[0] == "building_spot")
+ {
+ return true;
+ }
+ else if (split_obj[0] == "dead")
+ {
+ return true;
+ }
else if (split_obj[0] == "entity")
{
if (split_obj.GetSize() < 2)
@@ -1921,10 +2034,6 @@
{
return true;
}
- else if (split_obj[0] == "point")
- {
- return true;
- }
else if (split_obj[0] == "ownbuffer")
{
return true;
@@ -1937,6 +2046,18 @@
{
return true;
}
+ else if (split_obj[0] == "perception_type")
+ {
+ return true;
+ }
+ else if (split_obj[0] == "player")
+ {
+ return true;
+ }
+ else if (split_obj[0] == "point")
+ {
+ return true;
+ }
else if (split_obj[0] == "region")
{
return true;
@@ -2051,9 +2172,6 @@
ScriptOperation::OperationResult LocateOperation::Run(NPC *npc, bool interrupted)
{
- // Reset old target
- npc->SetTarget(NULL);
-
NPC::Locate located;
located.pos = csVector3(0.0f,0.0f,0.0f);
@@ -2061,20 +2179,48 @@
located.sector = NULL;
located.wp = NULL;
located.radius = 0.0f;
+ // located.target initialize to NULL through constructor.
float start_rot;
iSector *start_sector;
csVector3 start_pos;
psGameObject::GetPosition(npc->GetActor(),start_pos,start_rot,start_sector);
+
+ npc->Printf(5, "LocateOp - Locate object '%s' destination '%s'", object.GetDataSafe(),destination.GetDataSafe());
+
csString objectReplacedVariables = psGameObject::ReplaceNPCVariables(npc, object);
csString destinationVariablesReplaced = psGameObject::ReplaceNPCVariables(npc, destination);
csArray<csString> split_obj = psSplit(objectReplacedVariables,':');
- if (split_obj[0] == "building_spot")
+ if (split_obj[0] == "actor")
{
+ npc->Printf(5,"LocateOp - Actor");
+
+ iSector *sector;
+ csVector3 pos;
+ float destRange;
+ gemNPCActor* ent = npc->GetNearestActor(range,pos,sector,destRange);
+
+ if(ent)
+ {
+ located.target = ent;
+ }
+ else
+ {
+ return OPERATION_FAILED; // Nothing more to do for this op.
+ }
+
+ located.pos = pos;
+ located.angle = 0;
+ located.sector = sector;
+
+ located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
+ }
+ else if (split_obj[0] == "building_spot")
+ {
npc->Printf(5,"LocateOp - Building spot");
Tribe::Asset* buildingSpot = npc->GetBuildingSpot();
@@ -2096,24 +2242,54 @@
return OPERATION_FAILED;
}
}
+ else if (split_obj[0] == "dead")
+ {
+ npc->Printf(5,"LocateOp - Dead");
+
+ gemNPCActor* ent = npc->GetNearestDeadActor(range);
+
+ if(ent)
+ {
+ located.target = ent;
+ }
+ else
+ {
+ return OPERATION_FAILED; // Nothing more to do for this op.
+ }
+
+ float rot;
+ iSector *sector;
+ csVector3 pos;
+ psGameObject::GetPosition(ent,pos,rot,sector);
+
+ located.pos = pos;
+ located.angle = 0;
+ located.sector = sector;
+
+ located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
+ }
else if (split_obj[0] == "entity")
{
npc->Printf(5,"LocateOp - Entity");
gemNPCObject *entity = NULL;
- if (split_obj[1] == "name")
+ if (split_obj[1].CompareNoCase("name"))
{
entity = npcclient->FindEntityByName(split_obj[2]);
}
- else if (split_obj[1] == "pid")
+ else if (split_obj[1].CompareNoCase("pid"))
{
entity = npcclient->FindCharacterID(atoi(split_obj[2]));
}
+ else if (split_obj[1].CompareNoCase("eid"))
+ {
+ entity = npcclient->FindEntityID(atoi(split_obj[2]));
+ }
if(entity)
{
- npc->SetTarget(entity);
+ located.target = entity;
}
else
{
@@ -2128,7 +2304,34 @@
located.pos = pos;
located.angle = 0;
located.sector = sector;
+
+ located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
}
+ else if(split_obj[0] == "friend")
+ {
+ npc->Printf(5, "LocateOp - Friend");
+
+ gemNPCActor *ent = npc->GetNearestVisibleFriend(20);
+ if(ent)
+ {
+ located.target = ent;
+ }
+ else
+ {
+ return OPERATION_FAILED; // Nothing more to do for this op.
+ }
+
+ float rot;
+ iSector *sector;
+ csVector3 pos;
+ psGameObject::GetPosition(ent,pos,rot,sector);
+
+ located.pos = pos;
+ located.angle = 0;
+ located.sector = sector;
+
+ located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
+ }
else if (split_obj[0] == "perception")
{
npc->Printf(5,"LocateOp - Perception");
@@ -2142,11 +2345,35 @@
return OPERATION_FAILED; // Nothing more to do for this op.
}
- npc->SetTarget( npc->GetLastPerception()->GetTarget() );
+ located.target = npc->GetLastPerception()->GetTarget();
located.angle = 0; // not used in perceptions
located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
}
+ else if (split_obj[0] == "player")
+ {
+ npc->Printf(5,"LocateOp - Player");
+
+ iSector *sector;
+ csVector3 pos;
+ float destRange;
+ gemNPCActor* ent = npc->GetNearestPlayer(range,pos,sector,destRange);
+
+ if(ent)
+ {
+ located.target = ent;
+ }
+ else
+ {
+ return OPERATION_FAILED; // Nothing more to do for this op.
+ }
+
+ located.pos = pos;
+ located.angle = 0;
+ located.sector = sector;
+
+ located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
+ }
else if (split_obj[0] == "target")
{
npc->Printf(5,"LocateOp - Target");
@@ -2156,7 +2383,7 @@
if(ent)
{
- npc->SetTarget(ent);
+ located.target = ent;
}
else
{
@@ -2171,6 +2398,8 @@
located.pos = pos;
located.angle = 0;
located.sector = sector;
+
+ located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
}
else if (split_obj[0] == "owner")
{
@@ -2182,7 +2411,7 @@
if(owner)
{
- npc->SetTarget(owner);
+ located.target = owner;
}
else
{
@@ -2198,6 +2427,8 @@
located.pos = pos;
located.angle = 0;
located.sector = sector;
+
+ located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
}
else if (split_obj[0] == "point")
{
@@ -2239,25 +2470,33 @@
// In case region is curved, adjust point to be at the surface
{
- csVector3 start = pos + csVector3(0.0,5,0.0);
- csVector3 end = pos + csVector3(0.0,-5,0.0);
+ csSectorHitBeamResult result;
+ float range = 5.0; // Start looking for ground from 5 to -5 meters
+ while (range < 200.0) // Last to pass will be 160.0
+ {
+ csVector3 start = pos + csVector3(0.0,range,0.0);
+ csVector3 end = pos + csVector3(0.0,-range,0.0);
- csSectorHitBeamResult result = sector->HitBeam(start,end,false);
-
- // Check if we found a intersection
- if (result.mesh)
- {
- located.pos = result.isect;
- }
- else
- {
- Error4("NPC %s LocateOperation in region %s fails to find ground from %s",
- ShowID(npc->GetPID()),region->GetName(),
- toString(pos,sector).GetDataSafe());
- npc->Printf(1, "LocateOperation Failed to find round from %s in region %s",
- toString(pos,sector).GetDataSafe(), region->GetName());
- return OPERATION_FAILED; // Nothing more to do for this op.
- }
+ result = sector->HitBeam(start,end,false);
+ if (result.mesh) break;
+
+ range *= 2.0; // Double the search range for each iteration
+ }
+
+ // Check if we found a intersection
+ if (result.mesh)
+ {
+ located.pos = result.isect;
+ }
+ else
+ {
+ Error4("NPC %s LocateOperation in region %s fails to find ground from %s",
+ ShowID(npc->GetPID()),region->GetName(),
+ toString(pos,sector).GetDataSafe());
+ npc->Printf(1, "LocateOperation Failed to find round from %s in region %s",
+ toString(pos,sector).GetDataSafe(), region->GetName());
+ return OPERATION_FAILED; // Nothing more to do for this op.
+ }
}
// Find closest waypoint to the random location in the region
@@ -2274,7 +2513,7 @@
if(ent)
{
- npc->SetTarget(ent);
+ located.target = ent;
}
else
{
@@ -2289,6 +2528,8 @@
located.pos = pos;
located.angle = 0;
located.sector = sector;
+
+ located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
}
else if (split_obj[0] == "ownbuffer")
{
@@ -2319,6 +2560,8 @@
located.pos = npc->GetSpawnPosition();
located.angle = 0;
located.sector = npc->GetSpawnSector();
+
+ located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
}
else if (split_obj[0] == "tribe")
{
@@ -2340,6 +2583,8 @@
located.pos = pos;
located.angle = 0;
located.radius = radius;
+
+ located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
}
else if (split_obj[1] == "memory")
{
@@ -2366,11 +2611,14 @@
located.radius = memory->radius;
AddRandomRange(located.pos, memory->radius, 0.5);
+
+ located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
}
else if (split_obj[1] == "resource")
{
npc->GetTribe()->GetResource(npc,start_pos,start_sector,located.pos,located.sector,range,random);
located.angle = 0.0;
+ located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
}
else if (split_obj[0] == "target")
{
@@ -2386,7 +2634,7 @@
if(ent)
{
- npc->SetTarget(ent);
+ located.target = ent;
}
else
{
@@ -2405,29 +2653,6 @@
located.wp = CalculateWaypoint(npc,located.pos,located.sector,-1);
}
- else if(split_obj[0] == "friend")
- {
- npc->Printf(5, "LocateOp - Friend");
-
- gemNPCActor *ent = npc->GetNearestVisibleFriend(20);
- if(ent)
- {
- npc->SetTarget(ent);
- }
- else
- {
- return OPERATION_FAILED; // Nothing more to do for this op.
- }
-
- float rot;
- iSector *sector;
- csVector3 pos;
- psGameObject::GetPosition(ent,pos,rot,sector);
-
- located.pos = pos;
- located.angle = 0;
- located.sector = sector;
- }
else if (split_obj[0] == "waypoint" )
{
npc->Printf(5, "LocateOp - Waypoint");
@@ -2535,10 +2760,15 @@
}
npc->SetLocate(destinationVariablesReplaced,located);
+
+ if (destinationVariablesReplaced == "Active")
+ {
+ npc->SetTarget(located.target);
+ }
- npc->Printf(5, "LocateOp - Active location: pos %s rot %.2f wp %s",
- toString(located.pos,located.sector).GetData(),located.angle,
- (located.wp?located.wp->GetName():"(NULL)"));
+ npc->Printf(5, "LocateOp - %s location: pos %s rot %.2f wp %s target: %s",
+ destinationVariablesReplaced.GetDataSafe(),toString(located.pos,located.sector).GetData(),located.angle,
+ (located.wp?located.wp->GetName():"(NULL)"),located.target.IsValid()?located.target->GetName():"(NULL)");
return OPERATION_COMPLETED; // Nothing more to do for this op.
}
@@ -2782,7 +3012,7 @@
return OPERATION_COMPLETED; // Nothing more to do for this op.
}
- npc->Printf(5, ">>> Memorize '%s' '%s'.",percept->GetType(),percept->GetName(npc).GetDataSafe());
+ npc->Printf(5, ">>> Memorize '%s' '%s'.",percept->GetType().GetDataSafe(),percept->GetName(npc).GetDataSafe());
Tribe * tribe = npc->GetTribe();
@@ -2876,10 +3106,10 @@
npc->Printf(10,"Old position: %s",toString(oldPos,oldSector).GetDataSafe());
- npc->GetLinMove()->ExtrapolatePosition(timedelta);
+ int ret = npc->GetLinMove()->ExtrapolatePosition(timedelta);
npc->GetLinMove()->GetLastPosition(newPos, newRot, newSector);
- CheckMoveOk(npc, oldPos, oldSector, newPos, newSector, timedelta);
+ CheckMoveOk(npc, oldPos, oldSector, newPos, newSector, ret);
npc->Printf(10,"New position: %s",toString(newPos,newSector).GetDataSafe());
@@ -3271,6 +3501,8 @@
Error1("Percept operation need an event attribute");
return false;
}
+
+ type = node->GetAttributeValue("type");
maxRange = node->GetAttributeValueAsFloat("range");
@@ -3312,6 +3544,7 @@
PerceptOperation *op = new PerceptOperation;
op->perception = perception;
+ op->type = type;
op->target = target;
op->maxRange = maxRange;
op->condition = condition;
@@ -3369,9 +3602,11 @@
{
perceptionVariablesReplaced = psGameObject::ReplaceNPCVariables(npc, perception);
}
+
+ csString typeVariablesReplaced = psGameObject::ReplaceNPCVariables(npc, type);
// Now that we are sure this perception is going to be fired create the perception
- Perception pcpt(perceptionVariablesReplaced);
+ Perception pcpt(perceptionVariablesReplaced, typeVariablesReplaced);
// Check if perception should be fired now or later
if (delayed.IsEmpty())
@@ -4207,6 +4442,8 @@
}
}
+ npc->Printf(5,"Talk: %s",talkText.GetDataSafe());
+
csString talkTextVariablesReplaced = psGameObject::ReplaceNPCVariables(npc, talkText);
// Queue the talk to the server
@@ -4940,9 +5177,8 @@
npc->Printf(8, "advance: pos=%s rot=%.2f localDest=%s dist=%.3f timedelta=%.3f",
toString(myPos,mySector).GetDataSafe(), myRot,
destPoint->GetPosition().Description().GetData(),distance,timedelta);
-
+ int ret;
{
- int ret;
ScopedTimer st(250, "Movement extrapolate %.2f time for %s", timedelta, ShowID(npc->GetActor()->GetEID()));
ret = npc->GetLinMove()->ExtrapolatePosition(timedelta);
if (ret != PS_MOVE_SUCCEED)
@@ -4962,7 +5198,7 @@
toString(bodyVel).GetDataSafe(),toString(worldVel).GetDataSafe(),
toString(myNewPos,myNewSector).GetDataSafe());
- CheckMoveOk(npc, myPos, mySector, myNewPos, myNewSector, timedelta);
+ CheckMoveOk(npc, myPos, mySector, myNewPos, myNewSector, ret);
}
return OPERATION_NOT_COMPLETED;
}
Modified: trunk/src/npcclient/npcoperations.h
===================================================================
--- trunk/src/npcclient/npcoperations.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/npcclient/npcoperations.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -172,7 +172,7 @@
*
*/
virtual bool CheckMoveOk(NPC* npc, csVector3 oldPos, iSector* oldSector,
- const csVector3 & newPos, iSector* newSector, float timedelta);
+ const csVector3 & newPos, iSector* newSector, int resultFromExtrapolate );
/** Check if the end point where ok.
*
@@ -518,9 +518,10 @@
protected:
csString source;
csString destination;
+ unsigned int flags;
public:
- CopyLocateOperation(): ScriptOperation("CopyLocate") {};
+ CopyLocateOperation(): ScriptOperation("CopyLocate"), flags(NPC::LOCATION_ALL) {};
virtual ~CopyLocateOperation() {};
virtual OperationResult Run(NPC* npc,bool interrupted);
virtual bool Load(iDocumentNode* node);
@@ -579,6 +580,25 @@
//-----------------------------------------------------------------------------
/**
+* Control another actor.
+*/
+class ControlOperation : public ScriptOperation
+{
+protected:
+ bool control; // Should this operation take or release control
+public:
+
+ ControlOperation( bool control ): ScriptOperation("Control"), control(control) { }
+ virtual ~ControlOperation() { }
+ virtual bool Load(iDocumentNode* node);
+ virtual ScriptOperation* MakeCopy();
+
+ virtual OperationResult Run(NPC* npc,bool interrupted);
+};
+
+//-----------------------------------------------------------------------------
+
+/**
* Debug will turn on and off debug for the npc. Used for debuging
*/
class DebugOperation : public ScriptOperation
@@ -1011,7 +1031,7 @@
NPC* npc;
Perception pcpt;
TargetType target;
- float maxRange;
+ float maxRange;
public:
DelayedPerceptOperationGameEvent(int offsetTicks, NPC* npc, Perception& pcpt, TargetType target, float maxRange);
@@ -1020,6 +1040,7 @@
};
csString perception; ///< The perception name to send
+ csString type; ///< The type value of the perception
TargetType target; ///< Hold the target for the perception, default SELF
float maxRange; ///< Is there a max range for this, 0.0 is without limit
csString condition; ///< A condition for when the perception should be fired
Modified: trunk/src/npcclient/perceptions.h
===================================================================
--- trunk/src/npcclient/perceptions.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/npcclient/perceptions.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -145,7 +145,7 @@
bool SetValue(int i, int value);
csString GetValue();
int GetRandom(int i);
- const csString& GetType() { return type; }
+ const csString& GetType() const { return type; }
char GetOp();
csString GetAffectedBehaviors();
const csString& GetLastTriggerd() { return lastTriggered; }
@@ -184,7 +184,7 @@
virtual gemNPCObject *GetTarget() { return NULL; }
virtual csString GetName(NPC* npc);
- const char* GetType() { return type; }
+ const csString& GetType() const { return type; }
void SetType(const char* type) { this->type = type; }
virtual bool GetLocation(csVector3& pos, iSector*& sector) { return false; }
virtual float GetRadius() const { return 0.0; }
Modified: trunk/src/server/adminmanager.cpp
===================================================================
--- trunk/src/server/adminmanager.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/server/adminmanager.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -1353,6 +1353,8 @@
return "Syntax: \"" + command + " " + GetHelpMessagePartForTarget() + " [percept] [type]\"";
}
+//---------------------------------------------------------------------------------
+
AdminCmdDataChangeNPCType::AdminCmdDataChangeNPCType(AdminManager* msgManager, MsgEntry* me, psAdminCmdMessage &msg, Client* client, WordArray &words)
: AdminCmdDataTarget("/changenpctype", ADMINCMD_TARGET_TARGET | ADMINCMD_TARGET_PID | ADMINCMD_TARGET_AREA | ADMINCMD_TARGET_NPC | ADMINCMD_TARGET_EID | ADMINCMD_TARGET_CLIENTTARGET), npcType("")
{
@@ -1400,7 +1402,57 @@
return "Syntax: \"" + command + " " + GetHelpMessagePartForTarget() + " <npc type name>\"";
}
+//---------------------------------------------------------------------------------
+AdminCmdDataDebugNPC::AdminCmdDataDebugNPC(AdminManager* msgManager, MsgEntry* me, psAdminCmdMessage &msg, Client* client, WordArray &words)
+ : AdminCmdDataTarget("/debugnpc", ADMINCMD_TARGET_TARGET | ADMINCMD_TARGET_PID | ADMINCMD_TARGET_AREA | ADMINCMD_TARGET_NPC | ADMINCMD_TARGET_EID | ADMINCMD_TARGET_CLIENTTARGET), debugLevel(0)
+{
+ size_t index = 1;
+ bool found;
+
+ // when help is requested, return immediate
+ if(IsHelp(words[index]))
+ return;
+
+ // try first word as a target
+ if((found = ParseTarget(msgManager, me, msg, client, words[index])))
+ {
+ index++;
+ }
+
+ // if first word a target or client has selected a valid target
+ if(found || IsTargetType(ADMINCMD_TARGET_CLIENTTARGET))
+ {
+ // the debug level is mandatory
+ if(words.GetCount() == index+1)
+ {
+ debugLevel = atoi(words[index]);
+ index++;
+ }
+ else if(words.GetCount() > index)
+ {
+ ParseError(me, "Too many parameters");
+ }
+ else
+ {
+ ParseError(me, "Missing parameters");
+ }
+ }
+ else
+ {
+ ParseError(me, "No target specified");
+ }
+}
+
+ADMINCMDFACTORY_IMPLEMENT_MSG_FACTORY_CREATE(AdminCmdDataDebugNPC)
+
+csString AdminCmdDataDebugNPC::GetHelpMessage()
+{
+ return "Syntax: \"" + command + " " + GetHelpMessagePartForTarget() + " <debuglevel>\"";
+}
+
+//---------------------------------------------------------------------------------
+
AdminCmdDataSetStackable::AdminCmdDataSetStackable(AdminManager* msgManager, MsgEntry* me, psAdminCmdMessage &msg, Client* client, WordArray &words)
: AdminCmdDataTarget("/setstackable", ADMINCMD_TARGET_TARGET | ADMINCMD_TARGET_AREA | ADMINCMD_TARGET_ITEM | ADMINCMD_TARGET_EID |ADMINCMD_TARGET_CLIENTTARGET), subCommandList("info on off reset help")
{
@@ -4089,6 +4141,7 @@
RegisterMsgFactoryFunction(new AdminCmdDataKillNPC());
RegisterMsgFactoryFunction(new AdminCmdDataPercept());
RegisterMsgFactoryFunction(new AdminCmdDataChangeNPCType());
+ RegisterMsgFactoryFunction(new AdminCmdDataDebugNPC());
RegisterMsgFactoryFunction(new AdminCmdDataSetStackable());
RegisterMsgFactoryFunction(new AdminCmdDataLoadQuest());
RegisterMsgFactoryFunction(new AdminCmdDataItem());
@@ -4316,6 +4369,10 @@
{
ChangeNPCType(me, msg, data, client);
}
+ else if(data->command == "/debugnpc")
+ {
+ DebugNPC(me, msg, data, client);
+ }
else if(data->command == "/rndmsgtest")
{
RandomMessageTest(data, client);
@@ -8443,6 +8500,21 @@
psserver->SendSystemError(me->clientnum, "No NPC found to change type.");
}
+void AdminManager::DebugNPC(MsgEntry* me, psAdminCmdMessage &msg, AdminCmdData* cmddata, Client* client)
+{
+ AdminCmdDataDebugNPC* data = dynamic_cast<AdminCmdDataDebugNPC*>(cmddata);
+
+ gemNPC* target = dynamic_cast<gemNPC*>(data->targetObject);
+ if(target && target->GetClientID() == 0)
+ {
+ //send the change command.
+ psserver->GetNPCManager()->DebugNPC(target, client, data->debugLevel);
+ return;
+ }
+ psserver->SendSystemError(me->clientnum, "No NPC found to set debug level for.");
+}
+
+
void AdminManager::Admin(int clientnum, Client* client, int requestedLevel)
{
// Set client security level in case security level have
Modified: trunk/src/server/adminmanager.h
===================================================================
--- trunk/src/server/adminmanager.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/server/adminmanager.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -977,6 +977,47 @@
virtual csString GetHelpMessage();
};
+/** @brief Class for changing npc debug level.
+ */
+class AdminCmdDataDebugNPC : public AdminCmdDataTarget
+{
+public:
+ int debugLevel; ///< The debug level to set for npc.
+
+ /** @brief Creates obj for specified command that changes NPC Debug level.
+ */
+ AdminCmdDataDebugNPC()
+ : AdminCmdDataTarget("/debugnpc", ADMINCMD_TARGET_TARGET | ADMINCMD_TARGET_PID | ADMINCMD_TARGET_AREA | ADMINCMD_TARGET_NPC | ADMINCMD_TARGET_EID | ADMINCMD_TARGET_CLIENTTARGET), debugLevel(0)
+ {};
+
+ /** @brief Parses the given message to set debug level for npc.
+ * @param msgManager message manager that handles this command
+ * @param me The incoming message from the GM
+ * @param msg psAdminCmdMessage containing the message
+ * @param client client of the network communication
+ * @param words command message to parse
+ */
+ AdminCmdDataDebugNPC(AdminManager* msgManager, MsgEntry* me, psAdminCmdMessage& msg, Client *client, WordArray &words);
+
+ virtual ~AdminCmdDataDebugNPC()
+ {};
+
+ /** @brief Creates a command data object of the current class containing the parsed data.
+ * @param msgManager message manager that handles this command
+ * @param me The incoming message from the GM
+ * @param msg psAdminCmdMessage containing the message
+ * @param client client of the network communication
+ * @param words command message to parse
+ * @return AdminCmdData* pointer to object containing parsed data. When parsing failed the valid flag is set to false.
+ */
+ virtual AdminCmdData* CreateCmdData(AdminManager* msgManager, MsgEntry* me, psAdminCmdMessage& msg, Client *client, WordArray &words);
+
+ /** @brief Returns a helpmessage that fits to the parser of the class.
+ * @return csString: a help message to send back to the client
+ */
+ virtual csString GetHelpMessage();
+};
+
/** @brief Class for un/setting, displaying information on stackable items.
*/
class AdminCmdDataSetStackable : public AdminCmdDataTarget
@@ -3231,6 +3272,14 @@
*/
void ChangeNPCType(MsgEntry *me, psAdminCmdMessage& msg, AdminCmdData* cmddata, Client *client);
+ /** @brief Change the NPC Debug level.
+ * @param me The incoming message from the GM
+ * @param msg The cracked command message.
+ * @param data A pointer to the command parser object with target datat
+ * @param client The GM client the command came from.
+ */
+ void DebugNPC(MsgEntry *me, psAdminCmdMessage& msg, AdminCmdData* cmddata, Client *client);
+
/** @brief Creates an item or loads GUI for item creation.
*
* Gui for item creation is loaded when no item was specified.
Modified: trunk/src/server/database/mysql/command_access.sql
===================================================================
--- trunk/src/server/database/mysql/command_access.sql 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/server/database/mysql/command_access.sql 2012-08-04 12:01:36 UTC (rev 8414)
@@ -61,6 +61,7 @@
INSERT INTO command_group_assignment VALUES( "/time", 30 );
INSERT INTO command_group_assignment VALUES( "/version", 30 );
INSERT INTO command_group_assignment VALUES( "/changenpctype", 30 );
+INSERT INTO command_group_assignment VALUES( "/debugnpc", 30 );
INSERT INTO command_group_assignment VALUES( "move unpickupables/spawns", 30 );
INSERT INTO command_group_assignment VALUES( "/death", 30 );
INSERT INTO command_group_assignment VALUES( "/crystal", 30 );
Modified: trunk/src/server/database/mysql/npc_responses.sql
===================================================================
--- trunk/src/server/database/mysql/npc_responses.sql 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/server/database/mysql/npc_responses.sql 2012-08-04 12:01:36 UTC (rev 8414)
@@ -83,6 +83,8 @@
INSERT INTO `npc_responses` VALUES (44,35,'You just asked a question that I do not know the answer to.','','','','','','','','','','',0,NULL,NULL,NULL,NULL,NULL);
INSERT INTO `npc_responses` VALUES (45,36,NULL,NULL,NULL,NULL,NULL,'','','','','<response><run script="explore_area" with="Area = \'NPCroom1\'; Range = 100; Exp = 100;"/></response>','',NULL,NULL,NULL,NULL,NULL,NULL);
INSERT INTO `npc_responses` VALUES (46,37,NULL,NULL,NULL,NULL,NULL,'','','','','<response><run script="explore_area" with="Area = \'NPCroom2\'; Range = 100; Exp = 1000;"/></response>','',NULL,NULL,NULL,NULL,NULL,NULL);
+INSERT INTO `npc_responses` VALUES (47,38,'Ok, I will bring you up by send the \"bring up\" command to the beast.','','','','','','','','','<response><respond/><npccmd cmd=\"bring_up\" /></response>','',0,NULL,NULL,NULL,NULL,NULL);
+INSERT INTO `npc_responses` VALUES (48,39,'Welcome to the Winch. Tell me to "winch up" to have the winch beast winch up, "winch down" to have the winch beast winch down, or "bring me up" to have the winch beast bring you up.','','','','','','','','','','',0,NULL,NULL,NULL,NULL,NULL);
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
Modified: trunk/src/server/database/mysql/npc_triggers.sql
===================================================================
--- trunk/src/server/database/mysql/npc_triggers.sql 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/server/database/mysql/npc_triggers.sql 2012-08-04 12:01:36 UTC (rev 8414)
@@ -67,6 +67,8 @@
INSERT INTO `npc_triggers` VALUES (35,'unknown',0,'general');
INSERT INTO `npc_triggers` VALUES (36,'!anyrange',0,'NPCroom1');
INSERT INTO `npc_triggers` VALUES (37,'!anyrange',0,'NPCroom2');
+INSERT INTO `npc_triggers` VALUES (38,'bring me up',0,'WinchMovers');
+INSERT INTO `npc_triggers` VALUES (39,'grettings',0,'WinchMovers');
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
Modified: trunk/src/server/database/mysql/sc_npctypes.sql
===================================================================
--- trunk/src/server/database/mysql/sc_npctypes.sql 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/server/database/mysql/sc_npctypes.sql 2012-08-04 12:01:36 UTC (rev 8414)
@@ -60,7 +60,9 @@
</behavior>
<react event="move back in region" behavior="MoveBackInRegion" do_not_interrupt="GoToRegion,Move" />
-<react event="out of bounds" behavior="MoveBackInRegion" do_not_interrupt="GoToRegion,Move" />');
+<react event="out of bounds" behavior="MoveBackInRegion" do_not_interrupt="GoToRegion,Move" />
+<!-- In case of collision run MoveBackInRegion to select a new random point in the region -->
+<react event="collision" behavior="MoveBackInRegion" />');
INSERT INTO sc_npctypes VALUES("4","Fight","",0,"","","","","","1",
'<behavior name="Fight" initial="0" growth="0" decay="0" completion_decay="-1" >
@@ -1047,15 +1049,12 @@
<wander anim="walk" random="yes" underground="true" />
</behavior>');
-INSERT INTO sc_npctypes VALUES("120","WinchMover","DoNothing",0,"","","","","","0",
+INSERT INTO sc_npctypes VALUES("120","WinchMover","DoNothing,Move",0,"","","","","","0",
'<!-- Initial behavior to locate the starting point -->
-<behavior name="Initialize" completion_decay="-1" initial="1000">
+<behavior name="Initialize" resume="yes" completion_decay="-1" initial="1000">
<!--debug level="0" /-->
- <locate obj="waypoint" static="no" /> <!-- Locate nearest waypoint -->
- <navigate anim="walk" /> <!-- Local navigation -->
- <locate obj="winch:Mover pos 1" static="no" />
- <wander anim="walk" /> <!-- Navigate using waypoints -->
- <navigate anim="walk" /> <!-- Local navigation -->
+ <locate obj="winch:Mover pos 1" static="no" destination="Move" />
+ <percept event="move" />
<rotate type="absolute" value="180" ang_vel="20"/>
</behavior>
@@ -1065,9 +1064,20 @@
<wait duration="10" anim="stand" />
</behavior>
-<react event="talk" inactive_only="yes" faction_diff="-100" oper=">" behavior="turn to face" delta="100" when_invisible="yes" when_invincible="yes" />');
+<behavior name="bring_up" resume="yes" completion_decay="-1" >
+ <locate obj="player" range="20" destination="Player" />
+ <copy_locate source="Player" destination="Active" only="target" />
+ <talk text="I will have the winch beast bring you to the winch." target="true" />
+ <locate obj="entity:name:WinchBeast1" />
+ <talk text="$target please bring $LOCATION[Player.targetName] to the winch." target="false" />
+ <percept event="BringPlayer" type="entity:$LOCATION[Player.targetEID]" target="target" />
+</behavior>
-INSERT INTO sc_npctypes VALUES("121","WinchBeast","DoNothing",0,"","","","","","0",
+<react event="talk" inactive_only="yes" faction_diff="-100" oper=">" behavior="turn to face" delta="100" when_invisible="yes" when_invincible="yes" />
+<react event="npccmd:self:bring_up" behavior="bring_up" delta="100" inactive_only="yes"/>
+');
+
+INSERT INTO sc_npctypes VALUES("121","WinchBeast","DoNothing,Move",0,"","","","","","0",
'<!-- Test interaction with sequences in the map -->
<!-- and communication from other NPCs using NPCCMD -->
@@ -1097,6 +1107,22 @@
<rotate type="relative" value="90" ang_vel="20"/>
</behavior>
+<behavior name="bring_player" resume="yes" completion_decay="-1" >
+ <locate obj="$perception_type" static="no" destination="Player" /> <!-- Expected perception type to be "entity:eid:<eid>" -->
+ <copy_locate source="Player" destination="Move" />
+ <percept event="move" />
+ <copy_locate source="Player" destination="Active" only="target" />
+ <control />
+ <locate obj="winch:Beast pos 1" destination="Move" />
+ <percept event="move" />
+ <copy_locate source="Player" destination="Active" only="target" />
+ <wait duration="5" />
+ <release_control />
+ <rotate type="absolute" value="90" ang_vel="20"/>
+</behavior>
+<react event="BringPlayer" behavior="bring_player" />
+
+
<react event="npccmd:global:winch_up" behavior="winch_up" delta="100" inactive_only="yes"/>
<react event="npccmd:global:winch_down" behavior="winch_down" delta="100" inactive_only="yes"/>');
Modified: trunk/src/server/gem.cpp
===================================================================
--- trunk/src/server/gem.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/server/gem.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -1080,6 +1080,12 @@
return NULL;
}
+float gemObject::GetVelocity()
+{
+ return 0.0; // Objects can't move
+}
+
+
void gemObject::SendBehaviorMessage(const csString & str, gemObject *actor)
{
Error3("gemObject %s got behavior message %s in error.",GetName(),str.GetData());
@@ -3921,7 +3927,7 @@
uint32_t clientnum = GetClientID();
forcedSector = GetSector();
- psForcePositionMessage msg(clientnum, ++forceDRcounter, GetPosition(), GetAngle(), GetSector(),
+ psForcePositionMessage msg(clientnum, ++forceDRcounter, GetPosition(), GetAngle(), GetSector(), GetVelocity(),
cacheManager->GetMsgStrings(), loadDelay, background, point1, point2, widget);
msg.SendMessage();
}
@@ -4375,6 +4381,15 @@
return false;
}
+/** Get the z velocity of the actor.
+ */
+float gemActor::GetVelocity()
+{
+ csVector3 vel = pcmove->GetVelocity();
+ return vel.z;
+}
+
+
/* gemActor::ChatHistoryEntry function implementations */
/// ChatHistoryEntry(const char*, time_t = 0)
Modified: trunk/src/server/gem.h
===================================================================
--- trunk/src/server/gem.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/server/gem.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -343,6 +343,9 @@
*/
float GetAngle();
iSector* GetSector();
+ /** Get the z velocity of the actor.
+ */
+ virtual float GetVelocity();
const char *GetSectorName() { return GetSector() ? GetSector()->QueryObject()->GetName() : "(null)"; }
csArray<gemObject*> *GetObjectsInRange( float range );
//@}
@@ -1237,6 +1240,9 @@
// Target information
void SetTargetObject(gemObject *object){ targetObject = object; }
gemObject* GetTargetObject() const { return targetObject; }
+ /** Get the z velocity of the actor.
+ */
+ virtual float GetVelocity();
};
//-----------------------------------------------------------------------------
Modified: trunk/src/server/npcmanager.cpp
===================================================================
--- trunk/src/server/npcmanager.cpp 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/server/npcmanager.cpp 2012-08-04 12:01:36 UTC (rev 8414)
@@ -896,7 +896,7 @@
csRef<iDataBuffer> databuf = csPtr<iDataBuffer> (new csDataBuffer (len));
memcpy(databuf->GetData(), data, len);
// find the entity and Set the DR data for it
- gemNPC *actor = dynamic_cast<gemNPC*>(gemSupervisor->FindObject(drmsg.entityid));
+ gemActor *actor = dynamic_cast<gemActor*>(gemSupervisor->FindObject(drmsg.entityid));
if (!actor)
{
@@ -912,10 +912,11 @@
{
// Go ahead and update the server version
actor->SetDRData(drmsg);
+
// Now multicast to other clients
actor->UpdateProxList();
+ actor->MulticastDRUpdate();
- actor->MulticastDRUpdate();
if(drmsg.vel.y < -20 || drmsg.pos.y < -1000) //NPC has fallen down
{
// First print out what happend
@@ -1453,6 +1454,48 @@
psserver->GetWorkManager()->HandleProduction(gEntity,type,resource);
break;
}
+
+ case psNPCCommandsMessage::CMD_CONTROL:
+ {
+ // Extract the data
+ EID controllingEntity = EID(list.msg->GetUInt32());
+ // extract the DR data
+ uint32_t len = 0;
+ void *data = list.msg->GetBufferPointerUnsafe(len);
+
+ // Make sure we haven't run past the end of the buffer
+ if (list.msg->overrun)
+ {
+ Debug2(LOG_SUPERCLIENT, controllingEntity.Unbox(), "Received incomplete CMD_CONTROL from NPC client %u.\n", me->clientnum);
+ break;
+ }
+
+ psDRMessage drmsg(data,len,psserver->GetNetManager()->GetAccessPointers()); // alternate method of cracking
+
+ Debug5(LOG_SUPERCLIENT, controllingEntity.Unbox(), "-->Got control Controlling EID: %u Controlled EID: %u Pos: %s yRot: %.1f\n",
+ controllingEntity.Unbox(), drmsg.entityid.Unbox(),toString(drmsg.pos,drmsg.sector).GetDataSafe(),drmsg.yrot);
+
+ gemActor *controlled = dynamic_cast<gemActor*> (gemSupervisor->FindObject(drmsg.entityid));
+ if (!controlled)
+ {
+ Debug1(LOG_SUPERCLIENT, controllingEntity.Unbox(), "Couldn't find controlled entity.\n");
+ break;
+ }
+
+ // TODO: Allow for breake free
+
+ controlled->SetDRData(drmsg);
+ if (controlled->GetClient())
+ controlled->GetClient()->SetCheatMask(MOVE_CHEAT, true); // Tell paladin one of these is OK.
+
+ controlled->UpdateProxList();
+ controlled->MulticastDRUpdate();
+ controlled->ForcePositionUpdate();
+ controlled->BroadcastTargetStatDR(entityManager->GetClients());
+
+ break;
+ }
+
case psNPCCommandsMessage::CMD_DROP:
{
EID entity_id = list.msg->GetUInt32();
@@ -2662,8 +2705,19 @@
Debug2(LOG_NPC, npc->GetEID().Unbox(), "Added Brain Change perception for %s.\n", ShowID(npc->GetEID()));
}
+void NPCManager::DebugNPC(gemNPC* npc, Client* client, uint8_t debugLevel)
+{
+ CheckSendPerceptionQueue(sizeof(int8_t)+sizeof(uint32_t)*2+sizeof(uint8));
+ outbound->msg->Add( (int8_t) psNPCCommandsMessage::PCPT_DEBUG_NPC);
+ outbound->msg->Add(npc->GetEID().Unbox());
+ outbound->msg->Add(client->GetClientNum());
+ outbound->msg->Add(debugLevel);
+ cmd_count++;
+ Debug2(LOG_NPC, npc->GetEID().Unbox(), "Added Debug Level perception for %s.\n", ShowID(npc->GetEID()));
+}
+
void NPCManager::SendAllCommands(bool createNewTick)
{
if (cmd_count)
Modified: trunk/src/server/npcmanager.h
===================================================================
--- trunk/src/server/npcmanager.h 2012-08-04 10:58:56 UTC (rev 8413)
+++ trunk/src/server/npcmanager.h 2012-08-04 12:01:36 UTC (rev 8414)
@@ -150,6 +150,14 @@
*/
void ChangeNPCBrain(gemNPC* npc, Client* client, const char* brainName);
+ /**
+ * Requests the npcclient to change the debug level of this npc.
+ * @param npc The npc which will be changed.
+ * @param client The client requesting the action.
+ * @param debugLevel The new debug level for the npc.
+ */
+ void DebugNPC(gemNPC* npc, Client* client, uint8_t debugLevel);
+
/// Send all queued commands and perceptions to active superclients and reset the queues.
void SendAllCommands(bool createNewTick = true);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|