HowTo Add a NPC Operation

From PSwiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

Summary

NOTE: In Work

  1. Create new Operation Class in npcoperations
  2. Add Operation to npcbehave
  3. Create new Queue-Command in networkmgr
  4. Modify npcmessages
  5. 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).