Logo Search packages:      
Sourcecode: warzone2100 version File versions

structure.c

Go to the documentation of this file.
/*
      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
*/
/*!
 * \file structure.c
 *
 * Store Structure stats.
 * WARNING!!!!!!
 * Something wicked this way comes. This file is almost as evil as HCI.c
 */
#include <string.h>

#include "lib/framework/frame.h"
#include "lib/framework/strres.h"
#include "lib/framework/frameresource.h"
#include "objects.h"
#include "ai.h"
#include "map.h"
#include "lib/gamelib/gtime.h"
#include "visibility.h"
#include "structure.h"
#include "research.h"
#include "hci.h"
#include "power.h"
#include "miscimd.h"
#include "effects.h"
#include "combat.h"
#include "lib/sound/audio.h"
#include "lib/sound/audio_id.h"
#include "stats.h"
#include "lib/framework/math-help.h"
#include "edit3d.h"
#include "anim_id.h"
#include "lib/gamelib/anim.h"
#include "display3d.h"
#include "geometry.h"
// FIXME Direct iVis implementation include!
#include "lib/ivis_opengl/piematrix.h"
#include "order.h"
#include "droid.h"
#include "lib/script/script.h"
#include "scripttabs.h"
#include "scriptcb.h"
#include "formationdef.h"
#include "formation.h"
#include "text.h"
#include "action.h"
#include "group.h"
#include "transporter.h"
#include "fpath.h"
#include "mission.h"
#include "levels.h"
#include "console.h"
#include "cmddroid.h"
#include "feature.h"
#include "mapgrid.h"
#include "projectile.h"
#include "cluster.h"
#include "intdisplay.h"
#include "display.h"
#include "difficulty.h"
#include "scriptextern.h"
#include "keymap.h"
#include "game.h"

#include "advvis.h"
#include "multiplay.h"
#include "lib/netplay/netplay.h"
#include "multigifts.h"
#include "loop.h"

#include "scores.h"
#include "gateway.h"

// Possible types of wall to build
#define WALL_HORIZ            0
#define WALL_VERT       1
#define WALL_CORNER           2

#define STR_RECOIL_TIME (GAME_TICKS_PER_SEC/4)

// Maximum Distance allowed between a friendly structure and an assembly point.
#define ASSEMBLY_RANGE  (10*TILE_UNITS)

//Maximium slope of the terrin for building a structure
#define MAX_INCLINE           50//80//40

/* percentage of repair points cost to player power */
#define     REPAIR_FACILITY_COST_PERCENT  10

/* droid construction smoke cloud constants */
#define     DROID_CONSTRUCTION_SMOKE_OFFSET     30
#define     DROID_CONSTRUCTION_SMOKE_HEIGHT     20

//used to calculate how often to increase the resistance level of a structure
#define RESISTANCE_INTERVAL               2000

//used to calculate the time required for rearming
#define REARM_FACTOR                10
//used to calculate the time  required for repairing
#define VTOL_REPAIR_FACTOR          10

//holds the IMD pointers for the modules - ONLY VALID for player0
iIMDShape * factoryModuleIMDs[NUM_FACTORY_MODULES][NUM_FACMOD_TYPES];
iIMDShape * researchModuleIMDs[NUM_RESEARCH_MODULES];
iIMDShape * powerModuleIMDs[NUM_POWER_MODULES];

//Value is stored for easy access to this structure stat
UDWORD                  factoryModuleStat;
UDWORD                  powerModuleStat;
UDWORD                  researchModuleStat;

//holder for all StructureStats
STRUCTURE_STATS         *asStructureStats;
UDWORD                        numStructureStats;
//holder for the limits of each structure per map
STRUCTURE_LIMITS  *asStructLimits[MAX_PLAYERS];

//holds the upgrades attained through research for structure stats
STRUCTURE_UPGRADE asStructureUpgrade[MAX_PLAYERS];
WALLDEFENCE_UPGRADE     asWallDefenceUpgrade[MAX_PLAYERS];

//holds the upgrades for the functionality of structures through research
RESEARCH_UPGRADE  asResearchUpgrade[MAX_PLAYERS];
POWER_UPGRADE           asPowerUpgrade[MAX_PLAYERS];
REPAIR_FACILITY_UPGRADE       asRepairFacUpgrade[MAX_PLAYERS];
PRODUCTION_UPGRADE      asProductionUpgrade[MAX_PLAYERS][NUM_FACTORY_TYPES];
REARM_UPGRADE           asReArmUpgrade[MAX_PLAYERS];

//used to hold the modifiers cross refd by weapon effect and structureStrength
STRUCTSTRENGTH_MODIFIER       asStructStrengthModifier[WE_NUMEFFECTS][NUM_STRUCT_STRENGTH];

//specifies which numbers have been allocated for the assembly points for the factories
UBYTE                   factoryNumFlag[MAX_PLAYERS][NUM_FLAG_TYPES];

// the number of different (types of) droids that can be put into a production run
#define MAX_IN_RUN            9

//the list of what to build - only for selectedPlayer
PRODUCTION_RUN          asProductionRun[NUM_FACTORY_TYPES][MAX_FACTORY][MAX_PROD_RUN];

//stores which player the production list has been set up for
SBYTE               productionPlayer;

/* destroy building construction droid stat pointer */
static      STRUCTURE_STATS   *g_psStatDestroyStruct = NULL;

// the structure that was last hit
STRUCTURE   *psLastStructHit;

//flag for drawing radar
static            UBYTE hqExists[MAX_PLAYERS];
//flag for drawing all sat uplink sees
static            UBYTE satUplinkExists[MAX_PLAYERS];
//flag for when the player has one built - either completely or partially
static            UBYTE lasSatExists[MAX_PLAYERS];

static BOOL setFunctionality(STRUCTURE* psBuilding, STRUCTURE_TYPE functionType);
static void setFlagPositionInc(FUNCTIONALITY* pFunctionality, UDWORD player, UBYTE factoryType);
static void informPowerGen(STRUCTURE *psStruct);
static BOOL electronicReward(STRUCTURE *psStructure, UBYTE attackPlayer);
static void factoryReward(UBYTE losingPlayer, UBYTE rewardPlayer);
static void repairFacilityReward(UBYTE losingPlayer, UBYTE rewardPlayer);
static void findAssemblyPointPosition(UDWORD *pX, UDWORD *pY, UDWORD player);
static void removeStructFromMap(STRUCTURE *psStruct);
static void structUpdateRecoil( STRUCTURE *psStruct );
static void resetResistanceLag(STRUCTURE *psBuilding);
static void revealAll(UBYTE player);
static void cbNewDroid(STRUCTURE *psFactory, DROID *psDroid);


// last time the maximum units message was displayed
static UDWORD     lastMaxUnitMessage;

#define MAX_UNIT_MESSAGE_PAUSE 20000


/* New function from Alex M */
/* Tells you if a point is inside the footprint of a building */
BOOL  ptInStructure(STRUCTURE *psStruct, UDWORD x, UDWORD y)
{
      UDWORD tlX, tlY, brX, brY;
      UDWORD width, height;

      CHECK_STRUCTURE(psStruct);

      width = (psStruct->pStructureType->baseWidth * TILE_UNITS);
      height = (psStruct->pStructureType->baseBreadth * TILE_UNITS);


      tlX = psStruct->pos.x - (width/2);
      tlY = psStruct->pos.y - (height/2);

      brX = psStruct->pos.x + (width/2);
      brY = psStruct->pos.y + (height/2);

      if (x > tlX && x < brX && y > tlY && y < brY)
            return(true);
      return(false);
}

/*
Check to see if the stats is some kind of expansion module

... this replaces the thousands of occurance that is spread through out the code

... There were a couple of places where it skipping around a routine if the stat was a expansion module
      (loadSaveStructureV7 & 9) this code seemed suspect, and to clarify it we replaced it with the routine below
... the module stuff seemed to work though ...     TJC (& AB) 8-DEC-98
*/

BOOL IsStatExpansionModule(STRUCTURE_STATS *psStats)
{
      // If the stat is any of the 3 expansion types ... then return true
      if(   psStats->type == REF_POWER_MODULE  ||
            psStats->type == REF_FACTORY_MODULE  ||
            psStats->type == REF_RESEARCH_MODULE )
            {
                  return true;
            }
            else
            {
                  return false;
            }
}

void structureInitVars(void)
{
      int i, j;

      for(i=0; i<NUM_FACTORY_MODULES ; i++) {
            factoryModuleIMDs[i][0] = NULL;
            factoryModuleIMDs[i][1] = NULL;
      }
      for(i=0; i<NUM_RESEARCH_MODULES ; i++) {
            researchModuleIMDs[i] = NULL;
      }
      for(i=0; i<NUM_POWER_MODULES ; i++) {
            powerModuleIMDs[i] = NULL;
      }

      asStructureStats = NULL;
      numStructureStats = 0;
      factoryModuleStat = 0;
      powerModuleStat = 0;
      researchModuleStat = 0;
      lastMaxUnitMessage = 0;

      for (i = 0; i < MAX_PLAYERS; i++)
      {
            asStructLimits[i] = NULL;
            for (j = 0; j < NUM_FLAG_TYPES; j++)
            {
                  factoryNumFlag[i][j] = 0;
            }
      }
      for (i = 0; i < MAX_PLAYERS; i++)
      {
            hqExists[i] = false;
            satUplinkExists[i] = false;
            lasSatExists[i] = false;
      }
      //initialise the selectedPlayer's production run
      memset(&asProductionRun, 0, sizeof(PRODUCTION_RUN) * NUM_FACTORY_TYPES *
            MAX_FACTORY * MAX_PROD_RUN);
      //set up at beginning of game which player will have a production list
      productionPlayer = (SBYTE)selectedPlayer;
}

/*Initialise the production list and set up the production player*/
void changeProductionPlayer(UBYTE player)
{
      //clear the production run
      memset(&asProductionRun, 0, sizeof(PRODUCTION_RUN) * NUM_FACTORY_TYPES *
            MAX_FACTORY * MAX_PROD_RUN);
      //set this player to have the production list
      productionPlayer = player;
}


/*initialises the flag before a new data set is loaded up*/
void initFactoryNumFlag(void)
{
      UDWORD i, j;

      for(i=0; i< MAX_PLAYERS; i++)
      {
            //initialise the flag
            for (j=0; j < NUM_FLAG_TYPES; j++)
            {
                  factoryNumFlag[i][j] = (UBYTE)0;
            }
      }
}

//called at start of missions
void resetFactoryNumFlag(void)
{
      STRUCTURE*   psStruct;
      uint8_t      mask = 0;
      unsigned int i;

      for(i = 0; i < MAX_PLAYERS; i++)
      {
            //look throu the list of structures to see which have been used
            for (psStruct = apsStructLists[i]; psStruct != NULL; psStruct = psStruct->psNext)
            {
                  if (psStruct->pStructureType->type == REF_FACTORY
                   || psStruct->pStructureType->type == REF_CYBORG_FACTORY
                   || psStruct->pStructureType->type == REF_VTOL_FACTORY)
                  {
                        FACTORY* psFactory = &psStruct->pFunctionality->factory;
                        if (psFactory->psAssemblyPoint)
                        {
                              mask = (1 << psFactory->psAssemblyPoint->factoryInc);
                        }
                  }

                  if (psStruct->pStructureType->type == REF_FACTORY)
                              factoryNumFlag[i][FACTORY_FLAG] |= mask;
                  else if (psStruct->pStructureType->type == REF_CYBORG_FACTORY)
                              factoryNumFlag[i][CYBORG_FLAG] |= mask;
                  else if (psStruct->pStructureType->type == REF_VTOL_FACTORY)
                              factoryNumFlag[i][VTOL_FLAG] |= mask;
            }
      }
}

static const struct
{
      const char*     typeName;
      STRUCTURE_TYPE  type;
} structureTypeNames[] =
{
      { "HQ",                 REF_HQ                  },
      { "FACTORY",            REF_FACTORY             },
      { "FACTORY MODULE",     REF_FACTORY_MODULE      },
      { "RESEARCH",           REF_RESEARCH            },
      { "RESEARCH MODULE",    REF_RESEARCH_MODULE     },
      { "POWER GENERATOR",    REF_POWER_GEN           },
      { "POWER MODULE",       REF_POWER_MODULE        },
      { "RESOURCE EXTRACTOR", REF_RESOURCE_EXTRACTOR  },
      { "DEFENSE",            REF_DEFENSE             },
      { "WALL",               REF_WALL                },
      { "CORNER WALL",        REF_WALLCORNER          },
      { "REPAIR FACILITY",    REF_REPAIR_FACILITY     },
      { "COMMAND RELAY",      REF_COMMAND_CONTROL     },
      { "DEMOLISH",           REF_DEMOLISH            },
      { "CYBORG FACTORY",     REF_CYBORG_FACTORY      },
      { "VTOL FACTORY",       REF_VTOL_FACTORY        },
      { "LAB",                REF_LAB                 },
      { "DOOR",               REF_BLASTDOOR           },
      { "REARM PAD",          REF_REARM_PAD           },
      { "MISSILE SILO",       REF_MISSILE_SILO        },
      { "SAT UPLINK",         REF_SAT_UPLINK          },
};

static STRUCTURE_TYPE structureType(const char* typeName)
{
      unsigned int i;

      for (i = 0; i < ARRAY_SIZE(structureTypeNames); ++i)
      {
            if (strcmp(typeName, structureTypeNames[i].typeName) == 0)
            {
                  return structureTypeNames[i].type;
            }
      }

      ASSERT(!"unknown structure type", "Unknown Structure Type (%s)", typeName);
}


static const char* getStructName(const STRUCTURE_STATS* psStruct)
{
      return getName(psStruct->pName);
}

/*returns the structure strength based on the string name passed in */
static UBYTE getStructStrength(const char *pStrength)
{
      if (!strcmp(pStrength, "SOFT"))
      {
            return STRENGTH_SOFT;
      }
      else if (!strcmp(pStrength, "MEDIUM"))
      {
            return STRENGTH_MEDIUM;
      }
      else if (!strcmp(pStrength, "HARD"))
      {
            return STRENGTH_HARD;
      }
      else if (!strcmp(pStrength, "BUNKER"))
      {
            return STRENGTH_BUNKER;
      }

      return INVALID_STRENGTH;
}

static void initModulePIEs(char *PIEName,UDWORD i,STRUCTURE_STATS *psStructure)
{
      char GfxFile[MAX_STR_LENGTH];
      char charNum[2];
      UDWORD length, module = 0;

      strcpy(GfxFile,PIEName);

            //need to work out the IMD's for the modules - HACK!
            if (psStructure->type == REF_FACTORY_MODULE)
            {
                  length = strlen(GfxFile) - 5;
                  for (module = 1; module < NUM_FACTORY_MODULES+1; module++)
                  {
                        sprintf(charNum,"%d",module);
                        GfxFile[length] = *charNum;
                        factoryModuleIMDs[module-1][0] = (iIMDShape*) resGetData("IMD",
                              GfxFile);
                        if (factoryModuleIMDs[module-1][0] == NULL)
                        {
                              debug( LOG_ERROR, "Cannot find the PIE for factory module %d - %s", module, GfxFile );
                              abort();
                              return;
                        }
                  }
                  //store the stat for easy access later on
                  factoryModuleStat = i;
            }
            if (psStructure->type == REF_VTOL_FACTORY)
            {
                  length = strlen(GfxFile) - 5;
                  for (module = 1; module < NUM_FACTORY_MODULES+1; module++)
                  {
                        sprintf(charNum,"%d",module);
                        GfxFile[length] = *charNum;
                        factoryModuleIMDs[module-1][1] = (iIMDShape*) resGetData("IMD",
                              GfxFile);
                        if (factoryModuleIMDs[module-1][1] == NULL)
                        {
                              debug( LOG_ERROR, "Cannot find the PIE for vtol factory module %d - %s", module, GfxFile );
                              abort();
                              return;
                        }
                  }
            }

            // Setup the PIE's for the research modules.
            if (psStructure->type == REF_RESEARCH_MODULE)
            {
                  length = strlen(GfxFile) - 5;
                  GfxFile[length] = '4';

                  researchModuleIMDs[0] = (iIMDShape*) resGetData("IMD", GfxFile);
                  if (researchModuleIMDs[0] == NULL)
                  {
                        debug( LOG_ERROR, "Cannot find the PIE for research module %d - %s", module, GfxFile );
                        abort();
                        return;
                  }

                  researchModuleIMDs[1] = researchModuleIMDs[0];
                  researchModuleIMDs[2] = researchModuleIMDs[0];
                  researchModuleIMDs[3] = researchModuleIMDs[0];

                  //store the stat for easy access later on
                  researchModuleStat = i;
            }

            // Setup the PIE's for the power modules.
            if (psStructure->type == REF_POWER_MODULE)
            {
                  length = strlen(GfxFile) - 5;

                  GfxFile[length] = '4';

                  powerModuleIMDs[0] = (iIMDShape*) resGetData("IMD", GfxFile);
                  if (powerModuleIMDs[0] == NULL)
                  {
                        debug( LOG_ERROR, "Cannot find the PIE for power module %d - %s", module, GfxFile );
                        abort();
                        return;
                  }

                  powerModuleIMDs[1] = powerModuleIMDs[0];
                  powerModuleIMDs[2] = powerModuleIMDs[0];
                  powerModuleIMDs[3] = powerModuleIMDs[0];

                  //store the stat for easy access later on
                  powerModuleStat = i;
            }
}

/* load the Structure stats from the Access database */
BOOL loadStructureStats(const char *pStructData, UDWORD bufferSize)
{
      const unsigned int NumStructures = numCR(pStructData, bufferSize);
      UDWORD i, inc, player, numWeaps, weapSlots;
      char                    StructureName[MAX_STR_LENGTH], foundation[MAX_STR_LENGTH],
                                    type[MAX_STR_LENGTH], techLevel[MAX_STR_LENGTH],
                                    strength[MAX_STR_LENGTH];
      char                    GfxFile[MAX_STR_LENGTH], baseIMD[MAX_STR_LENGTH];
      char                    ecmType[MAX_STR_LENGTH], sensorType[MAX_STR_LENGTH];
      STRUCTURE_STATS         *psStructure, *pStartStats;
      ECM_STATS*              pECMType;
      SENSOR_STATS*           pSensorType;
      UDWORD                        module;
      UDWORD                        iID;
      UDWORD              dummyVal;

#if (MAX_PLAYERS != 8)
      char NotUsedString[MAX_STR_LENGTH];
#endif

      //initialise the module IMD structs
      for (module = 0; module < NUM_FACTORY_MODULES; module++)
      {
            factoryModuleIMDs[module][0] = NULL;
            factoryModuleIMDs[module][1] = NULL;
      }
      for (module = 0; module < NUM_RESEARCH_MODULES; module++)
      {
            researchModuleIMDs[module] = NULL;
      }
      for (module = 0; module < NUM_POWER_MODULES; module++)
      {
            powerModuleIMDs[module] = NULL;
      }

      asStructureStats = (STRUCTURE_STATS*)malloc(sizeof(STRUCTURE_STATS)* NumStructures);
      numStructureStats = NumStructures;

      if (asStructureStats == NULL)
      {
            debug( LOG_ERROR, "Structure Stats - Out of memory" );
            abort(); // FIXME exit(EXIT_FAILURE)?
            return false;
      }

      //save the starting address
      pStartStats = asStructureStats;

      //get the start of the structure_stats storage
      psStructure = asStructureStats;

      for (i = 0; i < NumStructures; i++)
      {
            memset(psStructure, 0, sizeof(STRUCTURE_STATS));

            //read the data into the storage - the data is delimeted using comma's
            GfxFile[0] = '\0';
            StructureName[0] = '\0';
            type[0] = '\0';
            strength[0] = '\0';
            foundation[0] = '\0';
            ecmType[0] = '\0';
            sensorType[0] = '\0';
            baseIMD[0] = '\0';

            sscanf(pStructData,"%[^','],%[^','],%[^','],%[^','],%d,%d,%d,%[^','],\
                  %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%[^','],%[^','],%d,%[^','],%[^','],\
                  %d,%d",
                  StructureName, type, techLevel, strength, &psStructure->terrainType,
                  &psStructure->baseWidth, &psStructure->baseBreadth, foundation,
                  &psStructure->buildPoints, &psStructure->height,
                  &psStructure->armourValue, &psStructure->bodyPoints,
                  &psStructure->repairSystem, &psStructure->powerToBuild,
                  &dummyVal, &psStructure->resistance,
                  &dummyVal, &psStructure->sizeModifier,
                  ecmType, sensorType, &weapSlots, GfxFile,
                  baseIMD, &psStructure->numFuncs, &numWeaps);

#if MAX_PLAYERS != 4 && MAX_PLAYERS != 8
# error Invalid number of players
#endif

            //allocate storage for the name
            if (!allocateName(&psStructure->pName, StructureName))
            {
                  return false;
            }

            psStructure->ref = REF_STRUCTURE_START + i;

            //determine the structure type
            psStructure->type = structureType(type);

            //determine the tech level
            if (!setTechLevel((BASE_STATS *)psStructure, techLevel))
            {
                  return false;
            }

            //set the struct strength
            psStructure->strength = getStructStrength(strength);
            if (psStructure->strength == INVALID_STRENGTH)
            {
                  debug(LOG_ERROR, "Unknown structure strength for %s", getStatName(psStructure) );
                  abort();
                  return false;
            }

            //get the ecm stats pointer
            if (!strcmp(ecmType,"0"))
            {
                  psStructure->pECM = NULL;
            }
            else
            {
                  pECMType = asECMStats;
                  if (!getResourceName(ecmType))
                  {
                        return false;
                  }

                  for (inc=0; inc < numECMStats; inc++)
                  {
                        //compare the names
                        if (!strcmp(ecmType, pECMType->pName))
                        {
                              psStructure->pECM = pECMType;
                              break;
                        }
                        pECMType++;
                  }
            }
            //get the sensor stats pointer
            if (!strcmp(sensorType,"0"))
            {
                  psStructure->pSensor = NULL;
            }
            else
            {
                  if (!getResourceName(sensorType))
                  {
                        return false;
                  }
                  pSensorType = asSensorStats;
                  for (inc=0; inc < numSensorStats; inc++)
                  {
                        //compare the names
                        if (!strcmp(sensorType, pSensorType->pName))
                        {
                              psStructure->pSensor = pSensorType;
                              break;
                        }
                        pSensorType++;
                  }
                  //check not allocating a turret sensor if have weapons attached
                  ASSERT( psStructure->pSensor != NULL,
                        "loadStructureStats: should have a sensor attached to %s!", StructureName );
                  if (psStructure->pSensor->location == LOC_TURRET && numWeaps)
                  {
                        debug(LOG_ERROR, "a Turret Sensor and weapon have been assigned to %s", StructureName);
                        abort();
                  }
            }

            //get the IMD for the structure
            psStructure->pIMD = (iIMDShape *) resGetData("IMD", GfxFile);
            if (psStructure->pIMD == NULL)
            {
                  debug( LOG_ERROR, "Cannot find the structure PIE for record %s", getStructName(psStructure) );
                  abort();
                  return false;
            }

            if (strcmp(baseIMD, "0"))
            {
                  psStructure->pBaseIMD = (iIMDShape *) resGetData("IMD", baseIMD);
                  if (psStructure->pIMD == NULL)
                  {
                        debug( LOG_ERROR, "Cannot find the structure base PIE for record %s", getStructName(psStructure) );
                        abort();
                        return false;
                  }
            }
            else
            {
                  psStructure->pBaseIMD = NULL;
            }

            initModulePIEs(GfxFile,i,psStructure);

            //Only having one weapon per structure now...AB 24/01/99
            if (weapSlots > STRUCT_MAXWEAPS || numWeaps > weapSlots)
            {
                  debug( LOG_ERROR, "Allocated more weapons than allowed for Structure" );
                  abort();
                  return false;
            }
            //Watermelon:I need numWeaps to draw multiple weapons
            psStructure->numWeaps = numWeaps;

            //allocate storage for the functions - if any
            psStructure->defaultFunc = -1;
            if (psStructure->numFuncs > 0)
            {
                  psStructure->asFuncList = (FUNCTION **)malloc(psStructure->numFuncs *
                        sizeof(FUNCTION*));
                  if (psStructure->asFuncList == NULL)
                  {
                        debug( LOG_ERROR, "Out of memory assigning structure Functions" );
                        abort();
                        return false;
                  }
            }
            //increment the pointer to the start of the next record
            pStructData = strchr(pStructData,'\n') + 1;
            //increment the list to the start of the next storage block
            psStructure++;
      }

      asStructureStats = pStartStats;

      /* get global dummy stat pointer - GJ */
      for (iID = 0; iID < numStructureStats; iID++)
      {
            if (asStructureStats[iID].type == REF_DEMOLISH)
            {
                  break;
            }
      }
      if (iID > numStructureStats)
      {
            debug(LOG_ERROR, "destroy structure stat not found");
            abort();
      }
      g_psStatDestroyStruct = asStructureStats + iID;

      //allocate the structureLimits structure
      for (player = 0; player < MAX_PLAYERS; player++)
      {
            asStructLimits[player] = malloc(sizeof(STRUCTURE_LIMITS) * numStructureStats);
            if (asStructLimits[player] == NULL)
            {
                  debug( LOG_ERROR, "Unable to allocate structure limits" );
                  abort();
                  return false;
            }
      }
      initStructLimits();

      //initialise the structure upgrade arrays
      memset(asStructureUpgrade, 0, MAX_PLAYERS * sizeof(STRUCTURE_UPGRADE));
      memset(asWallDefenceUpgrade, 0, MAX_PLAYERS * sizeof(WALLDEFENCE_UPGRADE));
      memset(asResearchUpgrade, 0, MAX_PLAYERS * sizeof(RESEARCH_UPGRADE));
      memset(asPowerUpgrade, 0, MAX_PLAYERS * sizeof(POWER_UPGRADE));
      memset(asRepairFacUpgrade, 0, MAX_PLAYERS * sizeof(REPAIR_FACILITY_UPGRADE));
      memset(asProductionUpgrade, 0, MAX_PLAYERS * NUM_FACTORY_TYPES *
            sizeof(PRODUCTION_UPGRADE));
      memset(asReArmUpgrade, 0, MAX_PLAYERS * sizeof(REARM_UPGRADE));

      return true;
}

//initialise the structure limits structure
void initStructLimits(void)
{
      UDWORD                        i, player;

      for (player = 0; player < MAX_PLAYERS; player++)
      {
            STRUCTURE_LIMITS  *psStructLimits = asStructLimits[player];
            STRUCTURE_STATS         *psStat = asStructureStats;

            for (i = 0; i < numStructureStats; i++)
            {
                  psStructLimits[i].limit = LOTS_OF;
                  psStructLimits[i].currentQuantity = 0;
                  psStructLimits[i].globalLimit = LOTS_OF;
                  if (isLasSat(psStat) || psStat->type == REF_SAT_UPLINK)
                  {
                        psStructLimits[i].limit = 1;
                        psStructLimits[i].globalLimit = 1;
                  }
                  psStat++;
            }
      }
}

/* set the current number of structures of each type built */
void setCurrentStructQuantity(BOOL displayError)
{
      UDWORD            player, inc;

      for (player = 0; player < MAX_PLAYERS; player++)
      {
            STRUCTURE_LIMITS  *psStructLimits = asStructLimits[player];
            STRUCTURE         *psCurr;

            //initialise the current quantity for all structures
            for (inc = 0; inc < numStructureStats; inc++)
            {
                  psStructLimits[inc].currentQuantity = 0;
            }

            for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr =
                  psCurr->psNext)
            {
                  inc = psCurr->pStructureType - asStructureStats;
                  psStructLimits[inc].currentQuantity++;
                  if (displayError)
                  {
                        //check quantity never exceeds the limit
                        if (psStructLimits[inc].currentQuantity > psStructLimits[inc].limit)
                        {
                              ASSERT( false, "There appears to be too many %s on this map!",
                                    getStructName(&asStructureStats[inc] ) );
                        }
                  }
            }
      }
}

//Load the weapons assigned to Structure in the Access database
BOOL loadStructureWeapons(const char *pWeaponData, UDWORD bufferSize)
{
      const unsigned int NumToAlloc = numCR(pWeaponData, bufferSize);
      UDWORD                        i, incS, incW;
      char                    StructureName[MAX_STR_LENGTH];//, WeaponName[MAX_STR_LENGTH];
      char                    WeaponName[STRUCT_MAXWEAPS][MAX_STR_LENGTH];
      STRUCTURE_STATS         *pStructure = asStructureStats;
      WEAPON_STATS            *pWeapon = asWeaponStats;
      BOOL                    weaponFound, structureFound;
      UBYTE                   j;

      for (i=0; i < NumToAlloc; i++)
      {
            //read the data into the storage - the data is delimeted using comma's
            StructureName[0] = '\0';
            for (j = 0;j < STRUCT_MAXWEAPS;j++)
            {
                  WeaponName[j][0] = '\0';
            }

            sscanf(pWeaponData, "%[^','],%[^','],%[^','],%[^','],%[^','],%*d", StructureName, WeaponName[0], WeaponName[1], WeaponName[2], WeaponName[3]);

            if (!getResourceName(StructureName))
            {
                  return false;
            }
            if (!getResourceName(WeaponName[0]))
            {
                  return false;
            }
            weaponFound = structureFound = false;
            //loop through each Structure_Stat to compare the name

            for (incS=0; incS < numStructureStats; incS++)
            {
                  if (!(strcmp(StructureName, pStructure[incS].pName)))
                  {
                        //Structure found, so loop through each weapon
                        structureFound = true;
                        for (j = 0;j < pStructure[incS].numWeaps;j++)
                        {
                              for (incW=0; incW < numWeaponStats; incW++)
                              {
                                    if (!(strcmp(WeaponName[j], pWeapon[incW].pName)))
                                    {
                                          weaponFound = true;

                                          //Watermelon:read and store multiple weapon Stats
                                          pStructure[incS].psWeapStat[j] = &pWeapon[incW];
                                          break;
                                    }
                              }
                              //if weapon not found - error
                              if (!weaponFound)
                              {
                                    debug( LOG_ERROR, "Unable to find stats for weapon %s for structure %s", WeaponName[i], StructureName );
                                    abort();
                                    return false;
                              }
                        }
                  }
            }
            //if structure not found - error
            if (!structureFound)
            {
                  debug( LOG_ERROR, "Unable to find stats for structure %s", StructureName );
                  abort();
                  return false;
            }
            //increment the pointer to the start of the next record
            pWeaponData = strchr(pWeaponData,'\n') + 1;
      }
      return true;
}

//Load the programs assigned to Droids in the Access database
BOOL loadStructureFunctions(const char *pFunctionData, UDWORD bufferSize)
{
      const unsigned int NumToAlloc = numCR(pFunctionData, bufferSize);
      UDWORD                        i, incS, incF;
      char                    StructureName[MAX_STR_LENGTH], FunctionName[MAX_STR_LENGTH];
      STRUCTURE_STATS         *pStructure = asStructureStats;
      FUNCTION                *pFunction, **pStartFunctions = asFunctions;
      BOOL                    functionFound, structureFound;



      for (i=0; i < NumToAlloc; i++)
      {
            //read the data into the storage - the data is delimeted using comma's
            StructureName[0] = '\0';
            FunctionName[0] = '\0';
            sscanf(pFunctionData, "%[^','],%[^','],%*d", StructureName, FunctionName);
            functionFound = structureFound = false;

            if (!getResourceName(StructureName))
            {
                  return false;
            }

            //loop through each Structure_Stat to compare the name
            for (incS=0; incS < numStructureStats; incS++)
            {
                  if (!(strcmp(StructureName, pStructure[incS].pName)))
                  {
                        //Structure found, so loop through each Function
                        structureFound = true;
                        pStartFunctions = asFunctions;
                        for (incF=0; incF < numFunctions; incF++)
                        {
                              pFunction = *pStartFunctions;
                              if (!(strcmp(FunctionName, pFunction->pName)))
                              {
                                    //function found alloc this function to the current Structure
                                    functionFound = true;
                                    pStructure[incS].defaultFunc++;
                                    //check not allocating more than allowed
                                    if (pStructure[incS].defaultFunc >
                                                            (SDWORD)pStructure[incS].numFuncs)
                                    {
                                          debug( LOG_ERROR, "Trying to allocate more functions than allowed for Structure" );
                                          abort();
                                          return false;
                                    }
                                    pStructure[incS].asFuncList[pStructure[incS].defaultFunc] =
                                          pFunction;
                                    break;
                              }
                              pStartFunctions++;
                        }
                        //if function not found - error
                        if (!functionFound)
                        {
                              debug( LOG_ERROR, "Unable to find stats for function %s for structure %s", FunctionName, StructureName );
                              abort();
                              return false;
                        }
                  }
            }
            //if structure not found - error
            if (!structureFound)
            {
                  debug( LOG_ERROR, "Unable to find stats for structure %s", StructureName );
                  abort();
                  return false;
            }
            //increment the pointer to the start of the next record
            pFunctionData = strchr(pFunctionData,'\n') + 1;
      }

      /**************************************************************************/
      //Wall Function requires a structure stat so can allocate it now
      pStartFunctions = asFunctions;
      for (incF=0; incF < numFunctions; incF++)
      {
            pFunction = *pStartFunctions;
            if (pFunction->type == WALL_TYPE)
            {
                  //loop through all structures to find the stat
                  pStructure = asStructureStats;
                  ((WALL_FUNCTION *)pFunction)->pCornerStat = NULL;

                  for (i=0; i < numStructureStats; i++)
                  {
                        //compare the names
                        if (!strcmp(((WALL_FUNCTION *)pFunction)->pStructName, pStructure->pName))
                        {
                              ((WALL_FUNCTION *)pFunction)->pCornerStat = pStructure;
                              break;
                        }
                        pStructure++;
                  }
                  //if haven't found the STRUCTURE STAT, then problem
                  if (!((WALL_FUNCTION *)pFunction)->pCornerStat)
                  {
                        debug( LOG_ERROR, "Unknown Corner Wall stat for function %s", pFunction->pName );
                        abort();
                        return false;
                  }
            }
            pStartFunctions++;
      }

      return true;
}

