Logo Search packages:      
Sourcecode: warzone2100 version File versions

multibot.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;
            uint32_t body = psDroid->body;
            Vector3uw pos = psDroid->pos;

            NETuint8_t(&player);
            NETuint32_t(&droid);
            NETenum(&sec);
            NETenum(&state);
            NETuint32_t(&body);
            NETVector3uw(&pos);
      }
      return NETend();
}

// recv
BOOL recvDroidSecondary()
{
      DROID*          psDroid;
      SECONDARY_ORDER sec = DSO_ATTACK_RANGE;
      SECONDARY_STATE state = DSS_NONE;
      uint32_t body;
      Vector3uw pos;

      NETbeginDecode(NET_SECONDARY);
      {
            uint8_t player;
            uint32_t droid;

            NETuint8_t(&player);
            NETuint32_t(&droid);
            NETenum(&sec);
            NETenum(&state);
            NETuint32_t(&body);
            NETVector3uw(&pos);

            // 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);

      // Set the droids body points (HP)
      psDroid->body = body;

      // Set the droids position if it is more than two tiles out
      if (abs(pos.x - psDroid->pos.x) > (TILE_UNITS * 2)
       || abs(pos.y - psDroid->pos.y) > (TILE_UNITS * 2))
      {
            int oldx = psDroid->pos.x;
            int oldy = psDroid->pos.y;

            // Jump it, even if it is on screen (may want to change this)
            psDroid->pos = pos;

            // Tell the grid system that the object has moved
            gridMoveDroid(psDroid, oldx, oldy);
      }

      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);
                        NETuint32_t(&psDroid->body);
                        NETVector3uw(&psDroid->pos);
                        --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);
                  NETuint32_t(&psDroid->body);
                  NETVector3uw(&psDroid->pos);
            }
      }
      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;
      uint32_t *droidBodies;
      Vector3uw *droidPositions;

      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));

            // Plus some more space for the body points of the droids
            droidBodies = alloca(droidCount * sizeof(uint32_t));

            // Finally some space for the positions of the droids
            droidPositions = alloca(droidCount * sizeof(Vector3uw));

            // 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;
                  }

                  // Get the body points of the droid
                  NETuint32_t(&droidBodies[i]);

                  // Get the position of the droid
                  NETVector3uw(&droidPositions[i]);
            }
      }
      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;
            uint32_t body = droidBodies[i];
            Vector3uw pos = droidPositions[i];

            // 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);
            }

            // Update the droids body points
            psDroid->body = body;

            // Set the droids position if it is more than two tiles out
            if (abs(pos.x - psDroid->pos.x) > (TILE_UNITS * 2)
             || abs(pos.y - psDroid->pos.y) > (TILE_UNITS * 2))
            {
                  int oldx = psDroid->pos.x;
                  int oldy = psDroid->pos.y;

                  // Jump it, even if it is on screen (may want to change this)
                  psDroid->pos = pos;

                  // Tell the grid system that the object has moved
                  gridMoveDroid(psDroid, oldx, oldy);
            }
      }

      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;
}

Generated by  Doxygen 1.6.0   Back to index