1091 lines
26 KiB
C
1091 lines
26 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 "basedef.h" // for sending WHOLE droids.
|
|
#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"
|
|
|
|
#define ANYPLAYER 99
|
|
#define UNKNOWN 99
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// External Stuff.
|
|
extern DROID_ORDER chooseOrderLoc(DROID *psDroid, UDWORD x,UDWORD y);
|
|
extern DROID_ORDER chooseOrderObj(DROID *psDroid, BASE_OBJECT *psObj);
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// Local Prototypes
|
|
|
|
static BOOL sendRequestDroid(UDWORD droidId);
|
|
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(DROID *psDroid)
|
|
{
|
|
NETMSG m;
|
|
|
|
if (!myResponsibility(psDroid->player))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
NetAdd(m,0,psDroid->player);
|
|
NetAdd(m,1,psDroid->id);
|
|
m.size =5;
|
|
m.type =NET_VTOL;
|
|
|
|
return NETbcast(&m,FALSE);
|
|
}
|
|
|
|
BOOL recvHappyVtol(NETMSG *pMsg)
|
|
{
|
|
DROID *pD;
|
|
UBYTE player;
|
|
UDWORD id;
|
|
int i;
|
|
|
|
NetGet(pMsg,0,player);
|
|
NetGet(pMsg,1,id);
|
|
|
|
if (!IdToDroid(id,player,&pD)) //find droid.
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// 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(DROID *psDroid, SECONDARY_ORDER sec, SECONDARY_STATE state)
|
|
{
|
|
NETMSG m;
|
|
|
|
NetAdd(m,0,psDroid->id);
|
|
NetAdd(m,4,sec);
|
|
NetAdd(m,8,state);
|
|
m.body[12] = (char) psDroid->player;
|
|
|
|
m.size = 13;
|
|
m.type = NET_SECONDARY;
|
|
return NETbcast(&m,FALSE);
|
|
}
|
|
|
|
// recv
|
|
BOOL recvDroidSecondary(NETMSG *pMsg)
|
|
{
|
|
DROID *psDroid;
|
|
SECONDARY_ORDER sec;
|
|
SECONDARY_STATE state;
|
|
UDWORD id,player;
|
|
|
|
NetGet(pMsg,0,id);
|
|
NetGet(pMsg,4,sec);
|
|
NetGet(pMsg,8,state);
|
|
player = pMsg->body[12];
|
|
|
|
// If we can not find the droid should we not ask for it?
|
|
if(!IdToDroid(id,player,&psDroid)) //find droid.
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Set the droids secondary order
|
|
turnOffMultiMsg(TRUE);
|
|
secondarySetState(psDroid, sec, state);
|
|
turnOffMultiMsg(FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL sendDroidSecondaryAll(DROID *psDroid)
|
|
{
|
|
NETMSG m;
|
|
|
|
NetAdd(m,0,psDroid->id);
|
|
NetAdd(m,4,psDroid->secondaryOrder);
|
|
m.body[8] = (char) psDroid->player;
|
|
|
|
m.size = 9;
|
|
m.type = NET_SECONDARY_ALL;
|
|
return NETbcast(&m,FALSE);
|
|
|
|
}
|
|
|
|
BOOL recvDroidSecondaryAll(NETMSG *pMsg)
|
|
{
|
|
DROID *psDroid;
|
|
UDWORD id,player,sorder;
|
|
|
|
NetGet(pMsg,0,id);
|
|
NetGet(pMsg,4,sorder);
|
|
player = pMsg->body[8];
|
|
|
|
if(!IdToDroid(id,player,&psDroid)) //find droid.
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(psDroid)
|
|
{
|
|
psDroid->secondaryOrder = sorder;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL sendDroidEmbark(DROID *psDroid)
|
|
{
|
|
NETMSG m;
|
|
|
|
NetAdd(m,0,psDroid->id);
|
|
m.body[4] = (char) psDroid->player;
|
|
|
|
m.size = 5;
|
|
m.type = NET_DROIDEMBARK;
|
|
|
|
return NETbcast(&m,FALSE);
|
|
|
|
}
|
|
|
|
BOOL recvDroidEmbark(NETMSG *pMsg)
|
|
{
|
|
DROID *psDroid;
|
|
UDWORD id,player;
|
|
|
|
NetGet(pMsg,0,id);
|
|
player = pMsg->body[4];
|
|
|
|
if(!IdToDroid(id,player,&psDroid)) //find droid.
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(psDroid)
|
|
{
|
|
// Take it out of the world without destroying it
|
|
droidRemove(psDroid, apsDroidLists);
|
|
|
|
// Init the order for when disembark
|
|
psDroid->order = DORDER_NONE;
|
|
setDroidTarget(psDroid, NULL);
|
|
psDroid->psTarStats = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL sendDroidDisEmbark(DROID *psDroid)
|
|
{
|
|
NETMSG m;
|
|
|
|
NetAdd(m,0,psDroid->id);
|
|
NetAdd(m,4,psDroid->pos.x);
|
|
NetAdd(m,6,psDroid->pos.y);
|
|
m.body[8] = (char) psDroid->player;
|
|
|
|
m.size = 9;
|
|
m.type = NET_DROIDDISEMBARK;
|
|
|
|
return NETbcast(&m,FALSE);
|
|
|
|
}
|
|
|
|
BOOL recvDroidDisEmbark(NETMSG *pMsg)
|
|
{
|
|
DROID *psDroid;
|
|
UDWORD id, player;
|
|
UWORD x, y;
|
|
|
|
NetGet(pMsg,0,id);
|
|
NetGet(pMsg,4,x);
|
|
NetGet(pMsg,6,y);
|
|
player = pMsg->body[8];
|
|
|
|
if(!IdToDroid(id,player,&psDroid)) //find droid.
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(psDroid)
|
|
{
|
|
// Add it back into the world at the x/y
|
|
psDroid->pos.x = x;
|
|
psDroid->pos.y = y;
|
|
|
|
if (!worldOnMap(x, y))
|
|
{
|
|
debug(LOG_ERROR, "recvDroidDisEmbark: droid not disembarked on map");
|
|
return FALSE;
|
|
}
|
|
|
|
updateDroidOrientation(psDroid);
|
|
|
|
// Initialise the movement data
|
|
initDroidMovement(psDroid);
|
|
|
|
// Reset droid orders
|
|
orderDroid(psDroid, DORDER_STOP);
|
|
gridAddObject((BASE_OBJECT *)psDroid);
|
|
psDroid->cluster = 0;
|
|
|
|
addDroid(psDroid, apsDroidLists);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// Droids
|
|
|
|
// posibly Send an updated droid movement order.
|
|
BOOL SendDroidMove(DROID *pDroid, uint32_t x, uint32_t y, BOOL bFormation)
|
|
{
|
|
NETMSG m;
|
|
|
|
// Don't allow a move to happen at all if it is not our responsibility
|
|
if (!myResponsibility(pDroid->player))
|
|
{
|
|
return FALSE; // Do not allow move
|
|
}
|
|
|
|
// If the unit has no actions or orders, allow it to happen but do not send
|
|
if (pDroid->action == DACTION_NONE || pDroid->order == DORDER_MOVE)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
NetAdd(m,0,pDroid->id); //droid to move
|
|
NetAdd(m,4,x); //x pos
|
|
NetAdd(m,8,y); //y pos
|
|
NetAdd(m,12,pDroid->player); //owner of droid(for speed!)
|
|
NetAdd(m,13,bFormation); //use a formation?
|
|
m.size = 14;
|
|
m.type = NET_DROIDMOVE;
|
|
NETbcast(&m,FALSE);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
// recv and updated droid position
|
|
BOOL recvDroidMove(NETMSG *m)
|
|
{
|
|
UDWORD player,id,x,y;
|
|
DROID *psDroid;
|
|
UBYTE bFormation;
|
|
|
|
NetGet(m,0,id);
|
|
NetGet(m,4,x);
|
|
NetGet(m,8,y);
|
|
player = m->body[12];
|
|
NetGet(m,13,bFormation);
|
|
|
|
/*
|
|
* If we could not find the droid, request it. We can safely return here
|
|
* as when the droid is sent it will contain the updated movement position.
|
|
*/
|
|
if(!(IdToDroid(id,player,&psDroid)))
|
|
{
|
|
sendRequestDroid(id);
|
|
return TRUE;
|
|
}
|
|
|
|
turnOffMultiMsg(TRUE);
|
|
if (bFormation)
|
|
{
|
|
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(DROID_TEMPLATE *pTemplate, uint32_t x, uint32_t y, uint8_t player, uint32_t id)
|
|
{
|
|
NETMSG m;
|
|
|
|
// 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;
|
|
}
|
|
|
|
|
|
NetAdd(m,0,player); //ok since <255 players!
|
|
NetAdd(m,1,x); //x pos of new droid
|
|
NetAdd(m,5,y); //y pos of new droid
|
|
NetAdd(m,9,id); //id of droid to create
|
|
|
|
m.body[13] = powerCalculated;
|
|
|
|
// new version
|
|
NetAdd(m,14,pTemplate->multiPlayerID);
|
|
m.size = 14+sizeof(pTemplate->multiPlayerID);
|
|
|
|
m.type=NET_DROID;
|
|
NETbcast(&m,FALSE); // send it.
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// receive droid creation information from other players
|
|
BOOL recvDroid(NETMSG * m)
|
|
{
|
|
DROID_TEMPLATE *pT;
|
|
UDWORD x,y,id;
|
|
UDWORD player;
|
|
UDWORD targetRef;
|
|
DROID *d;
|
|
|
|
player=m->body[0];
|
|
NetGet(m,1,x); // new droids x position
|
|
NetGet(m,5,y); // new droids y position
|
|
NetGet(m,9,id); // droid to build's id.
|
|
|
|
NetGet(m,14,targetRef);
|
|
|
|
pT = IdToTemplate(targetRef,player);
|
|
|
|
// If we can not find the template ask for the entire droid instead
|
|
if (!pT)
|
|
{
|
|
debug(LOG_NET, "Couldn't find template to build recvd droid");
|
|
sendRequestDroid(id);
|
|
return FALSE;
|
|
}
|
|
|
|
// If the power to build the droid has been calculated
|
|
if(m->body[13] != 0)
|
|
{
|
|
// Use the power required to build the droid
|
|
if (!usePower(player,pT->powerPoints))
|
|
{
|
|
debug(LOG_NET, "Not enough power to build recvd droid, player=%u", player);
|
|
// Build anyway..
|
|
}
|
|
}
|
|
|
|
// Create that droid on this machine.
|
|
turnOffMultiMsg(TRUE);
|
|
d = buildDroid(pT, x, y, player, FALSE);
|
|
turnOffMultiMsg(FALSE);
|
|
|
|
// If we were able to build the droid set it up
|
|
if (d)
|
|
{
|
|
d->id = id;
|
|
addDroid(d, apsDroidLists);
|
|
}
|
|
else
|
|
{
|
|
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, BASE_OBJECT *psObj)
|
|
{
|
|
NETMSG m;
|
|
DROID *pDroid;
|
|
uint16_t droidCount = 0;
|
|
DROID_ORDER order = UNKNOWN;
|
|
NET_ORDER_SUBTYPE subType = (psObj) ? NET_ORDER_SUBTYPE_OBJECT : NET_ORDER_SUBTYPE_POSITION;
|
|
BOOL cmdOrder = FALSE;
|
|
unsigned int i;
|
|
|
|
switch (subType)
|
|
{
|
|
// If they are being ordered to `goto' an object
|
|
case NET_ORDER_SUBTYPE_OBJECT:
|
|
NetAdd(m,0, psObj->id);
|
|
NetAdd(m,4, psObj->type);
|
|
break;
|
|
// If the droids are being ordered to `goto' a specific position
|
|
case NET_ORDER_SUBTYPE_POSITION:
|
|
NetAdd(m,0,x);
|
|
NetAdd(m,4,y);
|
|
break;
|
|
default:
|
|
assert(!"Unexpected droid-order-targettype!");
|
|
break;
|
|
}
|
|
|
|
m.body[8] = subType;
|
|
|
|
m.body[10] = cmdOrder; // not a cmd order.
|
|
|
|
m.body[12] = order; // set the order.
|
|
|
|
m.size = 13;
|
|
|
|
// Work out the number of droids to send
|
|
for (pDroid = apsDroidLists[player]; pDroid; pDroid = pDroid->psNext)
|
|
{
|
|
if (pDroid->selected)
|
|
droidCount++;
|
|
}
|
|
|
|
// If there are less than 2 droids don't bother (to allow individual orders)
|
|
if (droidCount < 2)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Add the droids to the message (break when reached droidCount, as the rest must be unselected droids)
|
|
for (i = 0, pDroid = apsDroidLists[player]; i < droidCount && pDroid; pDroid = pDroid->psNext)
|
|
{
|
|
if (pDroid->selected)
|
|
{
|
|
NetAdd(m,m.size,pDroid->id);
|
|
m.size += sizeof(UDWORD);
|
|
i++;
|
|
}
|
|
}
|
|
|
|
// Add the number of droids to the message
|
|
NetAdd(m,9,droidCount);
|
|
m.type = NET_GROUPORDER;
|
|
NETbcast(&m,FALSE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL SendGroupOrderGroup(DROID_GROUP *psGroup, DROID_ORDER order, uint32_t x, uint32_t y, BASE_OBJECT *psObj)
|
|
{
|
|
NETMSG m;
|
|
DROID *pDroid;
|
|
uint16_t droidCount = 0;
|
|
NET_ORDER_SUBTYPE subType = (psObj) ? NET_ORDER_SUBTYPE_OBJECT : NET_ORDER_SUBTYPE_POSITION;
|
|
BOOL cmdOrder = FALSE;
|
|
|
|
switch (subType)
|
|
{
|
|
// If they are being ordered to `goto' an object
|
|
case NET_ORDER_SUBTYPE_OBJECT:
|
|
NetAdd(m,0, psObj->id);
|
|
NetAdd(m,4, psObj->type);
|
|
break;
|
|
// If the droids are being ordered to `goto' a specific position
|
|
case NET_ORDER_SUBTYPE_POSITION:
|
|
NetAdd(m,0,x);
|
|
NetAdd(m,4,y);
|
|
break;
|
|
default:
|
|
assert(!"Unexpected droid-order-targettype!");
|
|
break;
|
|
}
|
|
|
|
m.body[8] = subType;
|
|
|
|
m.body[10] = cmdOrder; // not a cmd order.
|
|
|
|
m.body[12] = order; // set the order.
|
|
|
|
m.size = 13;
|
|
|
|
// Work out the number of droids to send
|
|
for (pDroid = psGroup->psList; pDroid; pDroid = pDroid->psGrpNext)
|
|
{
|
|
droidCount++;
|
|
}
|
|
|
|
// Add the droids to the message
|
|
for (pDroid = psGroup->psList; pDroid; pDroid = pDroid->psGrpNext)
|
|
{
|
|
NetAdd(m,m.size,pDroid->id);
|
|
m.size += sizeof(UDWORD);
|
|
}
|
|
|
|
// Add the number of droids to the message
|
|
NetAdd(m,9,droidCount);
|
|
m.type = NET_GROUPORDER;
|
|
NETbcast(&m,FALSE);
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// receive a group order.
|
|
BOOL recvGroupOrder(NETMSG *pMsg)
|
|
{
|
|
uint32_t x = 0, y = 0, id = 0, destid = 0;
|
|
DROID *psDroid = NULL;
|
|
OBJECT_TYPE desttype = OBJ_DROID;
|
|
DROID_ORDER order = pMsg->body[12];
|
|
NET_ORDER_SUBTYPE subType = pMsg->body[8];
|
|
BOOL cmdOrder = pMsg->body[10];
|
|
uint16_t droidCount;
|
|
unsigned int i;
|
|
|
|
// Get the droid count
|
|
NetGet(pMsg,9,droidCount);
|
|
|
|
switch (subType)
|
|
{
|
|
// It's target is an object
|
|
case NET_ORDER_SUBTYPE_OBJECT:
|
|
NetGet(pMsg,0,destid);
|
|
NetGet(pMsg,4,desttype);
|
|
break;
|
|
// It's target is a position
|
|
case NET_ORDER_SUBTYPE_POSITION:
|
|
NetGet(pMsg,0,x);
|
|
NetGet(pMsg,4,y);
|
|
break;
|
|
default:
|
|
assert(!"Unexpected droid-order-targettype!");
|
|
break;
|
|
}
|
|
|
|
// for each droid
|
|
for (i = 0; i < droidCount; i++)
|
|
{
|
|
NetGet(pMsg, 13 + i * sizeof(UDWORD), id);
|
|
IdToDroid(id, ANYPLAYER, &psDroid); // find the droid
|
|
if (psDroid == NULL)
|
|
{
|
|
sendRequestDroid(id); //droid not found, request it.
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* 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 && psDroid->droidType != DROID_COMMAND
|
|
&& psDroid->psGroup != NULL && psDroid->psGroup->type == GT_COMMAND)
|
|
{
|
|
grpLeave(psDroid->psGroup, psDroid);
|
|
}
|
|
|
|
// Process the droids order
|
|
ProcessDroidOrder(psDroid,order,x,y,desttype,destid); // process the order.
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// Droid update information
|
|
BOOL SendDroidInfo(DROID *psDroid, DROID_ORDER order, uint32_t x, uint32_t y, BASE_OBJECT *psObj)
|
|
{
|
|
NETMSG m;
|
|
NET_ORDER_SUBTYPE subType = psObj ? NET_ORDER_SUBTYPE_OBJECT :
|
|
( (x == 0 && y == 0) ? NET_ORDER_SUBTYPE_SPECIAL : NET_ORDER_SUBTYPE_POSITION );
|
|
|
|
if (!myResponsibility(psDroid->player))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
NetAdd(m,0,psDroid->id);
|
|
NetAdd(m,4,order);
|
|
|
|
switch (subType)
|
|
{
|
|
// If they are being ordered to `goto' an object
|
|
case NET_ORDER_SUBTYPE_OBJECT:
|
|
NetAdd(m,8, psObj->id);
|
|
NetAdd(m,12,psObj->type);
|
|
break;
|
|
// If the droids are being ordered to `goto' a specific position or have special orders
|
|
case NET_ORDER_SUBTYPE_POSITION:
|
|
case NET_ORDER_SUBTYPE_SPECIAL:
|
|
NetAdd(m,8,x);
|
|
NetAdd(m,12,y);
|
|
break;
|
|
}
|
|
|
|
m.body[16] = subType;
|
|
|
|
m.size= (4*(sizeof(UDWORD))) + 1;
|
|
m.type = NET_DROIDINFO;
|
|
return NETbcast(&m,FALSE);
|
|
}
|
|
|
|
|
|
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// receive droid information form other players.
|
|
BOOL recvDroidInfo(NETMSG *pMsg)
|
|
{
|
|
uint32_t x = 0, y = 0, id = 0, destid = 0;
|
|
DROID_ORDER order;
|
|
NET_ORDER_SUBTYPE subType = pMsg->body[16];
|
|
DROID *psDroid = NULL;
|
|
OBJECT_TYPE desttype = OBJ_DROID;
|
|
|
|
NetGet(pMsg,0,id); //droid's id
|
|
NetGet(pMsg,4,order); //droid's order
|
|
|
|
// If we could not find the droid, request it
|
|
if(!IdToDroid(id, ANYPLAYER, &psDroid))
|
|
{
|
|
sendRequestDroid(id);
|
|
return (FALSE);
|
|
}
|
|
|
|
// Now process the actual order
|
|
switch (subType)
|
|
{
|
|
// If they are being ordered to `goto' an object
|
|
case NET_ORDER_SUBTYPE_OBJECT:
|
|
NetGet(pMsg,8,destid);
|
|
NetGet(pMsg,12,desttype);
|
|
ProcessDroidOrder(psDroid, order, 0, 0, desttype, destid);
|
|
break;
|
|
// If the droids are being ordered to `goto' a specific position
|
|
case NET_ORDER_SUBTYPE_POSITION:
|
|
NetGet(pMsg,8,x);
|
|
NetGet(pMsg,12,y);
|
|
ProcessDroidOrder(psDroid, order, x, y, 0, 0);
|
|
break;
|
|
case NET_ORDER_SUBTYPE_SPECIAL:
|
|
turnOffMultiMsg(TRUE);
|
|
orderDroid(psDroid, order);
|
|
turnOffMultiMsg(FALSE);
|
|
break;
|
|
}
|
|
|
|
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:
|
|
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(DROID *pD)
|
|
{
|
|
NETMSG m;
|
|
|
|
NetAdd(m,0,pD->id); // id of the droid to be destroyed
|
|
m.size = sizeof(UDWORD);
|
|
m.type = NET_DROIDDEST;
|
|
|
|
return( NETbcast(&m,TRUE)); //guaranteed msg?????
|
|
}
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// Accept a droid which was destroyed on another machine
|
|
BOOL recvDestroyDroid(NETMSG *pMsg)
|
|
{
|
|
DROID *pD;
|
|
UDWORD r;
|
|
|
|
NetGet(pMsg,0,r); // get the id of the droid.
|
|
if( !IdToDroid(r, ANYPLAYER, &pD) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if(!pD->died)
|
|
{
|
|
turnOffMultiMsg(TRUE);
|
|
destroyDroid(pD); // remove the droid recvd from the remote players machine.
|
|
turnOffMultiMsg(FALSE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// stuff for sending the WHOLE of a droid!
|
|
BOOL sendWholeDroid(DROID *pD, UDWORD dest)
|
|
{
|
|
NETMSG m;
|
|
UDWORD sizecount = 0;
|
|
UDWORD noTarget = 0;
|
|
SDWORD asParts[DROID_MAXCOMP];
|
|
UDWORD asWeaps[DROID_MAXWEAPS];
|
|
unsigned int i;
|
|
BOOL bNoTarget = TRUE;
|
|
uint16_t direction = pD->direction * 32; // preserve precision
|
|
|
|
if (pD->numWeaps == 0)
|
|
{
|
|
if (pD->asWeaps[0].nStat > 0) // build some bits for the template.
|
|
{
|
|
asWeaps[0] = pD->asWeaps[0].nStat;
|
|
}
|
|
else
|
|
{
|
|
asWeaps[0] = 0;
|
|
}
|
|
}
|
|
|
|
asParts[COMP_BODY] = pD->asBits[COMP_BODY].nStat; //allocate the components
|
|
asParts[COMP_BRAIN] = pD->asBits[COMP_BRAIN].nStat;
|
|
asParts[COMP_PROPULSION]= pD->asBits[COMP_PROPULSION].nStat;
|
|
asParts[COMP_SENSOR] = pD->asBits[COMP_SENSOR].nStat;
|
|
asParts[COMP_ECM] = pD->asBits[COMP_ECM].nStat;
|
|
asParts[COMP_REPAIRUNIT]= pD->asBits[COMP_REPAIRUNIT].nStat;
|
|
asParts[COMP_CONSTRUCT] = pD->asBits[COMP_CONSTRUCT].nStat;
|
|
asParts[COMP_WEAPON] = pD->asBits[COMP_WEAPON].nStat;
|
|
NetAdd(m,sizecount,asParts); sizecount+=sizeof(asParts);
|
|
NetAdd(m,sizecount,pD->numWeaps); sizecount+=sizeof(pD->numWeaps);
|
|
NetAdd(m,sizecount,pD->asWeaps); sizecount+=sizeof(pD->asWeaps); // to build a template.
|
|
|
|
NetAdd(m,sizecount,pD->pos.x); sizecount+=sizeof(pD->pos.x);
|
|
NetAdd(m,sizecount,pD->pos.y); sizecount+=sizeof(pD->pos.y);
|
|
NetAdd(m,sizecount,pD->pos.z); sizecount+=sizeof(pD->pos.z);
|
|
NetAdd(m,sizecount,pD->player); sizecount+=sizeof(pD->player);
|
|
|
|
NetAddSt(m,sizecount,pD->aName); sizecount+=strlen(pD->aName)+1;
|
|
|
|
// that's enough to build a template, now the specific stuff!
|
|
NetAdd(m,sizecount,pD->id); sizecount+=sizeof(pD->id);
|
|
|
|
NetAdd(m,sizecount,pD->NameVersion); sizecount+=sizeof(pD->NameVersion);
|
|
NetAdd(m,sizecount,pD->droidType); sizecount+=sizeof(pD->droidType);
|
|
|
|
NetAdd(m,sizecount,direction); sizecount+=sizeof(direction);
|
|
NetAdd(m,sizecount,pD->pitch); sizecount+=sizeof(pD->pitch);
|
|
NetAdd(m,sizecount,pD->roll); sizecount+=sizeof(pD->roll);
|
|
NetAdd(m,sizecount,pD->visible); sizecount+=sizeof(pD->visible);
|
|
NetAdd(m,sizecount,pD->inFire); sizecount+=sizeof(pD->inFire);
|
|
NetAdd(m,sizecount,pD->burnDamage); sizecount+=sizeof(pD->burnDamage);
|
|
|
|
NetAdd(m,sizecount,pD->body); sizecount+=sizeof(pD->body);
|
|
NetAdd(m,sizecount,pD->secondaryOrder); sizecount+=sizeof(pD->secondaryOrder);
|
|
NetAdd(m,sizecount,pD->order); sizecount+=sizeof(pD->order);
|
|
NetAdd(m,sizecount,pD->orderX); sizecount+=sizeof(pD->orderX);
|
|
NetAdd(m,sizecount,pD->orderY); sizecount+=sizeof(pD->orderY);
|
|
NetAdd(m,sizecount,pD->orderX2); sizecount+=sizeof(pD->orderX2);
|
|
NetAdd(m,sizecount,pD->orderY2); sizecount+=sizeof(pD->orderY2);
|
|
|
|
for (i = 0; i < pD->numWeaps; i++)
|
|
{
|
|
if (pD->psActionTarget[i])
|
|
{
|
|
NetAdd(m,sizecount,pD->psActionTarget[i]->id); sizecount+=sizeof(pD->psActionTarget[i]->id);
|
|
bNoTarget = FALSE;
|
|
}
|
|
}
|
|
|
|
if (bNoTarget)
|
|
{
|
|
NetAdd(m,sizecount,noTarget); sizecount+=sizeof(noTarget);
|
|
}
|
|
|
|
if (pD->psTarStats)
|
|
{
|
|
NetAdd(m,sizecount,pD->psTarStats->ref); sizecount+=sizeof(pD->psTarStats->ref);
|
|
}
|
|
else
|
|
{
|
|
NetAdd(m,sizecount,noTarget); sizecount+=sizeof(noTarget);
|
|
}
|
|
|
|
|
|
m.type = NET_WHOLEDROID;
|
|
m.size = (UWORD)sizecount;
|
|
return NETsend(&m,dest,FALSE);
|
|
}
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// receive a whole droid!!!!
|
|
BOOL receiveWholeDroid(NETMSG *m)
|
|
{
|
|
UDWORD sizecount=0;
|
|
DROID_TEMPLATE dt;
|
|
DROID *pD,*existingdroid;
|
|
UWORD x,y,z;
|
|
UDWORD id;
|
|
UBYTE player;
|
|
UBYTE i;
|
|
uint16_t direction;
|
|
|
|
// get the stuff
|
|
NetGet(m,sizecount,dt.asParts); sizecount+=sizeof(dt.asParts); // build a template
|
|
NetGet(m,sizecount,dt.asWeaps); sizecount+=sizeof(dt.asWeaps);
|
|
NetGet(m,sizecount,dt.numWeaps); sizecount+=sizeof(dt.numWeaps); // numWeaps
|
|
NetGet(m,sizecount,x); sizecount+=sizeof(x); // edit it.
|
|
NetGet(m,sizecount,y); sizecount+=sizeof(y);
|
|
NetGet(m,sizecount,z); sizecount+=sizeof(z);
|
|
NetGet(m,sizecount,player); sizecount+=sizeof(player);
|
|
|
|
dt.pName = (char*)&dt.aName;
|
|
strlcpy(dt.aName, &(m->body[sizecount]), sizeof(dt.aName));
|
|
sizecount+=strlen(dt.pName)+1; // name is pointed at directly into the buffer.
|
|
|
|
if(dt.asWeaps[0] == 0)
|
|
{
|
|
dt.numWeaps =0;
|
|
}
|
|
|
|
dt.powerPoints = calcTemplatePower(&dt);
|
|
|
|
NetGet(m,sizecount,id); sizecount+=sizeof(id);
|
|
|
|
if(IdToDroid(id,ANYPLAYER,&existingdroid))// if a droid of id already exists then go no further.
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// could do usepower , but we usually do this in an emergency, so leave it!
|
|
turnOffMultiMsg(TRUE);
|
|
pD = buildDroid(&dt,x,y,player, FALSE); // make a droid
|
|
turnOffMultiMsg(FALSE);
|
|
|
|
if(!pD) // failed to build it, give up.
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
STATIC_ASSERT(sizeof(id) == sizeof(pD->id));
|
|
|
|
// now the instance specific stuff.
|
|
pD->id = id;
|
|
pD->pos.x = x; //correct builddroid to use exact pos, not tile center
|
|
pD->pos.y = y;
|
|
pD->pos.z = z;
|
|
|
|
NetGet(m,sizecount,pD->NameVersion); sizecount+=sizeof(pD->NameVersion);
|
|
NetGet(m,sizecount,pD->droidType); sizecount+=sizeof(pD->droidType);
|
|
|
|
NetGet(m,sizecount, direction); sizecount+=sizeof(direction);
|
|
pD->direction = (float)direction / 32;
|
|
NetGet(m,sizecount,pD->pitch); sizecount+=sizeof(pD->pitch);
|
|
NetGet(m,sizecount,pD->roll); sizecount+=sizeof(pD->roll);
|
|
|
|
NetGet(m,sizecount,pD->visible); sizecount+=sizeof(pD->visible);
|
|
NetGet(m,sizecount,pD->inFire); sizecount+=sizeof(pD->inFire);
|
|
NetGet(m,sizecount,pD->burnDamage); sizecount+=sizeof(pD->burnDamage);
|
|
|
|
NetGet(m,sizecount,pD->body); sizecount+=sizeof(pD->body);
|
|
|
|
NetGet(m,sizecount,pD->secondaryOrder); sizecount+=sizeof(pD->secondaryOrder);
|
|
|
|
for (i = 0;i < dt.numWeaps;i++)
|
|
{
|
|
NetGet(m, sizecount, id); sizecount += sizeof(id);
|
|
pD->psActionTarget[i] = IdToPointer(id, ANYPLAYER);
|
|
}
|
|
pD->psTarStats = NULL;
|
|
|
|
addDroid(pD, apsDroidLists);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
// Functions for cases where a machine receives a netmessage about a certain
|
|
// droid. The droid is unknown, so the machine uses tese functions in order to
|
|
// find out about it.
|
|
BOOL sendRequestDroid(uint32_t droidId)
|
|
{
|
|
NETMSG msg;
|
|
|
|
if (ingame.localJoiningInProgress) // Don't worry if still joining.
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
NetAdd(msg,0,droidId);
|
|
NetAdd(msg,4,player2dpid[selectedPlayer] );
|
|
|
|
debug( LOG_NEVER, "multibot: unknown droid %d, requesting info\n", droidId );
|
|
|
|
msg.type = NET_REQUESTDROID;
|
|
msg.size = sizeof(UDWORD)+sizeof(UDWORD); // DPID + UDWORD
|
|
|
|
NETbcast(&msg,FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
// ////////////////////////////////////////////////////////////////////////////
|
|
BOOL recvRequestDroid(NETMSG *pMsg)
|
|
{
|
|
DROID *pDroid;
|
|
UDWORD droidid,dpid;
|
|
|
|
NetGet(pMsg,0,droidid); // get the droid's id
|
|
NetGet(pMsg,4,dpid); // get the player who needs it.
|
|
|
|
|
|
// Get the droid
|
|
if (!(IdToDroid(droidid, ANYPLAYER, &pDroid)))
|
|
{
|
|
// Can't find it, so ignore
|
|
return TRUE;
|
|
}
|
|
|
|
// If we are responsible, send it
|
|
if (myResponsibility(pDroid->player))
|
|
{
|
|
sendWholeDroid(pDroid,dpid);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|