HowTo Add a NPC Operation: Difference between revisions
No edit summary |
No edit summary |
||
(3 intermediate revisions by the same user not shown) | |||
Line 3: | Line 3: | ||
NOTE: In Work | NOTE: In Work | ||
# Create new Operation Class in | # Create new Operation Class in npcoperations | ||
# Add Operation to npcbehave | # Add Operation to npcbehave | ||
# Create new Queue-Command in | # Create new Queue-Command in networkmgr | ||
# Modify | # Modify npcmessages | ||
# Process Command in | # Process Command in npcmanager and execute desired action | ||
== New Operation Class == | == New Operation Class == | ||
Line 164: | Line 164: | ||
Add case for new Command in NPCManager::HandleCommandList(): | Add case for new Command in NPCManager::HandleCommandList(): | ||
src/ | src/server/npcmanager.cpp | ||
<code> | <code> | ||
<nowiki> | <nowiki> | ||
Line 174: | Line 174: | ||
EID entity_id = EID(list.msg->GetUInt32()); | EID entity_id = EID(list.msg->GetUInt32()); | ||
csString type = list.msg->GetStr(); | csString type = list.msg->GetStr(); | ||
Debug3(LOG_SUPERCLIENT, entity_id.Unbox(), "-->Got loot cmd: Entity %s to loot for %s\n", | |||
ShowID(entity_id), type.GetData()); | ShowID(entity_id), type.GetData()); | ||
Line 183: | Line 183: | ||
break; | break; | ||
} | } | ||
gemActor* actor = dynamic_cast<gemActor*>(gemSupervisor->FindObject(entity_id)); | |||
if( | if(actor) | ||
{ | { | ||
if(psserver->GetUserManager()->CheckTargetLootable(actor, NULL)) | |||
{ | { | ||
psserver->GetUserManager()->LootMoney(actor, NULL); | |||
psserver->GetUserManager()->LootItems(actor, NULL, type); | |||
} | } | ||
// TODO: Add inventory change perception to make npc put items into tribe's resources | |||
} | } | ||
else | else | ||
Line 219: | Line 207: | ||
Create a behavior to test with: | Create a behavior to test with: | ||
Add new <behavior> and <react event> to AbstractTribesman in sc_npctypes.sql | Add new <behavior> and <react event> to AbstractTribesman in sc_npctypes.sql. | ||
<code> | <code> | ||
Line 227: | Line 215: | ||
<!-- [...] --> | <!-- [...] --> | ||
<behavior "test_loot" > | <behavior name="test_loot" > | ||
<loot type="all"/> | <loot type="all"/> | ||
</behavior> | </behavior> | ||
Line 234: | Line 222: | ||
<!-- [...] --> | <!-- [...] --> | ||
');</nowiki></code> | ');</nowiki></code> | ||
Reload the table: | |||
<code>mysql -u planeshift -pplaneshift planeshift | |||
mysql> source sc_npctypes.sql;</code> | |||
On the psclient click on a NPC Hunter or Miner (has to be one of these due to "AbstractTribesman") and execute these commands: | |||
<code> | |||
/debugnpc | |||
/percept test_loot test_loot</code> | |||
Depending on where you sent DEBUG Messages, you can either see them in psnpcclient or psserver. Make sure LOG_SUPERCLIENT is active (setlog LOG_SUPERCLIENT). | |||
[[Category:Engine documents]] [[Category:NPCClient Design]] | [[Category:Engine documents]] [[Category:NPCClient Design]] |
Latest revision as of 16:35, 4 April 2013
Summary
NOTE: In Work
- Create new Operation Class in npcoperations
- Add Operation to npcbehave
- Create new Queue-Command in networkmgr
- Modify npcmessages
- Process Command in npcmanager and execute desired action
New Operation Class
Example: LootOperation
src/npcclient/npcoperations.h
/** Loot will make the NPC loot specified items
*
* This class is the implementation of the loot operations
* used in behavior scripts for NPCS.
*
* Examples: <pre>
* \<loot type="all" /\>
* \<loot type="weapons" /\> </pre>
*/
class LootOperation : public ScriptOperation
{
protected:
csString type; ///< Type of items to loot
public:
LootOperation(): ScriptOperation("Loot") {};
virtual ~LootOperation() {};
virtual OperationResult Run(NPC* npc,bool interrupted);
virtual bool Load(iDocumentNode* node);
virtual ScriptOperation* MakeCopy();
};
src/npcclient/npcoperations.cpp
bool LootOperation::Load(iDocumentNode *node)
{
type = node->GetAttributeValue("type");
if(type.IsEmpty())
type = "all";
return true;
}
ScriptOperation* LootOperation::MakeCopy()
{
LootOperation* op = new LootOperation;
op->type = type;
return op;
}
ScriptOperation::OperationResult LootOperation::Run(NPC *npc, bool interrupted)
{
npcclient->GetNetworkMgr()->QueueLootCommand(npc->GetActor(), type);
return OPERATION_COMPLETED; // Nothing more to do for this op.
}
Add Operation to npcbehave
Add new operation to Behavior::LoadScript():
src/npcclient/npcbehave.cpp
bool Behavior::LoadScript(iDocumentNode *node,bool top_level)
{
// [...]
else if ( strcmp( node->GetValue(), "loot" ) == 0 )
{
op = new LootOperation;
}
// [...]
}
New Queue-Command
src/npcclient/networkmgr.h
/**
* Send a command to loot selected target.
*/
void QueueLootCommand(gemNPCActor *entity, const csString& type);
src/npcclient/networkmgr.cpp
void NetworkManager::QueueLootCommand(gemNPCActor *entity, const csString& type)
{
CheckCommandsOverrun(sizeof(uint8_t) + sizeof(uint32_t) + (type.Length()+1));
outbound->msg->Add((int8_t) psNPCCommandsMessage::CMD_LOOT);
outbound->msg->Add(entity->GetEID().Unbox());
outbound->msg->Add(type);
if(outbound->msg->overrun)
{
CS_ASSERT(!"NetworkManager::QueueLootCommand put message in overrun state!\n");
}
cmd_count++;
}
Modify npcmessages
Add new CMD to PerceptionType:
src/common/net/npcmessages.h
enum PerceptionType
{
// Commands go from superclient to server
// [...]
CMD_CONTROL,
CMD_LOOT, // new CMD for looting
// Perceptions go from server to superclient
PCPT_ANYRANGEPLAYER,
// [...]
};
Add new case to psNPCCOmmandsMessage::ToString():
src/common/net/npcmessages.cpp
csString psNPCCommandsMessage::ToString(NetBase::AccessPointers * accessPointers)
{
// [...]
case psNPCCommandsMessage::CMD_LOOT:
{
msgtext.Append("CMD_LOOT: ");
// Extract the data
EID entity_id = EID(msg->GetUInt32());
csString type = msg->GetStr();
// Make sure we haven't run past the end of the buffer
if(msg->overrun)
{
Debug2(LOG_SUPERCLIENT,msg->clientnum,"Received incomplete CMD_LOOT from NPC client %u.\n",msg->clientnum);
break;
}
msgtext.AppendFmt("EID: %u Type: %s", entity_id.Unbox(), type.GetData());
break;
}
// [...]
}
Process Command
Add case for new Command in NPCManager::HandleCommandList():
src/server/npcmanager.cpp
void NPCManager::HandleCommandList(MsgEntry* me,Client* client)
{
// [...]
case psNPCCommandsMessage::CMD_LOOT:
{
EID entity_id = EID(list.msg->GetUInt32());
csString type = list.msg->GetStr();
Debug3(LOG_SUPERCLIENT, entity_id.Unbox(), "-->Got loot cmd: Entity %s to loot for %s\n",
ShowID(entity_id), type.GetData());
// Make sure we haven't run past the end of the buffer
if(list.msg->overrun)
{
Debug2(LOG_SUPERCLIENT, entity_id.Unbox(), "Received incomplete CMD_LOOT from NPC client %u.\n", me->clientnum);
break;
}
gemActor* actor = dynamic_cast<gemActor*>(gemSupervisor->FindObject(entity_id));
if(actor)
{
if(psserver->GetUserManager()->CheckTargetLootable(actor, NULL))
{
psserver->GetUserManager()->LootMoney(actor, NULL);
psserver->GetUserManager()->LootItems(actor, NULL, type);
}
// TODO: Add inventory change perception to make npc put items into tribe's resources
}
else
Error1("NPC Client try to loot with no existing npc");
break;
}
// [...]
}
Test
Create a behavior to test with:
Add new <behavior> and <react event> to AbstractTribesman in sc_npctypes.sql.
INSERT INTO sc_npctypes VALUES("109","AbstractTribesman","DoNothing,Move",0,"","","","","","1",
'<!-- Abstract base npc type for tribes -->
<!-- [...] -->
<behavior name="test_loot" >
<loot type="all"/>
</behavior>
<react event="test_loot" behavior="test_loot" />
<!-- [...] -->
');
Reload the table:
mysql -u planeshift -pplaneshift planeshift
mysql> source sc_npctypes.sql;
On the psclient click on a NPC Hunter or Miner (has to be one of these due to "AbstractTribesman") and execute these commands:
/debugnpc
/percept test_loot test_loot
Depending on where you sent DEBUG Messages, you can either see them in psnpcclient or psserver. Make sure LOG_SUPERCLIENT is active (setlog LOG_SUPERCLIENT).