/*Load the Structure Strength Modifiers from the file exported from Access*/
BOOL loadStructureStrengthModifiers(const char *pStrengthModData, UDWORD bufferSize)
{
      const unsigned int NumRecords = numCR(pStrengthModData, bufferSize);
      STRUCT_STRENGTH         strengthInc;
      WEAPON_EFFECT           effectInc;
      UDWORD                        i, j, modifier;
      char                    weaponEffectName[MAX_STR_LENGTH], strengthName[MAX_STR_LENGTH];

      //initialise to 100%
      for (i=0; i < WE_NUMEFFECTS; i++)
      {
            for (j=0; j < NUM_STRUCT_STRENGTH; j++)
            {
                  asStructStrengthModifier[i][j] = 100;
            }
      }

      for (i=0; i < NumRecords; i++)
      {
            //read the data into the storage - the data is delimeted using comma's
            sscanf(pStrengthModData,"%[^','],%[^','],%d",
                  weaponEffectName, strengthName, &modifier);

            //get the weapon effect inc
            effectInc = getWeaponEffect(weaponEffectName);
            if (effectInc == INVALID_WEAPON_EFFECT)
            {
                  debug(LOG_ERROR, "Invalid Weapon Effect - %s", weaponEffectName);
                  abort();
                  return false;
            }
            //get the propulsion inc
            strengthInc = getStructStrength(strengthName);
            if (strengthInc == INVALID_STRENGTH)
            {
                  debug(LOG_ERROR, "Invalid Strength type - %s", strengthName);
                  abort();
                  return false;
            }

            if (modifier > UWORD_MAX)
            {
                  debug(LOG_ERROR, "modifier for effect %s, strength %s is too large", weaponEffectName, strengthName);
                  abort();
                  return false;
            }
            //store in the appropriate index
            asStructStrengthModifier[effectInc][strengthInc] = (UWORD)modifier;

            //increment the pointer to the start of the next record
            pStrengthModData = strchr(pStrengthModData,'\n') + 1;
      }

      return true;
}


BOOL structureStatsShutDown(void)
{
      UDWORD      inc;
      STRUCTURE_STATS*  pStructure = asStructureStats;

      for(inc=0; inc < numStructureStats; inc++, pStructure++)
      {
            if (pStructure->numFuncs > 0)
            {
                  free(pStructure->asFuncList);
                  pStructure->asFuncList = NULL;
            }
      }

      if(numStructureStats) {
            free(asStructureStats);
            asStructureStats = NULL;
      }

      //free up the structLimits structure
      for (inc = 0; inc < MAX_PLAYERS ; inc++)
      {
            if (asStructLimits[inc])
            {
                  free(asStructLimits[inc]);
                  asStructLimits[inc] = NULL;
            }
      }

      return true;
}

// TODO: The abandoned code needs to be factored out, see: saveMissionData
void handleAbandonedStructures()
{
      // FIXME: We should control the calling frequency externally from this
      //        function, rather than controlling the amount of times this
      //        function performs work internally.
      static int lastHandled = 0;
      int player;

      // We only need to run once every two seconds (2000ms)
      if (gameTime - lastHandled < 2000)
      {
            return;
      }

      // Update when we last ran
      lastHandled = gameTime;

      for (player = 0; player < MAX_PLAYERS; ++player)
      {
            STRUCTURE *psCurr, *psNext;

            for (psCurr = apsStructLists[player]; psCurr; psCurr = psNext)
            {
                  // Save the next structure in the list
                  psNext = psCurr->psNext;

                  // We are only interested in structures accruing
                  if (psCurr->status == SS_BEING_BUILT
                   && psCurr->currentPowerAccrued < structPowerToBuild(psCurr))
                  {
                        DROID *psDroid;
                        bool beingBuilt = false;

                        // See is there are any droids building it
                        for (psDroid = apsDroidLists[player];
                             psDroid;
                             psDroid = psDroid->psNext)
                        {
                              // The droid is working on it and therefore not abandoned
                              if ((STRUCTURE *) orderStateObj(psDroid, DORDER_BUILD) == psCurr)
                              {
                                    beingBuilt = true;
                                    break;
                              }
                        }

                        // Being worked on, nothing to see here
                        if (beingBuilt)
                        {
                              continue;
                        }
                        // Abandoned
                        else
                        {
                              int reductionAmount = 8;

                              // Work out how much power to deduct
                              CLIP(reductionAmount, 0, psCurr->currentPowerAccrued);

                              // Do the reduction
                              psCurr->currentPowerAccrued -= reductionAmount;
                              addPower(player, reductionAmount);

                              // Remove the structure if no power is accrued
                              if (!psCurr->currentPowerAccrued)
                              {
                                    removeStruct(psCurr, true);
                              }
                        }
                  }
            }
      }
}

/* Deals damage to a Structure.
 * \param psStructure structure to deal damage to
 * \param damage amount of damage to deal
 * \param weaponClass the class of the weapon that deals the damage
 * \param weaponSubClass the subclass of the weapon that deals the damage
 * \return < 0 when the dealt damage destroys the structure, > 0 when the structure survives
 */
float structureDamage(STRUCTURE *psStructure, UDWORD damage, UDWORD weaponClass,
                       UDWORD weaponSubClass, HIT_SIDE impactSide)
{
      float       relativeDamage;

      CHECK_STRUCTURE(psStructure);

      debug(LOG_ATTACK, "structure %d, body %d, armour %d, damage: %d",
            psStructure->id, psStructure->body, psStructure->armour[impactSide][weaponClass], damage);

      relativeDamage = objDamage((BASE_OBJECT *)psStructure, damage, structureBody(psStructure), weaponClass, weaponSubClass, impactSide);

      // If the shell did sufficient damage to destroy the structure
      if (relativeDamage < 0.0f)
      {
            debug(LOG_ATTACK, "DESTROYED");
            destroyStruct(psStructure);
      }
      else
      {
            // Survived
            CHECK_STRUCTURE(psStructure);
      }
      
      return relativeDamage;
}

float getStructureDamage(const STRUCTURE* psStructure)
{
      float health;

      health = (float)psStructure->body / (float)structureBody(psStructure);
      CLIP(health, 0., 1.f);

      return 1. - health;
}

/* Set the type of droid for a factory to build */
BOOL structSetManufacture(STRUCTURE *psStruct, DROID_TEMPLATE *psTempl, UBYTE quantity)
{
      FACTORY           *psFact;

      CHECK_STRUCTURE(psStruct);

      ASSERT( psStruct != NULL && psStruct->type == OBJ_STRUCTURE &&
                  (psStruct->pStructureType->type == REF_FACTORY ||
                  psStruct->pStructureType->type == REF_CYBORG_FACTORY ||
                  psStruct->pStructureType->type == REF_VTOL_FACTORY),
            "structSetManufacture: invalid Factory pointer" );
      /* psTempl might be NULL if the build is being cancelled in the middle */

      //assign it to the Factory
      psFact = &psStruct->pFunctionality->factory;
      psFact->psSubject = (BASE_STATS *)psTempl;

      //set up the start time and build time
      if (psTempl != NULL)
      {
            //only use this for non selectedPlayer
            if (psStruct->player != selectedPlayer)
            {
                  //set quantity to produce
                  psFact->quantity = quantity;
            }

            psFact->timeStarted = ACTION_START_TIME;//gameTime;
            psFact->powerAccrued = 0;
            psFact->timeStartHold = 0;

            psFact->timeToBuild = psTempl->buildPoints / psFact->productionOutput;
            //check for zero build time - usually caused by 'silly' data!
            if (psFact->timeToBuild == 0)
            {
                  //set to 1/1000th sec - ie very fast!
                  psFact->timeToBuild = 1;
            }
      }
      return true;
}


/*****************************************************************************/
/*
* All this wall type code is horrible, but I really can't think of a better way to do it.
*        John.
*/

// look at where other walls are to decide what type of wall to build
static SDWORD structWallScan(BOOL aWallPresent[5][5], SDWORD x,SDWORD y)
{
      SDWORD cx,cy, numWalls;

      numWalls = 0;
      for(cx = x-1; cx <= x+1; cx += 1)
      {
            for(cy = y-1; cy <= y+1; cy += 1)
            {
                  // do not look at walls diagonally from this wall
                  if (((cx == x && cy != y) ||
                        (cx != x && cy == y))
                        && aWallPresent[cx][cy])
                  {
                        numWalls += 1;
                  }
            }
      }

      // Now decide what type of wall is needed
      if (numWalls == 1)
      {
            if (aWallPresent[x-1][y] || aWallPresent[x+1][y])
            {
                  // there is a wall horizontally adjacent
                  return WALL_HORIZ;
            }
            else
            {
                  return WALL_VERT;
            }
      }
      else if (numWalls == 2)
      {
            if (aWallPresent[x-1][y] && aWallPresent[x+1][y])
            {
                  // there is a wall horizontally adjacent
                  return WALL_HORIZ;
            }
            else if (aWallPresent[x][y-1] && aWallPresent[x][y+1])
            {
                  // there is a wall vertically adjacent
                  return WALL_VERT;
            }
            else
            {
                  return WALL_CORNER;
            }
      }
      else if (numWalls > 2)
      {
            // definately need a corner wall
            return WALL_CORNER;
      }

      return WALL_VERT;
}

// Choose a type of wall for a location - and update any neighbouring walls
static SDWORD structChooseWallType(UDWORD player, UDWORD mapX, UDWORD mapY)
{
      BOOL        aWallPresent[5][5];
      SDWORD            xdiff,ydiff, x,y;
      STRUCTURE   *psStruct;
      STRUCTURE   *apsStructs[5][5];
      SDWORD            nayborType, scanType;
      STRUCTURE_STATS   *psStats;
      UDWORD            sx,sy;
      UDWORD            oldBuildPoints;

      // scan around the location looking for walls
      memset(aWallPresent, 0, sizeof(aWallPresent));
      for(psStruct=apsStructLists[player]; psStruct; psStruct=psStruct->psNext)
      {
            xdiff = (SDWORD)mapX - map_coord((SDWORD)psStruct->pos.x);
            ydiff = (SDWORD)mapY - map_coord((SDWORD)psStruct->pos.y);
            if (xdiff >= -2 && xdiff <= 2 &&
                  ydiff >= -2 && ydiff <= 2 &&
                  (psStruct->pStructureType->type == REF_WALL ||
                  psStruct->pStructureType->type == REF_WALLCORNER ||
                  psStruct->pStructureType->type == REF_DEFENSE))
            {
                  aWallPresent[xdiff+2][ydiff+2] = true;
                  apsStructs[xdiff+2][ydiff+2] = psStruct;
            }
      }
      // add in the wall about to be built
      aWallPresent[2][2] = true;

      // now make sure that all the walls around this one are OK
      for(x=1; x<=3; x+=1)
      {
            for(y=1; y<=3; y+=1)
            {
                  // do not look at walls diagonally from this wall
                  if (((x == 2 && y != 2) ||
                        (x != 2 && y == 2)) &&
                        aWallPresent[x][y])
                  {
                        // figure out what type the wall currently is
                        psStruct = apsStructs[x][y];
                        if (psStruct->pStructureType->type == REF_WALL)
                        {
                              if ( (int)psStruct->direction == 90 )
                              {
                                    nayborType = WALL_VERT;
                              }
                              else
                              {
                                    nayborType = WALL_HORIZ;
                              }
                        }
                        else
                        {
                              // do not need to adjust anything apart from walls
                              continue;
                        }

                        // see what type the wall should be
                        scanType = structWallScan(aWallPresent, x,y);

                        if (nayborType != scanType)
                        {
                              // Got to change the wall
                              if (scanType == WALL_CORNER)
                              {
                                    // change to a corner
                                    if (psStruct->pStructureType->asFuncList[0]->type == WALL_TYPE)
                                    {
                                          /* Still being built - so save and load build points */
                                          if(psStruct->status == SS_BEING_BUILT)
                                          {
                                                oldBuildPoints = psStruct->currentBuildPts;
                                                psStats = ((WALL_FUNCTION *)psStruct
                                                                        ->pStructureType->asFuncList[0])
                                                                                    ->pCornerStat;
                                                sx = psStruct->pos.x; sy = psStruct->pos.y;
                                                removeStruct(psStruct, true);
                                                powerCalc(false);
                                                psStruct = buildStructure(psStats, sx,sy, player, true);
                                                powerCalc(true);
                                                if(psStruct !=NULL)
                                                {
                                                      psStruct->status = SS_BEING_BUILT;
                                                      psStruct->currentBuildPts = (SWORD)oldBuildPoints;
                                                }
                                          }
                                          else
                                          {
                                                psStats = ((WALL_FUNCTION *)psStruct
                                                                        ->pStructureType->asFuncList[0])
                                                                                    ->pCornerStat;
                                                sx = psStruct->pos.x; sy = psStruct->pos.y;
                                                removeStruct(psStruct, true);
                                                powerCalc(false);
                                                psStruct = buildStructure(psStats, sx,sy, player, true);
                                                powerCalc(true);
                                                if(psStruct !=NULL)
                                                {
                                                      psStruct->status = SS_BUILT;
                                                      buildingComplete(psStruct);
                                                }
                                          }
                                    }
                              }
                              else if (scanType == WALL_HORIZ)
                              {
                                    // change to a horizontal wall
                                    psStruct->direction = 0;
                              }
                              else
                              {
                                    // change to a vertical wall
                                    psStruct->direction = 90;
                              }
                        }
                  }
            }
      }

      // finally return the type for this wall
      return structWallScan(aWallPresent, 2,2);
}


static void buildFlatten(STRUCTURE_STATS *pStructureType, UDWORD x, UDWORD y, UDWORD h)
{
      UBYTE                   width;
      UBYTE                   breadth;

      for (breadth=0; breadth <= (UBYTE)(pStructureType->baseBreadth/* + 1*/); breadth++)
      {
            for (width=0; width <= (UBYTE)(pStructureType->baseWidth /*+ 1*/); width++)
            {
                  if ( (pStructureType->type != REF_WALL) &&
                         (pStructureType->type != REF_WALLCORNER) )
                  {
                        setTileHeight(x + width, y + breadth, h);//-1
                        // We need to raise features on raised tiles to the new height
                        if(TileHasFeature(mapTile(x+width,y+breadth)))
                        {
                              getTileFeature(x+width, y+breadth)->pos.z = (UWORD)h;
                        }
                  }
            }
      }
}

void alignStructure(STRUCTURE *psBuilding)
{
      int width, breadth;
      int x = psBuilding->pos.x;
      int y = psBuilding->pos.y;
      int mapX = map_coord(x) - (psBuilding->pStructureType->baseWidth / 2);
      int mapY = map_coord(y) - (psBuilding->pStructureType->baseBreadth / 2);

      /* DEFENSIVE structures are pulled to the terrain */
      if (psBuilding->pStructureType->type != REF_DEFENSE)
      {
            int mapH = buildFoundation(psBuilding->pStructureType, x, y);

            buildFlatten(psBuilding->pStructureType, mapX, mapY, mapH);
            psBuilding->pos.z = mapH;
      }
      else
      {
            psBuilding->pos.z = TILE_MIN_HEIGHT;

            /* Set it at the higher coord */
            for (width = 0; width < psBuilding->pStructureType->baseWidth; width++)
            {
                  for (breadth = 0; breadth < psBuilding->pStructureType->baseBreadth; breadth++)
                  {
                        UDWORD tmpMax, tmpMin;

                        getTileMaxMin(map_coord(x) + width, map_coord(y) + breadth, &tmpMax, &tmpMin);
                        psBuilding->pos.z = MAX(tmpMax, psBuilding->pos.z);
                  }
            }
      }
}

/*Builds an instance of a Structure - the x/y passed in are in world coords. */
STRUCTURE* buildStructure(STRUCTURE_STATS* pStructureType, UDWORD x, UDWORD y, UDWORD player, BOOL FromSave)
{
      UDWORD            mapX, mapY;
      UDWORD            width, breadth, weapon, capacity;
      float bodyDiff = 0.f;
      SDWORD            wallType = 0, preScrollMinX = 0, preScrollMinY = 0, preScrollMaxX = 0, preScrollMaxY = 0;
      int               i;
      STRUCTURE   *psBuilding = NULL;

      assert(pStructureType);
      ASSERT(pStructureType->type != REF_DEMOLISH, "You cannot build demolition!");

      if (IsStatExpansionModule(pStructureType)==false)
      {
            //some prelim tests...
            UDWORD      max = pStructureType - asStructureStats;

            if (max > numStructureStats)
            {
                  ASSERT(!"invalid structure type", "buildStructure: Invalid structure type");
                  return NULL;
            }
            // Don't allow more than interface limits
            if (asStructLimits[player][max].currentQuantity + 1 > asStructLimits[player][max].limit)
            {
                  debug(LOG_ERROR, "Player %u: Building %s could not be built due to building limits (has %d, max %d)!", 
                        player, pStructureType->pName, asStructLimits[player][max].currentQuantity, 
                        asStructLimits[player][max].limit);
                  return NULL;
            }

            // snap the coords to a tile
            x = ((pStructureType->baseWidth % 2) == 0) ? (x & ~TILE_MASK) : (x & ~TILE_MASK) + TILE_UNITS/2;
            y = ((pStructureType->baseBreadth % 2) == 0) ? (y & ~TILE_MASK) : (y & ~TILE_MASK) + TILE_UNITS/2;

            //check not trying to build too near the edge
            if (map_coord(x) < TOO_NEAR_EDGE
             || map_coord(x) > (mapWidth - TOO_NEAR_EDGE))
            {
                  debug(LOG_ERROR, "attempting to build too closely to map-edge, "
                        "x coord (%u) too near edge (req. distance is %u)", x, TOO_NEAR_EDGE);
                  return NULL;
            }
            if (map_coord(y) < TOO_NEAR_EDGE
             || map_coord(y) > (mapHeight - TOO_NEAR_EDGE))
            {
                  debug(LOG_ERROR, "attempting to build too closely to map-edge, "
                        "y coord (%u) too near edge (req. distance is %u)", y, TOO_NEAR_EDGE);
                  return NULL;
            }

            if (!FromSave && pStructureType->type == REF_WALL)
            {
                  wallType = structChooseWallType(player, map_coord(x), map_coord(y));
                  if (wallType == WALL_CORNER)
                  {
                        if (pStructureType->asFuncList[0]->type == WALL_TYPE)
                        {
                              pStructureType = ((WALL_FUNCTION *)pStructureType->asFuncList[0])->pCornerStat;
                        }
                  }
            }

            // allocate memory for and initialize a structure object
            psBuilding = createStruct(player);
            if (psBuilding == NULL)
            {
                  return NULL;
            }

            psBuilding->psCurAnim = NULL;

            //fill in other details
            psBuilding->pStructureType = pStructureType;

            psBuilding->pos.x = (UWORD)x;
            psBuilding->pos.y = (UWORD)y;

            //This needs to be done before the functionality bit...
            //load into the map data and structure list if not an upgrade
            mapX = map_coord(x) - (pStructureType->baseWidth/2);
            mapY = map_coord(y) - (pStructureType->baseBreadth/2);

            //set up the imd to use for the display
            psBuilding->sDisplay.imd = pStructureType->pIMD;

            /* if resource extractor - need to remove oil feature first, but do not do any
             * consistency checking here - save games do not have any feature to remove
             * to remove when placing oil derricks! */
            if (pStructureType->type == REF_RESOURCE_EXTRACTOR)
            {
                  FEATURE *psFeature = getTileFeature(map_coord(x), map_coord(y));

                  if (psFeature && psFeature->psStats->subType == FEAT_OIL_RESOURCE)
                  {
                        // remove it from the map
                        turnOffMultiMsg(true); // dont send this one!
                        removeFeature(psFeature);
                        turnOffMultiMsg(false);
                  }
            }

            for (width = 0; width < pStructureType->baseWidth; width++)
            {
                  for (breadth = 0; breadth < pStructureType->baseBreadth; breadth++)
                  {
                        MAPTILE *psTile = mapTile(mapX+width,mapY+breadth);

                        /* Remove any walls underneath the building. You can build defense buildings on top
                         * of walls, you see. This is not the place to test whether we own it! */
                        if (pStructureType->type == REF_DEFENSE && TileHasWall(psTile))
                        {
                              removeStruct((STRUCTURE *)psTile->psObject, true);
                        }

                        // don't really think this should be done here, but dont know otherwise.alexl
                        if(pStructureType->type == REF_WALLCORNER || pStructureType->type == REF_WALL)
                        {
                              if(TileHasStructure(mapTile(mapX+width,mapY+breadth)))
                              {
                                    if(getTileStructure(mapX+width,mapY+breadth)->pStructureType->type == REF_WALLCORNER)
                                    {
                                          return NULL; // dont build.
                                    }
                              }
                        }
                        // end of dodgy stuff
                        else if (TileHasStructure(psTile))
                        {
                              debug(LOG_ERROR,
                                     "building %s at (%d, %d) but found %s already at (%d, %d)",
                                     pStructureType->pName, mapX, mapY,
                                     getTileStructure(mapX + width, mapY + breadth)->pStructureType->pName,
                                     mapX + width, mapY + breadth);
                              free(psBuilding);
                              return NULL;
                        }

                        psTile->psObject = (BASE_OBJECT*)psBuilding;
                  }
            }

            alignStructure(psBuilding);

            //set up the rest of the data
            for (i = 0;i < STRUCT_MAXWEAPS;i++)
            {
                  psBuilding->turretRotation[i] = 0;
                  psBuilding->turretPitch[i] = 0;
                  psBuilding->psTarget[i] = NULL;
            }
            psBuilding->targetted = 0;

            psBuilding->lastEmission = 0;
            psBuilding->timeLastHit = 0;
            psBuilding->lastHitWeapon = UDWORD_MAX;   // no such weapon

            psBuilding->inFire = false;
            psBuilding->burnStart = 0;
            psBuilding->burnDamage = 0;

            psBuilding->direction = 0;
            psBuilding->pitch = 0;
            psBuilding->roll = 0;
            psBuilding->selected = false;
            psBuilding->status = SS_BEING_BUILT;
            psBuilding->currentBuildPts = 0;
            psBuilding->currentPowerAccrued = 0;
            psBuilding->cluster = 0;

            // rotate a wall if necessary
            if (!FromSave && pStructureType->type == REF_WALL && wallType == WALL_VERT)
            {
                  psBuilding->direction = 90;
            }

            //set up the sensor stats
            if (psBuilding->pStructureType->pSensor)
            {
                  psBuilding->sensorRange = (UWORD)sensorRange(psBuilding->pStructureType->
                        pSensor, (UBYTE)player);
                  psBuilding->sensorPower = (UWORD)sensorPower(psBuilding->pStructureType->
                        pSensor, (UBYTE)player);
            }
            else
            {
                  //give them the default sensor for droids if not
                  psBuilding->sensorRange = (UWORD)sensorRange(asSensorStats +
                        aDefaultSensor[player], (UBYTE)player);
                  psBuilding->sensorPower = (UWORD)sensorPower(asSensorStats +
                        aDefaultSensor[player], (UBYTE)player);
            }
            //set up the ecm stat
            if (psBuilding->pStructureType->pECM)
            {
                  psBuilding->ECMMod = (UWORD)ecmPower(psBuilding->pStructureType->pECM,
                        psBuilding->player);
            }
            else
            {
                  psBuilding->ECMMod = 0;
            }

            /* Store the weapons */
            memset(psBuilding->asWeaps, 0, sizeof(WEAPON));
            //Watermelon:can only have the STRUCT_MAXWEAPS weapon now
            psBuilding->numWeaps = 0;
            if (pStructureType->numWeaps > 0)
            {
                  for(weapon=0; weapon < pStructureType->numWeaps; weapon++)
                  {
                        //can only have the one weapon now - AB 24/01/99
                        if (pStructureType->psWeapStat[weapon])
                        {
                              psBuilding->asWeaps[weapon].lastFired = 0;
                              //in multiPlayer make the Las-Sats require re-loading from the start
                              if (bMultiPlayer && pStructureType->psWeapStat[0]->
                                    weaponSubClass == WSC_LAS_SAT)
                              {
                                    psBuilding->asWeaps[0].lastFired = gameTime;
                              }
                              psBuilding->asWeaps[weapon].nStat = pStructureType->psWeapStat[weapon] - asWeaponStats;
                              psBuilding->asWeaps[weapon].hitPoints = (asWeaponStats + psBuilding->
                                    asWeaps[weapon].nStat)->hitPoints;
                              psBuilding->asWeaps[weapon].ammo = (asWeaponStats + psBuilding->
                                    asWeaps[weapon].nStat)->numRounds;
                              psBuilding->asWeaps[weapon].recoilValue = 0;
                              psBuilding->numWeaps++;
                        }
                  }
            }
            else
            {
                  if (pStructureType->psWeapStat[0])
                  {
                        psBuilding->asWeaps[0].lastFired = 0;
                        //in multiPlayer make the Las-Sats require re-loading from the start
                        if (bMultiPlayer && pStructureType->psWeapStat[0]->
                              weaponSubClass == WSC_LAS_SAT)
                        {
                              psBuilding->asWeaps[0].lastFired = gameTime;
                        }
                        psBuilding->asWeaps[0].nStat =      pStructureType->psWeapStat[0] - asWeaponStats;
                        psBuilding->asWeaps[0].hitPoints = (asWeaponStats + psBuilding->
                              asWeaps[0].nStat)->hitPoints;
                        psBuilding->asWeaps[0].ammo = (asWeaponStats + psBuilding->
                              asWeaps[0].nStat)->numRounds;
                        psBuilding->asWeaps[0].recoilValue = 0;
                  }
            }

            // Structures currently do not have varied armour
            for (i = 0; i < NUM_HIT_SIDES; i++)
            {
                  int j;

                  for (j = 0; j < NUM_WEAPON_CLASS; j++)
                  {
                        psBuilding->armour[i][j] = (UWORD)structureArmour(pStructureType, (UBYTE)player);
                  }
            }
            psBuilding->resistance = (UWORD)structureResistance(pStructureType, (UBYTE)player);
            psBuilding->lastResistance = ACTION_START_TIME;

            //do the visiblilty stiff before setFunctionality - so placement of DP's can work
            memset(psBuilding->visible, 0, (sizeof(UBYTE) * MAX_PLAYERS) );

            /* Structure is trivially visible to the builder (owner) */
            psBuilding->visible[player] = UBYTE_MAX;

            // Reveal any tiles that can be seen by the structure
            visTilesUpdate((BASE_OBJECT *)psBuilding);

            /*if we're coming from a SAVEGAME and we're on an Expand_Limbo mission,
            any factories that were built previously for the selectedPlayer will
            have DP's in an invalid location - the scroll limits will have been
            changed to not include them. This is the only HACK I can think of to
            enable them to be loaded up. So here goes...*/
            if (FromSave && player == selectedPlayer && missionLimboExpand())
            {
                  //save the current values
                  preScrollMinX = scrollMinX;
                  preScrollMinY = scrollMinY;
                  preScrollMaxX = scrollMaxX;
                  preScrollMaxY = scrollMaxY;
                  //set the current values to mapWidth/mapHeight
                  scrollMinX = 0;
                  scrollMinY = 0;
                  scrollMaxX = mapWidth;
                  scrollMaxY = mapHeight;
            }
            //set the functionality dependant on the type of structure
            if(!setFunctionality(psBuilding, pStructureType->type))
            {
                  removeStructFromMap(psBuilding);
                  free(psBuilding);
                  //better reset these if you couldn't build the structure!
                  if (FromSave && player == selectedPlayer && missionLimboExpand())
                  {
                        //reset the current values
                        scrollMinX = preScrollMinX;
                        scrollMinY = preScrollMinY;
                        scrollMaxX = preScrollMaxX;
                        scrollMaxY = preScrollMaxY;
                  }
                  return NULL;
            }

            //reset the scroll values if adjusted
            if (FromSave && player == selectedPlayer && missionLimboExpand())
            {
                  //reset the current values
                  scrollMinX = preScrollMinX;
                  scrollMinY = preScrollMinY;
                  scrollMaxX = preScrollMaxX;
                  scrollMaxY = preScrollMaxY;
            }

            psBuilding->body = (UWORD)structureBody(psBuilding);

            //add the structure to the list - this enables it to be drawn whilst being built
            addStructure(psBuilding);

            gridAddObject((BASE_OBJECT *)psBuilding);

            clustNewStruct(psBuilding);
            asStructLimits[player][max].currentQuantity++;
      }
      else //its an upgrade
      {
            BOOL bUpgraded = false;

            psBuilding = getTileStructure(map_coord(x), map_coord(y));

            //don't create the Structure use existing one

            if (psBuilding == NULL)
            {
                  ASSERT(!"module has no owning structure", "No owning structure for this module - %s", getStructName(pStructureType));
                  return false;
            }
            if (pStructureType->type == REF_FACTORY_MODULE)
            {
                  if (psBuilding->pStructureType->type != REF_FACTORY &&
                        psBuilding->pStructureType->type != REF_VTOL_FACTORY)
                  {
                        return NULL;
                  }
                  //increment the capacity and output for the owning structure
                  if (psBuilding->pFunctionality->factory.capacity < SIZE_SUPER_HEAVY)
                  {
                        //store the % difference in body points before upgrading
                        bodyDiff = 1. - getStructureDamage(psBuilding);

                        ++psBuilding->pFunctionality->factory.capacity;
                        bUpgraded = true;
                        //put any production on hold
                        holdProduction(psBuilding);

                        //quick check not trying to add too much
                        ASSERT(psBuilding->pFunctionality->factory.productionOutput +
                              ((PRODUCTION_FUNCTION*)pStructureType->asFuncList[0])->productionOutput < UBYTE_MAX,
                              "building factory module - productionOutput too big");

                        psBuilding->pFunctionality->factory.productionOutput += ((
                              PRODUCTION_FUNCTION*)pStructureType->asFuncList[0])->productionOutput;
                        //need to change which IMD is used for player 0
                        //Need to do a check its not Barbarian really!

                        if (   (bMultiPlayer && isHumanPlayer(psBuilding->player))
                              || (bMultiPlayer && (game.type == SKIRMISH) && (psBuilding->player < game.maxPlayers))
                              || !bMultiPlayer)
                        {
                              capacity = psBuilding->pFunctionality->factory.capacity;
                              if (capacity < NUM_FACTORY_MODULES)
                              {
                                    if (psBuilding->pStructureType->type == REF_FACTORY)
                                    {
                                          psBuilding->sDisplay.imd = factoryModuleIMDs[
                                                capacity-1][0];
                                    }
                                    else
                                    {
                                          psBuilding->sDisplay.imd = factoryModuleIMDs[
                                                capacity-1][1];
                                    }
                              }
                              else
                              {
                                    if (psBuilding->pStructureType->type == REF_FACTORY)
                                    {
                                          psBuilding->sDisplay.imd = factoryModuleIMDs[
                                                NUM_FACTORY_MODULES-1][0];
                                    }
                                    else
                                    {
                                          psBuilding->sDisplay.imd = factoryModuleIMDs[
                                                NUM_FACTORY_MODULES-1][1];
                                    }
                              }
                        }
                  }
            }

            if (pStructureType->type == REF_RESEARCH_MODULE)
            {
                  if (psBuilding->pStructureType->type != REF_RESEARCH)
                  {
                        return NULL;
                  }
                  //increment the capacity and research points for the owning structure
                  if (psBuilding->pFunctionality->researchFacility.capacity < NUM_RESEARCH_MODULES)
                  {
                        //store the % difference in body points before upgrading
                        bodyDiff = 1. - getStructureDamage(psBuilding);

                        //add all the research modules in one go AB 24/06/98
                        //((RESEARCH_FACILITY*)psBuilding->pFunctionality)->capacity++;
                        psBuilding->pFunctionality->researchFacility.capacity = NUM_RESEARCH_MODULES;
                        psBuilding->pFunctionality->researchFacility.researchPoints += ((
                              RESEARCH_FUNCTION*)pStructureType->asFuncList[0])->
                              researchPoints;
                        bUpgraded = true;
                        //cancel any research - put on hold now
                        if (psBuilding->pFunctionality->researchFacility.psSubject)
                        {
                              //cancel the topic
                              holdResearch(psBuilding);
                        }

                        //need to change which IMD is used for player 0
                        //Need to do a check its not Barbarian really!
                        if (   (bMultiPlayer && isHumanPlayer(psBuilding->player))
                              || (bMultiPlayer && (game.type == SKIRMISH) && (psBuilding->player < game.maxPlayers))
                              || !bMultiPlayer)
                        {
                              capacity = psBuilding->pFunctionality->researchFacility.capacity;
                              if (capacity < NUM_RESEARCH_MODULES)
                              {
                                    psBuilding->sDisplay.imd = researchModuleIMDs[capacity-1];
                              }
                              else
                              {
                                    psBuilding->sDisplay.imd = researchModuleIMDs[NUM_RESEARCH_MODULES-1];
                              }
                        }
                  }
            }

            if (pStructureType->type == REF_POWER_MODULE)
            {
                  if (psBuilding->pStructureType->type != REF_POWER_GEN)
                  {
                        return NULL;
                  }
                  //increment the capacity and research points for the owning structure
                  if (psBuilding->pFunctionality->powerGenerator.capacity < NUM_POWER_MODULES)
                  {
                        //store the % difference in body points before upgrading
                        bodyDiff = 1. - getStructureDamage(psBuilding);

                        //increment the power output, multiplier and capacity
                        //add all the research modules in one go AB 24/06/98
                        psBuilding->pFunctionality->powerGenerator.capacity = NUM_POWER_MODULES;
                        psBuilding->pFunctionality->powerGenerator.power += ((
                              POWER_GEN_FUNCTION*)pStructureType->asFuncList[0])->
                              powerOutput;
                        psBuilding->pFunctionality->powerGenerator.multiplier += ((
                              POWER_GEN_FUNCTION*)pStructureType->asFuncList[0])->
                              powerMultiplier;
                        bUpgraded = true;

                        //need to change which IMD is used for player 0
                        //Need to do a check its not Barbarian really!

                        if (   (bMultiPlayer && isHumanPlayer(psBuilding->player))
                              || (bMultiPlayer && (game.type == SKIRMISH) && (psBuilding->player < game.maxPlayers))
                              || !bMultiPlayer)
                        {
                              capacity = psBuilding->pFunctionality->powerGenerator.capacity;
                              if (capacity < NUM_POWER_MODULES)
                              {
                                    psBuilding->sDisplay.imd = powerModuleIMDs[capacity-1];
                              }
                              else
                              {
                                    psBuilding->sDisplay.imd = powerModuleIMDs[NUM_POWER_MODULES-1];
                              }
                        }
                        //need to inform any res Extr associated that not digging until complete
                        releasePowerGen(psBuilding);
                  }
            }
            if (bUpgraded)
            {
                  //calculate the new body points of the owning structure
                  psBuilding->body = (UWORD)(structureBody(psBuilding) * bodyDiff);

                  //initialise the build points
                  psBuilding->currentBuildPts = 0;
                  psBuilding->currentPowerAccrued = 0;
                  //start building again
                  psBuilding->status = SS_BEING_BUILT;
                  if (psBuilding->player == selectedPlayer && !FromSave)
                  {
                        intRefreshScreen();
                  }
                  //inform power system that won't be needing power until built
                  powerDestroyObject((BASE_OBJECT *)psBuilding);
            }
      }
      if(pStructureType->type!=REF_WALL && pStructureType->type!=REF_WALLCORNER)
      {
            if(player == selectedPlayer)
            {
                  scoreUpdateVar(WD_STR_BUILT);
            }
      }

      /* why is this necessary - it makes tiles under the structure visible */
      setUnderTilesVis((BASE_OBJECT*)psBuilding,player);

      return psBuilding;
}


