warzone2100/src/multibot.c

1046 lines
24 KiB
C

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2007 Warzone Resurrection Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* Multibot.c
*
* Alex Lee , 97/98 Pumpkin Studios, Bath
* Multiplay stuff relevant to droids only.
*/
#include "lib/framework/frame.h"
#include "droid.h" // for droid sending and ordering.
#include "droiddef.h"
#include "stats.h"
#include "move.h" // for ordering droids
#include "objmem.h"
#include "power.h" // for powercalculated
#include "order.h"
#include "geometry.h" // for formations.
#include "map.h"
#include "group.h"
#include "formation.h"
#include "lib/netplay/netplay.h" // the netplay library.
#include "multiplay.h" // warzone net stuff.
#include "multijoin.h"
#include "cmddroid.h" // command droids
#include "action.h"
#include "console.h"
#include "mapgrid.h"
#include "multirecv.h"
#define ANYPLAYER 99
#define UNKNOWN 99
// ////////////////////////////////////////////////////////////////////////////
// Local Prototypes
static void ProcessDroidOrder(DROID *psDroid, DROID_ORDER order, UDWORD x, UDWORD y, OBJECT_TYPE desttype, UDWORD destid);
// ////////////////////////////////////////////////////////////////////////////
// Command Droids.
// sod em.
// ////////////////////////////////////////////////////////////////////////////
// vtol bits.
// happy vtol = vtol ready to go back to attack.
BOOL sendHappyVtol(const DROID* psDroid)
{
if (!bMultiPlayer)
return true;
if (!myResponsibility(psDroid->player))
{
return false;
}
NETbeginEncode(NET_VTOL, NET_ALL_PLAYERS);
{
uint8_t player = psDroid->player;
uint32_t droid = psDroid->id;
NETuint8_t(&player);
NETuint32_t(&droid);
}
return NETend();
}
BOOL recvHappyVtol()
{
DROID* pD;
unsigned int i;
NETbeginDecode(NET_VTOL);
{
uint8_t player;
uint32_t droid;
NETuint8_t(&player);
NETuint32_t(&droid);
if (!IdToDroid(droid, player, &pD))
{
NETend();
return false;
}
}
NETend();
// Rearming also repairs VTOLs
pD->body = pD->originalBody;
for (i = 0; i < pD->numWeaps; i++)
{
pD->sMove.iAttackRuns[i] = 0; // finish it for next time round.
pD->asWeaps[i].ammo = asWeaponStats[pD->asWeaps[i].nStat].numRounds;
pD->asWeaps[i].lastFired = 0;
}
return true;
}
// ////////////////////////////////////////////////////////////////////////////
// Secondary Orders.
// Send
BOOL sendDroidSecondary(const DROID* psDroid, SECONDARY_ORDER sec, SECONDARY_STATE state)
{
if (!bMultiPlayer)
return true;
NETbeginEncode(NET_SECONDARY, NET_ALL_PLAYERS);
{
uint8_t player = psDroid->player;
uint32_t droid = psDroid->id;
NETuint8_t(&player);
NETuint32_t(&droid);
NETenum(&sec);
NETenum(&state);
}
return NETend();
}
// recv
BOOL recvDroidSecondary()
{
DROID* psDroid;
SECONDARY_ORDER sec = DSO_ATTACK_RANGE;
SECONDARY_STATE state = DSS_NONE;
NETbeginDecode(NET_SECONDARY);
{
uint8_t player;
uint32_t droid;
NETuint8_t(&player);
NETuint32_t(&droid);
NETenum(&sec);
NETenum(&state);
// If we can not find the droid should we not ask for it?
if (!IdToDroid(droid, player, &psDroid))
{
NETend();
return false;
}
}
NETend();
// Set the droids secondary order
turnOffMultiMsg(true);
secondarySetState(psDroid, sec, state);
turnOffMultiMsg(false);
return true;
}
BOOL sendDroidSecondaryAll(const DROID* psDroid)
{
if (!bMultiPlayer)
return true;
NETbeginEncode(NET_SECONDARY_ALL, NET_ALL_PLAYERS);
{
uint8_t player = psDroid->player;
uint32_t droid = psDroid->id;
uint32_t secOrder = psDroid->secondaryOrder;
NETuint8_t(&player);
NETuint32_t(&droid);
NETuint32_t(&secOrder);
}
return NETend();
}
BOOL recvDroidSecondaryAll()
{
DROID* psDroid;
NETbeginDecode(NET_SECONDARY_ALL);
{
uint8_t player;
uint32_t droid, secOrder;
NETuint8_t(&player);
NETuint32_t(&droid);
NETuint32_t(&secOrder);
if (!IdToDroid(droid, player, &psDroid))
{
NETend();
return false;
}
if (psDroid != NULL)
{
psDroid->secondaryOrder = secOrder;
}
}
NETend();
return true;
}
// broadcast droid & transporter loading information
BOOL sendDroidEmbark(const DROID* psDroid,const DROID* psTransporter )
{
if (!bMultiPlayer)
return true;
NETbeginEncode(NET_DROIDEMBARK, NET_ALL_PLAYERS);
{
uint8_t player = psDroid->player;
uint32_t droidID = psDroid->id;
uint32_t transporterID = psTransporter->id;
NETuint8_t(&player);
NETuint32_t(&droidID);
NETuint32_t(&transporterID);
}
return NETend();
}
// receive droid & transporter loading information
BOOL recvDroidEmbark()
{
DROID *psDroid;
DROID *psTransporterDroid;
BOOL bDroidRemoved;
NETbeginDecode(NET_DROIDEMBARK);
{
uint8_t player;
uint32_t droidID;
uint32_t transporterID;
NETuint8_t(&player);
NETuint32_t(&droidID);
NETuint32_t(&transporterID);
// we have to find the droid on our (local) list first.
if (!IdToDroid(droidID, player, &psDroid))
{
NETend();
// Possible it already died? (sync error?)
debug(LOG_WARNING,"player's %d droid %d wasn't found?",player,droidID);
return false;
}
if (!IdToDroid(transporterID, player, &psTransporterDroid))
{
NETend();
// Possible it already died? (sync error?)
debug(LOG_WARNING,"player's %d transport droid %d wasn't found?",player,transporterID);
return false;
}
if (psDroid == NULL)
{
// how can this happen?
return true;
}
// Take it out of the world without destroying it (just removes it from the droid list)
bDroidRemoved = droidRemove(psDroid, apsDroidLists);
// Init the order for when disembark
psDroid->order = DORDER_NONE;
setDroidTarget(psDroid, NULL);
psDroid->psTarStats = NULL;
if (bDroidRemoved)
{
// and now we need to add it to their transporter group!
grpJoin(psTransporterDroid->psGroup, psDroid);
}
else
{
// possible sync error?
debug(LOG_WARNING,"Eh? Where did unit %d go? Couldn't load droid onto transporter.",droidID);
}
}
NETend();
return true;
}
// sending information that droid is being unloaded from a transporter
BOOL sendDroidDisEmbark(const DROID* psDroid, const DROID* psTransporter)
{
if (!bMultiPlayer)
return true;
NETbeginEncode(NET_DROIDDISEMBARK, NET_ALL_PLAYERS);
{
uint8_t player = psDroid->player;
uint32_t droidID = psDroid->id;
uint32_t transporterID = psTransporter->id;
Vector3uw pos = psDroid->pos;
NETuint8_t(&player);
NETuint32_t(&droidID);
NETuint32_t(&transporterID);
NETVector3uw(&pos);
}
return NETend();
}
// getting informaton about droid is being unloaded from a transporter
BOOL recvDroidDisEmbark()
{
DROID *psFoundDroid = NULL, *psTransporterDroid = NULL;
DROID *psCheckDroid = NULL;
NETbeginDecode(NET_DROIDDISEMBARK);
{
uint8_t player;
uint32_t droidID;
uint32_t transporterID;
Vector3uw pos;
NETuint8_t(&player);
NETuint32_t(&droidID);
NETuint32_t(&transporterID);
NETVector3uw(&pos);
NETend();
// find the transporter first
if (!IdToDroid(transporterID, player, &psTransporterDroid))
{
// Possible it already died? (sync error?)
debug(LOG_WARNING,"player's %d transport droid %d wasn't found?",player,transporterID);
return false;
}
// we need to find the droid *in* the transporter
psCheckDroid = psTransporterDroid ->psGroup->psList;
while (psCheckDroid)
{
// is this the one we want?
if( psCheckDroid->id == droidID)
{
psFoundDroid = psCheckDroid;
break;
}
// not found, so check next one in *group*
psCheckDroid = psCheckDroid->psGrpNext;
}
// don't continue if we couldn't find it.
if (!psFoundDroid)
{
// I don't think this could ever be possible...but
debug(LOG_ERROR,"Couldn't find droid %d to disembark from player %d's transporter?",droidID,player);
return false;
}
// remove it from the transporter
grpLeave(psFoundDroid->psGroup, psFoundDroid);
// and add it back to the bloody droid list
addDroid(psFoundDroid, apsDroidLists);
// Add it back into the world at the x/y
psFoundDroid->pos = pos;
if (!droidOnMap(psFoundDroid))
{
debug(LOG_ERROR,"droid %d disembarked was NOT on map?",psFoundDroid->id);
return false;
}
updateDroidOrientation(psFoundDroid);
// Initialise the movement data
initDroidMovement(psFoundDroid);
// must add it to the grid for targeting to work
gridAddObject((BASE_OBJECT *)psFoundDroid);
} // NetBeginDecode
return true;
}
// ////////////////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////////////////
// Droids
// posibly Send an updated droid movement order.
BOOL SendDroidMove(const DROID* psDroid, uint32_t x, uint32_t y, BOOL formation)
{
if (!bMultiPlayer)
return true;
ASSERT(x > 0 && y > 0, "SendDroidMove: Invalid move order");
// Don't allow a move to happen at all if it is not our responsibility
if (!myResponsibility(psDroid->player))
{
return false; // Do not allow move
}
// If the unit has no actions or orders, allow it to happen but do not send
if (psDroid->action == DACTION_NONE || psDroid->order == DORDER_MOVE)
{
return true;
}
NETbeginEncode(NET_DROIDMOVE, NET_ALL_PLAYERS);
{
uint8_t player = psDroid->player;
uint32_t droid = psDroid->id;
NETuint8_t(&player);
NETuint32_t(&droid);
NETuint32_t(&x);
NETuint32_t(&y);
NETbool(&formation);
}
return NETend();
}
// recv and updated droid position
BOOL recvDroidMove()
{
DROID* psDroid;
uint32_t x, y;
BOOL formation;
NETbeginDecode(NET_DROIDMOVE);
{
uint8_t player;
uint32_t droid;
NETuint8_t(&player);
NETuint32_t(&droid);
NETuint32_t(&x);
NETuint32_t(&y);
NETbool(&formation);
NETend();
if ((x == 0 && y == 0) || x > world_coord(mapWidth) || y > world_coord(mapHeight))
{
/* Probably an invalid droid position */
debug(LOG_ERROR, "Received an invalid droid position from %d", NETgetSource());
return false;
}
if (!IdToDroid(droid, player, &psDroid))
{
debug(LOG_ERROR, "recvDroidMove: Packet from %d refers to non-existent droid %d!",
NETgetSource(), (int)droid);
return false;
}
}
turnOffMultiMsg(true);
if (formation)
{
moveDroidTo(psDroid, x, y); // Do the move
}
else
{
moveDroidToNoFormation(psDroid, x, y); // Move, no form...
}
turnOffMultiMsg(false);
return true;
}
// ////////////////////////////////////////////////////////////////////////////
// Send a new Droid to the other players
BOOL SendDroid(const DROID_TEMPLATE* pTemplate, uint32_t x, uint32_t y, uint8_t player, uint32_t id)
{
if (!bMultiPlayer)
return true;
ASSERT(x != 0 && y != 0, "SendDroid: Invalid droid coordinates");
// Dont send other droids during campaign setup
if (ingame.localJoiningInProgress)
{
return true;
}
// Only send the droid if we are responsible
if (!myResponsibility(player))
{
// Don't build if we are not responsible
return false;
}
NETbeginEncode(NET_DROID, NET_ALL_PLAYERS);
{
Vector3uw pos = { x, y, 0 };
uint32_t templateID = pTemplate->multiPlayerID;
NETuint8_t(&player);
NETuint32_t(&id);
NETVector3uw(&pos);
NETuint32_t(&templateID);
NETbool(&powerCalculated);
}
return NETend();
}
// ////////////////////////////////////////////////////////////////////////////
// receive droid creation information from other players
BOOL recvDroid()
{
DROID_TEMPLATE* pT;
DROID* psDroid;
uint8_t player;
uint32_t id;
Vector3uw pos;
BOOL power;
uint32_t templateID;
NETbeginDecode(NET_DROID);
{
NETuint8_t(&player);
NETuint32_t(&id);
NETVector3uw(&pos);
NETuint32_t(&templateID);
NETbool(&power);
pT = IdToTemplate(templateID, player);
}
NETend();
if ((pos.x == 0 && pos.y == 0) || pos.x > world_coord(mapWidth) || pos.y > world_coord(mapHeight))
{
debug(LOG_ERROR, "recvDroid: Received bad droid position (%d, %d) from %d", (int)pos.x, (int)pos.y, NETgetSource());
return false;
}
// If we can not find the template ask for the entire droid instead
if (!pT)
{
debug(LOG_ERROR, "recvDroid: Packet from %d refers to non-existent template %d!",
NETgetSource(), (int)templateID);
return false;
}
// If the power to build the droid has been calculated
if (power
// Use the power required to build the droid
&& !usePower(player, pT->powerPoints))
{
debug(LOG_ERROR, "recvDroid: Not enough power to build droid for player = %hhu", player);
// Build anyway..
}
// Create that droid on this machine.
turnOffMultiMsg(true);
psDroid = buildDroid(pT, pos.x, pos.y, player, false);
turnOffMultiMsg(false);
// If we were able to build the droid set it up
if (psDroid)
{
psDroid->id = id;
addDroid(psDroid, apsDroidLists);
}
else
{
debug(LOG_ERROR, "recvDroid: Packet from %d cannot create droid!", NETgetSource());
DBCONPRINTF(ConsoleString, (ConsoleString, "MULTIPLAYER: Couldn't build a remote droid, relying on checking to resync"));
return false;
}
return true;
}
/*!
* Type of the target of the movement
*/
typedef enum {
NET_ORDER_SUBTYPE_POSITION,
NET_ORDER_SUBTYPE_OBJECT,
NET_ORDER_SUBTYPE_SPECIAL // x and y are 0, no idea what that means
} NET_ORDER_SUBTYPE;
// ////////////////////////////////////////////////////////////////////////////
/*!
* Droid Group/selection orders.
* Minimises comms by sending orders for whole groups, rather than each droid
*/
BOOL SendGroupOrderSelected(uint8_t player, uint32_t x, uint32_t y, const BASE_OBJECT* psObj)
{
if (!bMultiPlayer)
return true;
NETbeginEncode(NET_GROUPORDER, NET_ALL_PLAYERS);
{
DROID_ORDER order = UNKNOWN;
BOOL subType = (psObj) ? true : false, cmdOrder = false;
DROID* psDroid;
uint8_t droidCount;
NETenum(&order);
NETbool(&cmdOrder);
NETbool(&subType);
// If they are being ordered to `goto' an object
if (subType)
{
uint32_t id = psObj->id;
uint32_t type = psObj->type;
NETuint32_t(&id);
NETenum(&type);
}
// Else if the droids are being ordered to `goto' a specific position
else
{
NETuint32_t(&x);
NETuint32_t(&y);
}
// Work out the number of droids to send
for (psDroid = apsDroidLists[player], droidCount = 0; psDroid; psDroid = psDroid->psNext)
{
if (psDroid->selected)
++droidCount;
}
// If there are less than 2 droids don't bother (to allow individual orders)
if (droidCount < 2)
{
return false;
}
// Add the number of droids to the message
NETuint8_t(&droidCount);
// Add the droids to the message
for (psDroid = apsDroidLists[player]; psDroid && droidCount; psDroid = psDroid->psNext)
{
if (psDroid->selected)
{
NETuint32_t(&psDroid->id);
--droidCount;
}
}
}
return NETend();
}
BOOL SendGroupOrderGroup(const DROID_GROUP* psGroup, DROID_ORDER order, uint32_t x, uint32_t y, const BASE_OBJECT* psObj)
{
/* Check if the order is valid */
if ((psObj && !validOrderForObj(order)) || (!psObj && !validOrderForLoc(order)))
{
ASSERT(false, "SendGroupOrderGroup: Bad order");
return false;
}
if (!bMultiPlayer)
return true;
NETbeginEncode(NET_GROUPORDER, NET_ALL_PLAYERS);
{
BOOL subType = (psObj) ? true : false, cmdOrder = false;
DROID* psDroid;
uint8_t droidCount;
NETenum(&order);
NETbool(&cmdOrder);
NETbool(&subType);
// If they are being ordered to `goto' an object
if (subType)
{
uint32_t id = psObj->id;
uint32_t type = psObj->type;
NETuint32_t(&id);
NETenum(&type);
}
// Else if the droids are being ordered to `goto' a specific position
else
{
NETuint32_t(&x);
NETuint32_t(&y);
}
// Work out the number of droids to send
for (psDroid = psGroup->psList, droidCount = 0; psDroid; psDroid = psDroid->psGrpNext)
{
++droidCount;
}
// Add the number of droids to the message
NETuint8_t(&droidCount);
// Add the droids to the message
for (psDroid = psGroup->psList; psDroid; psDroid = psDroid->psGrpNext)
{
NETuint32_t(&psDroid->id);
}
}
return NETend();
}
// ////////////////////////////////////////////////////////////////////////////
// receive a group order.
BOOL recvGroupOrder()
{
DROID_ORDER order = DORDER_NONE;
BOOL subType, cmdOrder;
uint32_t destId, x, y;
OBJECT_TYPE destType = OBJ_DROID; // Dummy initialisation to workaround NETenum macro
uint8_t droidCount, i;
uint32_t* droidIDs;
NETbeginDecode(NET_GROUPORDER);
{
NETenum(&order);
NETbool(&cmdOrder);
NETbool(&subType);
// If they are being ordered to `goto' an object
if (subType)
{
NETuint32_t(&destId);
NETenum(&destType);
}
// Else if the droids are being ordered to `goto' a specific position
else
{
NETuint32_t(&x);
NETuint32_t(&y);
}
// Get the droid count
NETuint8_t(&droidCount);
// Allocate some space on the stack to hold the droid IDs
droidIDs = alloca(droidCount * sizeof(uint32_t));
// Retrieve the droids from the message
for (i = 0; i < droidCount; ++i)
{
// Retrieve the id number for the current droid
if (!NETuint32_t(&droidIDs[i]))
{
// If somehow we fail assume the message got truncated prematurely
debug(LOG_NET, "recvGroupOrder: error retrieving droid ID number; while there are (supposed to be) still %u droids left",
(unsigned int)(droidCount - i));
NETend();
return false;
}
}
}
NETend();
/* Check if the order is valid */
if (order != UNKNOWN && ((subType && !validOrderForObj(order)) || (!subType && !validOrderForLoc(order))))
{
debug(LOG_ERROR, "recvGroupOrder: Invalid group order received from %d!", NETgetSource());
return false;
}
// Process the given order for all droids we've retrieved
for (i = 0; i < droidCount; ++i)
{
DROID* psDroid;
// Retrieve the droid associated with the current ID
if (!IdToDroid(droidIDs[i], ANYPLAYER, &psDroid))
{
debug(LOG_ERROR, "recvGroupOrder: Packet from %d refers to non-existent droid %d!",
NETgetSource(), (int)droidIDs[i]);
continue; // continue working on next droid; crossing fingers...
}
/*
* If the current order not is a command order and we are not a
* commander yet are in the commander group remove us from it.
*/
if (!cmdOrder && hasCommander(psDroid))
{
grpLeave(psDroid->psGroup, psDroid);
}
// Process the droid's order
if (subType)
{
/* If they are being ordered to `goto' an object then we don't
* have any X and Y coordinate.
*/
ProcessDroidOrder(psDroid, order, 0, 0, destType, destId);
}
else
{
/* Otherwise if the droids are being ordered to `goto' a
* specific position. Then we don't have any destination info
*/
ProcessDroidOrder(psDroid, order, x, y, 0, 0);
}
}
return true;
}
// ////////////////////////////////////////////////////////////////////////////
// Droid update information
BOOL SendDroidInfo(const DROID* psDroid, DROID_ORDER order, uint32_t x, uint32_t y, const BASE_OBJECT* psObj)
{
if (!bMultiPlayer)
return true;
if (!myResponsibility(psDroid->player))
{
return true;
}
NETbeginEncode(NET_DROIDINFO, NET_ALL_PLAYERS);
{
uint32_t droidId = psDroid->id;
BOOL subType = (psObj) ? true : false;
// Send the droid's ID
NETuint32_t(&droidId);
// Send the droid's order
NETenum(&order);
NETbool(&subType);
if (subType)
{
uint32_t destId = psObj->id;
uint32_t destType = psObj->type;
NETuint32_t(&destId);
NETenum(&destType);
}
else
{
NETuint32_t(&x);
NETuint32_t(&y);
}
}
return NETend();
}
// ////////////////////////////////////////////////////////////////////////////
// receive droid information form other players.
BOOL recvDroidInfo()
{
NETbeginDecode(NET_DROIDINFO);
{
uint32_t droidId;
DROID* psDroid;
DROID_ORDER order = DORDER_NONE;
BOOL subType;
// Get the droid
NETuint32_t(&droidId);
if (!IdToDroid(droidId, ANYPLAYER, &psDroid))
{
debug(LOG_ERROR, "recvDroidInfo: Packet from %d refers to non-existent droid %d!",
NETgetSource(), (int)droidId);
return false;
}
// Get the droid's order
NETenum(&order);
NETbool(&subType);
if (subType)
{
uint32_t destId, destType = 0;
NETuint32_t(&destId);
NETenum(&destType);
ProcessDroidOrder(psDroid, order, 0, 0, destType, destId);
}
else
{
uint32_t x, y;
NETuint32_t(&x);
NETuint32_t(&y);
// If both the X _and_ Y coordinate are zero we've been given a
// "special" order.
if (x == 0 && y == 0)
{
turnOffMultiMsg(true);
orderDroid(psDroid, order);
turnOffMultiMsg(false);
}
// Otherwise it is just a normal "goto location" order
else
{
ProcessDroidOrder(psDroid, order, x, y, 0, 0);
}
}
}
NETend();
return true;
}
// ////////////////////////////////////////////////////////////////////////////
// process droid order
static void ProcessDroidOrder(DROID *psDroid, DROID_ORDER order, uint32_t x, uint32_t y, OBJECT_TYPE desttype, uint32_t destid)
{
// Target is a location
if (destid == 0 && desttype == 0)
{
// Don't bother if it is close
if (abs(psDroid->pos.x - x) < (TILE_UNITS/2)
&& abs(psDroid->pos.y - y) < (TILE_UNITS/2))
{
return;
}
// If no specific order was passed work one out based on the location
if (order == UNKNOWN)
{
order = chooseOrderLoc(psDroid, x, y);
}
turnOffMultiMsg(true);
orderDroidLoc(psDroid, order, x, y);
turnOffMultiMsg(false);
}
// Target is an object
else
{
BASE_OBJECT *psObj = NULL;
DROID *pD;
switch (desttype)
{
case OBJ_DROID:
if (IdToDroid(destid, ANYPLAYER, &pD))
{
psObj = (BASE_OBJECT*)pD;
}
break;
case OBJ_STRUCTURE:
psObj = (BASE_OBJECT*)IdToStruct(destid,ANYPLAYER);
break;
case OBJ_FEATURE:
psObj = (BASE_OBJECT*)IdToFeature(destid,ANYPLAYER);
break;
// We should not get this!
case OBJ_PROJECTILE:
debug(LOG_ERROR, "ProcessDroidOrder: order specified destination as a bullet. what am i to do??");
break;
default:
debug(LOG_ERROR, "ProcessDroidOrder: unknown object type");
break;
}
// If we did not find anything, return
if (!psObj) // failed to find it;
{
return;
}
// If we didn't sepcify an order, then pick one
if (order == UNKNOWN)
{
order = chooseOrderObj(psDroid, psObj);
}
turnOffMultiMsg(true);
orderDroidObj(psDroid, order, psObj);
turnOffMultiMsg(false);
}
}
// ////////////////////////////////////////////////////////////////////////////
// Inform other players that a droid has been destroyed
BOOL SendDestroyDroid(const DROID* psDroid)
{
if (!bMultiPlayer)
{
return true;
}
NETbeginEncode(NET_DROIDDEST, NET_ALL_PLAYERS);
{
uint32_t id = psDroid->id;
// Send the droid's ID
debug(LOG_DEATH, "Requested all players to destroy droid %u", (unsigned int)id);
NETuint32_t(&id);
}
return NETend();
}
// ////////////////////////////////////////////////////////////////////////////
// Accept a droid which was destroyed on another machine
BOOL recvDestroyDroid()
{
DROID* psDroid;
NETbeginDecode(NET_DROIDDEST);
{
uint32_t id;
// Retrieve the droid
NETuint32_t(&id);
if (!IdToDroid(id, ANYPLAYER, &psDroid))
{
return false;
}
}
NETend();
// If the droid has not died on our machine yet, destroy it
if(!psDroid->died)
{
turnOffMultiMsg(true);
debug(LOG_DEATH, "Killing droid %d on request from player %d", psDroid->id, NETgetSource());
destroyDroid(psDroid);
turnOffMultiMsg(false);
}
return true;
}