BOOL setFunctionality(STRUCTURE     *psBuilding, STRUCTURE_TYPE functionType)
{
      CHECK_STRUCTURE(psBuilding);

      switch (functionType)
      {
            case REF_FACTORY:
            case REF_CYBORG_FACTORY:
            case REF_VTOL_FACTORY:
            case REF_RESEARCH:
            case REF_POWER_GEN:
            case REF_RESOURCE_EXTRACTOR:
            case REF_REPAIR_FACILITY:
            case REF_REARM_PAD:
                  // Allocate space for the buildings functionality
                  psBuilding->pFunctionality = calloc(1, sizeof(*psBuilding->pFunctionality));

                  if (psBuilding->pFunctionality == NULL)
                  {
                        debug(LOG_ERROR, "Out of memory");
                        abort();
                        return false;
                  }
                  break;

            default:
                  psBuilding->pFunctionality = NULL;
                  break;
      }

      switch (functionType)
      {
            case REF_FACTORY:
            case REF_CYBORG_FACTORY:
            case REF_VTOL_FACTORY:
            {
                  FACTORY* psFactory = &psBuilding->pFunctionality->factory;
                  unsigned int x, y;

                  psFactory->capacity = (UBYTE) ((PRODUCTION_FUNCTION*)psBuilding->pStructureType->asFuncList[0])->capacity;
                  psFactory->productionOutput = (UBYTE) ((PRODUCTION_FUNCTION*)psBuilding->pStructureType->asFuncList[0])->productionOutput;
                  psFactory->psSubject = NULL;

                  // Default the secondary order - AB 22/04/99
                  psFactory->secondaryOrder = DSS_ARANGE_DEFAULT | DSS_REPLEV_NEVER
                                      | DSS_ALEV_ALWAYS | DSS_HALT_GUARD;

                  // Create the assembly point for the factory
                  if (!createFlagPosition(&psFactory->psAssemblyPoint, psBuilding->player))
                  {
                        return false;
                  }

                  // initialise the assembly point position
                  x = map_coord(psBuilding->pos.x + 256);
                  y = map_coord(psBuilding->pos.y + 256);

                  // Set the assembly point
                  setAssemblyPoint(psFactory->psAssemblyPoint, world_coord(x), world_coord(y), psBuilding->player, true);

                  // Add the flag to the list
                  addFlagPosition(psFactory->psAssemblyPoint);
                  switch (functionType)
                  {
                        case REF_FACTORY:
                              setFlagPositionInc(psBuilding->pFunctionality, psBuilding->player, FACTORY_FLAG);
                              break;
                        case REF_CYBORG_FACTORY:
                              setFlagPositionInc(psBuilding->pFunctionality, psBuilding->player, CYBORG_FLAG);
                              break;
                        case REF_VTOL_FACTORY:
                              setFlagPositionInc(psBuilding->pFunctionality, psBuilding->player, VTOL_FLAG);
                              break;
                        default:
                              ASSERT(!"invalid factory type", "Invalid factory type");
                  }

                  // Take advantage of upgrades
                  structureProductionUpgrade(psBuilding);
                  break;
            }
            case REF_RESEARCH:
            {
                  RESEARCH_FACILITY* psResFac = &psBuilding->pFunctionality->researchFacility;

                  psResFac->researchPoints = ((RESEARCH_FUNCTION *) psBuilding->pStructureType->asFuncList[0])->researchPoints;

                  // Take advantage of upgrades
                  structureResearchUpgrade(psBuilding);
                  break;
            }
            case REF_POWER_GEN:
            {
                  POWER_GEN* psPowerGen = &psBuilding->pFunctionality->powerGenerator;

                  psPowerGen->power = ((POWER_GEN_FUNCTION *) psBuilding->pStructureType->asFuncList[0])->powerOutput;
                  psPowerGen->multiplier = ((POWER_GEN_FUNCTION *) psBuilding->pStructureType->asFuncList[0])->powerMultiplier;
                  psPowerGen->capacity = 0;

                  // Take advantage of upgrades
                  structurePowerUpgrade(psBuilding);
                  break;
            }
            case REF_RESOURCE_EXTRACTOR:
            {
                  RES_EXTRACTOR* psResExtracter = &psBuilding->pFunctionality->resourceExtractor;

                  psResExtracter->power = ((RESOURCE_FUNCTION*)psBuilding->pStructureType->asFuncList[0])->maxPower;

                  // Make the structure inactive
                  psResExtracter->active = false;
                  psResExtracter->psPowerGen = NULL;
                  break;
            }
            case REF_HQ:
            {
                  // If an HQ has just been built make sure the radar is displayed!
                  radarOnScreen = true;
                  break;
            }
            case REF_REPAIR_FACILITY:
            {
                  REPAIR_FACILITY* psRepairFac = &psBuilding->pFunctionality->repairFacility;
                  REPAIR_DROID_FUNCTION* pFuncRepair = (REPAIR_DROID_FUNCTION*)psBuilding->pStructureType->asFuncList[0];
                  unsigned int x, y;

                  psRepairFac->power = pFuncRepair->repairPoints;
                  psRepairFac->psObj = NULL;

                  if (!grpCreate(&((REPAIR_FACILITY*)psBuilding->pFunctionality)->psGroup))
                  {
                        debug(LOG_NEVER, "couldn't create repair facility group");
                  }
                  else
                  {
                        // Add NULL droid to the group
                        grpJoin(psRepairFac->psGroup, NULL);
                  }

                  // Take advantage of upgrades
                  structureRepairUpgrade(psBuilding);

                  // Create an assembly point for repaired droids
                  if (!createFlagPosition(&psRepairFac->psDeliveryPoint, psBuilding->player))
                  {
                        return false;
                  }

                  // Initialise the assembly point
                  x = map_coord(psBuilding->pos.x+256);
                  y = map_coord(psBuilding->pos.y+256);

                  // Set the assembly point
                  setAssemblyPoint(psRepairFac->psDeliveryPoint, world_coord(x),
                                   world_coord(y), psBuilding->player, true);

                  // Add the flag (triangular marker on the ground) at the delivery point
                  addFlagPosition(psRepairFac->psDeliveryPoint);
                  setFlagPositionInc(psBuilding->pFunctionality, psBuilding->player, REPAIR_FLAG);
                  break;
            }
            case REF_REARM_PAD:
            {
                  REARM_PAD* psReArmPad = &psBuilding->pFunctionality->rearmPad;

                  psReArmPad->reArmPoints = ((REARM_PAD *)psBuilding->pStructureType->asFuncList[0])->reArmPoints;

                  // Take advantage of upgrades
                  structureReArmUpgrade(psBuilding);
                  break;
            }

            // Structure types without a FUNCTIONALITY
            default:
                  break;
      }

      return true;
}


// Set the command droid that factory production should go to
void assignFactoryCommandDroid(STRUCTURE *psStruct, DROID *psCommander)
{
      FACTORY                 *psFact;
      FLAG_POSITION     *psFlag, *psNext, *psPrev;
      SDWORD                  factoryInc,typeFlag;

      CHECK_STRUCTURE(psStruct);
      ASSERT( StructIsFactory(psStruct),"assignFactoryCommandUnit: structure not a factory" );

      psFact = &psStruct->pFunctionality->factory;

      switch(psStruct->pStructureType->type)
      {
            case REF_FACTORY:
                  typeFlag = FACTORY_FLAG;
                  break;
            case REF_VTOL_FACTORY:
                  typeFlag = VTOL_FLAG;
                  break;
            case REF_CYBORG_FACTORY:
                  typeFlag = CYBORG_FLAG;
                  break;
            default:
                  ASSERT(!"unknown factory type", "assignfactorycommandUnit: unknown factory type");
                  typeFlag = FACTORY_FLAG;
                  break;
      }

      // removing a commander from a factory
      if ( psFact->psCommander != NULL )
      {
            if (typeFlag == FACTORY_FLAG)
            {
                  secondarySetState(psFact->psCommander, DSO_CLEAR_PRODUCTION,
                              (SECONDARY_STATE)(1 << ( psFact->psAssemblyPoint->factoryInc + DSS_ASSPROD_SHIFT)) );
            }
            else if (typeFlag == CYBORG_FLAG)
            {
                  secondarySetState(psFact->psCommander, DSO_CLEAR_PRODUCTION,
                              (SECONDARY_STATE)(1 << ( psFact->psAssemblyPoint->factoryInc + DSS_ASSPROD_CYBORG_SHIFT)) );
            }
            else
            {
                  secondarySetState(psFact->psCommander, DSO_CLEAR_PRODUCTION,
                              (SECONDARY_STATE)(1 << ( psFact->psAssemblyPoint->factoryInc + DSS_ASSPROD_VTOL_SHIFT)) );
            }

            psFact->psCommander = NULL;
            if (!missionIsOffworld())
            {
                  addFlagPosition(psFact->psAssemblyPoint); // add the assembly point back into the list
            }
            else
            {
                  psFact->psAssemblyPoint->psNext = mission.apsFlagPosLists[psFact->psAssemblyPoint->player];
                  mission.apsFlagPosLists[psFact->psAssemblyPoint->player] = psFact->psAssemblyPoint;
            }
      }

      if ( psCommander != NULL )
      {
            ASSERT( !missionIsOffworld(), "assignFactoryCommandDroid: cannot assign a commander to a factory when off world" );

            factoryInc = psFact->psAssemblyPoint->factoryInc;
            psPrev = NULL;

            for (psFlag = apsFlagPosLists[psStruct->player]; psFlag; psFlag = psNext)
            {
                  psNext = psFlag->psNext;

                  if ( (psFlag->factoryInc == factoryInc) && (psFlag->factoryType == typeFlag)   )
                  {
                        if ( psFlag != psFact->psAssemblyPoint )
                        {
                              removeFlagPosition(psFlag);
                        }
                        else
                        {
                              // need to keep the assembly point(s) for the factory
                              // but remove it(the primary) from the list so it doesn't get
                              // displayed
                              if ( psPrev == NULL )
                              {
                                    apsFlagPosLists[psStruct->player] = psFlag->psNext;
                              }
                              else
                              {
                                    psPrev->psNext = psFlag->psNext;
                              }
                              psFlag->psNext = NULL;
                        }
                  }
                  else
                  {
                        psPrev = psFlag;
                  }
            }
            psFact->psCommander = psCommander;
      }
}


// remove all factories from a command droid
void clearCommandDroidFactory(DROID *psDroid)
{
      STRUCTURE   *psCurr;

      for(psCurr = apsStructLists[selectedPlayer]; psCurr; psCurr=psCurr->psNext)
      {
            if ( (psCurr->pStructureType->type == REF_FACTORY) ||
                  (psCurr->pStructureType->type == REF_CYBORG_FACTORY) ||
                  (psCurr->pStructureType->type == REF_VTOL_FACTORY) )
            {
                  if (psCurr->pFunctionality->factory.psCommander == psDroid)
                  {
                        assignFactoryCommandDroid(psCurr, NULL);
                  }
            }
      }
      for(psCurr = mission.apsStructLists[selectedPlayer]; psCurr; psCurr=psCurr->psNext)
      {
            if ( (psCurr->pStructureType->type == REF_FACTORY) ||
                  (psCurr->pStructureType->type == REF_CYBORG_FACTORY) ||
                  (psCurr->pStructureType->type == REF_VTOL_FACTORY) )
            {
                  if (psCurr->pFunctionality->factory.psCommander == psDroid)
                  {
                        assignFactoryCommandDroid(psCurr, NULL);
                  }
            }
      }
}

/* Check that a tile is vacant for a droid to be placed */
static BOOL structClearTile(UWORD x, UWORD y)
{
      UDWORD      player;
      DROID *psCurr;

      /* Check for a structure */
      if(fpathBlockingTile(x,y))
      {
            debug(LOG_NEVER, "failed - blocked");
            return false;
      }

      /* Check for a droid */
      for(player=0; player< MAX_PLAYERS; player++)
      {
            for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
            {
                  if (map_coord(psCurr->pos.x) == x
                   && map_coord(psCurr->pos.y) == y)
                  {
                        debug(LOG_NEVER, "failed - not vacant");
                        return false;
                  }
            }
      }

      debug(LOG_NEVER, "succeeded");
      return true;
}

/*find a location near to a structure to start the droid of*/
BOOL placeDroid(STRUCTURE *psStructure, UDWORD *droidX, UDWORD *droidY)
{
      SWORD             sx,sy, xmin,xmax, ymin,ymax, x,y, xmid;
      BOOL              placed;

      CHECK_STRUCTURE(psStructure);

      /* Get the tile coords for the top left of the structure */
      sx = (SWORD)(psStructure->pos.x - psStructure->pStructureType->baseWidth * TILE_UNITS/2);
      sx = map_coord(sx);
      sy = (SWORD)(psStructure->pos.y - psStructure->pStructureType->baseBreadth * TILE_UNITS/2);
      sy = map_coord(sy);

      /* Find the four corners of the square */
      xmin = (SWORD)(sx - 1);
      xmax = (SWORD)(sx + psStructure->pStructureType->baseWidth);
      xmid = (SWORD)(sx + (psStructure->pStructureType->baseWidth-1)/2);
      ymin = (SWORD)(sy - 1);
      ymax = (SWORD)(sy + psStructure->pStructureType->baseBreadth);
      if (xmin < 0)
      {
            xmin = 0;
      }
      if (xmax > (SDWORD)mapWidth)
      {
            xmax = (SWORD)mapWidth;
      }
      if (ymin < 0)
      {
            ymin = 0;
      }
      if (ymax > (SDWORD)mapHeight)
      {
            ymax = (SWORD)mapHeight;
      }

      /* Look for a clear location for the droid across the bottom */
      /* start in the middle */
      placed = false;
      y = ymax;
      /* middle to right */
      for(x = xmid; x < xmax; x++)
      {
            if (structClearTile(x, y))
            {
                  placed = true;
                  break;
            }
      }
      /* left to middle */
      if (!placed)
      {
            for(x = xmin; x < xmid; x++)
            {
                  if (structClearTile(x, y))
                  {
                        placed = true;
                        break;
                  }
            }
      }
      /* across the top */
      if (!placed)
      {
            y = ymin;
            for(x = xmin; x < xmax; x++)
            {
                  if (structClearTile(x, y))
                  {
                        placed = true;
                        break;
                  }
            }
      }
      /* the left */
      if (!placed)
      {
            x = xmin;
            for(y = ymin; y < ymax; y++)
            {
                  if (structClearTile(x, y))
                  {
                        placed = true;
                        break;
                  }
            }
      }
      /* the right */
      if (!placed)
      {
            x = xmax;
            for(y = ymin; y < ymax; y++)
            {
                  if (structClearTile(x, y))
                  {
                        placed = true;
                        break;
                  }
            }
      }
      *droidX = x;
      *droidY = y;
      return placed;
}

/* Place a newly manufactured droid next to a factory  and then send if off
to the assembly point, returns true if droid was placed successfully */
static BOOL structPlaceDroid(STRUCTURE *psStructure, DROID_TEMPLATE *psTempl,
                                          DROID **ppsDroid)
{
      UDWORD                  x,y;
      BOOL              placed;//bTemp = false;
      DROID             *psNewDroid;
      FACTORY                 *psFact;
      SDWORD                  apx,apy;
      FLAG_POSITION     *psFlag;
      Vector3i iVecEffect;
      UBYTE             factoryType;
      BOOL              assignCommander;

      CHECK_STRUCTURE(psStructure);

      placed = placeDroid(psStructure, &x, &y);

      if (placed)
      {
            //create a droid near to the structure
            psNewDroid = buildDroid(psTempl, world_coord(x), world_coord(y),
                  psStructure->player, false);
            if (!psNewDroid)
            {
                  *ppsDroid = NULL;
                  return false;
            }

            //set the droids order to that of the factory - AB 22/04/99
            psNewDroid->secondaryOrder = psStructure->pFunctionality->factory.secondaryOrder;

            if(bMultiPlayer)
            {
                  sendDroidSecondaryAll(psNewDroid);
            }

            if(psStructure->visible[selectedPlayer])
            {
                  /* add smoke effect to cover the droid's emergence from the factory */
                  iVecEffect.x = psNewDroid->pos.x;
                  iVecEffect.y = map_Height( psNewDroid->pos.x, psNewDroid->pos.y ) + DROID_CONSTRUCTION_SMOKE_HEIGHT;
                  iVecEffect.z = psNewDroid->pos.y;
                  addEffect( &iVecEffect,EFFECT_CONSTRUCTION,CONSTRUCTION_TYPE_DRIFTING,false,NULL,0 );
                  iVecEffect.x = psNewDroid->pos.x - DROID_CONSTRUCTION_SMOKE_OFFSET;
                  iVecEffect.z = psNewDroid->pos.y - DROID_CONSTRUCTION_SMOKE_OFFSET;
                  addEffect( &iVecEffect,EFFECT_CONSTRUCTION,CONSTRUCTION_TYPE_DRIFTING,false,NULL,0 );
                  iVecEffect.z = psNewDroid->pos.y + DROID_CONSTRUCTION_SMOKE_OFFSET;
                  addEffect( &iVecEffect,EFFECT_CONSTRUCTION,CONSTRUCTION_TYPE_DRIFTING,false,NULL,0 );
                  iVecEffect.x = psNewDroid->pos.x + DROID_CONSTRUCTION_SMOKE_OFFSET;
                  addEffect( &iVecEffect,EFFECT_CONSTRUCTION,CONSTRUCTION_TYPE_DRIFTING,false,NULL,0 );
                  iVecEffect.z = psNewDroid->pos.y - DROID_CONSTRUCTION_SMOKE_OFFSET;
                  addEffect( &iVecEffect,EFFECT_CONSTRUCTION,CONSTRUCTION_TYPE_DRIFTING,false,NULL,0 );
            }
            /* add the droid to the list */
            addDroid(psNewDroid, apsDroidLists);
            *ppsDroid = psNewDroid;
            if ( psNewDroid->player == selectedPlayer )
            {
                  audio_QueueTrack( ID_SOUND_DROID_COMPLETED );
                  intRefreshScreen();     // update any interface implications.
            }

            // update the droid counts
            incNumDroids(psNewDroid->player);
            switch (psNewDroid->droidType)
            {
            case DROID_COMMAND:
                  incNumCommandDroids(psNewDroid->player);
                  break;
            case DROID_CONSTRUCT:
            case DROID_CYBORG_CONSTRUCT:
                  incNumConstructorDroids(psNewDroid->player);
                  break;
            default:
                  break;
            }

            psFact = &psStructure->pFunctionality->factory;
            apx = psFact->psAssemblyPoint->coords.x;
            apy = psFact->psAssemblyPoint->coords.y;

            // if we've built a command droid - make sure that it isn't assigned to another commander
            assignCommander = false;
            if ((psNewDroid->droidType == DROID_COMMAND) &&
                  (psFact->psCommander != NULL))
            {
                  assignFactoryCommandDroid(psStructure, NULL);
                  assignCommander = true;
            }

            if ( psFact->psCommander != NULL )
            {
                  if (idfDroid(psNewDroid) ||
                        isVtolDroid(psNewDroid))
                  {
                        orderDroidObj(psNewDroid, DORDER_FIRESUPPORT, (BASE_OBJECT *)psFact->psCommander);
                        moveToRearm(psNewDroid);
                  }
                  else
                  {
                        cmdDroidAddDroid(psFact->psCommander, psNewDroid);
                  }
            }
            else
            {
                  //check flag against factory type
                  factoryType = FACTORY_FLAG;
                  if (psStructure->pStructureType->type == REF_CYBORG_FACTORY)
                  {
                        factoryType = CYBORG_FLAG;
                  }
                  else if (psStructure->pStructureType->type == REF_VTOL_FACTORY)
                  {
                        factoryType = VTOL_FLAG;
                  }
                  //if vtol droid - send it to ReArm Pad if one exists
                  placed = false;
                  if (isVtolDroid(psNewDroid))
                  {
                        moveToRearm(psNewDroid);
                  }
                  if (!placed)
                  {
                        //find flag in question.
                        for(psFlag = apsFlagPosLists[psFact->psAssemblyPoint->player];
                                    !( (psFlag->factoryInc == psFact->psAssemblyPoint->factoryInc) // correct fact.
                                    &&(psFlag->factoryType == factoryType)); // correct type
                              psFlag = psFlag->psNext);

                        if (psNewDroid->droidType == DROID_TRANSPORTER)
                        {
                              UDWORD droidX = psFlag->coords.x;
                              UDWORD droidY = psFlag->coords.y;
                              //find a suitable location near the delivery point
                              actionVTOLLandingPos(psNewDroid, &droidX, &droidY);
                              actionDroidLoc(psNewDroid, DACTION_MOVE, droidX,droidY);
                        }
                        else
                        {
                              orderDroidLoc(psNewDroid,DORDER_MOVE,psFlag->coords.x,psFlag->coords.y);
                        }
                  }
            }

            if (assignCommander)
            {
                  assignFactoryCommandDroid(psStructure, psNewDroid);
            }
            if ( psNewDroid->player == selectedPlayer )
            {
                  eventFireCallbackTrigger((TRIGGER_TYPE)CALL_DROIDBUILT);
            }

            return true;
      }
      else
      {
            *ppsDroid = NULL;
      }

      return false;
}


static BOOL IsFactoryCommanderGroupFull(FACTORY *psFactory)
{
      SDWORD DroidsInGroup;

      // If we don't have a commander return false (group not full)
      if (psFactory->psCommander==NULL) return false;

      // allow any number of IDF droids
      if (templateIsIDF((DROID_TEMPLATE *)psFactory->psSubject))
      {
            return false;
      }

      // Get the number of droids in the commanders group
      DroidsInGroup = psFactory->psCommander->psGroup ? grpNumMembers( psFactory->psCommander->psGroup ) : 0;

      // if the number in group is less than the maximum allowed then return false (group not full)
      if (DroidsInGroup < cmdDroidMaxGroup( psFactory->psCommander )) return false;

      // the number in group has reached the maximum
      return true;
}


// Disallow manufacture of units once these limits are reached,
// dos'nt mean that these numbers can't be exceeded if units are
// put down in the editor or by the scripts.
static UWORD MaxDroidsAllowedPerPlayer[MAX_PLAYERS] = {100, 999, 999, 999, 999, 999, 999, 999};
static UWORD MaxDroidsAllowedPerPlayerMultiPlayer[MAX_PLAYERS] = {300, 300, 300, 300, 300, 300, 300, 300};

BOOL IsPlayerStructureLimitReached(UDWORD PlayerNumber)
{
      // PC currently doesn't limit number of structures a player can build.
      return false;
}


UDWORD getMaxDroids(UDWORD PlayerNumber)
{
      return (bMultiPlayer ? MaxDroidsAllowedPerPlayerMultiPlayer[PlayerNumber] : MaxDroidsAllowedPerPlayer[PlayerNumber] );

}


BOOL IsPlayerDroidLimitReached(UDWORD PlayerNumber)
{
      unsigned int numDroids = getNumDroids(PlayerNumber) + getNumMissionDroids(PlayerNumber) + getNumTransporterDroids(PlayerNumber);

      if (bMultiPlayer)
      {
            if ( numDroids >= MaxDroidsAllowedPerPlayerMultiPlayer[PlayerNumber] )
                  return true;
      }
      else
      {
            if( numDroids >= MaxDroidsAllowedPerPlayer[PlayerNumber] )
                  return true;
      }

      return false;
}


static BOOL maxDroidsByTypeReached(STRUCTURE *psStructure)
{
      FACTORY           *psFact = &psStructure->pFunctionality->factory;

      CHECK_STRUCTURE(psStructure);

      if (droidTemplateType((DROID_TEMPLATE *)psFact->psSubject) == DROID_COMMAND
       && getNumCommandDroids(psStructure->player) >= MAX_COMMAND_DROIDS)
      {
            return true;
      }

      if ((droidTemplateType((DROID_TEMPLATE *)psFact->psSubject) == DROID_CONSTRUCT
        || droidTemplateType((DROID_TEMPLATE *)psFact->psSubject) == DROID_CYBORG_CONSTRUCT)
       && getNumConstructorDroids(psStructure->player) >= MAX_CONSTRUCTOR_DROIDS)
      {
            return true;
      }

      return false;
}


// Check for max number of units reached and halt production.
//
BOOL CheckHaltOnMaxUnitsReached(STRUCTURE *psStructure)
{
      CHECK_STRUCTURE(psStructure);

      // if the players that owns the factory has reached his (or hers) droid limit
      // then put production on hold & return - we need a message to be displayed here !!!!!!!
      if (IsPlayerDroidLimitReached(psStructure->player) ||
            maxDroidsByTypeReached(psStructure))
      {
            if ((psStructure->player == selectedPlayer) &&
                  (lastMaxUnitMessage + MAX_UNIT_MESSAGE_PAUSE < gameTime))
            {
                  addConsoleMessage(_("Command Control Limit Reached - Production Halted"),DEFAULT_JUSTIFY,SYSTEM_MESSAGE);
                  lastMaxUnitMessage = gameTime;
            }
            return true;
      }

      return false;
}


static void aiUpdateStructure(STRUCTURE *psStructure)
{
      BASE_STATS              *pSubject = NULL;
      UDWORD                        pointsToAdd;//, iPower;
      PLAYER_RESEARCH         *pPlayerRes = asPlayerResList[psStructure->player];
      UDWORD                        structureMode = 0;
      DROID                   *psDroid;
      BASE_OBJECT             *psChosenObjs[STRUCT_MAXWEAPS] = {NULL};
      BASE_OBJECT             *psChosenObj = NULL;
      UBYTE                   Quantity;
      SDWORD                        iDt;
      FACTORY                       *psFactory;
      REPAIR_FACILITY         *psRepairFac = NULL;
      RESEARCH_FACILITY *psResFacility;
      REARM_PAD               *psReArmPad;
      Vector3i iVecEffect;
      BOOL                    bFinishAction,bDroidPlaced;
      WEAPON_STATS            *psWStats;
      SDWORD                        xdiff,ydiff, mindist, currdist;
#ifdef INCLUDE_FACTORYLISTS
      DROID_TEMPLATE          *psNextTemplate;
#endif
      UDWORD                        i;

      CHECK_STRUCTURE(psStructure);

      // Will go out into a building EVENT stats/text file
      /* Spin round yer sensors! */
      if (psStructure->numWeaps == 0)
      {
            if ((psStructure->asWeaps[0].nStat == 0) &&
                  (psStructure->pStructureType->type != REF_REPAIR_FACILITY))
            {

      //////
      // - radar should rotate every three seconds ... 'cause we timed it at Heathrow !
      // gameTime is in milliseconds - one rotation every 3 seconds = 1 rotation event 3000 millisecs
                  psStructure->turretRotation[0] = (UWORD)(((gameTime*360)/3000)%360);

                  psStructure->turretPitch[0] = 0;
            }
      }

      structUpdateRecoil(psStructure);

      /* See if there is an enemy to attack */
      if (psStructure->numWeaps > 0)
      {
            //structures always update their targets
            for (i = 0;i < psStructure->numWeaps;i++)
            {
                  if (psStructure->asWeaps[i].nStat > 0 &&
                        asWeaponStats[psStructure->asWeaps[i].nStat].weaponSubClass != WSC_LAS_SAT)
                  {
                        if ((psStructure->id % 20) == (frameGetFrameNumber() % 20))
                        {
                              if ( aiChooseTarget((BASE_OBJECT *)psStructure, &psChosenObjs[i], i, true) )
                              {
                                    debug( LOG_ATTACK, "Struct(%d) attacking : %d\n",
                                                psStructure->id, psChosenObjs[i]->id );
                                    setStructureTarget(psStructure, psChosenObjs[i], i);
                              }
                              else
                              {
                                    if ( aiChooseTarget((BASE_OBJECT *)psStructure, &psChosenObjs[0], 0, true) )
                                    {
                                          if (psChosenObjs[0])
                                          {
                                                debug( LOG_ATTACK, "Struct(%d) attacking : %d\n",
                                                            psStructure->id, psChosenObjs[0]->id );
                                                setStructureTarget(psStructure, psChosenObjs[0], i);
                                                psChosenObjs[i] = psChosenObjs[0];
                                          }
                                          else
                                          {
                                                setStructureTarget(psStructure, NULL, i);
                                                psChosenObjs[i] = NULL;
                                          }
                                    }
                                    else
                                    {
                                          setStructureTarget(psStructure, NULL, i);
                                          psChosenObjs[i] = NULL;
                                    }
                              }
                        }
                        else
                        {
                              psChosenObjs[i] = psStructure->psTarget[0];
                        }

                        if (psChosenObjs[i] != NULL)
                        {
                              // get the weapon stat to see if there is a visible turret to rotate
                              psWStats = asWeaponStats + psStructure->asWeaps[i].nStat;

                              //if were going to shoot at something move the turret first then fire when locked on
                              if (psWStats->pMountGraphic == NULL)//no turret so lock on whatever
                              {
                                    psStructure->turretRotation[i] = (UWORD)calcDirection(psStructure->pos.x,
                                          psStructure->pos.y, psChosenObjs[i]->pos.x, psChosenObjs[i]->pos.y);
                                    combFire(&psStructure->asWeaps[i], (BASE_OBJECT *)psStructure, psChosenObjs[i], i);
                              }
                              else if(actionTargetTurret((BASE_OBJECT*)psStructure, psChosenObjs[i],
                                                                  &(psStructure->turretRotation[i]),
                                                                  &(psStructure->turretPitch[i]),
                                                                  psWStats, false, i))
                              {
                                    combFire(&psStructure->asWeaps[i], (BASE_OBJECT *)psStructure, psChosenObjs[i], i);
                              }
                        }
                        else
                        {
                              // realign the turret
                              if ( ((psStructure->turretRotation[i] % 90) != 0) ||
                                    (psStructure->turretPitch[i] != 0) )
                              {
                                    actionAlignTurret((BASE_OBJECT *)psStructure, i);
                              }
                        }
                  }
            }
      }

      /* See if there is an enemy to attack for Sensor Towers that have weapon droids attached*/
      else if (psStructure->pStructureType->pSensor)
      {
            if (structStandardSensor(psStructure) || structVTOLSensor(psStructure))
            {
                  if ((psStructure->id % 20) == (frameGetFrameNumber() % 20))
                  {
                        if (aiChooseSensorTarget((BASE_OBJECT *)psStructure, &psChosenObj))
                        {
                              debug( LOG_ATTACK, "Struct(%d) attacking : %d\n",
                                    psStructure->id, psChosenObj->id );
                              setStructureTarget(psStructure, psChosenObj, 0);
                        }
                        else
                        {
                              setStructureTarget(psStructure, NULL, 0);
                        }
                  }
                  psChosenObj = psStructure->psTarget[0];
            }
            else
            {
                  psChosenObj = psStructure->psTarget[0];
            }

            // you can always see anything that a CB sensor is targeting
            // Anyone commenting this out again will get a knee capping from John.
            // You have been warned!!
            if ((structCBSensor(psStructure) || structVTOLCBSensor(psStructure)) &&
                  psStructure->psTarget[0] != NULL)
            {
                  psStructure->psTarget[0]->visible[psStructure->player] = UBYTE_MAX;
            }
      }
      //only interested if the Structure "does" something!
      if (psStructure->pFunctionality == NULL)
      {
            return;
      }

      //check if any power available
      if (structUsesPower(psStructure))
      {
            if (checkPower(psStructure->player, POWER_PER_CYCLE, false))
            {
                  //check if this structure is due some power
                  if (getLastPowered((BASE_OBJECT *)psStructure))
                  {
                        //get some power if necessary
                        if (accruePower((BASE_OBJECT *)psStructure))
                        {
                              updateLastPowered((BASE_OBJECT *)psStructure, psStructure->player);
                        }
                  }
            }
      }

      /* Process the functionality according to type
      * determine the subject stats (for research or manufacture)
      * or base object (for repair) or update power levels for resourceExtractor
      */
      switch (psStructure->pStructureType->type)
      {
            case REF_RESEARCH:
            {
                  pSubject = psStructure->pFunctionality->researchFacility.psSubject;
                  structureMode = REF_RESEARCH;
                  break;
            }
            case REF_FACTORY:
            case REF_CYBORG_FACTORY:
            case REF_VTOL_FACTORY:
            {
                  pSubject = psStructure->pFunctionality->factory.psSubject;
                  structureMode = REF_FACTORY;
                  //check here to see if the factory's commander has died
                  if (psStructure->pFunctionality->factory.psCommander &&
                        psStructure->pFunctionality->factory.psCommander->died)
                  {
                        //remove the commander from the factory
                        assignFactoryCommandDroid(psStructure, NULL);
                  }
                  break;
            }
            case REF_REPAIR_FACILITY:
            {
                  psRepairFac = &psStructure->pFunctionality->repairFacility;
                  psChosenObj = psRepairFac->psObj;
                  structureMode = REF_REPAIR_FACILITY;
                  psDroid = (DROID *)psChosenObj;

                  // skip droids that are doing anything else
                  if (psDroid != NULL
                      && (!orderState(psDroid, DORDER_RTR)
                          || psDroid->psTarget != (BASE_OBJECT *)psStructure))
                  {
                        psChosenObj = NULL;
                        psDroid = NULL;
                        psRepairFac->psObj = NULL;
                  }

                  /* select next droid if none being repaired */
                  if ( psChosenObj == NULL )
                  {
                        ASSERT( psRepairFac->psGroup != NULL,
                              "aiUpdateStructure: invalid repair facility group pointer" );

                        mindist = SDWORD_MAX;
                        for(psDroid = apsDroidLists[psStructure->player]; psDroid; psDroid = psDroid->psNext)
                        {
                              BASE_OBJECT * const psTarget = orderStateObj(psDroid, DORDER_RTR);

                              if (psTarget
                               && psTarget == (BASE_OBJECT *)psStructure
                               && psDroid->action == DACTION_WAITFORREPAIR)
                              {
                                    xdiff = (SDWORD)psDroid->pos.x - (SDWORD)psStructure->pos.x;
                                    ydiff = (SDWORD)psDroid->pos.y - (SDWORD)psStructure->pos.y;
                                    currdist = xdiff*xdiff + ydiff*ydiff;
                                    if (currdist < mindist)
                                    {
                                          mindist = currdist;
                                          psChosenObj = (BASE_OBJECT *)psDroid;
                                    }
                              }
                        }
                        psDroid = (DROID *)psChosenObj;
                  }

                  // send the droid to be repaired
                  if ( psDroid != NULL &&
                        psDroid->action == DACTION_WAITFORREPAIR )
                  {
                        /* set chosen object */
                        psChosenObj = (BASE_OBJECT *)psDroid;
                        psRepairFac->psObj = (BASE_OBJECT *)psDroid;

                        /* move droid to repair point at rear of facility */
                        actionDroidObjLoc( psDroid, DACTION_MOVETOREPAIRPOINT,
                                    (BASE_OBJECT *) psStructure, psStructure->pos.x, psStructure->pos.y);
                        /* reset repair started */
                        psRepairFac->timeStarted = ACTION_START_TIME;
                        psRepairFac->currentPtsAdded = 0;
                        break;
                  }
                  break;
            }
            case REF_REARM_PAD:
            {
                  psReArmPad = &psStructure->pFunctionality->rearmPad;
                  psChosenObj = psReArmPad->psObj;
                  structureMode = REF_REARM_PAD;
                  psDroid = NULL;

                  /* select next droid if none being rearmed*/
                  if (psChosenObj == NULL)
                  {
                        for(psDroid = apsDroidLists[psStructure->player]; psDroid;
                              psDroid = psDroid->psNext)
                        {
                              // move next droid waiting on ground to rearm pad
                              if (vtolReadyToRearm(psDroid, psStructure) &&
                                    (psChosenObj == NULL || (((DROID *)psChosenObj)->actionStarted > psDroid->actionStarted)) )
                              {
                                    psChosenObj = (BASE_OBJECT *)psDroid;
                              }
                        }
                        psDroid = (DROID *)psChosenObj;
                        if (psDroid != NULL)
                        {
                              actionDroidObj( psDroid, DACTION_MOVETOREARMPOINT, (BASE_OBJECT *)psStructure);
                        }
                  }
                  else
                  {
                        psDroid = (DROID *) psChosenObj;
                        if ( (psDroid->sMove.Status == MOVEINACTIVE ||
                              psDroid->sMove.Status == MOVEHOVER       ) &&
                              psDroid->action == DACTION_WAITFORREARM        )
                        {
                              actionDroidObj( psDroid, DACTION_MOVETOREARMPOINT, (BASE_OBJECT *)psStructure);
                        }
                  }

                  // if found a droid to rearm assign it to the rearm pad
                  if (psDroid != NULL)
                  {
                        /* set chosen object */
                        psChosenObj = (BASE_OBJECT *)psDroid;
                        psReArmPad->psObj = psChosenObj;
                        if ( psDroid->action == DACTION_MOVETOREARMPOINT )
                        {
                              /* reset rearm started */
                              psReArmPad->timeStarted = ACTION_START_TIME;
                              psReArmPad->currentPtsAdded = 0;
                        }
                  }
                  break;
            }
            default:
                  break;
      }

      /* check subject stats (for research or manufacture) */
      if (pSubject != NULL)
      {
            //if subject is research...
            if (structureMode == REF_RESEARCH)
            {
                  psResFacility = &psStructure->pFunctionality->researchFacility;

                  //if on hold don't do anything
                  if (psResFacility->timeStartHold)
                  {
                        return;
                  }

                  //electronic warfare affects the functionality of some structures in multiPlayer
                  if (bMultiPlayer)
                  {
                        if (psStructure->resistance < (SWORD)structureResistance(psStructure->
                              pStructureType, psStructure->player))
                        {
                              return;
                        }
                  }

                  pPlayerRes += (pSubject->ref - REF_RESEARCH_START);
                  //check research has not already been completed by another structure
                  if (IsResearchCompleted(pPlayerRes)==0)
                  {
                        //check to see if enough power to research has accrued
                        if (psResFacility->powerAccrued < ((RESEARCH *)pSubject)->researchPower)
                        {
                              //wait until enough power
                              return;
                        }


                        // don't update if not responsible (106)
                        if(bMultiPlayer && !myResponsibility(psStructure->player))
                        {
                              return;
                        }

                        if (psResFacility->timeStarted == ACTION_START_TIME)
                        {
                              //set the time started
                              psResFacility->timeStarted = gameTime;
                        }

                        pointsToAdd = (psResFacility->researchPoints * (gameTime -
                              psResFacility->timeStarted)) / GAME_TICKS_PER_SEC;

                        //check if Research is complete
                        if ((pointsToAdd + pPlayerRes->currentPoints) > (
                              (RESEARCH *)pSubject)->researchPoints)

                        {
                              if(bMultiPlayer)
                              {
                                    SendResearch(psStructure->player,pSubject->ref - REF_RESEARCH_START);
                              }

                              //store the last topic researched - if its the best
                              if (psResFacility->psBestTopic == NULL)
                              {
                                    psResFacility->psBestTopic = psResFacility->psSubject;
                              }
                              else
                              {
                                    if (((RESEARCH *)psResFacility->psSubject)->researchPoints >
                                          ((RESEARCH *)psResFacility->psBestTopic)->researchPoints)
                                    {
                                          psResFacility->psSubject = psResFacility->psSubject;
                                    }
                              }
                              psResFacility->psSubject = NULL;
                              intResearchFinished(psStructure);
                              researchResult(pSubject->ref - REF_RESEARCH_START,
                                    psStructure->player, true,psStructure);
                              //check if this result has enabled another topic
                              intCheckResearchButton();
                        }
                  }
                  else
                  {
                        //cancel this Structure's research since now complete
                        psResFacility->psSubject = NULL;
                        intResearchFinished(psStructure);
                  }
            }
            //check for manufacture
            else if (structureMode == REF_FACTORY)
            {
                  psFactory = &psStructure->pFunctionality->factory;
                  Quantity = psFactory->quantity;

                  //if on hold don't do anything
                  if (psFactory->timeStartHold)
                  {
                        return;
                  }

                  //electronic warfare affects the functionality of some structures in multiPlayer
                  if (bMultiPlayer)
                  {
                        if (psStructure->resistance < (SWORD)structureResistance(psStructure->
                              pStructureType, psStructure->player))
                        {
                              return;
                        }
                  }

                  if (psFactory->timeStarted == ACTION_START_TIME)
                  {
                        // also need to check if a command droid's group is full

                        // If the factory commanders group is full - return
                        if (IsFactoryCommanderGroupFull(psFactory)==true)
                        {
                              return;
                        }

                        if(CheckHaltOnMaxUnitsReached(psStructure) == true) {
                              return;
                        }
                  }

                  //check enough power has accrued to build the droid
                  if (psFactory->powerAccrued < ((DROID_TEMPLATE *)pSubject)->
                              powerPoints)
                  {
                        //wait until enough power
                        return;
                  }

                  /*must be enough power so subtract that required to build*/
                  if (psFactory->timeStarted == ACTION_START_TIME)
                  {
                        //set the time started
                        psFactory->timeStarted = gameTime;
                  }

                  pointsToAdd = (gameTime - psFactory->timeStarted) / GAME_TICKS_PER_SEC;

                  //check for manufacture to be complete
                  if ((pointsToAdd > psFactory->timeToBuild) &&
                        !IsFactoryCommanderGroupFull(psFactory) &&
                        !CheckHaltOnMaxUnitsReached(psStructure))
                  {
                        /* Place the droid on the map */
                        bDroidPlaced = structPlaceDroid(psStructure, (DROID_TEMPLATE *)pSubject, &psDroid);

                        //reset the start time
                        psFactory->timeStarted = ACTION_START_TIME;
                        psFactory->powerAccrued = 0;

#ifdef INCLUDE_FACTORYLISTS
                        //next bit for productionPlayer only
                        if (productionPlayer == psStructure->player)
                        {
                              psNextTemplate = factoryProdUpdate(psStructure,(DROID_TEMPLATE *)pSubject);
                              if (psNextTemplate)
                              {
                                    structSetManufacture(psStructure, psNextTemplate,Quantity);
                                    return;
                              }
                              else
                              {
                                    //nothing more to manufacture - reset the Subject and Tab on HCI Form
                                    intManufactureFinished(psStructure);
                                    psFactory->psSubject = NULL;

                                    //script callback, must be called after factory was flagged as idle
                                    if(bDroidPlaced)
                                    {
                                          cbNewDroid(psStructure, psDroid);
                                    }
                              }
                        }
                        else
#endif
                        {
                              //decrement the quantity to manufacture if not set to infinity
                              if (Quantity && Quantity != NON_STOP_PRODUCTION)
                              {
                                    psFactory->quantity--;
                                    Quantity--;
                              }

                              // If quantity not 0 then kick of another manufacture.
                              if(Quantity)
                              {
                                    // Manufacture another.
                                    structSetManufacture(psStructure, (DROID_TEMPLATE*)pSubject,Quantity);
                                    return;
                              }
                              else
                              {
                                    //when quantity = 0, reset the Subject and Tab on HCI Form
                                    psFactory->psSubject = NULL;
                                    intManufactureFinished(psStructure);

                                    //script callback, must be called after factory was flagged as idle
                                    if(bDroidPlaced)
                                    {
                                          cbNewDroid(psStructure, psDroid);
                                    }
                              }
                        }
                  }
            }
      }

      /* check base object (for repair / rearm) */
      if ( psChosenObj != NULL )
      {
            if ( structureMode == REF_REPAIR_FACILITY )
            {
                  UDWORD  powerCost;

                  psDroid = (DROID *) psChosenObj;
                  ASSERT( psDroid != NULL,
                              "aiUpdateStructure: invalid droid pointer" );
                  psRepairFac = &psStructure->pFunctionality->repairFacility;

                  if ( psDroid->action == DACTION_WAITDURINGREPAIR &&
                        actionTargetTurret((BASE_OBJECT*)psStructure, psChosenObj,
                                                      &(psStructure->turretRotation[0]),
                                                      &(psStructure->turretPitch[0]),
                                                      NULL, false, 0))
                  {
                        //check droid is not healthy
                        if (psDroid->body < psDroid->originalBody)
                        {
                              //if in multiPlayer, and a Transporter - make sure its on the ground before repairing
                              if (bMultiPlayer && psDroid->droidType == DROID_TRANSPORTER)
                              {
                                    if (!(psDroid->sMove.Status == MOVEINACTIVE &&
                                          psDroid->sMove.iVertSpeed == 0))
                                    {
                                          return;
                                    }
                              }

                              //don't do anything if the resistance is low in multiplayer
                              if (bMultiPlayer)
                              {
                                    if (psStructure->resistance < (SWORD)structureResistance(psStructure->
                                          pStructureType, psStructure->player))
                                    {
                                          return;
                                    }
                              }

                              //check if enough power to do any
                              powerCost = powerReqForDroidRepair(psDroid);
                              if (powerCost > psDroid->powerAccrued)
                              {
                                    powerCost = (powerCost - psDroid->powerAccrued) / POWER_FACTOR;
                              }
                              else
                              {
                                    powerCost = 0;
                              }
                              //if the power cost is 0 (due to rounding) then do for free!
                              if (powerCost)
                              {
                                    if (!psDroid->powerAccrued)
                                    {
                                          //reset the actionStarted time and actionPoints added so the correct
                                          //amount of points are added when there is more power
                                          psRepairFac->timeStarted = gameTime;
                                          //init so repair points to add won't be huge when start up again
                                          psRepairFac->currentPtsAdded = 0;
                                          return;
                                    }
                              }

                              if (psRepairFac->timeStarted == ACTION_START_TIME)
                              {
                                    //set the time started
                                    psRepairFac->timeStarted = gameTime;
                                    //reset the points added
                                    psRepairFac->currentPtsAdded = 0;
                              }

                              /* do repairing */
                              iDt = gameTime - psRepairFac->timeStarted;
                              //- this was a bit exponential ...
                              pointsToAdd = (iDt * psRepairFac->power / GAME_TICKS_PER_SEC) -
                                    psRepairFac->currentPtsAdded;
                              bFinishAction = false;

                              //do some repair
                              if (pointsToAdd)
                              {
                                    //just add the points if the power cost is negligable
                                    //if these points would make the droid healthy again then just add
                                    if (!powerCost || (psDroid->body + pointsToAdd >= psDroid->originalBody))
                                    {
                                          //anothe HACK but sorts out all the rounding errors when values get small
                                          psDroid->body += pointsToAdd;
                                          psRepairFac->currentPtsAdded += pointsToAdd;
                                    }
                                    else
                                    {
                                          //see if we have enough power to do this amount of repair
                                          powerCost = pointsToAdd * repairPowerPoint(psDroid);
                                          if (powerCost <= psDroid->powerAccrued)
                                          {
                                                psDroid->body += pointsToAdd;
                                                psRepairFac->currentPtsAdded += pointsToAdd;
                                                //subtract the power cost for these points
                                                psDroid->powerAccrued -= powerCost;
                                          }
                                          else
                                          {
                                                /*reset the actionStarted time and actionPoints added so the correct
                                                amount of points are added when there is more power*/
                                                psRepairFac->timeStarted = gameTime;
                                                psRepairFac->currentPtsAdded = 0;
                                          }
                                    }
                              }
                        }

                        if ( psDroid->body >= psDroid->originalBody )
                        {
                              debug(LOG_NEVER, "repair completed");

                              psRepairFac->psObj = NULL;

                              /* set droid points to max */
                              psDroid->body = psDroid->originalBody;
                              //reset the power accrued
                              psDroid->powerAccrued = 0;

                              // if completely repaired reset order
                              secondarySetState(psDroid, DSO_RETURN_TO_LOC, DSS_NONE);

                              if (hasCommander(psDroid))
                              {
                                    // return a droid to it's command group
                                    DROID *psCommander = psDroid->psGroup->psCommander;

                                    orderDroidObj(psDroid, DORDER_GUARD, (BASE_OBJECT *)psCommander);
                              }
                              else if (psRepairFac->psDeliveryPoint != NULL)
                              {
                                    // move the droid out the way
                                    orderDroidLoc( psDroid, DORDER_MOVE,
                                          psRepairFac->psDeliveryPoint->coords.x,
                                          psRepairFac->psDeliveryPoint->coords.y );
                              }
                        }

                        if (psStructure->visible[selectedPlayer] && psDroid->visible[selectedPlayer])
                        {
                              /* add plasma repair effect whilst being repaired */
                              iVecEffect.x = psDroid->pos.x + (10-rand()%20);
                              iVecEffect.y = psDroid->pos.z + (10-rand()%20);
                              iVecEffect.z = psDroid->pos.y + (10-rand()%20);
                              effectSetSize(100);
                              addEffect( &iVecEffect,EFFECT_EXPLOSION,EXPLOSION_TYPE_SPECIFIED,
                                                true,getImdFromIndex(MI_FLAME),0 );
                        }
                  }
            }
            //check for rearming
            else if (structureMode == REF_REARM_PAD)
            {
                  psReArmPad = &psStructure->pFunctionality->rearmPad;

                  psDroid = (DROID *)psChosenObj;
                  ASSERT( psDroid != NULL,
                              "aiUpdateStructure: invalid droid pointer" );
                  ASSERT( isVtolDroid(psDroid),"aiUpdateStructure: invalid droid type" );

                  //check hasn't died whilst waiting to be rearmed
                  // also clear out any previously repaired droid
                  if ( psDroid->died ||
                              ( psDroid->action != DACTION_MOVETOREARMPOINT &&
                                psDroid->action != DACTION_WAITDURINGREARM ) )
                  {
                        psReArmPad->psObj = NULL;
                        return;
                  }

                  //if waiting to be rearmed
                  if ( psDroid->action == DACTION_WAITDURINGREARM &&
                        psDroid->sMove.Status == MOVEINACTIVE )
                  {
                        if (psReArmPad->timeStarted == ACTION_START_TIME)
                        {
                              //set the time started
                              psReArmPad->timeStarted = gameTime;
                        }

                        bFinishAction = false;

                        // dont rearm on remote pcs.
                        if(!bMultiPlayer || myResponsibility(psDroid->player))
                        {
                              /* do rearming */
                              if (psDroid->sMove.iAttackRuns != 0)
                              {
                                    UDWORD      pointsRequired;

                                    //amount required is a factor of the droids' weight
                                    pointsRequired = psDroid->weight / REARM_FACTOR;
                                    //Watermelon:take numWeaps into consideration
                                    pointsToAdd = psReArmPad->reArmPoints * (gameTime - psReArmPad->timeStarted) * psDroid->numWeaps /
                                          GAME_TICKS_PER_SEC;
                                    //if ((SDWORD)(psDroid->sMove.iAttackRuns - pointsToAdd) <= 0)
                                    if (pointsToAdd >= pointsRequired)
                                    {
                                          for (i = 0;i < psDroid->numWeaps;i++)
                                          {
                                                /* set rearm value to no runs made */
                                                psDroid->sMove.iAttackRuns[i] = 0;
                                                //reset ammo and lastTimeFired
                                                psDroid->asWeaps[i].ammo = asWeaponStats[psDroid->
                                                      asWeaps[i].nStat].numRounds;
                                                psDroid->asWeaps[i].lastFired = 0;
                                          }
                                    }
                                    else
                                    {
                                          for (i = 0;i < psDroid->numWeaps;i++)
                                          {
                                                //Watermelon:make sure that slot is depleted and dont divide integer by zero
                                                if ( psDroid->sMove.iAttackRuns[i] > 0 )
                                                {
                                                      if (pointsToAdd >= pointsRequired/psDroid->sMove.iAttackRuns[i])
                                                      {
                                                            psDroid->sMove.iAttackRuns[i]--;
                                                      }
                                                }
                                          }
                                    }
                              }
                        }

                        /* do repairing */
                        if (psDroid->body < psDroid->originalBody)
                        {
                              pointsToAdd =  VTOL_REPAIR_FACTOR * (gameTime -
                                    psReArmPad->timeStarted) / GAME_TICKS_PER_SEC;
                              //this was exponential...
                              if ((pointsToAdd - psReArmPad->currentPtsAdded) > 0)
                              {
                                    psDroid->body += (pointsToAdd - psReArmPad->currentPtsAdded);
                                    psReArmPad->currentPtsAdded = pointsToAdd;
                              }
                              if (psDroid->body >= psDroid->originalBody)
                              {
                                    /* set droid points to max */
                                    psDroid->body = psDroid->originalBody;
                              }
                        }

                        //check for fully armed and fully repaired
                        if (vtolHappy(psDroid))
                        {
                              if( bMultiPlayer)
                              {
                                    sendHappyVtol(psDroid);
                              }

                              //clear the rearm pad
                              psDroid->action = DACTION_NONE;
                              bFinishAction = true;
                              psReArmPad->psObj = NULL;

                        }
                  }
            }
      }
}


/* Decides whether a structure should emit smoke when it's damaged */
static BOOL canSmoke(STRUCTURE *psStruct)
{
      if(psStruct->pStructureType->type == REF_WALL ||
            psStruct->pStructureType->type == REF_WALLCORNER)
      {
            return(false);
      }
      else
      {
            return(true);
      }
}

static float CalcStructureSmokeInterval(float damage)
{
      return (((1. - damage) + 0.1) * 10) * STRUCTURE_DAMAGE_SCALING;
}

/* The main update routine for all Structures */
void structureUpdate(STRUCTURE *psBuilding)
{
      UDWORD widthScatter,breadthScatter;
      UDWORD emissionInterval, iPointsToAdd, iPointsRequired;
      Vector3i dv;
      int i;

      // Remove invalid targets. This must be done each frame.
      for (i = 0; i < STRUCT_MAXWEAPS; i++)
      {
            if (psBuilding->psTarget[i] && psBuilding->psTarget[i]->died)
            {
                  setStructureTarget(psBuilding, NULL, i);
            }
      }

      //update the manufacture/research of the building once complete
      if (psBuilding->status == SS_BUILT)
      {
            aiUpdateStructure(psBuilding);
      }

      if(psBuilding->status!=SS_BUILT)
      {
            if(psBuilding->selected)
            {
                  psBuilding->selected = false;
            }
      }

      /* Only add smoke if they're visible and they can 'burn' */
      if(psBuilding->visible[selectedPlayer] && canSmoke(psBuilding))
      {
            const float damage = getStructureDamage(psBuilding);

            // Is there any damage?
            if (damage > 0.)
            {
                  emissionInterval = CalcStructureSmokeInterval(damage);
                  if(gameTime > (psBuilding->lastEmission + emissionInterval))
                  {
                        widthScatter = ((psBuilding->pStructureType->baseWidth) * TILE_UNITS/2)/3;
                        breadthScatter = ((psBuilding->pStructureType->baseBreadth) * TILE_UNITS/2)/3;
                        dv.x = psBuilding->pos.x + widthScatter - rand()%(2*widthScatter);
                        dv.z = psBuilding->pos.y + breadthScatter - rand()%(2*breadthScatter);
                        dv.y = psBuilding->pos.z;
                        dv.y += (psBuilding->sDisplay.imd->max.y * 3) / 4;
                        addEffect(&dv,EFFECT_SMOKE,SMOKE_TYPE_DRIFTING_HIGH,false,NULL,0);
                        psBuilding->lastEmission = gameTime;
                  }
            }
      }

      if ((psBuilding->id % 10) == (frameGetFrameNumber() % 10))
      {
            processVisibility((BASE_OBJECT*)psBuilding);
      }

      /* Update the fire damage data */
      if (psBuilding->inFire & IN_FIRE)
      {
            /* Still in a fire, reset the fire flag to see if we get out this turn */
            psBuilding->inFire = 0;
      }
      else
      {
            /* The fire flag has not been set so we must be out of the fire */
            psBuilding->burnStart = 0;
            psBuilding->burnDamage = 0;
      }

      //check the resistance level of the structure
      iPointsRequired = structureResistance(psBuilding->pStructureType,
            psBuilding->player);
      if (psBuilding->resistance < (SWORD)iPointsRequired)
      {
            //start the resistance increase
            if (psBuilding->lastResistance == ACTION_START_TIME)
            {
                  psBuilding->lastResistance = gameTime;
            }
            //increase over time if low
            if ((gameTime - psBuilding->lastResistance) > RESISTANCE_INTERVAL)
            {
                  psBuilding->resistance++;

                  //in multiplayer, certain structures do not function whilst low resistance
                  if (bMultiPlayer)
                  {
                        resetResistanceLag(psBuilding);
                  }

                  psBuilding->lastResistance = gameTime;
                  //once the resistance is back up reset the last time increased
                  if (psBuilding->resistance >= (SWORD)iPointsRequired)
                  {
                        psBuilding->lastResistance = ACTION_START_TIME;
                  }
            }
      }
      else
      {
            //if selfrepair has been researched then check the health level of the
            //structure once resistance is fully up
            iPointsRequired = structureBody(psBuilding);
            if (selfRepairEnabled(psBuilding->player) && (psBuilding->body < (SWORD)
                  iPointsRequired))
            {
                  //start the self repair off
                  if (psBuilding->lastResistance == ACTION_START_TIME)
                  {
                        psBuilding->lastResistance = gameTime;
                  }

                  /*since self repair, then add half repair points depending on the time delay for the stat*/
                  iPointsToAdd = (repairPoints(asRepairStats + aDefaultRepair[
                        psBuilding->player], psBuilding->player) / 4) * ((gameTime -
                        psBuilding->lastResistance) / (asRepairStats +
                        aDefaultRepair[psBuilding->player])->time);

                  //add the blue flashing effect for multiPlayer
                  if(bMultiPlayer && ONEINTEN)
                  {
                        Vector3i position;
                        Vector3f *point;
                        SDWORD      realY;
                        UDWORD      pointIndex;

                        pointIndex = rand()%(psBuilding->sDisplay.imd->npoints-1);
                        point = &(psBuilding->sDisplay.imd->points[pointIndex]);
                        position.x = psBuilding->pos.x + point->x;
                        realY = structHeightScale(psBuilding) * point->y;
                        position.y = psBuilding->pos.z + realY;
                        position.z = psBuilding->pos.y - point->z;

                        effectSetSize(30);
                        addEffect(&position, EFFECT_EXPLOSION, EXPLOSION_TYPE_SPECIFIED, true,
                              getImdFromIndex(MI_PLASMA), 0);
                  }

                  if (iPointsToAdd)
                  {
                        psBuilding->body = (UWORD)(psBuilding->body + iPointsToAdd);
                        psBuilding->lastResistance = gameTime;
                        if ( psBuilding->body > iPointsRequired)
                        {
                              psBuilding->body = (UWORD)iPointsRequired;
                              psBuilding->lastResistance = ACTION_START_TIME;
                        }
                  }
            }
      }
      CHECK_STRUCTURE(psBuilding);
}


/* Release all resources associated with a structure */
void structureRelease(STRUCTURE *psBuilding)
{
      /* remove animation if present */
      if ( psBuilding->psCurAnim != NULL )
      {
            animObj_Remove( &psBuilding->psCurAnim, psBuilding->psCurAnim->psAnim->uwID );
            psBuilding->psCurAnim = NULL;
      }

      // free up the space used by the functionality array
      free(psBuilding->pFunctionality);
      psBuilding->pFunctionality = NULL;
}


/*
fills the list with Structure that can be built. There is a limit on how many can
be built at any one time. Pass back the number available.
There is now a limit of how many of each type of structure are allowed per mission
*/
UDWORD fillStructureList(STRUCTURE_STATS **ppList, UDWORD selectedPlayer, UDWORD limit)
{
      UDWORD                  inc, count;
      BOOL              researchModule, factoryModule, powerModule;
      STRUCTURE         *psCurr;
      STRUCTURE_STATS   *psBuilding;

      //check to see if able to build research/factory modules
      researchModule = factoryModule = powerModule = false;

      //if currently on a mission can't build factory/research/power/derricks
      if (!missionIsOffworld())
      {
            for (psCurr = apsStructLists[selectedPlayer]; psCurr != NULL; psCurr =
                  psCurr->psNext)
            {
                  if (psCurr->pStructureType->type == REF_RESEARCH && psCurr->status ==
                        SS_BUILT)
                  {
                        researchModule = true;
                  }
                  else if (psCurr->pStructureType->type == REF_FACTORY && psCurr->status ==
                        SS_BUILT)
                  {
                        factoryModule = true;
                  }
                  else if (psCurr->pStructureType->type == REF_POWER_GEN && psCurr->status == SS_BUILT)
                  {
                        powerModule = true;
                  }
            }
      }

      count = 0;
      //set the list of Structures to build
      for (inc=0; inc < numStructureStats; inc++)
      {
            //if the structure is flagged as available, add it to the list
            if (apStructTypeLists[selectedPlayer][inc] & AVAILABLE)
            {
                  //check not built the maximum allowed already
                  if (asStructLimits[selectedPlayer][inc].currentQuantity < asStructLimits[selectedPlayer][inc].limit)
                  {
                        psBuilding = asStructureStats + inc;

                        //don't want corner wall to appear in list
                        if (psBuilding->type == REF_WALLCORNER)
                        {
                              continue;
                        }

                        // Remove the demolish stat from the list for tutorial
                        // tjc 4-dec-98  ...
                        if (bInTutorial)
                        {
                              if (psBuilding->type == REF_DEMOLISH) continue;
                        }

                        //can't build list when offworld
                        if (missionIsOffworld())
                        {
                              if (psBuilding->type == REF_FACTORY ||
                                    psBuilding->type == REF_POWER_GEN ||
                                    psBuilding->type == REF_RESOURCE_EXTRACTOR ||
                                    psBuilding->type == REF_RESEARCH ||
                                    psBuilding->type == REF_CYBORG_FACTORY ||
                                    psBuilding->type == REF_VTOL_FACTORY)
                              {
                                    continue;
                              }
                        }

                        if (psBuilding->type == REF_RESEARCH_MODULE)
                        {
                              //don't add to list if Research Facility not presently built
                              if (!researchModule)
                              {
                                    continue;
                              }
                        }
                        else if (psBuilding->type == REF_FACTORY_MODULE)
                        {
                              //don't add to list if Factory not presently built
                              if (!factoryModule)
                              {
                                    continue;
                              }
                        }
                        else if (psBuilding->type == REF_POWER_MODULE)
                        {
                              //don't add to list if Power Gen not presently built
                              if (!powerModule)
                              {
                                    continue;
                              }
                        }
                        //paranoid check!!
                        if (psBuilding->type == REF_FACTORY ||
                              psBuilding->type == REF_CYBORG_FACTORY ||
                              psBuilding->type == REF_VTOL_FACTORY)
                        {
                              //NEVER EVER EVER WANT MORE THAN 5 FACTORIES
                              if (asStructLimits[selectedPlayer][inc].currentQuantity >= MAX_FACTORY)
                              {
                                    continue;
                              }
                        }
                        //HARD_CODE don't ever want more than one Las Sat structure
                        if (isLasSat(psBuilding) && getLasSatExists(selectedPlayer))
                        {
                              continue;
                        }
                        //HARD_CODE don't ever want more than one Sat Uplink structure
                        if (psBuilding->type == REF_SAT_UPLINK)
                        {
                              if (asStructLimits[selectedPlayer][inc].currentQuantity >= 1)
                              {
                                    continue;
                              }
                        }

                        debug(LOG_NEVER, "adding %s (%x)", psBuilding->pName, apStructTypeLists[selectedPlayer][inc]);
                        ppList[count++] = psBuilding;
                        if (count == limit)
                        {
                              return count;
                        }
                  }
            }
      }
      return count;
}


/* checks that the location is a valid one to build on and sets the outline colour
x and y in tile-coords*/
BOOL validLocation(BASE_STATS *psStats, UDWORD x, UDWORD y, UDWORD player,
                        BOOL bCheckBuildQueue)
{
      STRUCTURE               *psStruct;
      FEATURE                       *psFeat;
      STRUCTURE_STATS         *psBuilding;
      BOOL                    valid = true;
      SDWORD                        i, j;
      UDWORD                        min, max;
      HIGHLIGHT               site;
      FLAG_POSITION           *psCurrFlag;

      //make sure we are not too near map edge and not going to go over it
      if( !tileInsideBuildRange((SDWORD)x, (SDWORD)y) )
      {
            return false;
      }

      psBuilding = (STRUCTURE_STATS *)psStats;

      // initialise the buildsite structure
      // gets rid of the nasty bug when the GLOBAL buildSite was
      // used in here
      // Now now...we can quite easily hack this a bit more...
      if (psStats->ref >= REF_STRUCTURE_START &&
            psStats->ref < (REF_STRUCTURE_START + REF_RANGE))
      {
            site.xTL = (UWORD)x;
            site.yTL = (UWORD)y;
            site.xBR = (UWORD)(x + psBuilding->baseWidth - 1);
            site.yBR = (UWORD)(y + psBuilding->baseBreadth - 1);

            // increase the size of a repair facility
            if (psBuilding->type == REF_REPAIR_FACILITY)
            {
                  site.xTL -= 1;
                  site.yTL -= 1;
                  site.xBR += 1;
                  site.yBR += 1;
            }
            //if we're dragging the wall/defense we need to check along the current dragged size
            if (wallDrag.status != DRAG_INACTIVE
                && (psBuilding->type == REF_WALL || psBuilding->type == REF_DEFENSE)
                && !isLasSat(psBuilding))
            {
                        UWORD    dx,dy;

                        wallDrag.x2 = mouseTileX;
                        wallDrag.y2 = mouseTileY;

                        dx = (UWORD)(abs(mouseTileX - wallDrag.x1));
                        dy = (UWORD)(abs(mouseTileY - wallDrag.y1));

                        if(dx >= dy)
                        {
                              //build in x direction
                              site.xTL = (UWORD)wallDrag.x1;
                              site.xBR = (UWORD)wallDrag.x2;
                              site.yTL = (UWORD)wallDrag.y1;
                              if (dy > 0)
                              {
                                    site.yBR = (UWORD)(site.yTL+1);
                              }
                              else
                              {
                                    site.yBR = (UWORD)(site.yTL);
                              }
                              if (site.xTL > site.xBR)
                              {
                                    dx = site.xBR;
                                    site.xBR = site.xTL;
                                    site.xTL = dx;
                              }
                        }
                        else if(dx < dy)
                        {
                              //build in y direction
                              site.yTL = (UWORD)wallDrag.y1;
                              site.yBR = (UWORD)wallDrag.y2;
                              site.xTL = (UWORD)wallDrag.x1;
                              if (dx > 0)
                              {
                                    site.xBR = (UWORD)(site.xTL+1);
                              }
                              else
                              {
                                    site.xBR = (UWORD)(site.xTL);
                              }
                              if (site.yTL > site.yBR)
                              {
                                    dy = site.yBR;
                                    site.yBR = site.yTL;
                                    site.yTL = dy;
                              }
                        }
            }
      }
      else
      {
            // If the stat's not a structure then assume it's size is 1x1.
            site.xTL = (UWORD)x;
            site.yTL = (UWORD)y;
            site.xBR = (UWORD)x;
            site.yBR = (UWORD)y;
      }

      for (i = site.xTL; i <= site.xBR && valid; i++) {
            for (j = site.yTL; j <= site.yBR && valid; j++) {
                  // Can't build outside of scroll limits.
                  if( ((SDWORD)i < scrollMinX+2) || ((SDWORD)i > scrollMaxX-5) ||
                        ((SDWORD)j < scrollMinY+2) || ((SDWORD)j > scrollMaxY-5)) {
                        valid = false;
                        goto failed;
                  }

                  // check i or j off map.
                  if(i<=2 || j<=2 || i>=(SDWORD)mapWidth-2 || j>=(SDWORD)mapHeight-2)
                  {
                        valid = false;
                        goto failed;
                  }

#ifdef NDEBUG
                  /*God Awful HACK!! - AB 30/04/99 - gets round a problem with
                  UrbanDuel map where an oil derrick cannot be built - when the
                  map has been edited this hack can be removed*/
                  if (psBuilding->type != REF_RESOURCE_EXTRACTOR)
                  {
                        if( gwZoneReachable(gwGetZone(i,j)) == false)
                        {
                              // Can't ever drive there
                              valid = false;
                              goto failed;
                        }
                  }
#endif

                  //don't check tile is visible for placement of a delivery point
                  if (psStats->ref >= REF_STRUCTURE_START &&
                        psStats->ref < (REF_STRUCTURE_START + REF_RANGE))
                  {
                        //allow us to do so in debug mode!
                        if (!getDebugMappingStatus() && !bMultiPlayer)
                        {
                              // Can't build where we haven't been yet.
                              if(!TEST_TILE_VISIBLE(player,mapTile(i,j))) {
                                    valid = false;
                                    goto failed;
                              }
                        }
                  }
            }
      }

      // cant place on top of a delivery point...
      for (psCurrFlag = apsFlagPosLists[selectedPlayer]; psCurrFlag; psCurrFlag = psCurrFlag->psNext)
      {
            ASSERT(psCurrFlag->coords.x != ~0, "flag has invalid position");
            i = map_coord(psCurrFlag->coords.x);
            j = map_coord(psCurrFlag->coords.y);

            if (i >= site.xTL && i <= site.xBR &&
                  j >= site.yTL && j <= site.yBR)
            {
                  valid = false;
                  goto failed;
            }
      }

      // can't build next to a repair facility
      for (psStruct = apsStructLists[player]; psStruct; psStruct = psStruct->psNext)
      {
            if (psStruct->pStructureType->type == REF_REPAIR_FACILITY)
            {
                  // get the top left of the struct
                  i = map_coord(psStruct->pos.x) - 1;
                  j = map_coord(psStruct->pos.y) - 1;

                  // see if the x extents overlap
                  if ((site.xTL >= i && site.xTL <= (i+2)) ||
                        (site.xBR >= i && site.xBR <= (i+2)))
                  {
                        // now see if y extents overlap
                        if ((site.yTL >= j && site.yTL <= (j+2)) ||
                              (site.yBR >= j && site.yBR <= (j+2)))
                        {
                              valid = false;
                              goto failed;
                        }
                  }
            }
      }

      if (psStats->ref >= REF_STRUCTURE_START &&
            psStats->ref < (REF_STRUCTURE_START + REF_RANGE))
      {
            MAPTILE     *psTile;

            switch(psBuilding->type)
            {
                  case REF_DEMOLISH:
                        break;
                  case NUM_DIFF_BUILDINGS:
                  case REF_BRIDGE:
                        ASSERT(!"invalid structure type", "validLocation: Bad structure type %u", psBuilding->type);
                        break;
                  case REF_HQ:
                  case REF_FACTORY:
                  case REF_LAB:
                  case REF_RESEARCH:
                  case REF_POWER_GEN:
                  case REF_WALL:
                  case REF_WALLCORNER:
                  case REF_DEFENSE:
                  case REF_REPAIR_FACILITY:
                  case REF_COMMAND_CONTROL:
                  case REF_CYBORG_FACTORY:
                  case REF_VTOL_FACTORY:
                  case REF_BLASTDOOR:
                  case REF_REARM_PAD:
                  case REF_MISSILE_SILO:
                  case REF_SAT_UPLINK:
                        /*need to check each tile the structure will sit on is not water*/
                        for (i = site.xTL; i <= site.xBR && valid; i++)
                        {
                              for (j = site.yTL; j <= site.yBR && valid; j++)
                              {
                                    psTile = mapTile(i,j);
                                    if ((terrainType(psTile) == TER_WATER) ||
                                          (terrainType(psTile) == TER_CLIFFFACE) )
                                    {
                                          valid = false;
                                    }
                              }
                        }
                        //don't bother checking if already found a problem
                        if (valid)
                        {
                              //check not within landing zone
                              for (i = site.xTL; i <= site.xBR && valid; i++)
                              {
                                    for (j = site.yTL; j <= site.yBR && valid; j++)
                                    {
                                          if (withinLandingZone(i, j))
                                          {
                                                valid = false;
                                          }
                                    }
                              }
                        }

                        // special droid/max-min test for repair facility
                        if ( valid && (psBuilding->type == REF_REPAIR_FACILITY))
                        {
                              getTileMaxMin(x, y, &max, &min);
                              if ((max - min) > MAX_INCLINE)
                              {
                                    valid = false;
                              }
                              if (valid &&
                                    !noDroid(x,y))
                              {
                                    valid = false;
                              }
                        }

                        if (valid &&      // only do if necessary
                              (psBuilding->type != REF_REPAIR_FACILITY))
                        {
                              for (i = site.xTL; i <= site.xBR && valid; i++)
                              {
                                    for (j = site.yTL; j <= site.yBR && valid; j++)
                                    {
                                          // This really needs to check to see if the droid that's in the way is the droid that wants to build
                                          // in which case it should'nt invalidate the location.
                                          if(noDroid(i,j) == false)
                                          {
                                                valid = false;
                                          }
                                    }
                              }
                        }

                        //walls/defensive structures can be built along any ground
                        if (valid &&      // only do if necessary
                              (!(psBuilding->type == REF_REPAIR_FACILITY ||
                              psBuilding->type == REF_DEFENSE ||
                              psBuilding->type == REF_WALL)))
                        {
                              /*cannot build on ground that is too steep*/
                              min = 0;
                              max = 0;
                              for (i = site.xTL; i <= site.xBR && valid; i++)
                              {
                                    for (j = site.yTL; j <= site.yBR && valid; j++)
                                    {
                                          getTileMaxMin(i, j, &max, &min);
                                          if ((max - min) > MAX_INCLINE)
                                          {
                                                valid = false;
                                          }
                                    }
                              }
                        }
                        //don't bother checking if already found a problem
                        if (valid)
                        {
                              //on PC - defence structures can be built next to anything now- AB 22/09/98
                              //and the Missile_Silo (special case) - AB 01/03/99
                              if (!(psBuilding->type == REF_DEFENSE ||
                                    psBuilding->type == REF_WALL ||
                                    psBuilding->type == REF_WALLCORNER ||
                                    psBuilding->type == REF_MISSILE_SILO))
                              {
                                    /*need to check there is one tile between buildings*/
                                    for (i = (UWORD)(site.xTL-1); i <= (UWORD)(site.xBR+1); i++)
                                    {
                                          for (j = (UWORD)(site.yTL-1); j <= (UWORD)(site.yBR+1); j++)
                                          {
                                                //skip the actual area the structure will cover
                                                if (i < site.xTL || i > site.xBR ||
                                                      j < site.yTL || j > site.yBR)
                                                {
                                                      if (TileHasStructure(mapTile(i,j)))
                                                      {
                                                            psStruct = getTileStructure(i,j);
                                                            if (psStruct)
                                                            {
                                                                  //you can build anything next to a defensive structure
                                                                  if ((psStruct->pStructureType->type != REF_DEFENSE) &&
                                                                        (psStruct->pStructureType->type != REF_WALL)    &&
                                                                        (psStruct->pStructureType->type != REF_WALLCORNER)
                                                                  )
                                                                  {
                                                                        //Walls can be built next to walls and defenses - AB 03/03/99
                                                                        if (psBuilding->type == REF_WALL)
                                                                        {
                                                                              if (!(psStruct->pStructureType->type == REF_WALL ||
                                                                              psStruct->pStructureType->type == REF_WALLCORNER))
                                                                              {
                                                                                    valid = false;
                                                                              }
                                                                        }
                                                                        else
                                                                        {
                                                                              valid = false;
                                                                        }
                                                                  }
                                                                  else  // is a defense.
                                                                  {           // skirmish players don't build defensives next to each other.(route hack)
                                                                        if( bMultiPlayer && game.type == SKIRMISH && !isHumanPlayer(player) )
                                                                        {
                                                                              valid = false;
                                                                        }
                                                                  }
                                                            }
                                                      }
                                                      //cannot build within one tile of a oil resource
                                                      if(TileHasFeature(mapTile(i,j)))
                                                      {
                                                            psFeat = getTileFeature(i,j);
                                                            if (psFeat && psFeat->psStats->subType ==
                                                                  FEAT_OIL_RESOURCE)
                                                            {
                                                                  valid = false;
                                                            }
                                                      }
                                                }
                                          }
                                    }
                              }
                        }
                        //don't bother checking if already found a problem
                        if (valid)
                        {
                              /*need to check each tile the structure will sit on*/
                              for (i = site.xTL; i <= site.xBR && valid; i++)
                              {
                                    for (j = site.yTL; j <= site.yBR && valid; j++)
                                    {
                                          psTile = mapTile(i,j);
                                          if (TileIsOccupied(psTile))
                                          {
                                                if (TileHasWall(psTile)
                                                    && (psBuilding->type == REF_DEFENSE ||
-                                                                       psBuilding->type == REF_WALL))
                                                {
                                                      psStruct = getTileStructure(i,j);
                                                      if (psStruct != NULL &&
                                                            psStruct->player != player)
                                                      {
                                                            valid = false;
                                                      }
                                                }
                                                else
                                                {
                                                      valid = false;
                                                }
                                          }
                                    }
                              }
                        }
                        break;
                  case REF_FACTORY_MODULE:
                        valid = false;
                        if(TileHasStructure(mapTile(x,y)))
                        {
                              psStruct = getTileStructure(x,y);
                              if(psStruct && (
                                    psStruct->pStructureType->type == REF_FACTORY ||
                                    psStruct->pStructureType->type == REF_VTOL_FACTORY) &&
                                    psStruct->status == SS_BUILT)
                              {
                                    valid = true;
                              }
                        }
                        break;
                  case REF_RESEARCH_MODULE:
                        valid = false;
                        //check that there is a research facility at the location
                        if(TileHasStructure(mapTile(x,y)))
                        {
                              psStruct = getTileStructure(x,y);
                              if(psStruct && psStruct->pStructureType->type == REF_RESEARCH &&
                                    psStruct->status == SS_BUILT)
                              {
                                    valid = true;
                              }
                        }

                        break;
                  case REF_POWER_MODULE:
                        valid = false;
                        if(TileHasStructure(mapTile(x,y)))
                        {
                              psStruct = getTileStructure(x,y);
                              if(psStruct && psStruct->pStructureType->type == REF_POWER_GEN &&
                                    psStruct->status == SS_BUILT)
                              {
                                    valid = true;
                              }
                        }
                        break;
                  case REF_RESOURCE_EXTRACTOR:
                        valid = false;
                        //check that there is a oil resource at the location
                        if(TileHasFeature(mapTile(x,y)))
                        {
                              psFeat = getTileFeature(x,y);
                              if(psFeat && psFeat->psStats->subType == FEAT_OIL_RESOURCE)
                              {
                                    valid = true;
                              }
                        }
                        break;
            }
            //if setting up a build queue need to check against future sites as well - AB 4/5/99
            if (ctrlShiftDown() && player == selectedPlayer && bCheckBuildQueue)
            {
                  DROID   *psDroid;
                  SDWORD  order,left,right,up,down,size;
                  BOOL    validCombi;

                  //defense and missile silo's can be built next to anything so don't need to check
                  if (!(psBuilding->type == REF_DEFENSE || psBuilding->type ==
                        REF_MISSILE_SILO))
                  {
                        for (psDroid = apsDroidLists[player]; psDroid; psDroid = psDroid->psNext)
                        {
                              //once its invalid stop checking
                              if (valid == false)
                              {
                                    break;
                              }
                              if (psDroid->droidType == DROID_CONSTRUCT ||
                                    psDroid->droidType == DROID_CYBORG_CONSTRUCT)
                              {
                                    //look thru' the list of orders to see if more building sites
                                    for (order = 0; order < psDroid->listSize; order++)
                                    {
                                          if (psDroid->asOrderList[order].order == DORDER_BUILD)
                                          {
                                                validCombi = false;
                                                if (((STRUCTURE_STATS *)psDroid->asOrderList[order].
                                                      psOrderTarget)->type == REF_DEFENSE ||
                                                      ((STRUCTURE_STATS *)psDroid->asOrderList[order].
                                                      psOrderTarget)->type == REF_MISSILE_SILO)
                                                {
                                                      validCombi = true;
                                                }
                                                //walls can be built next to walls and defence
                                                if ((psBuilding->type == REF_WALL || psBuilding->type == REF_WALLCORNER)
                              && (((STRUCTURE_STATS *)psDroid->asOrderList[order].psOrderTarget)->type == REF_WALL
                              || ((STRUCTURE_STATS *)psDroid->asOrderList[order].psOrderTarget)->type == REF_WALLCORNER))
                                                {
                                                      validCombi = true;
                                                }
                                                //don't bother checking if valid combination of building types
                                                if (!validCombi)
                                                {
                                                      /*need to check there is one tile between buildings*/
                                                      //check if any corner is within the build site
                                                      size = ((STRUCTURE_STATS *)psDroid->asOrderList[order].
                                                            psOrderTarget)->baseWidth;
                                                      left = map_coord(psDroid->asOrderList[order].x) - size/2;
                                                      right = left + size;
                                                      size = ((STRUCTURE_STATS *)psDroid->asOrderList[order].
                                                            psOrderTarget)->baseBreadth;
                                                      up = map_coord(psDroid->asOrderList[order].y) - size/2;
                                                      down = up + size;
                                                      // increase the size of a repair facility
                                                      if (((STRUCTURE_STATS *)psDroid->asOrderList[
                                                            order].psOrderTarget)->type == REF_REPAIR_FACILITY)
                                                      {
                                                            left -= 1;
                                                            up -= 1;
                                                            right += 1;
                                                            down += 1;
                                                      }
                                                      if (((left > site.xTL-1 && left <= site.xBR+1) &&
                                                            (up > site.yTL-1 && up <= site.yBR+1)) ||
                                                            ((right > site.xTL-1 && right <= site.xBR+1) &&
                                                            (up > site.yTL-1 && up <= site.yBR+1)) ||
                                                            ((left > site.xTL-1 && left <= site.xBR+1) &&
                                                            (down > site.yTL-1 && down <= site.yBR+1)) ||
                                                            ((right > site.xTL-1 && right <= site.xBR+1) &&
                                                            (down > site.yTL-1 && down <= site.yBR+1)))
                                                      {
                                                            valid = false;
                                                            break;
                                                      }
                                                }
                                          }
                                    }
                              }
                        }
                  }
            }
      }
      else
      {
            // not positioning a structure
            valid = true;
            if (fpathGroundBlockingTile(x,y))
            {
                  valid = false;
            }
      }

failed:
      if (!valid)
      {
            // Only set the hilight colour if it's the selected player.
            if(player == selectedPlayer)
            {
                  outlineTile = false;
            }

            return false;
      }

      // Only set the hilight colour if it's the selected player.
      if (player == selectedPlayer)
      {
            outlineTile = true;
      }

      return true;
}

/*
for a new structure, find a location along an edge which the droid can get
to and return this as the destination for the droid.
*/
BOOL getDroidDestination(BASE_STATS *psStats, UDWORD structX,
      UDWORD structY, UDWORD *pDroidX, UDWORD *pDroidY)
{
      UWORD                   start;
      UDWORD                        structTileX, structTileY, width = 0, breadth = 0;

      if (StatIsStructure(psStats))
      {
            width = ((STRUCTURE_STATS *)psStats)->baseWidth;
            breadth = ((STRUCTURE_STATS *)psStats)->baseBreadth;
      }
      else if (StatIsFeature(psStats))
      {
            width = ((FEATURE_STATS *)psStats)->baseWidth;
            breadth = ((FEATURE_STATS *)psStats)->baseBreadth;
      }

      //get a random starting place 0=top left
      start = (UWORD)(rand() % ((width + breadth) * 2));

      //search in a clockwise direction around the structure from the starting point
      if (start == 0 || start < width)
      {
            //top side first
            structTileX = map_coord(structX);
            structTileY = map_coord(structY) - 1;
            if (checkWidth(width, structTileX, structTileY, pDroidX, pDroidY))
            {
                  return true;
            }
            structTileX += width;
            structTileY += 1;

            if (checkLength(breadth, structTileX, structTileY,pDroidX, pDroidY))
            {
                  return true;
            }
            structTileX = map_coord(structX);
            structTileY += breadth;

            if (checkWidth(width, structTileX, structTileY, pDroidX, pDroidY))
            {
                  return true;
            }
            structTileX -= 1;
            structTileY = map_coord(structY);

            if (checkLength(breadth, structTileX, structTileY, pDroidX, pDroidY))
            {
                  return true;
            }
      }
      else if (start == width || start < (width + breadth))
      {
            //right side first
            structTileX = (map_coord(structX)) + width;
            structTileY = map_coord(structY);

            if (checkLength(breadth, structTileX, structTileY,pDroidX, pDroidY))
            {
                  return true;
            }
            structTileX = map_coord(structX);
            structTileY += breadth;

            if (checkWidth(width, structTileX, structTileY, pDroidX, pDroidY))
            {
                  return true;
            }
            structTileX -= 1;
            structTileY = map_coord(structY);

            if (checkLength(breadth, structTileX, structTileY, pDroidX, pDroidY))
            {
                  return true;
            }
            structTileX += 1;
            structTileY -= 1;

            if (checkWidth(width, structTileX, structTileY, pDroidX, pDroidY))
            {
                  return true;
            }
      }
      else if (start == (width + breadth) || start < (width * breadth))
      {
            //bottom first
            structTileX = map_coord(structX);
            structTileY = map_coord(structY) + breadth;

            if (checkWidth(width, structTileX, structTileY, pDroidX, pDroidY))
            {
                  return true;
            }
            structTileX -= 1;
            structTileY = map_coord(structY);

            if (checkLength(breadth, structTileX, structTileY, pDroidX, pDroidY))
            {
                  return true;
            }
            structTileX += 1;
            structTileY -= 1;

            if (checkWidth(width, structTileX, structTileY, pDroidX, pDroidY))
            {
                  return true;
            }
            structTileX += width;
            structTileY += 1;

            if (checkLength(breadth, structTileX, structTileY,pDroidX, pDroidY))
            {
                  return true;
            }
      }
      else
      {
            //left side first
            structTileX = (map_coord(structX)) - 1;
            structTileY = map_coord(structY);

            if (checkLength(breadth, structTileX, structTileY, pDroidX, pDroidY))
            {
                  return true;
            }
            structTileX += 1;
            structTileY -= 1;

            if (checkWidth(width, structTileX, structTileY, pDroidX, pDroidY))
            {
                  return true;
            }
            structTileX += width;
            structTileY += 1;

            if (checkLength(breadth, structTileX, structTileY,pDroidX, pDroidY))
            {
                  return true;
            }
            structTileX = map_coord(structX);
            structTileY += breadth;

            if (checkWidth(width, structTileX, structTileY, pDroidX, pDroidY))
            {
                  return true;
            }
      }

      //not found a valid location so return false
      return false;
}

/* check along the width of a structure for an empty space */
BOOL checkWidth(UDWORD maxRange, UDWORD x, UDWORD y, UDWORD *pDroidX, UDWORD *pDroidY)
{
      UDWORD            side;

      for (side = 0; side < maxRange; side++)
      {
            if( x+side < mapWidth && y < mapHeight && !TileIsOccupied(mapTile(x+side,y)) )
            {
                  *pDroidX = world_coord(x + side);
                  *pDroidY = world_coord(y);

                  ASSERT( worldOnMap(*pDroidX,*pDroidY),"checkWidth : Insane droid position" );

                  return true;
            }
      }

      return false;
}

/* check along the length of a structure for an empty space */
BOOL checkLength(UDWORD maxRange, UDWORD x, UDWORD y, UDWORD *pDroidX, UDWORD *pDroidY)
{
      UDWORD            side;

      for (side = 0; side < maxRange; side++)
      {
            if(y+side < mapHeight && x < mapWidth && !TileIsOccupied(mapTile(x,y+side)) )
            {
                  *pDroidX = world_coord(x);
                  *pDroidY = world_coord(y + side);

                  ASSERT( worldOnMap(*pDroidX,*pDroidY),"checkHeight : Insane droid position" );

                  return true;
            }
      }

      return false;
}


//remove a structure from the map
static void removeStructFromMap(STRUCTURE *psStruct)
{
      UDWORD            i,j;
      UDWORD            mapX, mapY;
      MAPTILE           *psTile;

      /* set tiles drawing */
      mapX = map_coord(psStruct->pos.x - psStruct->pStructureType->baseWidth * TILE_UNITS / 2);
      mapY = map_coord(psStruct->pos.y - psStruct->pStructureType->baseBreadth * TILE_UNITS / 2);
      for (i = 0; i < psStruct->pStructureType->baseWidth; i++)
      {
            for (j = 0; j < psStruct->pStructureType->baseBreadth; j++)
            {
                  psTile = mapTile(mapX+i, mapY+j);
                  psTile->psObject = NULL;
                  CLEAR_TILE_NOTBLOCKING(psTile);
            }
      }
}

// remove a structure from a game without any visible effects
// bDestroy = true if the object is to be destroyed
// (for example used to change the type of wall at a location)
BOOL removeStruct(STRUCTURE *psDel, BOOL bDestroy)
{
      BOOL        resourceFound = false;
      UBYTE       mask;
      FACTORY           *psFactory;
      SDWORD            cluster;
      FLAG_POSITION     *psAssemblyPoint=NULL;

      ASSERT(psDel != NULL, "invalid structure pointer");

      if (bDestroy)
      {
            removeStructFromMap(psDel);
      }

      //tell the power system its gone
      powerDestroyObject((BASE_OBJECT *)psDel);

      if (bDestroy)
      {
            //if the structure is a resource extractor, need to put the resource back in the map
            /*ONLY IF ANY POWER LEFT - HACK HACK HACK!!!! OIL POOLS NEED TO KNOW
            HOW MUCH IS THERE && NOT RES EXTRACTORS */
            if (psDel->pStructureType->type == REF_RESOURCE_EXTRACTOR)
            {
                  if (psDel->pFunctionality->resourceExtractor.power)
                  {
                        buildFeature(&asFeatureStats[oilResFeature], psDel->pos.x, psDel->pos.y, false);
                        resourceFound = true;
                  }
            }
      }

      if (psDel->pStructureType->type == REF_RESOURCE_EXTRACTOR)
      {
            //tell associated Power Gen
            releaseResExtractor(psDel);
      }

      if (psDel->pStructureType->type == REF_POWER_GEN)
      {
            //tell associated Res Extractors
            releasePowerGen(psDel);
      }

      //check for a research topic currently under way
      if (psDel->pStructureType->type == REF_RESEARCH)
      {
            if (psDel->pFunctionality->researchFacility.psSubject)
            {
                  //cancel the topic
                  cancelResearch(psDel);
            }
      }

      //subtract one from the structLimits list so can build another - don't allow to go less than zero!
      if (asStructLimits[psDel->player][psDel->pStructureType - asStructureStats].currentQuantity)
      {
            asStructLimits[psDel->player][psDel->pStructureType - asStructureStats].currentQuantity--;
      }

      //if it is a factory - need to reset the factoryNumFlag
      if (StructIsFactory(psDel))
      {
            psFactory = &psDel->pFunctionality->factory;

            //need to initialise the production run as well
            cancelProduction(psDel);

            psAssemblyPoint = psFactory->psAssemblyPoint;
      }
      else if (psDel->pStructureType->type == REF_REPAIR_FACILITY)
      {
            psAssemblyPoint = psDel->pFunctionality->repairFacility.psDeliveryPoint;
      }

      if (psAssemblyPoint != NULL)
      {
            mask = (UBYTE)(1 << psAssemblyPoint->factoryInc);
            factoryNumFlag[psDel->player][psAssemblyPoint->factoryType] ^= mask;

            //need to cancel the repositioning of the DP if selectedPlayer and currently moving
            if (psDel->player == selectedPlayer)
            {
                  //if currently trying to place a DP
                  if (tryingToGetLocation())
                  {
                        //need to check if this factory's DP is trying to be re-positioned
                        if (psAssemblyPoint == sBuildDetails.UserData)
                        {
                              kill3DBuilding();
                        }
                  }
            }
      }

      // remove the structure from the grid
      gridRemoveObject((BASE_OBJECT *)psDel);

      // remove the structure from the cluster
      cluster = psDel->cluster;
      clustRemoveObject((BASE_OBJECT *)psDel);

      if (bDestroy)
      {
            killStruct(psDel);
      }

      /* remove animation if present */
      if ( psDel->psCurAnim != NULL )
      {
            animObj_Remove( &psDel->psCurAnim, psDel->psCurAnim->psAnim->uwID );
            psDel->psCurAnim = NULL;
      }

      clustUpdateCluster((BASE_OBJECT *)apsStructLists[psDel->player], cluster);

      if(psDel->player==selectedPlayer)
      {
            intRefreshScreen();
      }

      return resourceFound;
}

/* Remove a structure */
BOOL destroyStruct(STRUCTURE *psDel)
{
      UDWORD                  mapX, mapY, width,breadth;
      UDWORD                  i;
      UDWORD                  widthScatter,breadthScatter,heightScatter;
      Vector3i pos;
      BOOL              resourceFound = false;
      MAPTILE                 *psTile;
      BOOL              bMinor = false;

      CHECK_STRUCTURE(psDel);

      if (bMultiPlayer)
      {
            SendDestroyStructure(psDel);
      }

//---------------------------------------
      /* Only add if visible */
      if(psDel->visible[selectedPlayer])
      {
            /* Firstly, are we dealing with a wall section */
            if(psDel->pStructureType->type == REF_WALL || psDel->pStructureType->type == REF_WALLCORNER)
            {
                  bMinor = true;
            }

//---------------------------------------  Do we add immediate explosions?
            /* Set off some explosions, but not for walls */
            /* First Explosions */
            widthScatter = TILE_UNITS;
            breadthScatter = TILE_UNITS;
            heightScatter = TILE_UNITS;
            for(i=0; i<(UDWORD)(bMinor ? 2 : 4); i++) // only add two for walls - gets crazy otherwise
            {
                  pos.x = psDel->pos.x + widthScatter - rand()%(2*widthScatter);
                  pos.z = psDel->pos.y + breadthScatter - rand()%(2*breadthScatter);
                  pos.y = psDel->pos.z + 32 + rand()%heightScatter;
                  addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_MEDIUM,false,NULL,0);
            }

            /* Get coordinates for everybody! */
            pos.x = psDel->pos.x;
            pos.z = psDel->pos.y;
            pos.y = map_Height((UWORD)pos.x,(UWORD)pos.z);

//--------------------------------------- Do we add a fire?
            // Set off a fire, provide dimensions for the fire
            if(bMinor)
            {
                  effectGiveAuxVar(world_coord(psDel->pStructureType->baseWidth) / 4);
            }
            else
            {
                  effectGiveAuxVar(world_coord(psDel->pStructureType->baseWidth) / 3);
            }
            if(bMinor)                                       // walls
            {
                  /* Give a duration */
                  effectGiveAuxVarSec(1000);
                  /* Normal fire - no smoke */
                  addEffect(&pos,EFFECT_FIRE,FIRE_TYPE_LOCALISED,false,NULL,0);

            }
            else if(psDel->pStructureType->type == REF_RESOURCE_EXTRACTOR) // oil resources
            {
                  /* Oil resources burn AND puff out smoke AND for longer*/
                  effectGiveAuxVarSec(60000);
                  addEffect(&pos,EFFECT_FIRE,FIRE_TYPE_SMOKY,false,NULL,0);
            }
            else  // everything else
            {
                  /* Give a duration */
                  effectGiveAuxVarSec(10000);
                  addEffect(&pos,EFFECT_FIRE,FIRE_TYPE_LOCALISED,false,NULL,0);
            }

//--------------------------------------- Do we add a destruction seq, and if so, which?
            /* Power stations have their own desctruction sequence */
            if(psDel->pStructureType->type == REF_POWER_GEN)
            {
                  addEffect(&pos,EFFECT_DESTRUCTION,DESTRUCTION_TYPE_POWER_STATION,false,NULL,0);
                  pos.y += SHOCK_WAVE_HEIGHT;
                  addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_SHOCKWAVE,false,NULL,0);
                  // give some power back to the player.
                  addPower(psDel->player, structPowerToBuild(psDel));
                  //if it had a module attached, need to add the power for the base struct as well
                  if (psDel->pFunctionality->powerGenerator.capacity)
                  {
                        addPower(psDel->player, psDel->pStructureType->powerToBuild);
                  }
            }
            /* As do wall sections */
            else if(bMinor)
            {
                  addEffect(&pos,EFFECT_DESTRUCTION,DESTRUCTION_TYPE_WALL_SECTION,false,NULL,0);
            }
            else // and everything else goes here.....
            {
                  addEffect(&pos,EFFECT_DESTRUCTION,DESTRUCTION_TYPE_STRUCTURE,false,NULL,0);
            }

//--------------------------------------- Start an earthquake...!
            /* shake the screen if we're near enough */
            if(clipXY(pos.x,pos.z))
            {
                  shakeStart();
            }

//--------------------------------------- And finally, add a boom sound!!!!
            /* and add a sound effect */
            audio_PlayStaticTrack( psDel->pos.x, psDel->pos.y, ID_SOUND_EXPLOSION );
      }
//---------------------------------------------------------------------------------------

      resourceFound = removeStruct(psDel, true);

      //once a struct is destroyed - it leaves a wrecked struct FEATURE in its place
      // Wall's don't leave wrecked features
      if(psDel->visible[selectedPlayer])
      {
            if (!resourceFound && !(psDel->pStructureType->type == REF_WALL) &&
                  !(psDel->pStructureType->type == REF_WALLCORNER))
            {
                  mapX = map_coord(psDel->pos.x - psDel->pStructureType->baseWidth * TILE_UNITS / 2);
                  mapY = map_coord(psDel->pos.y - psDel->pStructureType->baseBreadth * TILE_UNITS / 2);
                  for (width = 0; width < psDel->pStructureType->baseWidth; width++)
                  {
                        for (breadth = 0; breadth < psDel->pStructureType->baseBreadth; breadth++)
                        {
                              psTile = mapTile(mapX+width,mapY+breadth);
                              if(TEST_TILE_VISIBLE(selectedPlayer,psTile))
                              {
                                    psTile->illumination /= 2;
                                    if(psTile->bMaxed && psTile->level > 0) //only do one's already seen
                                    {
                                          psTile->level/=2;
                                    }
                              }
                        }
                  }
            }
      }

      /* remove animation if present */
      if ( psDel->psCurAnim != NULL )
      {
            animObj_Remove( &psDel->psCurAnim, psDel->psCurAnim->psAnim->uwID );
            psDel->psCurAnim = NULL;
      }

      // updates score stats only if not wall
      if(psDel->pStructureType->type != REF_WALL && psDel->pStructureType->type != REF_WALLCORNER)
      {
            if(psDel->player == selectedPlayer)
            {
                  scoreUpdateVar(WD_STR_LOST);
            }
            else
            {
                  scoreUpdateVar(WD_STR_KILLED);
            }
      }

      return true;
}


/* For now all this does is work out what height the terrain needs to be set to
An actual foundation structure may end up being placed down
The x and y passed in are the CENTRE of the structure*/
SWORD buildFoundation(STRUCTURE_STATS *psStructStats, UDWORD x, UDWORD y)
{
      UDWORD      width, breadth;
      UDWORD      startX, startY;
      SWORD height,foundationMin, foundationMax;

      startX = map_coord(x) - psStructStats->baseWidth/2;
      startY = map_coord(y) - psStructStats->baseBreadth/2;

      //check the terrain is the correct type return -1 if not

      //shouldn't need to do this but doesn't take long hey?!
      /*check if there is a structure next to the new one - return the height of the
      structure if found*/
      for (breadth = 0; breadth <= psStructStats->baseBreadth; breadth++)
      {
            for (width = 0; width <= psStructStats->baseWidth; width++)
            {
                  if(TileHasStructure(mapTile(startX+width,startY+breadth)))
                  {
                        return((SWORD)map_TileHeight(startX+width,startY+breadth));
                  }
            }
      }

      //may also have to check that overlapping terrain can be set to the average height
      //eg water - don't want it to 'flow' into the structure if this effect is coded!

      startX = map_coord(x) - psStructStats->baseWidth/2;
      startY = map_coord(y) - psStructStats->baseBreadth/2;

      //initialise the starting values so they get set in loop
      foundationMin = TILE_MAX_HEIGHT;
      foundationMax = TILE_MIN_HEIGHT;

      for (breadth = 0; breadth <= psStructStats->baseBreadth; breadth++)
      {
            for (width = 0; width <= psStructStats->baseWidth; width++)
            {
                  height = map_TileHeight(startX+width,startY+breadth);
                  if (foundationMin > height)
                  {
                        foundationMin = height;
                  }
                  if (foundationMax < height)
                  {
                        foundationMax = height;
                  }
            }
      }
      //return the average of max/min height
      return ((SWORD)((foundationMin + foundationMax) / 2));
}


/* gets a structure stat from its name - relies on the name being unique (or it will
return the first one it finds!! */
SDWORD getStructStatFromName(char *pName)
{
      UDWORD                        inc;
      STRUCTURE_STATS         *psStat;

      for (inc = 0; inc < numStructureStats; inc++)
      {
            psStat = &asStructureStats[inc];
            if (!strcmp(psStat->pName, pName))
            {
                  return inc;
            }
      }
      return -1;
}


/*check to see if the structure is 'doing' anything  - return true if idle*/
BOOL  structureIdle(STRUCTURE *psBuilding)
{
      BASE_STATS        *pSubject = NULL;

      CHECK_STRUCTURE(psBuilding);

      if (psBuilding->pFunctionality == NULL)
            return true;

      //determine the Subject
      switch (psBuilding->pStructureType->type)
      {
            case REF_RESEARCH:
            {
                  pSubject = psBuilding->pFunctionality->researchFacility.psSubject;
                  break;
            }
            case REF_FACTORY:
            case REF_CYBORG_FACTORY:
            case REF_VTOL_FACTORY:
            {
                  pSubject = psBuilding->pFunctionality->factory.psSubject;
                  break;
            }
            default:
                  break;
      }

      if (pSubject != NULL)
      {
            return false;
      }

      return true;
}


/*checks to see if any structure exists of a specified type with a specified status */
BOOL checkStructureStatus( STRUCTURE_STATS *psStats, UDWORD player, UDWORD status)
{
      STRUCTURE   *psStructure;
      BOOL        found = false;

      for (psStructure = apsStructLists[player]; psStructure != NULL;
            psStructure = psStructure->psNext)
      {
            if (psStructure->pStructureType->type == psStats->type)
            {
                  //need to check if THIS instance of the type has the correct status
                  if (psStructure->status == status)
                  {
                        found = true;
                        break;
                  }
            }
      }
      return found;
}


/*checks to see if a specific structure type exists -as opposed to a structure
stat type*/
BOOL checkSpecificStructExists(UDWORD structInc, UDWORD player)
{
      STRUCTURE   *psStructure;
      BOOL        found = false;

      ASSERT( structInc < numStructureStats,
            "checkSpecificStructExists: invalid structure inc" );

      for (psStructure = apsStructLists[player]; psStructure != NULL;
            psStructure = psStructure->psNext)
      {
            if (psStructure->status == SS_BUILT)
            {
                  if ((psStructure->pStructureType->ref - REF_STRUCTURE_START) ==
                        structInc)
                  {
                        found = true;
                        break;
                  }
            }
      }
      return found;
}


/*finds a suitable position for the assembly point based on one passed in*/
void findAssemblyPointPosition(UDWORD *pX, UDWORD *pY, UDWORD player)
{
      //set up a dummy stat pointer
      STRUCTURE_STATS     sStats;
      UDWORD              passes = 0;
      SDWORD                  i,j,startX,endX,startY,endY;

      sStats.ref = 0;
      sStats.baseWidth = 1;
      sStats.baseBreadth = 1;

      /* Initial box dimensions and set iteration count to zero */
      startX = endX = *pX;    startY = endY = *pY;
      passes = 0;

      //if the value passed in is not a valid location - find one!
      if (!validLocation((BASE_STATS *)&sStats, *pX, *pY, player, false))
      {
            /* Keep going until we get a tile or we exceed distance */
            while(passes < LOOK_FOR_EMPTY_TILE)
            {
                  /* Process whole box */
                  for(i = startX; i <= endX; i++)
                  {
                        for(j = startY; j<= endY; j++)
                        {
                              /* Test only perimeter as internal tested previous iteration */
                              if(i==startX || i==endX || j==startY || j==endY)
                              {
                                    /* Good enough? */
                                    if(validLocation((BASE_STATS *)&sStats, i, j, player, false))
                                    {
                                          /* Set exit conditions and get out NOW */
                                          *pX = i;
                                          *pY = j;
                                          //jump out of the loop
                                          return;
                                    }
                              }
                        }
                  }
                  /* Expand the box out in all directions - off map handled by validLocation() */
                  startX--; startY--;     endX++;     endY++;     passes++;
            }
      }
      else
      {
            //the first location was valid
            return;
      }
      /* If we got this far, then we failed - passed in values will be unchanged */
      ASSERT(!"unable to find a valid location", "findAssemblyPointPosition: unable to find a valid location!");
}


/*sets the point new droids go to - x/y in world coords for a Factory
bCheck is set to true for initial placement of the Assembly Point*/
void setAssemblyPoint(FLAG_POSITION *psAssemblyPoint, UDWORD x, UDWORD y,
                              UDWORD player, BOOL bCheck)
{
      ASSERT( psAssemblyPoint != NULL,
            "setAssemblyPoint: invalid AssemblyPoint pointer" );

      //check its valid
      x = map_coord(x);
      y = map_coord(y);
      if (bCheck)
      {
            findAssemblyPointPosition(&x, &y, player);
      }
      //add half a tile so the centre is in the middle of the tile
      x = world_coord(x) + TILE_UNITS/2;
      y = world_coord(y) + TILE_UNITS/2;

      psAssemblyPoint->coords.x = x;
      psAssemblyPoint->coords.y = y;

      // Deliv Point sits at the height of the tile it's centre is on + arbitary amount!
      psAssemblyPoint->coords.z = map_Height(x, y) + ASSEMBLY_POINT_Z_PADDING;
}


/*sets the factory Inc for the Assembly Point*/
void setFlagPositionInc(FUNCTIONALITY* pFunctionality, UDWORD player, UBYTE factoryType)
{
      UBYTE             inc;
      UBYTE             mask = 1;
      FACTORY                 *psFactory;
      REPAIR_FACILITY *psRepair;

      ASSERT( player < MAX_PLAYERS, "setFlagPositionInc: invalid player number" );
      //find the first vacant slot
      for (inc = 0; inc < MAX_FACTORY; inc++)
      {
            if ((factoryNumFlag[player][factoryType] & mask) != mask)
            {
                  break;
            }
            mask <<= 1;
      }

      if (inc >= MAX_FACTORY)
      {
            //this may happen now with electronic warfare
#ifdef DEBUG
            const char* pType;

            switch (factoryType)
            {
                  case FACTORY_FLAG:
                        pType = "Factory";
                        break;
                  case CYBORG_FLAG:
                        pType = "Cyborg Factory";
                        break;
                  case VTOL_FLAG:
                        pType = "VTOL Factory";
                        break;
                  case REPAIR_FLAG:
                        pType = "Repair Facility";
                        break;
                  default:
                        pType = "";
                        break;
            }
            ASSERT(!"building more factories than allowed", "Building more than %d %s for player %d", MAX_FACTORY, pType, player);
#endif
            inc = 1;
      }

      if (factoryType == REPAIR_FLAG)
      {
            psRepair = &pFunctionality->repairFacility;
            psRepair->psDeliveryPoint->factoryInc = 0;
            psRepair->psDeliveryPoint->factoryType = factoryType;
      }
      else
      {
            psFactory = &pFunctionality->factory;
            psFactory->psAssemblyPoint->factoryInc = inc;
            psFactory->psAssemblyPoint->factoryType = factoryType;
            factoryNumFlag[player][factoryType] |= mask;
      }
}


/* called from order.c.. delivery/assembly point handler*/
/*now called from display.c */
void processDeliveryPoint(UDWORD player, UDWORD x, UDWORD y)
{
      FLAG_POSITION     *psCurrFlag;//,*psFlag;//,*psNewFlag

      for (psCurrFlag = apsFlagPosLists[player]; psCurrFlag; psCurrFlag = psCurrFlag->psNext)
      {
            // must be selected and have a valid pos.
            if (psCurrFlag->selected)
            {
                  setAssemblyPoint(psCurrFlag, x, y, player, true);

                  //deselect once moved
                  psCurrFlag->selected = false;
                  return;     //will want to break if more than one can be selected?
            }
      }
}


/*called when a structure has been built - checks through the list of callbacks
for the scripts*/
void structureCompletedCallback(STRUCTURE_STATS *psStructType)
{

      if (psStructType->type == REF_POWER_GEN)
      {
            eventFireCallbackTrigger((TRIGGER_TYPE)CALL_POWERGEN_BUILT);
      }
      if (psStructType->type == REF_RESOURCE_EXTRACTOR)
      {
            eventFireCallbackTrigger((TRIGGER_TYPE)CALL_RESEX_BUILT);
      }
      if (psStructType->type == REF_RESEARCH)
      {
            eventFireCallbackTrigger((TRIGGER_TYPE)CALL_RESEARCH_BUILT);
      }
      if (psStructType->type == REF_FACTORY ||
            psStructType->type == REF_CYBORG_FACTORY ||
            psStructType->type == REF_VTOL_FACTORY)
      {
            eventFireCallbackTrigger((TRIGGER_TYPE)CALL_FACTORY_BUILT);
      }
}


STRUCTURE_STATS * structGetDemolishStat( void )
{
      ASSERT(g_psStatDestroyStruct != NULL , "stat not initialised");
      return g_psStatDestroyStruct;
}


/*sets the flag to indicate a HQ Exists - so draw Radar*/
void setHQExists(BOOL state, UDWORD player)
{
      hqExists[player] = (UBYTE)state;
}


/*returns the status of the flag*/
BOOL getHQExists(UDWORD player)
{
      return hqExists[player];
}


/*sets the flag to indicate a SatUplink Exists - so draw everything!*/
void setSatUplinkExists(BOOL state, UDWORD player)
{
      satUplinkExists[player] = (UBYTE)state;
}


/*returns the status of the flag*/
BOOL getSatUplinkExists(UDWORD player)
{
      return satUplinkExists[player];
}


/*sets the flag to indicate a Las Sat Exists - ONLY EVER WANT ONE*/
void setLasSatExists(BOOL state, UDWORD player)
{
      lasSatExists[player] = (UBYTE)state;
}


/*returns the status of the flag*/
BOOL getLasSatExists(UDWORD player)
{
      return lasSatExists[player];
}


/* calculate muzzle tip location in 3d world */
BOOL calcStructureMuzzleLocation(STRUCTURE *psStructure, Vector3i *muzzle, int weapon_slot)
{
      Vector3i barrel;
      iIMDShape         *psShape = psStructure->pStructureType->pIMD, *psWeaponImd = NULL;

      CHECK_STRUCTURE(psStructure);

      if (psStructure->asWeaps[weapon_slot].nStat > 0)
      {
            psWeaponImd =  asWeaponStats[psStructure->asWeaps[weapon_slot].nStat].pIMD;
      }
      else
      {
            psWeaponImd =  NULL;
      }

      if(psShape && psShape->nconnectors)
      {
            pie_MatBegin();

            pie_TRANSLATE(psStructure->pos.x,-(SDWORD)psStructure->pos.z,psStructure->pos.y);
            //matrix = the center of droid
            pie_MatRotY( DEG( (SDWORD)psStructure->direction ) );
            pie_MatRotX( DEG( psStructure->pitch ) );
            pie_MatRotZ( DEG( -(SDWORD)psStructure->roll ) );
            pie_TRANSLATE( psShape->connectors[weapon_slot].x, -psShape->connectors[weapon_slot].z,
                              -psShape->connectors[weapon_slot].y);//note y and z flipped

            //matrix = the gun and turret mount on the body
            pie_MatRotY(DEG((SDWORD)psStructure->turretRotation[weapon_slot]));//+ve anticlockwise
            pie_MatRotX(DEG(psStructure->turretPitch[weapon_slot]));//+ve up
            pie_MatRotZ(DEG(0));
            //matrix = the muzzle mount on turret
            if( psWeaponImd && psWeaponImd->nconnectors )
            {
                  barrel.x = psWeaponImd->connectors->x;
                  barrel.y = -psWeaponImd->connectors->y;
                  barrel.z = -psWeaponImd->connectors->z;
            }
            else
            {
                  barrel.x = 0;
                  barrel.y = 0;
                  barrel.z = 0;
            }

            pie_RotateTranslate3iv(&barrel, muzzle);
            muzzle->z = -muzzle->z;

            pie_MatEnd();
      }
      else
      {
            muzzle->x = psStructure->pos.x;
            muzzle->y = psStructure->pos.y;
            muzzle->z = psStructure->pos.z + psStructure->sDisplay.imd->max.y;
      }

      return true;
}


/*Looks through the list of structures to see if there are any inactive
resource extractors*/
void checkForResExtractors(STRUCTURE *psBuilding)
{
      STRUCTURE         *psCurr;
      POWER_GEN         *psPowerGen;
      RES_EXTRACTOR     *psResExtractor;
      UDWORD                  i;
      SDWORD                  slot;

      if (psBuilding->pStructureType->type != REF_POWER_GEN)
      {
            ASSERT(!"invalid structure type", "checkForResExtractors: invalid structure type");
            return;
      }
      psPowerGen = &psBuilding->pFunctionality->powerGenerator;
      //count the number of allocated slots
      slot = 0;//-1;
      for (i=0; i < NUM_POWER_MODULES; i++)
      {
            if (psPowerGen->apResExtractors[i] != NULL)
            {
                  slot++;
                  //make sure the derrrick is active if any oil left
                  psResExtractor = &psPowerGen->apResExtractors[i]->pFunctionality->resourceExtractor;
                  if (psResExtractor->power)
                  {
                        psResExtractor->active = true;
                  }
            }
      }

      psResExtractor = NULL;
      //each Power Gen can cope with 4 Extractors now - 9/6/98 AB
      //check capacity against number of filled slots
      if (slot < NUM_POWER_MODULES)
      {
            for (psCurr = apsStructLists[psBuilding->player]; psCurr != NULL;
                  psCurr = psCurr->psNext)
            {
                  if (psCurr->pStructureType->type == REF_RESOURCE_EXTRACTOR)
                  {
                        psResExtractor = &psCurr->pFunctionality->resourceExtractor;

                        //check not connected and power left and built!
                        if (!psResExtractor->active
                         && psCurr->status == SS_BUILT
                         && psResExtractor->power)
                        {
                              //assign the extractor to the power generator - use first vacant slot
                              for (i = 0; i < NUM_POWER_MODULES; i++)
                              {
                                    if (psPowerGen->apResExtractors[i] == NULL)
                                    {
                                          psPowerGen->apResExtractors[i] = psCurr;
                                          break;
                                    }
                              }
                              //set the owning power gen up for the resource extractor
                              psResExtractor->psPowerGen = psBuilding;
                              //set the res Extr to active
                              psResExtractor->active = true;
                              psResExtractor->timeLastUpdated = gameTime;
                              slot++;
                              //each Power Gen can cope with 4 Extractors now - 9/6/98 AB
                              //check to see if any more vacant slots
                              if (slot >= NUM_POWER_MODULES)
                              {
                                    //full up so quit out
                                    break;
                              }
                        }
                  }
            }
      }
}


/*Looks through the list of structures to see if there are any Power Gens
with available slots for the new Res Ext*/
void checkForPowerGen(STRUCTURE *psBuilding)
{
      STRUCTURE         *psCurr;
      UDWORD                  i;
      SDWORD                  slot;
      POWER_GEN         *psPG;
      RES_EXTRACTOR     *psRE;

      if (psBuilding->pStructureType->type != REF_RESOURCE_EXTRACTOR)
      {
            ASSERT(!"invalid structure type", "checkForPowerGen: invalid structure type");
            return;
      }
      psRE = &psBuilding->pFunctionality->resourceExtractor;
      if (psRE->active)
      {
            return;
      }

      //loop thru the current structures
      for (psCurr = apsStructLists[psBuilding->player]; psCurr != NULL;
            psCurr = psCurr->psNext)
      {
            if (psCurr->pStructureType->type == REF_POWER_GEN && psCurr->status == SS_BUILT)
            {
                  psPG = &psCurr->pFunctionality->powerGenerator;
                  //check capacity against number of filled slots
                  slot = 0;//-1;
                  for (i=0; i < NUM_POWER_MODULES; i++)
                  {
                        if (psPG->apResExtractors[i] != NULL)
                        {
                              slot++;
                        }
                  }
                  //each Power Gen can cope with 4 Extractors now - 9/6/98 AB
                  //each Power Gen can cope with 4 extractors
                  if (slot < NUM_POWER_MODULES )
                  {
                        //find the first vacant slot
                        for (i=0; i < NUM_POWER_MODULES; i++)
                        {
                              if (psPG->apResExtractors[i] == NULL)
                              {
                                    psPG->apResExtractors[i] = psBuilding;
                                    psRE->psPowerGen = psCurr;
                                    psRE->active = true;
                                    psRE->timeLastUpdated = gameTime;
                                    return;
                              }
                        }
                  }
            }
      }
}


/*initialise the slot the Resource Extractor filled in the owning Power Gen*/
void informPowerGen(STRUCTURE *psStruct)
{
      UDWORD            i;
      POWER_GEN   *psPowerGen;

      if (psStruct->pStructureType->type != REF_RESOURCE_EXTRACTOR)
      {
            ASSERT(!"invalid structure type", "informPowerGen: invalid structure type");
            return;
      }

      //get the owning power generator
      psPowerGen = &psStruct->pFunctionality->resourceExtractor.psPowerGen->pFunctionality->powerGenerator;
      if (psPowerGen)
      {
            for (i=0; i < NUM_POWER_MODULES; i++)
            {
                  if (psPowerGen->apResExtractors[i] == psStruct)
                  {
                        //initialise the 'slot'
                        psPowerGen->apResExtractors[i] = NULL;
                        break;
                  }
            }
      }
}


/*called when a Res extractor is destroyed or runs out of power or is disconnected
adjusts the owning Power Gen so that it can link to a different Res Extractor if one
is available*/
void releaseResExtractor(STRUCTURE *psRelease)
{
      STRUCTURE   *psCurr;

      if (psRelease->pStructureType->type != REF_RESOURCE_EXTRACTOR)
      {
            ASSERT(!"invalid structure type", "releaseResExtractor:Invalid structure type");
            return;
      }

      //tell associated Power Gen
      if (psRelease->pFunctionality->resourceExtractor.psPowerGen)
      {
            informPowerGen(psRelease);
      }

      psRelease->pFunctionality->resourceExtractor.psPowerGen = NULL;

      //there may be spare resource extractors
      for (psCurr = apsStructLists[psRelease->player]; psCurr != NULL; psCurr =
            psCurr->psNext)
      {
            //check not connected and power left and built!
            if (psCurr->pStructureType->type == REF_RESOURCE_EXTRACTOR
             && psCurr != psRelease
             && !psCurr->pFunctionality->resourceExtractor.active
             && psCurr->pFunctionality->resourceExtractor.power
             && psCurr->status == SS_BUILT)
            {
                  checkForPowerGen(psCurr);
            }
      }
}


/*called when a Power Gen is destroyed or is disconnected
adjusts the associated Res Extractors so that they can link to different Power
Gens if any are available*/
void releasePowerGen(STRUCTURE *psRelease)
{
      STRUCTURE   *psCurr;
      POWER_GEN   *psPowerGen;
      UDWORD            i;

      if (psRelease->pStructureType->type != REF_POWER_GEN)
      {
            ASSERT(!"invalid structure type", "releasePowerGen: Invalid structure type");
            return;
      }

      psPowerGen = &psRelease->pFunctionality->powerGenerator;
      //go through list of res extractors, setting them to inactive
      for (i=0; i < NUM_POWER_MODULES; i++)
      {
            if (psPowerGen->apResExtractors[i])
            {
                  psPowerGen->apResExtractors[i]->pFunctionality->resourceExtractor.active = false;
                  psPowerGen->apResExtractors[i]->pFunctionality->resourceExtractor.psPowerGen = NULL;
                  psPowerGen->apResExtractors[i] = NULL;
            }
      }
      //may have a power gen with spare capacity
      for (psCurr = apsStructLists[psRelease->player]; psCurr != NULL; psCurr =
            psCurr->psNext)
      {
            if (psCurr->pStructureType->type == REF_POWER_GEN &&
                  psCurr != psRelease && psCurr->status == SS_BUILT)
            {
                  checkForResExtractors(psCurr);
            }
      }
}


/*this is called whenever a structure has finished building*/
void buildingComplete(STRUCTURE *psBuilding)
{
      CHECK_STRUCTURE(psBuilding);

      psBuilding->currentBuildPts = (SWORD)psBuilding->pStructureType->buildPoints;
      psBuilding->status = SS_BUILT;

      switch (psBuilding->pStructureType->type)
      {
            case REF_POWER_GEN:
                  checkForResExtractors(psBuilding);

                  if(selectedPlayer == psBuilding->player)
                  {
                        audio_PlayObjStaticTrack( (void *) psBuilding, ID_SOUND_POWER_HUM );
                  }

                  break;
            case REF_RESOURCE_EXTRACTOR:
                  checkForPowerGen(psBuilding);
                  /* GJ HACK! - add anim to deriks */
                  if (psBuilding->psCurAnim == NULL)
                  {
                        psBuilding->psCurAnim = animObj_Add(psBuilding, ID_ANIM_DERIK, 0, 0);
                  }

                  break;
            case REF_RESEARCH:
                  intCheckResearchButton();
                  //this deals with researc facilities that are upgraded whilst mid-research
                  releaseResearch(psBuilding);
                  break;
            case REF_FACTORY:
            case REF_CYBORG_FACTORY:
            case REF_VTOL_FACTORY:
                  //this deals with factories that are upgraded whilst mid-production
                  releaseProduction(psBuilding);
                  break;
            case REF_SAT_UPLINK:
                  revealAll(psBuilding->player);
            default:
                  //do nothing
                  break;
      }
}


/*for a given structure, return a pointer to its module stat */
STRUCTURE_STATS* getModuleStat(STRUCTURE *psStruct)
{
      STRUCTURE_STATS         *psStat;

      ASSERT( psStruct != NULL,
            "getModuleStat: Invalid structure pointer" );

      psStat = NULL;
      switch (psStruct->pStructureType->type)
      {
      case REF_POWER_GEN:
            psStat = &asStructureStats[powerModuleStat];
            break;
      case REF_FACTORY:
      case REF_VTOL_FACTORY:
            psStat = &asStructureStats[factoryModuleStat];
            break;
      case REF_RESEARCH:
            psStat = &asStructureStats[researchModuleStat];
            break;
      default:
            //no other structures can have modules attached
            break;
      }

      return psStat;
}

/**
 * Count the artillery and VTOL droids assigned to a structure.
 */
05798 static unsigned int countAssignedDroids(const STRUCTURE* psStructure)
{
      const DROID* psCurr;
      unsigned int num;

      CHECK_STRUCTURE(psStructure);

      // For non-debug builds
      if (psStructure == NULL)
            return 0;

      num = 0;
      for (psCurr = apsDroidLists[selectedPlayer]; psCurr; psCurr = psCurr->psNext)
      {
            if (psCurr->psTarget
             && psCurr->psTarget->id == psStructure->id
             && psCurr->player == psStructure->player)
            {
                  const MOVEMENT_MODEL weapontype = asWeaponStats[psCurr->asWeaps[0].nStat].movementModel;

                  if (weapontype == MM_INDIRECT
                   || weapontype == MM_HOMINGINDIRECT
                   || isVtolDroid(psCurr))
                  {
                        num++;
                  }
            }
      }

      return num;
}

//print some info at the top of the screen dependant on the structure
void printStructureInfo(STRUCTURE *psStructure)
{
      unsigned int numConnected, i;
      POWER_GEN   *psPowerGen;

      ASSERT( psStructure != NULL, "printStructureInfo: Invalid Structure pointer" );

      switch (psStructure->pStructureType->type)
      {
      case REF_HQ:
#ifdef DEBUG
            if (getDebugMappingStatus())
            {
                  CONPRINTF(ConsoleString, (ConsoleString, "%s - %d Units assigned - ID %d - sensor range %hu power %hu - ECM %u",
                            getStatName(psStructure->pStructureType), countAssignedDroids(psStructure),
                            psStructure->id, structSensorRange(psStructure), structSensorPower(psStructure), structConcealment(psStructure)));
            }
            else
#endif
            {
                  unsigned int assigned_droids = countAssignedDroids(psStructure);

                  CONPRINTF(ConsoleString, (ConsoleString, ngettext("%s - %u Unit assigned", "%s - %u Units assigned", assigned_droids),
                            getStatName(psStructure->pStructureType), assigned_droids));
            }
            break;
      case REF_DEFENSE:
            if (psStructure->pStructureType->pSensor == NULL)
            {
                  break;
            }
#ifdef DEBUG
            else if (getDebugMappingStatus())
            {
                  CONPRINTF(ConsoleString, (ConsoleString, "%s - %d Units assigned - ID %d - armour %d|%d - sensor range %hu power %hu - ECM %u",
                        getStatName(psStructure->pStructureType), countAssignedDroids(psStructure),
                        psStructure->id, psStructure->armour[0][WC_KINETIC], psStructure->armour[0][WC_HEAT],
                          structSensorRange(psStructure), structSensorPower(psStructure), structConcealment(psStructure)));
            }
#endif
            else
            {
                  unsigned int assigned_droids = countAssignedDroids(psStructure);

                  CONPRINTF(ConsoleString, (ConsoleString, ngettext("%s - %u Unit assigned", "%s - %u Units assigned", assigned_droids),
                        getStatName(psStructure->pStructureType), assigned_droids));
            }
            break;
      case REF_RESOURCE_EXTRACTOR:
#ifdef DEBUG
            if (getDebugMappingStatus())
            {
                  CONPRINTF(ConsoleString,(ConsoleString, "%s - Unique ID %d",
                            getStatName(psStructure->pStructureType), psStructure->id));
            }
            else
#endif
            {
                  CONPRINTF(ConsoleString, (ConsoleString, getStatName(psStructure->pStructureType)));
            }
            break;
      case REF_POWER_GEN:
            psPowerGen = &psStructure->pFunctionality->powerGenerator;
            numConnected = 0;
            for (i = 0; i < NUM_POWER_MODULES; i++)
            {
                  if (psPowerGen->apResExtractors[i])
                  {
                        numConnected++;
                  }
            }
#ifdef DEBUG
            if (getDebugMappingStatus())
            {
                  CONPRINTF(ConsoleString, (ConsoleString, "%s -  Connected %u of %u - Unique ID %u",
                            getStatName(psStructure->pStructureType), numConnected, NUM_POWER_MODULES,
                            psStructure->id));
            }
            else
#endif
            {
                  CONPRINTF(ConsoleString, (ConsoleString, _("%s - Connected %u of %u"),
                            getStatName(psStructure->pStructureType), numConnected, NUM_POWER_MODULES));
            }
            break;
      default:
#ifdef DEBUG
            if (getDebugMappingStatus())
            {
                  CONPRINTF(ConsoleString, (ConsoleString, "%s - Damage % 3.2f%% - Unique ID %u",
                            getStatName(psStructure->pStructureType), getStructureDamage(psStructure) * 100.f, psStructure->id));
            }
            else
#endif
            {
                  CONPRINTF(ConsoleString, (ConsoleString, _("%s - Damage %3.0f%%"),
                            getStatName(psStructure->pStructureType), getStructureDamage(psStructure) * 100.f));
            }
            break;
      }
}


/*Checks the template type against the factory type - returns false
if not a good combination!*/
BOOL validTemplateForFactory(DROID_TEMPLATE *psTemplate, STRUCTURE *psFactory)
{
      //not in multiPlayer! - AB 26/5/99
      if (!bMultiPlayer)
      {
            //ignore Transporter Droids
            if (psTemplate->droidType == DROID_TRANSPORTER)
            {
                  return false;
            }
      }

      //check if droid is a cyborg
      if (psTemplate->droidType == DROID_CYBORG ||
            psTemplate->droidType == DROID_CYBORG_SUPER ||
            psTemplate->droidType == DROID_CYBORG_CONSTRUCT ||
            psTemplate->droidType == DROID_CYBORG_REPAIR)
      {
            if (psFactory->pStructureType->type != REF_CYBORG_FACTORY)
            {
                  return false;
            }
      }
      //check for VTOL droid
      else if ((asPropulsionStats + psTemplate->asParts[COMP_PROPULSION])->
            propulsionType == LIFT)
      {
            if (psFactory->pStructureType->type != REF_VTOL_FACTORY)
            {
                  return false;
            }
      }

      //check if cyborg factory
      if (psFactory->pStructureType->type == REF_CYBORG_FACTORY)
      {
            //if (psTemplate->droidType != DROID_CYBORG)
            if (!(psTemplate->droidType == DROID_CYBORG ||
                  psTemplate->droidType == DROID_CYBORG_SUPER ||
                  psTemplate->droidType == DROID_CYBORG_CONSTRUCT ||
                  psTemplate->droidType == DROID_CYBORG_REPAIR))
            {
                  return false;
            }
      }
      //check if vtol factory
      else if (psFactory->pStructureType->type == REF_VTOL_FACTORY)
      {
            if ((asPropulsionStats + psTemplate->asParts[COMP_PROPULSION])->
                  propulsionType != LIFT)
            {
                  return false;
            }
      }

      //got through all the tests...
      return true;
}

/*calculates the damage caused to the resistance levels of structures - returns
true when captured*/
BOOL electronicDamage(BASE_OBJECT *psTarget, UDWORD damage, UBYTE attackPlayer)
{
      STRUCTURE   *psStructure;
      DROID       *psDroid;
      BOOL        bCompleted = true;
      Vector3i    pos;
      UDWORD            i;

      ASSERT(attackPlayer < MAX_PLAYERS, "electronicDamage: invalid player id %d", (int)attackPlayer);
      ASSERT(psTarget != NULL, "electronicDamage: target is NULL");
      if (attackPlayer >= MAX_PLAYERS || psTarget == NULL)
      {
            return false;
      }

      //structure electronic damage
      if (psTarget->type == OBJ_STRUCTURE)
      {
            psStructure = (STRUCTURE *)psTarget;
            bCompleted = false;

            if (psStructure->pStructureType->resistance == 0)
            {
                  return false;     // this structure type cannot be taken over
            }

            //if resistance is already less than 0 don't do any more
            if (psStructure->resistance < 0)
            {
                  bCompleted = true;
            }
            else
            {
                  //store the time it was hit
                  psStructure->timeLastHit = gameTime;

                  psStructure->lastHitWeapon = WSC_ELECTRONIC;

                  // tell the cluster system it has been attacked
                  clustObjectAttacked((BASE_OBJECT *)psStructure);

                  psStructure->resistance = (SWORD)(psStructure->resistance - damage);

                  if (psStructure->resistance < 0)
                  {
                        //add a console message for the selected Player
                        if (psStructure->player == selectedPlayer)
                        {
                              CONPRINTF(ConsoleString,(ConsoleString,
                                    _("%s - Electronically Damaged"),
                                    getStatName(psStructure->pStructureType)));
                              //tell the scripts if selectedPlayer has lost a structure
                              eventFireCallbackTrigger((TRIGGER_TYPE)CALL_ELECTRONIC_TAKEOVER);
                        }
                        bCompleted = true;
                        //give the structure to the attacking player
                        (void)giftSingleStructure(psStructure, attackPlayer, false);
                  }
            }
      }
      //droid electronic damage
      else if (psTarget->type == OBJ_DROID)
      {
            psDroid = (DROID *)psTarget;
            bCompleted = false;

            //in multiPlayer cannot attack a Transporter with EW
            if (bMultiPlayer)
            {
                  if (psDroid->droidType == DROID_TRANSPORTER)
                  {
                        ASSERT(!"can't attack a Transporter while in multiplayer", "electronicDamage: Cannot attack a Transporter in multiPlayer");
                        return true;
                  }
            }

            if (psDroid->resistance == ACTION_START_TIME)
            {
                  //need to set the current resistance level since not been previously attacked (by EW)
                  psDroid->resistance = droidResistance(psDroid);
            }

            if (psDroid->resistance < 0)
            {
                  bCompleted = true;
            }
            else
            {
                  // tell the cluster system it has been attacked
                  clustObjectAttacked((BASE_OBJECT *)psDroid);

                  psDroid->resistance = (SWORD)(psDroid->resistance - damage);

                  if (psDroid->resistance <= 0)
                  {
                        //add a console message for the selected Player
                        if (psDroid->player == selectedPlayer)
                        {
                              CONPRINTF(ConsoleString, (ConsoleString, _("%s - Electronically Damaged"), "Unit"));
                              //tell the scripts if selectedPlayer has lost a droid
                              eventFireCallbackTrigger((TRIGGER_TYPE)CALL_ELECTRONIC_TAKEOVER);
                        }
                        bCompleted = true;

                        //give the droid to the attacking player

                        if(psDroid->visible[selectedPlayer])
                        {
                              for(i=0; i<5; i++)
                              {
                                    pos.x = psDroid->pos.x + (30-rand()%60);
                                    pos.z = psDroid->pos.y + (30-rand()%60);
                                    pos.y = psDroid->pos.z + (rand()%8);
                                    effectGiveAuxVar(80);
                                    addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_FLAMETHROWER,false,NULL,0);
                              }
                        }

                        (void)giftSingleDroid(psDroid, attackPlayer);

                        // tell the world!
                        if (bMultiPlayer)
                        {
                              uint8_t giftType = DROID_GIFT, droid_count = 1;

                              NETbeginEncode(NET_GIFT, NET_ALL_PLAYERS);
                              {
                                    // We need to distinguish between gift types
                                    NETuint8_t(&giftType);

                                    // send the player originally owning this droid
                                    // and the new owner
                                    NETuint8_t(&psDroid->player);
                                    NETuint8_t(&attackPlayer);

                                    // the amount of droids (1 in this case)
                                    // followed by the droid's ID
                                    NETuint8_t(&droid_count);
                                    NETuint32_t(&psDroid->id);
                              }
                              NETend();
                        }

                        //check to see if droid limit reached, if so recycle.
                        // don't check for transporter/mission coz multiplayer only issue.
                        if( (getNumDroids(attackPlayer)+1 ) > getMaxDroids(attackPlayer) )
                        {
                              recycleDroid(psDroid);
                        }
                  }
            }
      }

      return bCompleted;
}


/* EW works differently in multiplayer mode compared with single player.*/
BOOL validStructResistance(STRUCTURE *psStruct)
{
      BOOL    bTarget = false;

      ASSERT( psStruct != NULL, "invalidStructResistance: invalid structure pointer" );

      if (psStruct->pStructureType->resistance != 0)
      {
            /*certain structures will only provide rewards in multiplayer so
            before they can become valid targets their resistance must be at least
            half the base value*/
            if (bMultiPlayer)
            {
                  switch(psStruct->pStructureType->type)
                  {
                  case REF_RESEARCH:
                  case REF_FACTORY:
                  case REF_VTOL_FACTORY:
                  case REF_CYBORG_FACTORY:
                  case REF_HQ:
                  case REF_REPAIR_FACILITY:
                        if (psStruct->resistance >= (SDWORD) (structureResistance(psStruct->
                              pStructureType, psStruct->player) / 2))
                        {
                              bTarget = true;
                        }
                        break;
                  default:
                        bTarget = true;
                  }
            }
            else
            {
                  bTarget = true;
            }
      }

      return bTarget;
}


/*Access functions for the upgradeable stats of a structure*/
UDWORD structureBody(const STRUCTURE *psStructure)
{
      const STRUCTURE_STATS *psStats = psStructure->pStructureType;
      const UBYTE player = psStructure->player;

      switch(psStats->type)
      {
            // wall/defence structures:
            case REF_DEFENSE:
            case REF_WALL:
            case REF_WALLCORNER:
            case REF_BLASTDOOR:
                  return psStats->bodyPoints + (psStats->bodyPoints * asWallDefenceUpgrade[player].body)/100;
            // all other structures:
            default:
                  return structureBaseBody(psStructure) + (structureBaseBody(psStructure) * asStructureUpgrade[player].body)/100;
      }
}


/*this returns the Base Body points of a structure - regardless of upgrade*/
UDWORD structureBaseBody(const STRUCTURE *psStructure)
{
      const STRUCTURE_STATS *psStats = psStructure->pStructureType;

      ASSERT( psStructure != NULL, "structureBaseBody: invalid structure pointer" );

      switch(psStats->type)
      {
            // modules may be attached:
            case REF_FACTORY:
            case REF_VTOL_FACTORY:
                  ASSERT(psStructure->pFunctionality != NULL, "structureBaseBody: invalid structure functionality pointer");
                  if (psStructure->pFunctionality->factory.capacity > 0)
                  {
                        //add on the default for the factory
                        return psStats->bodyPoints + asStructureStats[factoryModuleStat].bodyPoints * psStructure->pFunctionality->factory.capacity;
                  }
                  else
                  {
                        //no modules
                        return psStats->bodyPoints;
                  }
            case REF_RESEARCH:
                  ASSERT(psStructure->pFunctionality != NULL, "structureBaseBody: invalid structure functionality pointer");
                  if (psStructure->pFunctionality->researchFacility.capacity > 0)
                  {
                        //add on the default for the factory
                        return psStats->bodyPoints + asStructureStats[researchModuleStat].bodyPoints;
                  }
                  else
                  {
                        //no modules
                        return psStats->bodyPoints;
                  }
            case REF_POWER_GEN:
                  ASSERT(psStructure->pFunctionality != NULL, "structureBaseBody: invalid structure functionality pointer");
                  if (psStructure->pFunctionality->powerGenerator.capacity > 0)
                  {
                        //add on the default for the factory
                        return psStats->bodyPoints + asStructureStats[powerModuleStat].bodyPoints;
                  }
                  else
                  {
                        //no modules
                        return psStats->bodyPoints;
                  }
            // all other structures:
            default:
                  return psStats->bodyPoints;
      }
}


UDWORD      structureArmour(STRUCTURE_STATS *psStats, UBYTE player)
{
      switch(psStats->type)
      {
      case REF_DEFENSE:
      case REF_WALL:
      case REF_WALLCORNER:
      case REF_BLASTDOOR:
            return (psStats->armourValue + (psStats->armourValue *
                  asWallDefenceUpgrade[player].armour)/100);
            break;
      default:
            return (psStats->armourValue + (psStats->armourValue *
                  asStructureUpgrade[player].armour)/100);
            break;
      }
}


UDWORD      structureResistance(STRUCTURE_STATS *psStats, UBYTE player)
{
      switch(psStats->type)
      {
      case REF_WALL:
      case REF_WALLCORNER:
      case REF_BLASTDOOR:
            //not upgradeable
            return psStats->resistance;
            break;
      default:
            return (psStats->resistance + (psStats->resistance *
                  asStructureUpgrade[player].resistance)/100);
            break;
      }
}


/*gives the attacking player a reward based on the type of structure that has
been attacked*/
BOOL electronicReward(STRUCTURE *psStructure, UBYTE attackPlayer)
{
      BOOL    bRewarded = false;

      switch(psStructure->pStructureType->type)
      {
      case REF_RESEARCH:
            researchReward(psStructure->player, attackPlayer);
            bRewarded = true;
            break;
      case REF_FACTORY:
      case REF_VTOL_FACTORY:
      case REF_CYBORG_FACTORY:
            factoryReward(psStructure->player, attackPlayer);
            bRewarded = true;
            break;
      case REF_HQ:
            hqReward(psStructure->player,attackPlayer);
            if (attackPlayer == selectedPlayer)
            {
                  addConsoleMessage(_("Electronic Reward - Visibility Report"),     DEFAULT_JUSTIFY,SYSTEM_MESSAGE);
            }
            bRewarded = true;
            break;
      case REF_REPAIR_FACILITY:
            repairFacilityReward(psStructure->player,attackPlayer);
            bRewarded = true;
            break;
      default:
            bRewarded = false;
      }

      return bRewarded;
}


/*find the 'best' prop/body/weapon component the losing player has and
'give' it to the reward player*/
void factoryReward(UBYTE losingPlayer, UBYTE rewardPlayer)
{
      UDWORD            inc, comp = 0;

      //search through the propulsions first
      for (inc=0; inc < numPropulsionStats; inc++)
      {
            if (apCompLists[losingPlayer][COMP_PROPULSION][inc] == AVAILABLE &&
                  apCompLists[rewardPlayer][COMP_PROPULSION][inc] != AVAILABLE)
            {
                  if (asPropulsionStats[inc].buildPower > asPropulsionStats[comp].buildPower)
                  {
                        comp = inc;
                  }
            }
      }
      if (comp != 0)
      {
            apCompLists[rewardPlayer][COMP_PROPULSION][comp] = AVAILABLE;
            if (rewardPlayer == selectedPlayer)
            {
                  CONPRINTF(ConsoleString,(ConsoleString,"%s :- %s",
                        _("Factory Reward - Propulsion"),
                        getName(asPropulsionStats[comp].pName)));
            }
            return;
      }

      //haven't found a propulsion - look for a body
      for (inc=0; inc < numBodyStats; inc++)
      {
            if (apCompLists[losingPlayer][COMP_BODY][inc] == AVAILABLE &&
                  apCompLists[rewardPlayer][COMP_BODY][inc] != AVAILABLE)
            {
                  if (asBodyStats[inc].buildPower > asBodyStats[comp].buildPower)
                  {
                        comp = inc;
                  }
            }
      }
      if (comp != 0)
      {
            apCompLists[rewardPlayer][COMP_BODY][comp] = AVAILABLE;
            if (rewardPlayer == selectedPlayer)
            {
                  CONPRINTF(ConsoleString,(ConsoleString,"%s :- %s",
                        _("Factory Reward - Body"),
                        getName(asBodyStats[comp].pName)));
            }
            return;
      }

      //haven't found a body - look for a weapon
      for (inc=0; inc < numWeaponStats; inc++)
      {
            if (apCompLists[losingPlayer][COMP_WEAPON][inc] == AVAILABLE &&
                  apCompLists[rewardPlayer][COMP_WEAPON][inc] != AVAILABLE)
            {
                  if (asWeaponStats[inc].buildPower > asWeaponStats[comp].buildPower)
                  {
                        comp = inc;
                  }
            }
      }
      if (comp != 0)
      {
            apCompLists[rewardPlayer][COMP_WEAPON][comp] = AVAILABLE;
            if (rewardPlayer == selectedPlayer)
            {
                  CONPRINTF(ConsoleString,(ConsoleString,"%s :- %s",
                        _("Factory Reward - Weapon"),
                        getName(asWeaponStats[comp].pName)));
            }
            return;
      }

      //losing Player hasn't got anything better so don't gain anything!
      if (rewardPlayer == selectedPlayer)
      {
            addConsoleMessage(_("Factory Reward - Nothing"), DEFAULT_JUSTIFY,SYSTEM_MESSAGE);
      }
}

/*find the 'best' repair component the losing player has and
'give' it to the reward player*/
void repairFacilityReward(UBYTE losingPlayer, UBYTE rewardPlayer)
{
      UDWORD            inc, comp = 0;

      //search through the repair stats
      for (inc=0; inc < numRepairStats; inc++)
      {
            if (apCompLists[losingPlayer][COMP_REPAIRUNIT][inc] == AVAILABLE &&
                  apCompLists[rewardPlayer][COMP_REPAIRUNIT][inc] != AVAILABLE)
            {
                  if (asRepairStats[inc].buildPower > asRepairStats[comp].buildPower)
                  {
                        comp = inc;
                  }
            }
      }
      if (comp != 0)
      {
            apCompLists[rewardPlayer][COMP_REPAIRUNIT][comp] = AVAILABLE;
            if (rewardPlayer == selectedPlayer)
            {
                  CONPRINTF(ConsoleString,(ConsoleString,"%s :- %s",
                        _("Repair Facility Award - Repair"),
                        getName(asRepairStats[comp].pName)));
            }
            return;
      }
      if (rewardPlayer == selectedPlayer)
      {
            addConsoleMessage(_("Repair Facility Award - Nothing"), DEFAULT_JUSTIFY,SYSTEM_MESSAGE);
      }
}


/*makes the losing players tiles/structures/features visible to the reward player*/
void hqReward(UBYTE losingPlayer, UBYTE rewardPlayer)
{
      STRUCTURE *psStruct;
      FEATURE       *psFeat;
      DROID   *psDroid;
      UDWORD      x,y,i;
      MAPTILE     *psTile;

      //tiles
      for(x = 0; x < mapWidth; x++)
      {
            for(y = 0; y < mapHeight; y++)
            {
                  psTile = mapTile(x,y);

                  if(TEST_TILE_VISIBLE(losingPlayer,psTile))
                  {
                        SET_TILE_VISIBLE(rewardPlayer,psTile);

                        if(getRevealStatus())
                        {
                              if(rewardPlayer == selectedPlayer)
                              {
                                    avInformOfChange(x,y);
                              }
                        }
                  }
            }
      }

      //struct
      for(i=0;i<MAX_PLAYERS;i++)
      {
            for(psStruct = apsStructLists[i]; psStruct != NULL; psStruct = psStruct->psNext)
            {
                  if( psStruct->visible[losingPlayer] && !psStruct->died)
                  {
                        psStruct->visible[rewardPlayer] = psStruct->visible[losingPlayer];
                  }
            }

            //feature
            for(psFeat = apsFeatureLists[i]; psFeat != NULL; psFeat = psFeat->psNext)
            {
                  if(psFeat->visible[losingPlayer] )
                  {
                        psFeat->visible[rewardPlayer] = psFeat->visible[losingPlayer];
                  }
            }

            //droids.
            for(psDroid = apsDroidLists[i]; psDroid != NULL; psDroid = psDroid->psNext)
            {
                  if(psDroid->visible[losingPlayer] || psDroid->player == losingPlayer)
                  {
                        psDroid->visible[rewardPlayer] =UBYTE_MAX;
                  }
            }
      }
}


// Return true if structure is a factory of any type.
//
BOOL StructIsFactory(STRUCTURE *Struct)
{
      if( (Struct->pStructureType->type == REF_FACTORY) ||
            (Struct->pStructureType->type == REF_CYBORG_FACTORY) ||
            (Struct->pStructureType->type == REF_VTOL_FACTORY) )
      {
            return true;
      }

      return false;
}


// Return true if flag is a delivery point for a factory.
//
BOOL FlagIsFactory(FLAG_POSITION *psCurrFlag)
{
      if( (psCurrFlag->factoryType == FACTORY_FLAG) || (psCurrFlag->factoryType == CYBORG_FLAG) ||
            (psCurrFlag->factoryType == VTOL_FLAG) )
      {
            return true;
      }

      return false;
}


// Find a structure's delivery point , only if it's a factory.
// Returns NULL if not found or the structure isn't a factory.
//
FLAG_POSITION *FindFactoryDelivery(STRUCTURE *Struct)
{
      FLAG_POSITION     *psCurrFlag;

      if(StructIsFactory(Struct))
      {
            // Find the factories delivery point.
            for (psCurrFlag = apsFlagPosLists[selectedPlayer]; psCurrFlag;
                  psCurrFlag = psCurrFlag->psNext)
            {
                  if(FlagIsFactory(psCurrFlag)
                   && Struct->pFunctionality->factory.psAssemblyPoint->factoryInc == psCurrFlag->factoryInc
                   && Struct->pFunctionality->factory.psAssemblyPoint->factoryType == psCurrFlag->factoryType)
                  {
                        return psCurrFlag;
                  }
            }
      }

      return NULL;
}


//Find the factory associated with the delivery point - returns NULL if none exist
STRUCTURE   *findDeliveryFactory(FLAG_POSITION *psDelPoint)
{
      STRUCTURE   *psCurr;
      FACTORY           *psFactory;
      REPAIR_FACILITY *psRepair;

      for (psCurr = apsStructLists[psDelPoint->player]; psCurr != NULL; psCurr =
            psCurr->psNext)
      {
            if(StructIsFactory(psCurr))
            {
                  psFactory = &psCurr->pFunctionality->factory;
                  if (psFactory->psAssemblyPoint->factoryInc == psDelPoint->factoryInc &&
                        psFactory->psAssemblyPoint->factoryType == psDelPoint->factoryType)
                  {
                        return psCurr;
                  }
            }
            else if (psCurr->pStructureType->type == REF_REPAIR_FACILITY)
            {
                  psRepair = &psCurr->pFunctionality->repairFacility;
                  if (psRepair->psDeliveryPoint == psDelPoint)
                  {
                        return psCurr;
                  }
            }
      }
      return NULL;
}


/*cancels the production run for the factory and returns any power that was
accrued but not used*/
void cancelProduction(STRUCTURE *psBuilding)
{
      FACTORY           *psFactory;

      ASSERT(StructIsFactory(psBuilding), "structure not a factory");

      psFactory = &psBuilding->pFunctionality->factory;

      //check its the correct factory
      if (psBuilding->player == productionPlayer && psFactory->psSubject)
      {
            //clear the production run for this factory
            memset(asProductionRun[psFactory->psAssemblyPoint->factoryType][
                  psFactory->psAssemblyPoint->factoryInc], 0, sizeof(PRODUCTION_RUN) *
                  MAX_PROD_RUN);
            //return any accrued power
            if (psFactory->powerAccrued)
            {
                  addPower(psBuilding->player, psFactory->powerAccrued);
            }
            //clear the factories subject and quantity
            psFactory->psSubject = NULL;
            psFactory->quantity = 0;
            //tell the interface
            intManufactureFinished(psBuilding);
      }
}


/*set a factory's production run to hold*/
void holdProduction(STRUCTURE *psBuilding)
{

      FACTORY           *psFactory;

      ASSERT( StructIsFactory(psBuilding), "holdProduction: structure not a factory" );

      psFactory = &psBuilding->pFunctionality->factory;

      if (psFactory->psSubject)
      {
            //set the time the factory was put on hold
            psFactory->timeStartHold = gameTime;
            //play audio to indicate on hold
            if (psBuilding->player == selectedPlayer)
            {
                  audio_PlayTrack(ID_SOUND_WINDOWCLOSE);
            }
      }

}

/*release a factory's production run from hold*/
void releaseProduction(STRUCTURE *psBuilding)
{
      FACTORY           *psFactory;

      ASSERT( StructIsFactory(psBuilding),
            "releaseProduction: structure not a factory" );

      psFactory = &psBuilding->pFunctionality->factory;

      if (psFactory->psSubject && psFactory->timeStartHold)
      {
            //adjust the start time for the current subject
            if (psFactory->timeStarted != ACTION_START_TIME)
            {
                  psFactory->timeStarted += (gameTime - psFactory->timeStartHold);
            }
            psFactory->timeStartHold = 0;
      }
}


/*this is called when a factory produces a droid. The Template returned is the next
one to build - if any*/
DROID_TEMPLATE * factoryProdUpdate(STRUCTURE *psStructure, DROID_TEMPLATE *psTemplate)
{
      UDWORD            inc, factoryType, factoryInc;
      FACTORY           *psFactory;

      CHECK_STRUCTURE(psStructure);
      ASSERT( psStructure->player == productionPlayer,
            "factoryProdUpdate: called for incorrect player" );

      psFactory = &psStructure->pFunctionality->factory;
      factoryType = psFactory->psAssemblyPoint->factoryType;
      factoryInc = psFactory->psAssemblyPoint->factoryInc;

      if (psTemplate != NULL)
      {
            //find the entry in the array for this template
            for (inc=0; inc < MAX_PROD_RUN; inc++)
            {
                  if (asProductionRun[factoryType][factoryInc][inc].psTemplate == psTemplate)
                  {
                        asProductionRun[factoryType][factoryInc][inc].built++;
                        if (asProductionRun[factoryType][factoryInc][inc].built <
                              asProductionRun[factoryType][factoryInc][inc].quantity)
                        {
                              return psTemplate;
                        }
                        break;
                  }
            }
      }
      //find the next template to build - this just looks for the first uncompleted run
      for (inc=0; inc < MAX_PROD_RUN; inc++)
      {
            if (asProductionRun[factoryType][factoryInc][inc].built <
                  asProductionRun[factoryType][factoryInc][inc].quantity)
            {
                  return asProductionRun[factoryType][factoryInc][inc].psTemplate;
            }
      }
      /*If you've got here there's nothing left to build unless factory is
      on loop production*/
      if (psFactory->quantity)
      {
            //reduce the loop count if not infinite
            if (psFactory->quantity != INFINITE_PRODUCTION)
            {
                  psFactory->quantity--;
            }

            //need to reset the quantity built for each entry in the production list
            for (inc=0; inc < MAX_PROD_RUN; inc++)
            {
                  if (asProductionRun[factoryType][factoryInc][inc].psTemplate)
                  {
                        asProductionRun[factoryType][factoryInc][inc].built = 0;
                  }
            }
            //get the first to build again
            return (factoryProdUpdate(psStructure, NULL));
      }
      //if got to here then nothing left to produce so clear the array
      memset(asProductionRun[factoryType][factoryInc], 0,
            sizeof(PRODUCTION_RUN) * MAX_PROD_RUN);
      return NULL;
}


//adjust the production run for this template type
void factoryProdAdjust(STRUCTURE *psStructure, DROID_TEMPLATE *psTemplate, BOOL add)
{
      SDWORD            spare = -1;
      UDWORD            inc, factoryType, factoryInc;
      FACTORY           *psFactory;
      BOOL        bAssigned = false, bCheckForCancel = false;
      UBYTE built, quantity, remaining;

      CHECK_STRUCTURE(psStructure);
      ASSERT( psStructure->player == productionPlayer,
            "factoryProdAdjust: called for incorrect player" );

      psFactory = &psStructure->pFunctionality->factory;
      factoryType = psFactory->psAssemblyPoint->factoryType;
      factoryInc = psFactory->psAssemblyPoint->factoryInc;

      //see if the template is already in the list
      for (inc=0; inc < MAX_PROD_RUN; inc++)
      {
            if (asProductionRun[factoryType][factoryInc][inc].psTemplate == psTemplate)
            {
                  //adjust the prod run
                  if (add)    //user left clicked, so increase # in queue
                  {
                        // Allows us to queue up more units up to MAX_IN_RUN instead of ignoring how many we have built from that queue
                        quantity = ++asProductionRun[factoryType][factoryInc][inc].quantity;
                        built = asProductionRun[factoryType][factoryInc][inc].built;
                        remaining = quantity - built;
                        // check to see if user canceled all orders in queue
                        if (remaining > MAX_IN_RUN)
                        {
                              asProductionRun[factoryType][factoryInc][inc].quantity = 0;
                              //initialise the template
                              asProductionRun[factoryType][factoryInc][inc].psTemplate = NULL;
                              bCheckForCancel = true;
                              //add power back if we were working on this one
                              if (psFactory->psSubject == (BASE_STATS *)psTemplate)
                              {
                                    addPower(psStructure->player, psFactory->powerAccrued);
                                    //set the factory's power accrued back to zero
                                    psFactory->powerAccrued = 0;
                              }
                        }
                  }
                  else  //user right clicked, so we subtract form queue
                  {
                        if (asProductionRun[factoryType][factoryInc][inc].quantity == 0)
                        {
                              asProductionRun[factoryType][factoryInc][inc].quantity = MAX_IN_RUN;
                        }
                        else
                        {
                              asProductionRun[factoryType][factoryInc][inc].quantity--;
                              //add power back if we were working on this one
                              if (psFactory->psSubject == (BASE_STATS *)psTemplate)
                              {
                                    addPower(psStructure->player, psFactory->powerAccrued);
                                    //set the factory's power accrued back to zero
                                    psFactory->powerAccrued = 0;
                              }

                              if (asProductionRun[factoryType][factoryInc][inc].quantity == 0)
                              {
                                    //initialise the template
                                    asProductionRun[factoryType][factoryInc][inc].psTemplate = NULL;
                                    bCheckForCancel = true;
                              }
                        }
                  }
                  bAssigned = true;
                  break;
            }
            //check to see if any empty slots
            if (spare == -1 && asProductionRun[factoryType][factoryInc][inc].quantity == 0)
            {
                  spare = inc;
            }
      }

      if (!bAssigned && spare != -1)
      {
            //start off a new template
            asProductionRun[factoryType][factoryInc][spare].psTemplate = psTemplate;
            if (add)
            {
                  asProductionRun[factoryType][factoryInc][spare].quantity = 1;
            }
            else
            {
                  //wrap around to max value
                  asProductionRun[factoryType][factoryInc][spare].quantity = MAX_IN_RUN;
            }
      }
      //if nothing is allocated then the current factory may have been cancelled
      if (bCheckForCancel)
      {
            for (inc=0; inc < MAX_PROD_RUN; inc++)
            {
                  if (asProductionRun[factoryType][factoryInc][inc].psTemplate != NULL)
                  {
                        break;
                  }
            }
            if (inc == MAX_PROD_RUN)
            {
                  //must have cancelled eveything - so tell the struct
                  psFactory->quantity = 0;
            }
      }
}


//returns the quantity of a specific template in the production list
UDWORD      getProductionQuantity(STRUCTURE *psStructure, DROID_TEMPLATE *psTemplate)
{
      UDWORD            inc, factoryType, factoryInc;
      FACTORY           *psFactory;

      if (psStructure == NULL) return 0;
      if (psStructure->player == productionPlayer)
      {
            psFactory = &psStructure->pFunctionality->factory;
            factoryType = psFactory->psAssemblyPoint->factoryType;
            factoryInc = psFactory->psAssemblyPoint->factoryInc;

            //see if the template is in the list
            for (inc=0; inc < MAX_PROD_RUN; inc++)
            {
                  if (asProductionRun[factoryType][factoryInc][inc].psTemplate == psTemplate)
                  {
                        return asProductionRun[factoryType][factoryInc][inc].quantity;
                  }
            }
      }

      //not in the list so none being produced
      return 0;
}


/*returns the quantity of a specific template in the production list that
have already been built*/
UDWORD      getProductionBuilt(STRUCTURE *psStructure, DROID_TEMPLATE *psTemplate)
{
      UDWORD            inc, factoryType, factoryInc;
      FACTORY           *psFactory;

      if (psStructure == NULL) return 0;
      if (psStructure->player == productionPlayer)
      {
            psFactory = &psStructure->pFunctionality->factory;
            factoryType = psFactory->psAssemblyPoint->factoryType;
            factoryInc = psFactory->psAssemblyPoint->factoryInc;

            //see if the template is in the list
            for (inc=0; inc < MAX_PROD_RUN; inc++)
            {
                  if (asProductionRun[factoryType][factoryInc][inc].psTemplate == psTemplate)
                  {
                        return asProductionRun[factoryType][factoryInc][inc].built;
                  }
            }
      }

      //not in the list so none being produced
      return 0;
}


/*looks through a players production list to see how many command droids
are being built*/
UBYTE checkProductionForCommand(UBYTE player)
{
      UBYTE       factoryInc, inc, factoryType;
      UBYTE       mask = 1, quantity = 0;

      if (player == productionPlayer)
      {
            //assumes Cyborg or VTOL droids are not Command types!
            factoryType = FACTORY_FLAG;

            for (factoryInc = 0; factoryInc < MAX_FACTORY; factoryInc++)
            {
                  //check to see if there is a factory
                  if ((factoryNumFlag[player][factoryType] & mask) == mask)
                  {
                        for (inc=0; inc < MAX_PROD_RUN; inc++)
                        {
                              if (asProductionRun[factoryType][factoryInc][inc].psTemplate)
                              {
                                    if (asProductionRun[factoryType][factoryInc][inc].psTemplate->
                                          droidType == DROID_COMMAND)
                                    {
                                          quantity = (UBYTE)(quantity  + (asProductionRun[factoryType][
                                                factoryInc][inc].quantity -   asProductionRun[
                                                factoryType][factoryInc][inc].built));
                                    }
                              }
                        }
                  }
                  mask <<= 1;
            }
      }
      return quantity;
}


// Count number of factories assignable to a command droid.
//
UWORD countAssignableFactories(UBYTE player,UWORD factoryType)
{
      UWORD       factoryInc;
      UBYTE       mask = 1, quantity = 0;

      ASSERT( player == selectedPlayer,
            "countAssignableFactories: should only be called for selectedPlayer" );

      for (factoryInc = 0; factoryInc < MAX_FACTORY; factoryInc++)
      {
            //check to see if there is a factory
            if ((factoryNumFlag[player][factoryType] & mask) == mask)
            {
                  quantity++;
            }
            mask <<= 1;
      }

      return quantity;
}


// check whether a factory of a certain number and type exists
BOOL checkFactoryExists(UDWORD player, UDWORD factoryType, UDWORD inc)
{
      ASSERT( player < MAX_PLAYERS,
            "checkFactoryExists: invalid player" );
      ASSERT( factoryType < NUM_FACTORY_TYPES,
            "checkFactoryExists: invalid factoryType" );

      return (factoryNumFlag[player][factoryType] & (1 << inc)) != 0;
}


//check that delivery points haven't been put down in invalid location
void checkDeliveryPoints(UDWORD version)
{
      UBYTE             inc;
      STRUCTURE         *psStruct;
      FACTORY                 *psFactory;
      REPAIR_FACILITY   *psRepair;
      UDWORD                              x, y;

      //find any factories
      for (inc = 0; inc < MAX_PLAYERS; inc++)
      {
            //don't bother checking selectedPlayer's - causes problems when try and use
            //validLocation since it finds that the DP is on itself! And validLocation
            //will have been called to put in down in the first place
            if (inc != selectedPlayer)
            {
                  for (psStruct = apsStructLists[inc]; psStruct != NULL; psStruct =
                        psStruct->psNext)
                  {
                        if(StructIsFactory(psStruct))
                        {
                              //check the DP
                              psFactory = &psStruct->pFunctionality->factory;
                              if (psFactory->psAssemblyPoint == NULL)//need to add one
                              {
                                    ASSERT( psFactory->psAssemblyPoint != NULL,
                                          "checkDeliveryPoints: no delivery point for factory" );
                              }
                              else
                              {
                                    setAssemblyPoint(psFactory->psAssemblyPoint, psFactory->psAssemblyPoint->
                                          coords.x, psFactory->psAssemblyPoint->coords.y, inc, true);
                              }
                        }
                        else if (psStruct->pStructureType->type == REF_REPAIR_FACILITY)
                        {
                              psRepair = &psStruct->pFunctionality->repairFacility;

                              if (psRepair->psDeliveryPoint == NULL)//need to add one
                              {
                                    if (version >= VERSION_19)
                                    {
                                          ASSERT( psRepair->psDeliveryPoint != NULL,"checkDeliveryPoints: no delivery point for repair facility" );
                                    }
                                    else
                                    {
                                          // add an assembly point
                                          if (!createFlagPosition(&psRepair->psDeliveryPoint, psStruct->player))
                                          {
                                                ASSERT(!"can't create new deilivery point for repair facility", "checkDeliveryPoints: unable to create new delivery point for repair facility");
                                                return;
                                          }
                                          addFlagPosition(psRepair->psDeliveryPoint);
                                          setFlagPositionInc(psStruct->pFunctionality, psStruct->player, REPAIR_FLAG);
                                          //initialise the assembly point position
                                          x = map_coord(psStruct->pos.x + 256);
                                          y = map_coord(psStruct->pos.y + 256);
                                          // Belt and braces - shouldn't be able to build too near edge
                                          setAssemblyPoint( psRepair->psDeliveryPoint, world_coord(x),
                                                world_coord(y), inc, true);
                                    }
                              }
                              else//check existing one
                              {
                                    setAssemblyPoint(psRepair->psDeliveryPoint, psRepair->psDeliveryPoint->
                                          coords.x, psRepair->psDeliveryPoint->coords.y, inc, true);
                              }
                        }
                  }
            }
      }
}


//adjust the loop quantity for this factory
void factoryLoopAdjust(STRUCTURE *psStruct, BOOL add)
{
      FACTORY           *psFactory;

      ASSERT( StructIsFactory(psStruct),
            "factoryLoopAdjust: structure is not a factory" );
      ASSERT( psStruct->player == selectedPlayer,
            "factoryLoopAdjust: should only be called for selectedPlayer" );

      psFactory = &psStruct->pFunctionality->factory;

      if (add)
      {
            //check for wrapping to infinite production
            if (psFactory->quantity == MAX_IN_RUN)
            {
                  psFactory->quantity = 0;
            }
            else
            {
                  //increment the count
                  psFactory->quantity++;
                  //check for limit - this caters for when on infinite production and want to wrap around
                  if (psFactory->quantity > MAX_IN_RUN)
                  {
                        psFactory->quantity = INFINITE_PRODUCTION;
                  }
            }
      }
      else
      {
            //decrement the count
            if (psFactory->quantity == 0)
            {
                  psFactory->quantity = INFINITE_PRODUCTION;
            }
            else
            {
                  psFactory->quantity--;
            }
      }
}


/*This function is called after a game is loaded so that any resource extractors
that are active are initialised for when to start*/
void checkResExtractorsActive(void)
{
      STRUCTURE         *psStruct;
      UBYTE             inc;

      for (inc = 0; inc < MAX_PLAYERS; inc++)
      {
            for (psStruct = apsStructLists[inc]; psStruct != NULL; psStruct =
                  psStruct->psNext)
            {
                  if (psStruct->pStructureType->type == REF_RESOURCE_EXTRACTOR)
                  {
                        if (psStruct->pFunctionality->resourceExtractor.active)
                        {
                              psStruct->pFunctionality->resourceExtractor.timeLastUpdated = gameTime;
                        }
                  }
            }
      }
}


/*Used for determining how much of the structure to draw as being built or demolished*/
float structHeightScale(STRUCTURE *psStruct)
{
      float retVal = (float)psStruct->currentBuildPts / (float)psStruct->pStructureType->buildPoints;

      if(retVal < 0.05f)
            retVal = 0.05f;

      return retVal;

}


/*compares the structure sensor type with the droid weapon type to see if the
FIRE_SUPPORT order can be assigned*/
BOOL structSensorDroidWeapon(STRUCTURE *psStruct, DROID *psDroid)
{
      //Watermelon:another crash when nStat is marked as 0xcd... FIXME: Why is nStat not initialized properly?
      //Added a safety check: Only units with weapons can be assigned.
      if (psDroid->numWeaps > 0)
      {
            //Standard Sensor Tower + indirect weapon droid (non VTOL)
            //else if (structStandardSensor(psStruct) && (psDroid->numWeaps &&
            if (structStandardSensor(psStruct) && (psDroid->asWeaps[0].nStat > 0 &&
                  !proj_Direct(asWeaponStats + psDroid->asWeaps[0].nStat)) &&
                  !isVtolDroid(psDroid))
            {
                  return true;
            }
            //CB Sensor Tower + indirect weapon droid (non VTOL)
            //if (structCBSensor(psStruct) && (psDroid->numWeaps &&
            else if (structCBSensor(psStruct) && (psDroid->asWeaps[0].nStat > 0 &&
                  !proj_Direct(asWeaponStats + psDroid->asWeaps[0].nStat)) &&
                  !isVtolDroid(psDroid))
            {
                  return true;
            }
            //VTOL Intercept Sensor Tower + any weapon VTOL droid
            //else if (structVTOLSensor(psStruct) && psDroid->numWeaps &&
            else if (structVTOLSensor(psStruct) && psDroid->asWeaps[0].nStat > 0 &&
                  isVtolDroid(psDroid))
            {
                  return true;
            }
            //VTOL CB Sensor Tower + any weapon VTOL droid
            //else if (structVTOLCBSensor(psStruct) && psDroid->numWeaps &&
            else if (structVTOLCBSensor(psStruct) && psDroid->asWeaps[0].nStat > 0 &&
                  isVtolDroid(psDroid))
            {
                  return true;
            }
      }

      //case not matched
      return false;
}


/*checks if the structure has a Counter Battery sensor attached - returns
true if it has*/
BOOL structCBSensor(const STRUCTURE* psStruct)
{
      // Super Sensor works as any type
      if (psStruct->pStructureType->pSensor
       && (psStruct->pStructureType->pSensor->type == INDIRECT_CB_SENSOR
        || psStruct->pStructureType->pSensor->type == SUPER_SENSOR)
       && psStruct->pStructureType->pSensor->location == LOC_TURRET)
      {
            return true;
      }

      return false;
}


/*checks if the structure has a Standard Turret sensor attached - returns
true if it has*/
BOOL structStandardSensor(const STRUCTURE* psStruct)
{
      // Super Sensor works as any type
      if (psStruct->pStructureType->pSensor
       && (psStruct->pStructureType->pSensor->type == STANDARD_SENSOR
        || psStruct->pStructureType->pSensor->type == SUPER_SENSOR)
       && psStruct->pStructureType->pSensor->location == LOC_TURRET)
      {
            return true;
      }

      return false;
}


/*checks if the structure has a VTOL Intercept sensor attached - returns
true if it has*/
BOOL structVTOLSensor(const STRUCTURE* psStruct)
{
      // Super Sensor works as any type
      if (psStruct->pStructureType->pSensor
       && (psStruct->pStructureType->pSensor->type == VTOL_INTERCEPT_SENSOR
        || psStruct->pStructureType->pSensor->type == SUPER_SENSOR)
       && psStruct->pStructureType->pSensor->location == LOC_TURRET)
      {
            return true;
      }

      return false;
}


/*checks if the structure has a VTOL Counter Battery sensor attached - returns
true if it has*/
BOOL structVTOLCBSensor(const STRUCTURE* psStruct)
{
      // Super Sensor works as any type
      if (psStruct->pStructureType->pSensor
       && (psStruct->pStructureType->pSensor->type == VTOL_CB_SENSOR
        || psStruct->pStructureType->pSensor->type == SUPER_SENSOR)
       && psStruct->pStructureType->pSensor->location == LOC_TURRET)
      {
            return true;
      }

      return false;
}


// check whether a rearm pad is clear
BOOL clearRearmPad(STRUCTURE *psStruct)
{
      if (psStruct->pStructureType->type == REF_REARM_PAD
       && (psStruct->pFunctionality->rearmPad.psObj == NULL
       || vtolHappy((DROID*)psStruct->pFunctionality->rearmPad.psObj)))
      {
            return true;
      }

      return false;
}


// return the nearest rearm pad
// if bClear is true it tries to find the nearest clear rearm pad in
// the same cluster as psTarget
// psTarget can be NULL
STRUCTURE * findNearestReArmPad(DROID *psDroid, STRUCTURE *psTarget, BOOL bClear)
{
      STRUCTURE         *psStruct, *psNearest, *psTotallyClear;
      SDWORD                  xdiff,ydiff, mindist, currdist, totallyDist;
      SDWORD                  cx,cy;

      if (psTarget != NULL)
      {
            cx = (SDWORD)psTarget->pos.x;
            cy = (SDWORD)psTarget->pos.y;
      }
      else
      {
            cx = (SDWORD)psDroid->pos.x;
            cy = (SDWORD)psDroid->pos.y;
      }

      mindist = SDWORD_MAX;
      totallyDist = SDWORD_MAX;
      psNearest = NULL;
      psTotallyClear = NULL;
      for(psStruct = apsStructLists[psDroid->player]; psStruct; psStruct=psStruct->psNext)
      {
            if ((psStruct->pStructureType->type == REF_REARM_PAD) &&
                  (psTarget == NULL || psTarget->cluster == psStruct->cluster) &&
                  (!bClear || clearRearmPad(psStruct)))
            {
                  xdiff = (SDWORD)psStruct->pos.x - cx;
                  ydiff = (SDWORD)psStruct->pos.y - cy;
                  currdist = xdiff*xdiff + ydiff*ydiff;
                  if (bClear && !vtolOnRearmPad(psStruct, psDroid))
                  {
                        if (currdist < totallyDist)
                        {
                              totallyDist = currdist;
                              psTotallyClear = psStruct;
                        }
                  }
                  else
                  {
                        if (currdist < mindist)
                        {
                              mindist = currdist;
                              psNearest = psStruct;
                        }
                  }
            }
      }

      if (bClear && (psTotallyClear != NULL))
      {
            psNearest = psTotallyClear;
      }

      return psNearest;
}


// clear a rearm pad for a droid to land on it
void ensureRearmPadClear(STRUCTURE *psStruct, DROID *psDroid)
{
      DROID *psCurr;
      SDWORD      tx,ty;

      tx = map_coord(psStruct->pos.x);
      ty = map_coord(psStruct->pos.y);

      for(psCurr = apsDroidLists[psDroid->player]; psCurr; psCurr=psCurr->psNext)
      {
            if (psCurr != psDroid
             && map_coord(psCurr->pos.x) == tx
             && map_coord(psCurr->pos.y) == ty
             && isVtolDroid(psCurr))
            {
                  actionDroidObj(psCurr, DACTION_CLEARREARMPAD, (BASE_OBJECT *)psStruct);
            }
      }
}


// return whether a rearm pad has a vtol on it
BOOL vtolOnRearmPad(STRUCTURE *psStruct, DROID *psDroid)
{
      DROID *psCurr;
      SDWORD      tx,ty;
      BOOL  found;

      tx = map_coord(psStruct->pos.x);
      ty = map_coord(psStruct->pos.y);

      found = false;
      for(psCurr = apsDroidLists[psDroid->player]; psCurr; psCurr=psCurr->psNext)
      {
            if (psCurr != psDroid
             && map_coord(psCurr->pos.x) == tx
             && map_coord(psCurr->pos.y) == ty)
            {
                  found = true;
                  break;
            }
      }

      return found;
}


/* Just returns true if the structure's present body points aren't as high as the original*/
BOOL  structIsDamaged(STRUCTURE *psStruct)
{
      if(psStruct->body < structureBody(psStruct))
      {
            return(true);
      }
      else
      {
            return(false);
      }
}

// give a structure from one player to another - used in Electronic Warfare
//returns pointer to the new structure
STRUCTURE * giftSingleStructure(STRUCTURE *psStructure, UBYTE attackPlayer, BOOL bFromScript)
{
      STRUCTURE           *psNewStruct, *psStruct;
      DROID               *psCurr;
      STRUCTURE_STATS     *psType, *psModule;
      UDWORD              x, y;
      UBYTE               capacity = 0, originalPlayer;
      SWORD               buildPoints = 0, i;
      BOOL                bPowerOn;
      UWORD               direction;

      CHECK_STRUCTURE(psStructure);

      //this is not the case for EW in multiPlayer mode
      if (!bMultiPlayer)
      {
            //added 'selectedPlayer == 0' to be able to test the game by changing player...
            //in this version of Warzone, the attack Player can NEVER be the selectedPlayer (unless from the script)
            if (!bFromScript && selectedPlayer == 0 && attackPlayer == selectedPlayer)
            {
                  ASSERT(bFromScript || selectedPlayer != 0 || attackPlayer != selectedPlayer, "giftSingleStructure: EW attack by selectedPlayer on a structure");
                  return NULL;
            }
      }

      //don't want the hassle in multiplayer either
      //and now we do! - AB 13/05/99

      if (bMultiPlayer)
      {
            //certain structures give specific results - the rest swap sides!
            if (!electronicReward(psStructure, attackPlayer))
            {
                  originalPlayer = psStructure->player;

                  //tell the system the structure no longer exists
                  (void)removeStruct(psStructure, false);

                  // remove structure from one list
                  removeStructureFromList(psStructure, apsStructLists);

                  psStructure->selected = false;

                  // change player id
                  psStructure->player     = (UBYTE)attackPlayer;

                  //restore the resistance value
                  psStructure->resistance = (UWORD)structureResistance(psStructure->
                        pStructureType, psStructure->player);

                  // add to other list.
                  addStructure(psStructure);

                  //check through the 'attackPlayer' players list of droids to see if any are targetting it
                  for (psCurr = apsDroidLists[attackPlayer]; psCurr != NULL; psCurr = psCurr->psNext)
                  {
                        if (psCurr->psTarget == (BASE_OBJECT *)psStructure)
                        {
                              orderDroid(psCurr, DORDER_STOP);
                              break;
                        }
                        for (i = 0;i < psCurr->numWeaps;i++)
                        {
                              if (psCurr->psActionTarget[i] == (BASE_OBJECT *)psStructure)
                              {
                                    orderDroid(psCurr, DORDER_STOP);
                                    break;
                              }
                        }
                        //check through order list
                        for (i = 0; i < psCurr->listSize; i++)
                        {
                              if (psCurr->asOrderList[i].psOrderTarget == (BASE_OBJECT *)psStructure)
                              {
                                    removeDroidOrderTarget(psCurr, i);
                                    // move the rest of the list down
                                    memmove(&psCurr->asOrderList[i], &psCurr->asOrderList[i] + 1,
                                          (psCurr->listSize - i) * sizeof(ORDER_LIST));
                                    //adjust list size
                                    psCurr->listSize -= 1;
                                    //initialise the empty last slot
                                    memset(psCurr->asOrderList + psCurr->listSize, 0,
                                          sizeof(ORDER_LIST));
                              }
                        }
                  }

                  //check through the 'attackPlayer' players list of structures to see if any are targetting it
                  for (psStruct = apsStructLists[attackPlayer]; psStruct != NULL; psStruct =
                        psStruct->psNext)
                  {
                        if (psStruct->psTarget[0] == (BASE_OBJECT *)psStructure)
                        {
                              setStructureTarget(psStruct, NULL, 0);
                        }
                  }

                  //add back into cluster system
                  clustNewStruct(psStructure);

                  //add back into the grid system
                  gridAddObject((BASE_OBJECT *)psStructure);

                  if (psStructure->status == SS_BUILT)
                  {
                        buildingComplete(psStructure);
                  }
                  //since the structure isn't being rebuilt, the visibility code needs to be adjusted
                  if (attackPlayer == selectedPlayer)
                  {
                        //make sure this structure is visible to selectedPlayer
                        psStructure->visible[selectedPlayer] = UBYTE_MAX;
                  }
            }

            //ASSERT( false,
            //    "giftSingleStructure: EW attack in multiplayer" );
            return NULL;
      }


      //save info about the structure
      psType = psStructure->pStructureType;
      x = psStructure->pos.x;
      y = psStructure->pos.y;
      direction = psStructure->direction;
      originalPlayer = psStructure->player;
      //save how complete the build process is
      if (psStructure->status == SS_BEING_BUILT)
      {
            buildPoints = psStructure->currentBuildPts;
      }
      //check module not attached
      psModule = getModuleStat(psStructure);
      if (psModule)
      {
            switch(psType->type)
            {
            case REF_POWER_GEN:
                  capacity = (UBYTE)psStructure->pFunctionality->powerGenerator.capacity;
                  break;
            case REF_RESEARCH:
                  capacity = (UBYTE)psStructure->pFunctionality->researchFacility.capacity;
                  break;
            case REF_FACTORY:
            case REF_VTOL_FACTORY:
                  capacity = psStructure->pFunctionality->factory.capacity;
                  break;
            default:
                  break;
            }
      }
      //get rid of the structure
      (void)removeStruct(psStructure, true);

      //make sure power is not used to build
      bPowerOn = powerCalculated;
      powerCalculated = false;
      //build a new one for the attacking player - set last element to true so it doesn't adjust x/y
      psNewStruct = buildStructure(psType, x, y, attackPlayer, true);
      if (psNewStruct)
      {
            psNewStruct->direction = direction;
            if (capacity)
            {
                  switch(psType->type)
                  {
                  case REF_POWER_GEN:
                  case REF_RESEARCH:
                        //build the module for powerGen and research
                        buildStructure(psModule, psNewStruct->pos.x, psNewStruct->pos.y,
                              attackPlayer, false);
                        break;
                  case REF_FACTORY:
                  case REF_VTOL_FACTORY:
                        //build the appropriate number of modules
                        while (capacity)
                        {
                              buildStructure(psModule, psNewStruct->pos.x, psNewStruct->pos.y,
                                    attackPlayer, false);
                              capacity--;
                        }
                        break;
                  default:
                        break;
                  }
            }
            if (buildPoints)
            {
                  psNewStruct->status = SS_BEING_BUILT;
                  psNewStruct->currentBuildPts = buildPoints;
            }
            else
            {
                  psNewStruct->status = SS_BUILT;
                  buildingComplete(psNewStruct);
            }

            if (!bMultiPlayer)

            {
                  //inform selectedPlayer that takeover has happened
                  if (originalPlayer == selectedPlayer)
                  {
                        if (wallDefenceStruct(psNewStruct->pStructureType))
                        {

                              audio_QueueTrackPos( ID_SOUND_NEXUS_DEFENCES_ABSORBED,
                                    psNewStruct->pos.x, psNewStruct->pos.y, psNewStruct->pos.z );

                        }
                        else
                        {

                              audio_QueueTrackPos( ID_SOUND_NEXUS_STRUCTURE_ABSORBED,
                                    psNewStruct->pos.x, psNewStruct->pos.y, psNewStruct->pos.z );

                        }
                        //make sure this structure is visible to selectedPlayer if the structure used to be selectedPlayers'
                        psNewStruct->visible[selectedPlayer] = UBYTE_MAX;
                  }
            }
      }
      powerCalculated = bPowerOn;
      return psNewStruct;
}


/* Code to have the structure's weapon assembly rock back upon firing */
void  structUpdateRecoil( STRUCTURE *psStruct )
{
      UDWORD      percent;
      UDWORD      recoil;
      float fraction;

      /* Check it's actually got a weapon */
      if(psStruct->asWeaps[0].nStat == 0)
      {
            return;
      }

      /* We have a weapon */
      if(gameTime > (psStruct->asWeaps[0].lastFired + STR_RECOIL_TIME) )
      {
            /* Recoil effect is over */
            psStruct->asWeaps[0].recoilValue = 0;
            return;
      }

      /* Where should the assembly be? */
      percent = PERCENT((gameTime-psStruct->asWeaps[0].lastFired),STR_RECOIL_TIME);

      /* Outward journey */
      if(percent >= 50)
      {
            recoil = (100 - percent)/5;
      }
      /* Return journey */
      else
      {
            recoil = percent/5;
      }

      fraction = (float)asWeaponStats[psStruct->asWeaps[0].nStat].recoilValue / 100.f;

      recoil = (float)recoil * fraction;

      /* Put it into the weapon data */
      psStruct->asWeaps[0].recoilValue = recoil;
}


/*checks that the structure stats have loaded up as expected - must be done after
all StructureStats parts have been loaded*/
BOOL checkStructureStats(void)
{
      UDWORD      structInc, inc;

      for (structInc=0; structInc < numStructureStats; structInc++)
      {
            if (asStructureStats[structInc].numFuncs != 0)
            {
                  for (inc = 0; inc < asStructureStats[structInc].numFuncs; inc++)
                  {

                        ASSERT( asStructureStats[structInc].asFuncList[inc] != NULL,
                  "checkStructureStats: \
                              Invalid function for structure %s",
                              asStructureStats[structInc].pName );

                  }
            }
            else
            {
                  if (asStructureStats[structInc].asFuncList != NULL)
                  {

                        ASSERT( false, "checkStructureStats:Invalid functions attached to structure %s",
                              asStructureStats[structInc].pName );

                        return false;
                  }
            }
      }
      return true;
}


/*returns the power cost to build this structure*/
UDWORD  structPowerToBuild(STRUCTURE *psStruct)
{
      STRUCTURE_STATS     *psStat;
      UBYTE               capacity;

      //see if this structure can have a module attached
      psStat = getModuleStat(psStruct);
      if (psStat)
      {
            capacity = 0;
            //are we building the module or the base structure?
            switch(psStruct->pStructureType->type)
            {
            case REF_POWER_GEN:
                  capacity = (UBYTE)psStruct->pFunctionality->powerGenerator.capacity;
                  break;
            case REF_RESEARCH:
                  capacity = (UBYTE)psStruct->pFunctionality->researchFacility.capacity;
                  break;
            case REF_FACTORY:
            case REF_VTOL_FACTORY:
                  capacity = psStruct->pFunctionality->factory.capacity;
                  break;
            default:
                  break;
            }
            if (capacity)
            {
                  //return the cost to build the module
                  return psStat->powerToBuild;
            }
            else
            {
                  //no module attached so building the base structure
                  return psStruct->pStructureType->powerToBuild;
            }
      }
      else
      {
            //not a module structure, so power cost is the one associated with the stat
            return psStruct->pStructureType->powerToBuild;
      }
}


//for MULTIPLAYER ONLY
//this adjusts the time the relevant action started if the building is attacked by EW weapon
void resetResistanceLag(STRUCTURE *psBuilding)
{
      if (bMultiPlayer)
      {
            switch (psBuilding->pStructureType->type)
            {
                  case REF_RESEARCH:
                  {
                        RESEARCH_FACILITY *psResFacility = &psBuilding->pFunctionality->researchFacility;

                        //if working on a topic
                        if (psResFacility->psSubject)
                        {
                              //adjust the start time for the current subject
                              if (psResFacility->timeStarted != ACTION_START_TIME)
                              {
                                    psResFacility->timeStarted += (gameTime - psBuilding->lastResistance);
                              }
                        }
                  }
                  case REF_FACTORY:
                  case REF_VTOL_FACTORY:
                  case REF_CYBORG_FACTORY:
                  {
                        FACTORY    *psFactory = &psBuilding->pFunctionality->factory;

                        //if working on a unit
                        if (psFactory->psSubject)
                        {
                              //adjust the start time for the current subject
                              if (psFactory->timeStarted != ACTION_START_TIME)
                              {
                                    psFactory->timeStarted += (gameTime - psBuilding->lastResistance);
                              }
                        }
                  }
                  default: //do nothing
                        break;
            }
      }
}


/*reveals all the terrain in the map*/
void revealAll(UBYTE player)
{
      UWORD   i, j;
      MAPTILE     *psTile;

      //reveal all tiles
      for(i=0; i<mapWidth; i++)
      {
            for(j=0; j<mapHeight; j++)
            {
                  psTile = mapTile(i,j);
                  SET_TILE_VISIBLE(player, psTile);
                  avInformOfChange(i,j);
            }
      }

      //the objects gets revealed in processVisibility()
}


/*checks the structure passed in is a Las Sat structure which is currently
selected - returns true if valid*/
BOOL lasSatStructSelected(STRUCTURE *psStruct)
{
      if ( (psStruct->selected || (bMultiPlayer && !isHumanPlayer(psStruct->player)))
            && psStruct->asWeaps[0].nStat
            && (asWeaponStats[psStruct->asWeaps[0].nStat].weaponSubClass == WSC_LAS_SAT))
      {
            return true;
      }

      return false;
}


/* Call CALL_NEWDROID script callback */
static void cbNewDroid(STRUCTURE *psFactory, DROID *psDroid)
{
      ASSERT(psDroid != NULL,
            "cbNewDroid: no droid assigned for CALL_NEWDROID callback");

      psScrCBNewDroid = psDroid;
      psScrCBNewDroidFact = psFactory;
      eventFireCallbackTrigger((TRIGGER_TYPE)CALL_NEWDROID);
      psScrCBNewDroid = NULL;
      psScrCBNewDroidFact = NULL;
}

// Check that psVictimStruct is not referred to by any other object in the game
BOOL structureCheckReferences(STRUCTURE *psVictimStruct)
{
      int plr, i;

      for (plr = 0; plr < MAX_PLAYERS; plr++)
      {
            STRUCTURE *psStruct;
            DROID *psDroid;

            for (psStruct = apsStructLists[plr]; psStruct != NULL; psStruct = psStruct->psNext)
            {
                  for (i = 0; i < psStruct->numWeaps; i++)
                  {
                        if ((STRUCTURE *)psStruct->psTarget[i] == psVictimStruct && psVictimStruct != psStruct)
                        {
#ifdef DEBUG
                              ASSERT(!"Illegal reference to structure", "Illegal reference to structure from %s line %d",
                                     psStruct->targetFunc[i], psStruct->targetLine[i]);
#endif
                              return false;
                        }
                  }
            }
            for (psDroid = apsDroidLists[plr]; psDroid != NULL; psDroid = psDroid->psNext)
            {
                  if ((STRUCTURE *)psDroid->psTarget == psVictimStruct)
                  {
#ifdef DEBUG
                        ASSERT(!"Illegal reference to structure", "Illegal reference to structure from %s line %d",
                               psDroid->targetFunc, psDroid->targetLine);
#endif
                        return false;
                  }
                  for (i = 0; i < psDroid->numWeaps; i++)
                  {
                        if ((STRUCTURE *)psDroid->psActionTarget[i] == psVictimStruct)
                        {
#ifdef DEBUG
                              ASSERT(!"Illegal reference to structure", "Illegal action reference to structure from %s line %d",
                                     psDroid->actionTargetFunc[i], psDroid->actionTargetLine[i]);
#endif
                              return false;
                        }
                  }
                  if ((STRUCTURE *)psDroid->psBaseStruct == psVictimStruct)
                  {
#ifdef DEBUG
                        ASSERT(!"Illegal reference to structure", "Illegal action reference to structure from %s line %d",
                               psDroid->baseFunc, psDroid->baseLine);
#endif
                        return false;
                  }
            }
      }
      return true;
}

Generated by  Doxygen 1.6.0   Back to index