Logo Search packages:      
Sourcecode: warzone2100 version File versions

scriptfuncs.c

/*
      This file is part of Warzone 2100.
      Copyright (C) 1999-2004  Eidos Interactive
      Copyright (C) 2005-2007  Warzone Resurrection Project

      Warzone 2100 is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published by
      the Free Software Foundation; either version 2 of the License, or
      (at your option) any later version.

      Warzone 2100 is distributed in the hope that it will be useful,
      but WITHOUT ANY WARRANTY; without even the implied warranty of
      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
      along with Warzone 2100; if not, write to the Free Software
      Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
 * ScriptFuncs.c
 *
 * All the C functions callable from the script code
 *
 */

#include <time.h>
#include <string.h>

#include "lib/framework/frame.h"
#include "lib/framework/strres.h"
#include "lib/widget/widget.h"

#include "effects.h"
#include "lib/script/script.h"
#include "scripttabs.h"
#include "lib/gamelib/gtime.h"
#include "objects.h"
#include "hci.h"
#include "message.h"
#include "intelmap.h"
#include "map.h"
#include "structure.h"
#include "display3d.h"
#include "research.h"
#include "lib/sound/audio.h"
#include "lib/sound/audio_id.h"
#include "power.h"
#include "console.h"
#include "scriptfuncs.h"
#include "geometry.h"
#include "visibility.h"
#include "gateway.h"
#include "drive.h"
#include "display.h"
#include "component.h"
#include "scriptextern.h"
#include "seqdisp.h"

#include "configuration.h"
#include "fpath.h"

#include "warzoneconfig.h"
#include "lighting.h"
#include "atmos.h"
#include "lib/sound/cdaudio.h"
#include "lib/netplay/netplay.h"
#include "multiplay.h"
#include "multigifts.h"
#include "multilimit.h"
#include "advvis.h"

#include "lib/ivis_common/piestate.h"
#include "wrappers.h"
#include "order.h"
#include "orderdef.h"
#include "mission.h"
#include "loop.h"
#include "frontend.h"
#include "group.h"
#include "transporter.h"
#include "radar.h"
#include "levels.h"
#include "mission.h"
#include "projectile.h"
#include "cluster.h"
#include "multigifts.h"             //because of giftRadar()
#include "aiexperience.h"
#include "display3d.h"              //for showRangeAtPos()
#include "multimenu.h"
#include "lib/script/chat_processing.h"
#include "keymap.h"

static INTERP_VAL scrFunctionResult;      //function return value to be pushed to stack

// If this is defined then check max number of units not reached before adding more.
#define SCRIPT_CHECK_MAX_UNITS

static SDWORD     bitMask[] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
static char       strParam1[MAXSTRLEN], strParam2[MAXSTRLEN];           //these should be used as string parameters for stackPopParams()

// -----------------------------------------------------------------------------------------
static BOOL structHasModule(STRUCTURE *psStruct);

/******************************************************************************************/
/*                 Check for objects in areas                                             */

// check for a base object being in range of a point
BOOL objectInRange(BASE_OBJECT *psList, SDWORD x, SDWORD y, SDWORD range)
{
      BASE_OBJECT       *psCurr;
      SDWORD                  xdiff, ydiff, rangeSq;

      // See if there is a droid in range
      rangeSq = range * range;
      for(psCurr = psList; psCurr; psCurr = psCurr->psNext)
      {
            // skip partially build structures
            if ( (psCurr->type == OBJ_STRUCTURE) &&
                   (((STRUCTURE *)psCurr)->status != SS_BUILT) )
            {
                  continue;
            }

            // skip flying vtols
            if ( (psCurr->type == OBJ_DROID) &&
                   isVtolDroid((DROID *)psCurr) &&
                   ((DROID *)psCurr)->sMove.Status != MOVEINACTIVE )
            {
                  continue;
            }

            xdiff = (SDWORD)psCurr->pos.x - x;
            ydiff = (SDWORD)psCurr->pos.y - y;
            if (xdiff*xdiff + ydiff*ydiff < rangeSq)
            {
                  return true;
            }
      }

      return false;
}

// -----------------------------------------------------------------------------------------
// Check for any player object being within a certain range of a position
BOOL scrObjectInRange(void)
{
      SDWORD            range, player, x,y;
      BOOL        found;

      if (!stackPopParams(4, VAL_INT, &player, VAL_INT, &x, VAL_INT, &y, VAL_INT, &range))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrObjectInRange: invalid player number" );
            return false;
      }

      found = objectInRange((BASE_OBJECT *)apsDroidLists[player], x,y, range) ||
                  objectInRange((BASE_OBJECT *)apsStructLists[player], x,y, range);

      scrFunctionResult.v.bval = found;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Check for a droid being within a certain range of a position
BOOL scrDroidInRange(void)
{
      SDWORD            range, player, x,y;
      BOOL        found;

      if (!stackPopParams(4, VAL_INT, &player, VAL_INT, &x, VAL_INT, &y, VAL_INT, &range))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrUnitInRange: invalid player number" );
            return false;
      }

      found = objectInRange((BASE_OBJECT *)apsDroidLists[player], x,y, range);

      scrFunctionResult.v.bval = found;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Check for a struct being within a certain range of a position
BOOL scrStructInRange(void)
{
      SDWORD            range, player, x,y;
      BOOL        found;

      if (!stackPopParams(4, VAL_INT, &player, VAL_INT, &x, VAL_INT, &y, VAL_INT, &range))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrStructInRange: invalid player number" );
            return false;
      }

      found = objectInRange((BASE_OBJECT *)apsStructLists[player], x,y, range);

      scrFunctionResult.v.bval = found;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL scrPlayerPower(void)
{
      SDWORD player;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }
      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrPlayerPower: invalid player number" );
            return false;
      }

      scrFunctionResult.v.ival = getPower(player);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }
      return true;
}


// -----------------------------------------------------------------------------------------
// check for a base object being in an area
static BOOL objectInArea(BASE_OBJECT *psList, SDWORD x1, SDWORD y1, SDWORD x2, SDWORD y2)
{
      BASE_OBJECT       *psCurr;
      SDWORD                  ox,oy;

      // See if there is a droid in Area
      for(psCurr = psList; psCurr; psCurr = psCurr->psNext)
      {
            // skip partially build structures
            if ( (psCurr->type == OBJ_STRUCTURE) &&
                   (((STRUCTURE *)psCurr)->status != SS_BUILT) )
            {
                  continue;
            }

            ox = (SDWORD)psCurr->pos.x;
            oy = (SDWORD)psCurr->pos.y;
            if (ox >= x1 && ox <= x2 &&
                  oy >= y1 && oy <= y2)
            {
                  return true;
            }
      }

      return false;
}

// -----------------------------------------------------------------------------------------
// Check for any player object being within a certain area
BOOL scrObjectInArea(void)
{
      SDWORD            player, x1,y1, x2,y2;
      BOOL        found;

      if (!stackPopParams(5, VAL_INT, &player, VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrObjectInArea: invalid player number" );
            return false;
      }

      found = objectInArea((BASE_OBJECT *)apsDroidLists[player], x1,y1, x2,y2) ||
                  objectInArea((BASE_OBJECT *)apsStructLists[player], x1,y1, x2,y2);

      scrFunctionResult.v.bval = found;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Check for a droid being within a certain area
BOOL scrDroidInArea(void)
{
      SDWORD            player, x1,y1, x2,y2;
      BOOL        found;

      if (!stackPopParams(5, VAL_INT, &player, VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrUnitInArea: invalid player number" );
            return false;
      }

      found = objectInArea((BASE_OBJECT *)apsDroidLists[player], x1,y1, x2,y2);

      scrFunctionResult.v.bval = found;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Check for a struct being within a certain Area of a position
BOOL scrStructInArea(void)
{
      SDWORD            player, x1,y1, x2,y2;
      BOOL        found;

      if (!stackPopParams(5, VAL_INT, &player, VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrStructInArea: invalid player number" );
            return false;
      }

      found = objectInArea((BASE_OBJECT *)apsStructLists[player], x1,y1, x2,y2);

      scrFunctionResult.v.bval = found;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
BOOL scrSeenStructInArea(void)
{
      BOOL        walls=false,found = false;
      SDWORD            player,enemy,x1,y1, x2,y2;
      STRUCTURE   *psCurr;
      SDWORD            ox,oy;

      // player, enemyplayer, walls, x1,r1,x2,y2
      if (!stackPopParams(7, VAL_INT, &player, VAL_INT, &enemy, VAL_BOOL,&walls,VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }


      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSeenStructInArea: invalid player number" );
            return false;
      }

      for(psCurr = apsStructLists[enemy]; psCurr; psCurr = psCurr->psNext)
      {
            // skip partially build structures
            if ( (psCurr->type == OBJ_STRUCTURE) && (((STRUCTURE *)psCurr)->status != SS_BUILT) )
            {
                  continue;
            }

            // possible skip walls
            if(walls && (psCurr->pStructureType->type != REF_WALL  || psCurr->pStructureType->type !=REF_WALLCORNER))
            {
                  continue;
            }

            ox = (SDWORD)psCurr->pos.x;
            oy = (SDWORD)psCurr->pos.y;
            if (ox >= x1 && ox <= x2 &&   oy >= y1 && oy <= y2)
            {
                  // structure is in area.
                  if(psCurr->visible[player])
                  {
                        found = true;
                  }
            }
      }

      scrFunctionResult.v.bval = found;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Check for a players structures but no walls being within a certain area
BOOL scrStructButNoWallsInArea(void)
{
      SDWORD            player, x1,y1, x2,y2;
      SDWORD            ox,oy;
      STRUCTURE   *psStruct;
      SDWORD            found = false;

      if (!stackPopParams(5, VAL_INT, &player, VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrStructButNoWallsInArea: invalid player number" );
            return false;
      }

      for(psStruct = apsStructLists[player]; psStruct; psStruct = psStruct->psNext)
      {
            if ((psStruct->pStructureType->type != REF_WALL) &&
                  (psStruct->pStructureType->type != REF_WALLCORNER) &&
                  (psStruct->status == SS_BUILT) )
            {
                  ox = (SDWORD)psStruct->pos.x;
                  oy = (SDWORD)psStruct->pos.y;
                  if ((ox >= x1) && (ox <= x2) &&
                        (oy >= y1) && (oy <= y2))
                  {
                        found = true;
                        break;
                  }
            }
      }

      scrFunctionResult.v.bval = found;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
// check for the number of base objects in an area
static SDWORD numObjectsInArea(BASE_OBJECT *psList, SDWORD x1, SDWORD y1, SDWORD x2, SDWORD y2)
{
      BASE_OBJECT       *psCurr;
      SDWORD                  ox,oy;
      SDWORD                  count;

      // See if there is a droid in Area
      count = 0;
      for(psCurr = psList; psCurr; psCurr = psCurr->psNext)
      {
            // skip partially build structures
            if ( (psCurr->type == OBJ_STRUCTURE) &&
                   (((STRUCTURE *)psCurr)->status != SS_BUILT) )
            {
                  continue;
            }

            ox = (SDWORD)psCurr->pos.x;
            oy = (SDWORD)psCurr->pos.y;
            if (ox >= x1 && ox <= x2 &&
                  oy >= y1 && oy <= y2)
            {
                  count += 1;
            }
      }

      return count;
}

// -----------------------------------------------------------------------------------------
// Count the number of player objects within a certain area
BOOL scrNumObjectsInArea(void)
{
      SDWORD            player, x1,y1, x2,y2;
      SDWORD            count;

      if (!stackPopParams(5, VAL_INT, &player, VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrNumObjectsInArea: invalid player number" );
            return false;
      }

      count = numObjectsInArea((BASE_OBJECT *)apsDroidLists[player], x1,y1, x2,y2) +
                  numObjectsInArea((BASE_OBJECT *)apsStructLists[player], x1,y1, x2,y2);

      scrFunctionResult.v.ival = count;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
// Count the number of player droids within a certain area
BOOL scrNumDroidsInArea(void)
{
      SDWORD            player, x1,y1, x2,y2;
      SDWORD            count;

      if (!stackPopParams(5, VAL_INT, &player, VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrNumUnitInArea: invalid player number" );
            return false;
      }

      count = numObjectsInArea((BASE_OBJECT *)apsDroidLists[player], x1,y1, x2,y2);

      scrFunctionResult.v.ival = count;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
// Count the number of player structures within a certain area
BOOL scrNumStructsInArea(void)
{
      SDWORD            player, x1,y1, x2,y2;
      SDWORD            count;

      if (!stackPopParams(5, VAL_INT, &player, VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrNumStructsInArea: invalid player number" );
            return false;
      }

      count = numObjectsInArea((BASE_OBJECT *)apsStructLists[player], x1,y1, x2,y2);

      scrFunctionResult.v.ival = count;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
// Count the number of player structures but not walls within a certain area
BOOL scrNumStructsButNotWallsInArea(void)
{
      SDWORD            player, x1,y1, x2,y2;
      SDWORD            count, ox,oy;
      STRUCTURE   *psStruct;

      if (!stackPopParams(5, VAL_INT, &player, VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrNumStructsButNotWallsInArea: invalid player number" );
            return false;
      }

      count = 0;
      for(psStruct = apsStructLists[player]; psStruct; psStruct = psStruct->psNext)
      {
            if ((psStruct->pStructureType->type != REF_WALL) &&
                  (psStruct->pStructureType->type != REF_WALLCORNER) &&
                  (psStruct->status == SS_BUILT))
            {
                  ox = (SDWORD)psStruct->pos.x;
                  oy = (SDWORD)psStruct->pos.y;
                  if ((ox >= x1) && (ox <= x2) &&
                        (oy >= y1) && (oy <= y2))
                  {
                        count += 1;
                  }
            }
      }

      scrFunctionResult.v.ival = count;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
// Count the number of structures in an area of a certain type
BOOL scrNumStructsByTypeInArea(void)
{
      SDWORD            player, type, x1,y1, x2,y2;
      SDWORD            count, ox,oy;
      STRUCTURE   *psStruct;

      if (!stackPopParams(6, VAL_INT, &player, VAL_INT, &type,
                              VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrNumStructsByTypeInArea: invalid player number" );
            return false;
      }

      count = 0;
      for(psStruct = apsStructLists[player]; psStruct; psStruct = psStruct->psNext)
      {
            if ((psStruct->pStructureType->type == (UDWORD)type) &&
                  (psStruct->status == SS_BUILT))
            {
                  ox = (SDWORD)psStruct->pos.x;
                  oy = (SDWORD)psStruct->pos.y;
                  if ((ox >= x1) && (ox <= x2) &&
                        (oy >= y1) && (oy <= y2))
                  {
                        count += 1;
                  }
            }
      }

      scrFunctionResult.v.ival = count;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
// Check for a droid having seen a certain object
BOOL scrDroidHasSeen(void)
{
      SDWORD            player;
      BASE_OBJECT *psObj;
      BOOL        seen;

      if (!stackPopParams(2, ST_BASEOBJECT, &psObj, VAL_INT, &player))
      {
            return false;
      }

      if (psObj == NULL)
      {
            ASSERT( false, "scrUnitHasSeen: NULL object" );
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrUnitHasSeen:player number is too high" );
            return false;
      }

      // See if any droid has seen this object
      seen = false;
      if (psObj->visible[player])
      {
            seen = true;
      }

      scrFunctionResult.v.bval = seen;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Enable a component to be researched
BOOL scrEnableComponent(void)
{
      SDWORD            player;
      INTERP_VAL  sVal;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }
      if (!stackPop(&sVal))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrEnableComponent:player number is too high" );
            return false;
      }

      // enable the appropriate component
      switch (sVal.type)
      {
      case ST_BODY:
            apCompLists[player][COMP_BODY][sVal.v.ival] = FOUND;
            break;
      case ST_PROPULSION:
            apCompLists[player][COMP_PROPULSION][sVal.v.ival] = FOUND;
            break;
      case ST_ECM:
            apCompLists[player][COMP_ECM][sVal.v.ival] = FOUND;
            break;
      case ST_SENSOR:
            apCompLists[player][COMP_SENSOR][sVal.v.ival] = FOUND;
            break;
      case ST_CONSTRUCT:
            apCompLists[player][COMP_CONSTRUCT][sVal.v.ival] = FOUND;
            break;
      case ST_WEAPON:
            apCompLists[player][COMP_WEAPON][sVal.v.ival] = FOUND;
            break;
      case ST_REPAIR:
            apCompLists[player][COMP_REPAIRUNIT][sVal.v.ival] = FOUND;
            break;
      case ST_BRAIN:
            apCompLists[player][COMP_BRAIN][sVal.v.ival] = FOUND;
            break;
      default:
            ASSERT( false, "scrEnableComponent: unknown type" );
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Make a component available
BOOL scrMakeComponentAvailable(void)
{
      SDWORD            player;
      INTERP_VAL  sVal;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }
      if (!stackPop(&sVal))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrMakeComponentAvailable:player number is too high" );
            return false;
      }

      // make the appropriate component available
      switch (sVal.type)
      {
      case ST_BODY:
            apCompLists[player][COMP_BODY][sVal.v.ival] = AVAILABLE;
            break;
      case ST_PROPULSION:
            apCompLists[player][COMP_PROPULSION][sVal.v.ival] = AVAILABLE;
            break;
      case ST_ECM:
            apCompLists[player][COMP_ECM][sVal.v.ival] = AVAILABLE;
            break;
      case ST_SENSOR:
            apCompLists[player][COMP_SENSOR][sVal.v.ival] = AVAILABLE;
            break;
      case ST_CONSTRUCT:
            apCompLists[player][COMP_CONSTRUCT][sVal.v.ival] = AVAILABLE;
            break;
      case ST_WEAPON:
            apCompLists[player][COMP_WEAPON][sVal.v.ival] = AVAILABLE;
            break;
      case ST_REPAIR:
            apCompLists[player][COMP_REPAIRUNIT][sVal.v.ival] = AVAILABLE;
            break;
      case ST_BRAIN:
            apCompLists[player][COMP_BRAIN][sVal.v.ival] = AVAILABLE;
            break;
      default:
            ASSERT( false, "scrEnableComponent: unknown type" );
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Add a droid
BOOL scrAddDroidToMissionList(void)
{
      SDWORD                  player;
      DROID_TEMPLATE    *psTemplate;
      DROID             *psDroid;

      if (!stackPopParams(2, ST_TEMPLATE, &psTemplate, VAL_INT, &player))
      {
            return false;
      }

/*    if ((UBYTE)player == selectedPlayer )
      {
            ASSERT( false, "scrAddDroidToMissionList: can't add own player to list" );
            return false;
      }*/

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAddUnitToMissionList:player number is too high" );
            return false;
      }

      ASSERT( psTemplate != NULL,
            "scrAddUnitToMissionList: Invalid template pointer" );

#ifdef SCRIPT_CHECK_MAX_UNITS
      // Don't build a new droid if player limit reached, unless it's a transporter.
      if( IsPlayerDroidLimitReached(player) && (psTemplate->droidType != DROID_TRANSPORTER) ) {
            debug( LOG_NEVER, "scrAddUnit : Max units reached ,player %d\n", player );
            psDroid = NULL;
      } else
#endif
      {
            psDroid = buildMissionDroid( psTemplate, 128, 128, player );
      }

      scrFunctionResult.v.oval = psDroid;
      if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Add a droid
BOOL scrAddDroid(void)
{
      SDWORD                  x, y, player;
//    INTERP_VAL        sVal;
      DROID_TEMPLATE    *psTemplate;
      DROID             *psDroid;

      if (!stackPopParams(4, ST_TEMPLATE, &psTemplate, VAL_INT, &x, VAL_INT, &y, VAL_INT, &player))
      {
            return false;
      }
/*    if (!stackPop(&sVal))
      {
            return false;
      }
      if (sVal.type != ST_TEMPLATE)
      {
            ASSERT( false, "scrAddDroid: type mismatch for object" );
            return false;
      }
      psTemplate = (DROID_TEMPLATE *)sVal.v.ival;
*/
      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAddUnit:player number is too high" );
            return false;
      }

      ASSERT( psTemplate != NULL,
            "scrAddUnit: Invalid template pointer" );

#ifdef SCRIPT_CHECK_MAX_UNITS
      // Don't build a new droid if player limit reached, unless it's a transporter.
      if( IsPlayerDroidLimitReached(player) && (psTemplate->droidType != DROID_TRANSPORTER) ) {
            debug( LOG_NEVER, "scrAddUnit : Max units reached ,player %d\n", player );
            psDroid = NULL;
      } else
#endif
      {
            psDroid = buildDroid(psTemplate, x, y, player, false);
            if (psDroid)
            {
                  addDroid(psDroid, apsDroidLists);
                  if (isVtolDroid(psDroid))
                  {
                        // vtols start in the air
                        moveMakeVtolHover(psDroid);
                  }
            }
      }

      scrFunctionResult.v.oval = psDroid;
      if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Add droid to transporter

BOOL scrAddDroidToTransporter(void)
{
      DROID *psTransporter, *psDroid;

      if (!stackPopParams(2, ST_DROID, &psTransporter, ST_DROID, &psDroid))
      {
            return false;
      }

      if (psTransporter == NULL || psDroid == NULL)
      {
            ASSERT(false, "scrAddUnitToTransporter: null unit passed");
            return true; // allow to continue
      }

      ASSERT(psTransporter != NULL, "scrAddUnitToTransporter: invalid transporter pointer");
      ASSERT(psDroid != NULL, "scrAddUnitToTransporter: invalid unit pointer");
      ASSERT(psTransporter->droidType == DROID_TRANSPORTER, "scrAddUnitToTransporter: invalid transporter type");

      /* check for space */
      if (checkTransporterSpace(psTransporter, psDroid))
      {
            if (droidRemove(psDroid, mission.apsDroidLists))
            {
                  grpJoin(psTransporter->psGroup, psDroid);
            }
      }

      return true;
}

// -----------------------------------------------------------------------------------------
//check for a building to have been destroyed
BOOL scrBuildingDestroyed(void)
{
      SDWORD            player;
      UDWORD            structureID;
//    INTERP_VAL  sVal;
      BOOL        destroyed;
      STRUCTURE   *psCurr;

      if (!stackPopParams(2, ST_STRUCTUREID, &structureID, VAL_INT, &player))
      {
            return false;
      }
/*    if (!stackPop(&sVal))
      {
            return false;
      }

      if (sVal.type != ST_STRUCTUREID)
      {
            ASSERT( false, "scrBuildingDestroyed: type mismatch for object" );
            return false;
      }
      structureID = (UDWORD)sVal.v.ival;
*/
      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrBuildingDestroyed:player number is too high" );
            return false;
      }

      destroyed = true;
      //look thru the players list to see if the structure still exists
      for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext)
      {
            if (psCurr->id == structureID)
            {
                  destroyed = false;
            }
      }

      scrFunctionResult.v.bval = destroyed;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Enable a structure to be built
BOOL scrEnableStructure(void)
{
      SDWORD            player, index;
//    INTERP_VAL  sVal;

      if (!stackPopParams(2, ST_STRUCTURESTAT, &index, VAL_INT, &player))
      {
            return false;
      }
/*    if (!stackPop(&sVal))
      {
            return false;
      }

      if (sVal.type != ST_STRUCTURESTAT)
      {
            ASSERT( false, "scrEnableStructure: type mismatch for object" );
            return false;
      }*/
      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrEnableStructure:player number is too high" );
            return false;
      }

      if (index < (SDWORD)0 || index > (SDWORD)numStructureStats)
      {
            ASSERT( false, "scrEnableStructure:invalid structure stat" );
            return false;
      }

      // enable the appropriate structure
      apStructTypeLists[player][index] = AVAILABLE;

      return true;
}



// -----------------------------------------------------------------------------------------
// Check if a structure can be built.
// currently PC skirmish only.
BOOL scrIsStructureAvailable(void)
{
      SDWORD            player, index;
      BOOL        bResult;

      if (!stackPopParams(2, ST_STRUCTURESTAT, &index, VAL_INT, &player))
      {
            return false;
      }
      if(   apStructTypeLists[player][index] == AVAILABLE)
      {
            bResult = true;
      }
      else
      {
            bResult = false;
      }

      scrFunctionResult.v.bval = bResult;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }
      return true;
}


// -----------------------------------------------------------------------------------------
//make the droid with the matching id the currently selected droid
BOOL scrSelectDroidByID(void)
{
      SDWORD                  player, droidID;
//    INTERP_VAL        sVal;
      BOOL              selected;

      if (!stackPopParams(2, ST_DROIDID, &droidID, VAL_INT, &player))
      {
            return false;
      }
/*    if (!stackPop(&sVal))
      {
            return false;
      }

      if (sVal.type != ST_DROIDID)
      {
            ASSERT( false, "scrSelectDroidByID: type mismatch for object" );
            return false;
      }
*/
      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSelectUnitByID:player number is too high" );
            return false;
      }

      selected = false;
      if (selectDroidByID(droidID, player))
      {
            selected = true;
      }

      //store the result cos might need to check the droid exists before doing anything else
      scrFunctionResult.v.bval = selected;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }
      return true;
}


// -----------------------------------------------------------------------------------------
// Pop up a message box with a number value in it
BOOL scrNumMB(void)
{
      SDWORD      val;

      if (!stackPopParams(1, VAL_INT, &val))
      {
            return false;
      }

/*    gameTimeStop();
      DBERROR(("scrNumMB: called by script with value: %d", val));
      gameTimeStart();*/
      debug( LOG_NEVER, "scrNumMB: called by script with value: %d\n", val );

      return true;
}


// -----------------------------------------------------------------------------------------
// Do an approximation to a square root
BOOL scrApproxRoot(void)
{
      SDWORD      val1, val2;

      if (!stackPopParams(2, VAL_INT, &val1, VAL_INT, &val2))
      {
            return false;
      }

      if (val1 < val2)
      {
            scrFunctionResult.v.ival = val2 + (val1 >> 1);
      }
      else
      {
            scrFunctionResult.v.ival = val1 + (val2 >> 1);
      }

      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }
      return true;
}

// -----------------------------------------------------------------------------------------
// Add a reticule button to the interface
BOOL scrAddReticuleButton(void)
{
      SDWORD      val;

      if (!stackPopParams(1, VAL_INT, &val))
      {
            return false;
      }

      if (selfTest)
      {
            return true;      // hack to prevent crashing in self-test
      }

      //set the appropriate flag to 'draw' the button
      switch (val)
      {
      case IDRET_OPTIONS:
            // bit of a hack here to keep compatibility with old scripts
            widgReveal(psWScreen, IDRET_COMMAND);
            break;
      case IDRET_COMMAND:
            widgReveal(psWScreen, IDRET_COMMAND);
            break;
      case IDRET_BUILD:
            widgReveal(psWScreen, IDRET_BUILD);
            break;
      case IDRET_MANUFACTURE:
            widgReveal(psWScreen, IDRET_MANUFACTURE);
            break;
      case IDRET_RESEARCH:
            widgReveal(psWScreen, IDRET_RESEARCH);
            break;
      case IDRET_INTEL_MAP:
            widgReveal(psWScreen, IDRET_INTEL_MAP);
            break;
      case IDRET_DESIGN:
            widgReveal(psWScreen, IDRET_DESIGN);
            break;
      case IDRET_CANCEL:
            widgReveal(psWScreen, IDRET_CANCEL);
            break;
      default:
            ASSERT( false, "scrAddReticuleButton: Invalid reticule Button ID" );
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
//Remove a reticule button from the interface
BOOL scrRemoveReticuleButton(void)
{
      SDWORD      val;
      BOOL  bReset;

      if (!stackPopParams(2, VAL_INT, &val,VAL_BOOL, &bReset))
      {
            return false;
      }

      if (selfTest)
      {
            return true;
      }

      if(bInTutorial)
      {
            if(bReset)  // not always desirable
            {
                  intResetScreen(true);
            }
      }
      switch (val)
      {
      case IDRET_OPTIONS:
            // bit of a hack here to keep compatibility with old scripts
            widgHide(psWScreen, IDRET_COMMAND);
            break;
      case IDRET_COMMAND:
            widgHide(psWScreen, IDRET_COMMAND);
            break;
      case IDRET_BUILD:
            widgHide(psWScreen, IDRET_BUILD);
            break;
      case IDRET_MANUFACTURE:
            widgHide(psWScreen, IDRET_MANUFACTURE);
            break;
      case IDRET_RESEARCH:
            widgHide(psWScreen, IDRET_RESEARCH);
            break;
      case IDRET_INTEL_MAP:
            widgHide(psWScreen, IDRET_INTEL_MAP);
            break;
      case IDRET_DESIGN:
            widgHide(psWScreen, IDRET_DESIGN);
            break;
      case IDRET_CANCEL:
            widgHide(psWScreen, IDRET_CANCEL);
            break;
      default:
            ASSERT( false, "scrAddReticuleButton: Invalid reticule Button ID" );
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// add a message to the Intelligence Display
BOOL scrAddMessage(void)
{
      MESSAGE                 *psMessage;
      MESSAGE_TYPE            msgType;
      SDWORD                  player;
      BOOL              playImmediate;
//    INTERP_VAL        sVal;
      VIEWDATA          *psViewData;
      UDWORD                  height;


      if (!stackPopParams(4, ST_INTMESSAGE, &psViewData , VAL_INT, &msgType,
                        VAL_INT, &player, VAL_BOOL, &playImmediate))
      {
            return false;
      }

/*
      if (!stackPop(&sVal))
      {
            return false;
      }

      if (sVal.type != ST_INTMESSAGE)
      {
            ASSERT( false, "scrAddMessage: type mismatch for object" );
            return false;
      }
*/
      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAddMessage:player number is too high" );
            return false;
      }

      //create the message
      psMessage = addMessage(msgType, false, player);
      if (psMessage)
      {
            //set the data
            psMessage->pViewData = (MSG_VIEWDATA *)psViewData;
            debug(LOG_MSG, "Adding %s pViewData=%p", psViewData->pName, psMessage->pViewData);
            if (msgType == MSG_PROXIMITY)
            {
                  //check the z value is at least the height of the terrain
                  height = map_Height(((VIEW_PROXIMITY *)psViewData->pData)->x,
                        ((VIEW_PROXIMITY *)psViewData->pData)->y);
                  if (((VIEW_PROXIMITY *)psViewData->pData)->z < height)
                  {
                        ((VIEW_PROXIMITY *)psViewData->pData)->z = height;
                  }
            }

            if (playImmediate && !selfTest)
            {
                  displayImmediateMessage(psMessage);
                  stopReticuleButtonFlash(IDRET_INTEL_MAP);
            }
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// remove a message from the Intelligence Display
BOOL scrRemoveMessage(void)
{
      MESSAGE                 *psMessage;
      MESSAGE_TYPE            msgType;
      SDWORD                  player;
      VIEWDATA          *psViewData;

      if (!stackPopParams(3, ST_INTMESSAGE, &psViewData , VAL_INT, &msgType, VAL_INT, &player))
      {
            return false;
      }

      ASSERT(player < MAX_PLAYERS && player >= 0, "bad player number");
      if (player >= MAX_PLAYERS || player < 0)
      {
            return false;
      }

      //find the message
      psMessage = findMessage((MSG_VIEWDATA *)psViewData, msgType, player);
      ASSERT(psMessage, "cannot find message - %s", psViewData->pName);
      if (psMessage)
      {
            //delete it
            debug(LOG_MSG, "Removing %s", psViewData->pName);
            removeMessage(psMessage, player);
      }
      else
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// add a tutorial message to the Intelligence Display
/*BOOL scrAddTutorialMessage(void)
{
      SDWORD                  player;
      VIEWDATA          *psViewData;


      if (!stackPopParams(2, ST_INTMESSAGE, &psViewData , VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAddTutorialMessage:player number is too high" );
            return false;
      }

      //set the data
      tutorialMessage.pViewData = psViewData;
      tutorialMessage.player = player;

      //play the tutorial message immediately
      psCurrentMsg = &tutorialMessage;
      initTextDisplay(psCurrentMsg, font_regular, 255);
      addIntelScreen(true);

      return true;
}*/

// -----------------------------------------------------------------------------------------
/*builds a droid in the specified factory*/
BOOL scrBuildDroid(void)
{
      SDWORD                  player, productionRun;
//    INTERP_VAL        sVal, sVal2;
      STRUCTURE         *psFactory;
      DROID_TEMPLATE    *psTemplate;

      if (!stackPopParams(4, ST_TEMPLATE, &psTemplate, ST_STRUCTURE, &psFactory,
                              VAL_INT, &player, VAL_INT, &productionRun))
      {
            return false;
      }

      if (psFactory == NULL)
      {
            ASSERT( false, "scrBuildUnit: NULL factory object" );
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrBuildUnit:player number is too high" );
            return false;
      }

      if (productionRun > UBYTE_MAX)
      {
            ASSERT( false, "scrBuildUnit: production run too high" );
            return false;
      }

      ASSERT( psFactory != NULL,
            "scrBuildUnit: Invalid structure pointer" );
      ASSERT( (psFactory->pStructureType->type == REF_FACTORY ||
            psFactory->pStructureType->type == REF_CYBORG_FACTORY ||
            psFactory->pStructureType->type == REF_VTOL_FACTORY),
            "scrBuildUnit: structure is not a factory" );
      ASSERT( psTemplate != NULL,
            "scrBuildUnit: Invalid template pointer" );

      //check building the right sort of droid for the factory
      if (!validTemplateForFactory(psTemplate, psFactory))
      {

            ASSERT( false, "scrBuildUnit: invalid template - %s for factory - %s",
                  psTemplate->aName, psFactory->pStructureType->pName );

            return false;
      }

      structSetManufacture(psFactory, psTemplate, (UBYTE)productionRun);

      return true;
}

// -----------------------------------------------------------------------------------------
// for a specified structure, set the assembly point droids go to when built
BOOL  scrSetAssemblyPoint(void)
{
      SDWORD            x, y;
      STRUCTURE   *psBuilding;

      if (!stackPopParams(3, ST_STRUCTURE, &psBuilding, VAL_INT, &x, VAL_INT, &y))
      {
            return false;
      }

      if (psBuilding == NULL)
      {
            ASSERT( false, "scrSetAssemblyPoint: NULL structure" );
            return false;
      }

      if (psBuilding->pStructureType->type != REF_FACTORY &&
            psBuilding->pStructureType->type != REF_CYBORG_FACTORY &&
            psBuilding->pStructureType->type != REF_VTOL_FACTORY)
      {
            ASSERT( false, "scrSetAssemblyPoint: structure is not a factory" );
            return false;
      }

      setAssemblyPoint(((FACTORY *)psBuilding->pFunctionality)->psAssemblyPoint,x,y,
        psBuilding->player, true);

      return true;
}

// -----------------------------------------------------------------------------------------
// test for structure is idle or not
BOOL  scrStructureIdle(void)
{
//    INTERP_VAL  sVal;
      STRUCTURE   *psBuilding;
      BOOL        idle;

      if (!stackPopParams(1, ST_STRUCTURE, &psBuilding))
      {
            return false;
      }
//    DBPRINTF(("scrStructureIdle called\n"));

      if (psBuilding == NULL)
      {
            ASSERT( false, "scrStructureIdle: NULL structure" );
            return false;
      }

      //test for idle
      idle = false;
      if (structureIdle(psBuilding))
      {
            idle = true;
      }


//    DBPRINTF(("structure %p is %d\n",psBuilding,idle));

      scrFunctionResult.v.bval = idle;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// sends a players droids to a location to attack
BOOL  scrAttackLocation(void)
{
      SDWORD            player, x, y;

      if (!stackPopParams(3, VAL_INT, &x, VAL_INT, &y, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAttackLocation:player number is too high" );
            return false;
      }

      debug(LOG_ERROR, "UNUSED FUNCTION attackLocation called - do not use!");

      return true;
}

// -----------------------------------------------------------------------------------------
//Destroy a feature
BOOL scrDestroyFeature(void)
{
      FEATURE           *psFeature;
//    INTERP_VAL  sVal;

      if (!stackPopParams(1, ST_FEATURE, &psFeature))
      {
            return false;
      }

      if (psFeature == NULL)
      {
            ASSERT( psFeature != NULL,
                  "scrDestroyFeature: Invalid feature pointer" );
      }

      removeFeature(psFeature);

      return true;
}




// -----------------------------------------------------------------------------------------
// static vars to enum features.
static      FEATURE_STATS     *psFeatureStatToFind[MAX_PLAYERS];
static      SDWORD                  playerToEnum[MAX_PLAYERS];
static  SDWORD                getFeatureCount[MAX_PLAYERS]={0};
static      FEATURE                 *psCurrEnumFeature[MAX_PLAYERS];

// -----------------------------------------------------------------------------------------
// init enum visible features.
BOOL scrInitGetFeature(void)
{
      SDWORD                  player,iFeat,bucket;

      if ( !stackPopParams(3, ST_FEATURESTAT, &iFeat,  VAL_INT, &player,VAL_INT,&bucket) )
      {
            return false;
      }

      ASSERT(bucket >= 0 && bucket < MAX_PLAYERS,
            "scrInitGetFeature: bucket out of bounds: %d", bucket);

      ASSERT(player >= 0 && player < MAX_PLAYERS,
            "scrInitGetFeature: player out of bounds: %d", player);

      psFeatureStatToFind[bucket]         = (FEATURE_STATS *)(asFeatureStats + iFeat);                      // find this stat
      playerToEnum[bucket]                = player;                     // that this player can see
      psCurrEnumFeature[bucket]           = apsFeatureLists[0];
      getFeatureCount[bucket]             = 0;                          // start at the beginning of list.
      return true;
}

// -----------------------------------------------------------------------------------------
// get next visible feature of required type
// notes:   can't rely on just using the feature list, since it may change
//                between calls, Use an index into list instead.
//                Doesn't return Features sharing a tile with a structure.
//                Skirmish Only, dunno if kev uses this?
BOOL scrGetFeature(void)
{
      SDWORD      bucket,count;
      FEATURE     *psFeat;

      if ( !stackPopParams(1,VAL_INT,&bucket) )
      {
            ASSERT( false, "scrGetFeature: Failed to pop player number from stack" );
            return false;
      }

      ASSERT(bucket >= 0 && bucket < MAX_PLAYERS,
            "scrGetFeature: bucket out of bounds: %d", bucket);

      count =0;
      // go to the correct start point in the feature list.
      for(psFeat=apsFeatureLists[0];psFeat && count<getFeatureCount[bucket] ;count++)
      {
            psFeat = psFeat->psNext;
      }

      if(psFeat == NULL)            // no more to find.
      {
            scrFunctionResult.v.oval = NULL;
            if (!stackPushResult((INTERP_TYPE)ST_FEATURE, &scrFunctionResult))
            {
                  ASSERT( false, "scrGetFeature: Failed to push result" );
                  return false;
            }
            return true;
      }

      // check to see if badly called
      if(psFeatureStatToFind[bucket] == NULL)
      {
            debug( LOG_NEVER, "invalid feature to find. possibly due to save game\n" );
            scrFunctionResult.v.oval = NULL;
            if(!stackPushResult((INTERP_TYPE)ST_FEATURE, &scrFunctionResult))
            {
                  ASSERT( false, "scrGetFeature: Failed to push result" );
                  return false;
            }
            return true;
      }

      // begin searching the feature list for the required stat.
      while(psFeat)
      {
            if (psFeat->psStats->subType == psFeatureStatToFind[bucket]->subType
             && psFeat->visible[playerToEnum[bucket]] != 0
             && !TileHasStructure(mapTile(map_coord(psFeat->pos.x), map_coord(psFeat->pos.y)))
             && !fireOnLocation(psFeat->pos.x,psFeat->pos.y)            // not burning.
               )
            {
                  scrFunctionResult.v.oval = psFeat;
                  if (!stackPushResult((INTERP_TYPE)ST_FEATURE, &scrFunctionResult))      //    push scrFunctionResult
                  {
                        ASSERT( false, "scrGetFeature: Failed to push result" );
                        return false;
                  }

                  getFeatureCount[bucket]++;
                  return true;
            }
            getFeatureCount[bucket]++;
            psFeat=psFeat->psNext;
      }

      // none found
      scrFunctionResult.v.oval = NULL;
      if (!stackPushResult((INTERP_TYPE)ST_FEATURE,  &scrFunctionResult))
      {
            ASSERT( false, "scrGetFeature: Failed to push result" );
            return false;
      }
      return true;
}

/* Faster implementation of scrGetFeature -  assumes no features
 * are deleted between calls, unlike scrGetFeature also returns
 * burning features (mainly relevant for burning oil resources)
 */
BOOL scrGetFeatureB(void)
{
      SDWORD      bucket;

      if ( !stackPopParams(1,VAL_INT,&bucket) )
      {
            ASSERT( false, "scrGetFeatureB: Failed to pop player number from stack" );
            return false;
      }

      ASSERT(bucket >= 0 && bucket < MAX_PLAYERS,
            "scrGetFeatureB: bucket out of bounds: %d", bucket);

      // check to see if badly called
      if(psFeatureStatToFind[bucket] == NULL)
      {
            debug( LOG_NEVER, "invalid feature to find. possibly due to save game\n" );
            scrFunctionResult.v.oval = NULL;
            if(!stackPushResult((INTERP_TYPE)ST_FEATURE, &scrFunctionResult))
            {
                  ASSERT( false, "scrGetFeatureB: Failed to push result" );
                  return false;
            }
            return true;
      }

      // begin searching the feature list for the required stat.
      while(psCurrEnumFeature[bucket])
      {
            if(   ( psCurrEnumFeature[bucket]->psStats->subType == psFeatureStatToFind[bucket]->subType)
                  &&( psCurrEnumFeature[bucket]->visible[playerToEnum[bucket]]      != 0)
                  &&!TileHasStructure(mapTile(map_coord(psCurrEnumFeature[bucket]->pos.x), map_coord(psCurrEnumFeature[bucket]->pos.y)))
                   /*&&!fireOnLocation(psCurrEnumFeature[bucket]->pos.x,psCurrEnumFeature[bucket]->pos.y )*/            // not burning.
                  )
            {
                  scrFunctionResult.v.oval = psCurrEnumFeature[bucket];
                  if (!stackPushResult((INTERP_TYPE)ST_FEATURE, &scrFunctionResult))      //    push scrFunctionResult
                  {
                        ASSERT( false, "scrGetFeatureB: Failed to push result" );
                        return false;
                  }
                  psCurrEnumFeature[bucket] = psCurrEnumFeature[bucket]->psNext;
                  return true;
            }

            psCurrEnumFeature[bucket] = psCurrEnumFeature[bucket]->psNext;
      }

      // none found
      scrFunctionResult.v.oval = NULL;
      if (!stackPushResult((INTERP_TYPE)ST_FEATURE,  &scrFunctionResult))
      {
            ASSERT( false, "scrGetFeatureB: Failed to push result" );
            return false;
      }
      return true;
}


/*
// -----------------------------------------------------------------------------------------
// enum next visible feature of required type.
// note: wont return features covered by structures (ie oil resources)
// YUK NASTY BUG. CANT RELY ON THE FEATURE LIST BETWEEN CALLS.
BOOL scrGetFeature(void)
{
      SDWORD      bucket;

      if ( !stackPopParams(1,VAL_INT,&bucket) )
      {
            return false;
      }

      while(psCurrEnumFeature[bucket])
      {
            if(   ( psCurrEnumFeature[bucket]->psStats->subType == psFeatureStatToFind[bucket]->subType)
                  &&
                  ( psCurrEnumFeature[bucket]->visible[playerToEnum[bucket]]  != 0)
                  &&
                  !TileHasStructure(mapTile(map_coord(psCurrEnumFeature[bucket]->pos.x), map_coord(psCurrEnumFeature[bucket]->pos.y)))
               )
            {
                  if (!stackPushResult(ST_FEATURE,(UDWORD) psCurrEnumFeature[bucket]))                //    push scrFunctionResult
                  {
                        return false;
                  }
                  psCurrEnumFeature[bucket] = psCurrEnumFeature[bucket]->psNext;
                  return true;
            }

            psCurrEnumFeature[bucket] = psCurrEnumFeature[bucket]->psNext;
      }
      // push NULL, none found;
      if (!stackPushResult(ST_FEATURE, (UDWORD)NULL))
      {
            return false;
      }
      return true;
}
*/

// -----------------------------------------------------------------------------------------
//Add a feature
BOOL scrAddFeature(void)
{
      FEATURE_STATS     *psStat;
      FEATURE                 *psFeat = NULL;
      SDWORD                  iX, iY, iMapX, iMapY, iTestX, iTestY, iFeat;

      if ( !stackPopParams(3, ST_FEATURESTAT, &iFeat,
             VAL_INT, &iX, VAL_INT, &iY ) )
      {
            return false;
      }

      psStat = (FEATURE_STATS *)(asFeatureStats + iFeat);

      ASSERT( psStat != NULL,
                  "scrAddFeature: Invalid feature pointer" );

      if ( psStat != NULL )
      {
            iMapX = map_coord(iX);
            iMapY = map_coord(iY);

            /* check for wrecked feature already on-tile and remove */
            for(psFeat = apsFeatureLists[0]; psFeat; psFeat = psFeat->psNext)
            {
                  iTestX = map_coord(psFeat->pos.x);
                  iTestY = map_coord(psFeat->pos.y);

                  if ( (iTestX == iMapX) && (iTestY == iMapY) )
                  {
                        if ( psFeat->psStats->subType == FEAT_BUILD_WRECK )
                        {
                              /* remove feature */
                              removeFeature( psFeat );
                              break;
                        }
                        else
                        {
                              ASSERT( false,
                              "scrAddFeature: building feature on tile already occupied\n" );
                        }
                  }
            }

            psFeat = buildFeature( psStat, iX, iY, false );
      }

      scrFunctionResult.v.oval = psFeat;
      if (!stackPushResult((INTERP_TYPE)ST_FEATURE, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
//Add a structure
BOOL scrAddStructure(void)
{
      STRUCTURE_STATS         *psStat;
      STRUCTURE               *psStruct = NULL;
      SDWORD                        iX, iY, iMapX, iMapY;//, iWidth, iBreadth;
      SDWORD                        iStruct, iPlayer;//, iW, iB;

      if ( !stackPopParams( 4, ST_STRUCTURESTAT, &iStruct, VAL_INT, &iPlayer,
                                           VAL_INT, &iX, VAL_INT, &iY ) )
      {
            return false;
      }

      psStat = (STRUCTURE_STATS *)(asStructureStats + iStruct);

      ASSERT( psStat != NULL,
                  "scrAddStructure: Invalid feature pointer" );

      if ( psStat != NULL )
      {
            /* offset coords so building centre at (iX, iY) */
/*          no longer necessary - buildStruct no longer uses top left
            iX -= psStat->baseWidth*TILE_UNITS/2;
            iY -= psStat->baseBreadth*TILE_UNITS/2;*/

            iMapX = map_coord(iX);
            iMapY = map_coord(iY);

            /* check for structure already on-tile */
            if(TileHasStructure(mapTile(iMapX,iMapY)))
            {
                  ASSERT( false,
                  "scrAddStructure: tile already occupied by structure\n" );
            }

            psStruct = buildStructure( psStat, iX, iY, iPlayer, false );
            if ( psStruct != NULL )
            {
                  psStruct->status = SS_BUILT;
                  buildingComplete(psStruct);

            /*
            Apart from this being wrong (iWidth = 0 when psStat->baseWidth = 1
            and you end up in an infinite loop) we don't need to do this here
            since the map is flattened as part of buildStructure

                  iWidth   = psStat->baseWidth/2;
                  iBreadth = psStat->baseBreadth/2;

                  // flatten tiles across building base
                  for ( iW=iMapX; iW<=iMapX+(SDWORD)psStat->baseWidth; iW+=iWidth )
                  {
                        for ( iB=iMapY; iB<=iMapY+(SDWORD)psStat->baseBreadth; iB+=iBreadth )
                        {
                              setTileHeight(iW, iB, psStruct->pos.z);
                        }
                  }*/
            }
      }

      scrFunctionResult.v.oval = psStruct;
      if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
//Destroy a structure
BOOL scrDestroyStructure(void)
{
      STRUCTURE   *psStruct;

      if (!stackPopParams(1, ST_STRUCTURE, &psStruct))
      {
            return false;
      }

      if (psStruct == NULL)
      {
            ASSERT( psStruct != NULL,
                  "scrDestroyStructure: Invalid structure pointer" );
      }

      removeStruct( psStruct, true );

      return true;
}



// -----------------------------------------------------------------------------------------
//NEXT 2 FUNCS ONLY USED IN MULTIPLAYER AS FAR AS I KNOW (25 AUG98) alexl.
// static vars to enum structs;
static      STRUCTURE_STATS   *psStructStatToFind;
static      UDWORD                  playerToEnumStruct;
static      UDWORD                  enumStructCount;
static      BOOL              structfindany;
static      SDWORD                  playerVisibleStruct;          //player whose structures must be visible

//for the bucket version
static      STRUCTURE_STATS   *psStructStatToFindB[MAX_PLAYERS];
static      UDWORD                  playerToEnumStructB[MAX_PLAYERS];
static      UDWORD                  enumStructCountB[MAX_PLAYERS];
static      BOOL              structfindanyB[MAX_PLAYERS];
static      SDWORD                  playerVisibleStructB[MAX_PLAYERS];        //player whose structures must be visible
// init enum visible structures.
BOOL scrInitEnumStruct(void)
{
      SDWORD            lookingPlayer,iStat,targetPlayer;
      BOOL        any;

      if ( !stackPopParams(4,VAL_BOOL,&any, ST_STRUCTURESTAT, &iStat,  VAL_INT, &targetPlayer, VAL_INT, &lookingPlayer) )
      {
            return false;
      }

      ASSERT(targetPlayer >= 0 && targetPlayer < MAX_PLAYERS,
            "scrInitEnumStructB: targetPlayer out of bounds: %d", targetPlayer);

      ASSERT(lookingPlayer >= 0 && lookingPlayer < MAX_PLAYERS,
            "scrInitEnumStructB: lookingPlayer out of bounds: %d", lookingPlayer);

      structfindany = any;

      psStructStatToFind      = (STRUCTURE_STATS *)(asStructureStats + iStat);
      playerToEnumStruct      = (UDWORD)targetPlayer;
      playerVisibleStruct = lookingPlayer;            //remember who must be able to see the structure
      enumStructCount         = 0;
      return true;
}

// -----------------------------------------------------------------------------------------
BOOL scrEnumStruct(void)
{
      UDWORD            count;
      STRUCTURE   *psStruct;

      // go to the correct start point in the structure list.
      count = 0;
      for(psStruct=apsStructLists[playerToEnumStruct];psStruct && count<enumStructCount;count++)
      {
            psStruct = psStruct->psNext;
      }

      if(psStruct == NULL)          // no more to find.
      {
            scrFunctionResult.v.oval = NULL;
            if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
            {
                  return false;
            }
            return true;
      }

      while(psStruct)   // find a visible structure of required type.
      {
//          if(   (structfindany || (psStruct->pStructureType->type == psStructStatToFind->type))
            if(   (structfindany || (psStruct->pStructureType->ref == psStructStatToFind->ref))
                  &&
                  ((playerVisibleStruct < 0) || (psStruct->visible[playerVisibleStruct])) //fix: added playerVisibleStruct for visibility test
                  )
            {
                  scrFunctionResult.v.oval = psStruct;
                  if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))                //    push scrFunctionResult
                  {
                        return false;
                  }
                  enumStructCount++;

                  return true;
            }
            enumStructCount++;
            psStruct = psStruct->psNext;
      }
      // push NULL, none found;
      scrFunctionResult.v.oval = NULL;
      if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
      {
            return false;
      }
      return true;
}

// init enum visible structures - takes bucket as additional parameter
BOOL scrInitEnumStructB(void)
{
      SDWORD            lookingPlayer,iStat,targetPlayer,bucket;
      BOOL        any;

      if ( !stackPopParams(5,VAL_BOOL,&any, ST_STRUCTURESTAT, &iStat,
            VAL_INT, &targetPlayer, VAL_INT, &lookingPlayer, VAL_INT, &bucket) )
      {
            return false;
      }

      ASSERT(targetPlayer >= 0 && targetPlayer < MAX_PLAYERS,
            "scrInitEnumStructB: targetPlayer out of bounds: %d", targetPlayer);

      ASSERT(lookingPlayer >= 0 && lookingPlayer < MAX_PLAYERS,
            "scrInitEnumStructB: lookingPlayer out of bounds: %d", lookingPlayer);

      ASSERT(bucket >= 0 && bucket < MAX_PLAYERS,
            "scrInitEnumStructB: bucket out of bounds: %d", bucket);

      /* Any structure type regardless of the passed type? */
      structfindanyB[bucket] = any;

      psStructStatToFindB[bucket]   = (STRUCTURE_STATS *)(asStructureStats + iStat);
      playerToEnumStructB[bucket]   = (UDWORD)targetPlayer;
      playerVisibleStructB[bucket] = lookingPlayer;         //remember who must be able to see the structure
      enumStructCountB[bucket] = 0;

      return true;
}

// Similar to scrEnumStruct, but uses bucket
BOOL scrEnumStructB(void)
{
      SDWORD            bucket;
      UDWORD            count;
      STRUCTURE   *psStruct;

      if ( !stackPopParams(1, VAL_INT, &bucket) )
      {
            return false;
      }

      ASSERT(bucket >= 0 && bucket < MAX_PLAYERS,
            "scrEnumStructB: bucket out of bounds: %d", bucket);

      // go to the correct start point in the structure list.
      count = 0;
      for(psStruct=apsStructLists[playerToEnumStructB[bucket]];psStruct && count<enumStructCountB[bucket];count++)
      {
            psStruct = psStruct->psNext;
      }

      if(psStruct == NULL)          // no more to find.
      {
            scrFunctionResult.v.oval = NULL;
            if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
            {
                  return false;
            }
            return true;
      }

      while(psStruct)   // find a visible structure of required type.
      {
            if(   (structfindanyB[bucket] || (psStruct->pStructureType->ref == psStructStatToFindB[bucket]->ref))
                  &&
                  ((playerVisibleStructB[bucket] < 0) || (psStruct->visible[playerVisibleStructB[bucket]])) //perform visibility test
                  )
            {
                  scrFunctionResult.v.oval = psStruct;
                  if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))                //    push scrFunctionResult
                  {
                        return false;
                  }
                  enumStructCountB[bucket]++;

                  return true;
            }
            enumStructCountB[bucket]++;
            psStruct = psStruct->psNext;
      }
      // push NULL, none found;
      scrFunctionResult.v.oval = NULL;
      if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
      {
            return false;
      }
      return true;
}

// -----------------------------------------------------------------------------------------
/*looks to see if a structure (specified by type) exists and is being built*/
BOOL scrStructureBeingBuilt(void)
{
//    INTERP_VAL              sVal;
      UDWORD                        structInc;
      STRUCTURE_STATS         *psStats;
      SDWORD                        player;
      BOOL                    beingBuilt;

      if (!stackPopParams(2, ST_STRUCTURESTAT, &structInc, VAL_INT, &player))
      {
            return false;
      }

/*    if (!stackPop(&sVal))
      {
            return false;
      }

      if (sVal.type != ST_STRUCTURESTAT)
      {
            ASSERT( false, "scrStructureBeingBuilt: type mismatch for object" );
            return false;
      }
      psStats = (STRUCTURE_STATS *)(asStructureStats + sVal.v.ival);
*/
      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrStructureBeingBuilt:player number is too high" );
            return false;
      }

      psStats = (STRUCTURE_STATS *)(asStructureStats + structInc);
      beingBuilt = false;
      if (checkStructureStatus(psStats, player, SS_BEING_BUILT))
      {
            beingBuilt = true;
      }

      scrFunctionResult.v.bval = beingBuilt;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}



// -----------------------------------------------------------------------------------------
// multiplayer skirmish only for now.
// returns true if a specific struct is complete. I know it's like the previous func,
BOOL scrStructureComplete(void)
{
      STRUCTURE   *psStruct;
      BOOL        bResult;

      if (!stackPopParams(1, ST_STRUCTURE, &psStruct))
      {
            return false;
      }
      if(psStruct->status == SS_BUILT)
      {
            bResult = true;
      }
      else
      {
            bResult = false;
      }

      scrFunctionResult.v.bval = bResult;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}



// -----------------------------------------------------------------------------------------
/*looks to see if a structure (specified by type) exists and built*/
BOOL scrStructureBuilt(void)
{
//    INTERP_VAL              sVal;
      UDWORD                        structInc;
      STRUCTURE_STATS         *psStats;
      SDWORD                        player;
      BOOL                    built;

      if (!stackPopParams(2, ST_STRUCTURESTAT, &structInc, VAL_INT, &player))
      {
            return false;
      }

/*    if (!stackPop(&sVal))
      {
            return false;
      }

      if (sVal.type != ST_STRUCTURESTAT)
      {
            ASSERT( false, "scrStructureBuilt: type mismatch for object" );
            return false;
      }
      psStats = (STRUCTURE_STATS *)(asStructureStats + sVal.v.ival);
*/
      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrStructureBuilt:player number is too high" );
            return false;
      }

      psStats = (STRUCTURE_STATS *)(asStructureStats + structInc);

      built = false;
      if (checkStructureStatus(psStats, player, SS_BUILT))
      {
            built = true;
      }

      scrFunctionResult.v.bval = built;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }
      return true;
}

// -----------------------------------------------------------------------------------------
/*centre the view on an object - can be droid/structure or feature */
BOOL scrCentreView(void)
{
      BASE_OBJECT *psObj;
//    INTERP_VAL  sVal;

      if (!stackPopParams(1, ST_BASEOBJECT, &psObj))
      {
            return false;
      }

      if (psObj == NULL)
      {
            ASSERT( false, "scrCentreView: NULL object" );
            return false;
      }

      //centre the view on the objects x/y
      setViewPos(map_coord(psObj->pos.x), map_coord(psObj->pos.y), false);

      return true;
}

// -----------------------------------------------------------------------------------------
/*centre the view on a position */
BOOL scrCentreViewPos(void)
{
      SDWORD            x,y;

      if (!stackPopParams(2, VAL_INT, &x, VAL_INT, &y))
      {
            return false;
      }

      if ( (x < 0) || (x >= (SDWORD)mapWidth*TILE_UNITS) ||
             (y < 0) || (y >= (SDWORD)mapHeight*TILE_UNITS))
      {
            ASSERT( false, "scrCenterViewPos: coords off map" );
            return false;
      }

      //centre the view on the objects x/y
      setViewPos(map_coord(x), map_coord(y), false);

      return true;
}

// -----------------------------------------------------------------------------------------
// Get a pointer to a structure based on a stat - returns NULL if cannot find one
BOOL scrGetStructure(void)
{
      SDWORD                        player, index;
      STRUCTURE               *psStruct;
      UDWORD                        structType;
      BOOL                    found;

      if (!stackPopParams(2, ST_STRUCTURESTAT, &index, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrGetStructure:player number is too high" );
            return false;
      }

      structType = asStructureStats[index].ref;

      //search the players' list of built structures to see if one exists
      found = false;
      for (psStruct = apsStructLists[player]; psStruct != NULL; psStruct =
            psStruct->psNext)
      {
            if (psStruct->pStructureType->ref == structType)
            {
                  found = true;
                  break;
            }
      }

      //make sure pass NULL back if not got one
      if (!found)
      {
            psStruct = NULL;
      }

      scrFunctionResult.v.oval = psStruct;
      if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Get a pointer to a template based on a component stat - returns NULL if cannot find one
BOOL scrGetTemplate(void)
{
      SDWORD                        player;
      DROID_TEMPLATE          *psTemplate;
      BOOL                    found;
      INTERP_VAL              sVal;
      UDWORD                        i;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrGetTemplate:player number is too high" );
            return false;
      }

      if (!stackPop(&sVal))
      {
            return false;
      }

      //search the players' list of templates to see if one exists
      found = false;
      for (psTemplate = apsDroidTemplates[player]; psTemplate != NULL; psTemplate =
            psTemplate->psNext)
      {
            switch( sVal.type)
            {
            case ST_BODY:
                  if (psTemplate->asParts[COMP_BODY] == sVal.v.ival)
                  {
                        found = true;
                  }
                  break;
            case ST_PROPULSION:
                  if (psTemplate->asParts[COMP_PROPULSION] == sVal.v.ival)
                  {
                        found = true;
                  }
                  break;
            case ST_ECM:
                  if (psTemplate->asParts[COMP_ECM] == sVal.v.ival)
                  {
                        found = true;
                  }
                  break;
            case ST_SENSOR:
                  if (psTemplate->asParts[COMP_SENSOR] == sVal.v.ival)
                  {
                        found = true;
                  }
                  break;
            case ST_CONSTRUCT:
                  if (psTemplate->asParts[COMP_CONSTRUCT] == sVal.v.ival)
                  {
                        found = true;
                  }
                  break;
            case ST_REPAIR:
                  if (psTemplate->asParts[COMP_REPAIRUNIT] == sVal.v.ival)
                  {
                        found = true;
                  }
                  break;
            case ST_WEAPON:
                  for (i=0; i < DROID_MAXWEAPS; i++)
                  {
                        if (psTemplate->asWeaps[i] == (UDWORD)sVal.v.ival)
                        {
                              found = true;
                              break;
                        }
                  }
                  break;
            default:
                  ASSERT( false, "scrGetTemplate: unknown type" );
                  return false;
            }

            if (found)
            {
                  break;
            }
      }

      //make sure pass NULL back if not got one
      if (!found)
      {
            psTemplate = NULL;
      }

      scrFunctionResult.v.oval = psTemplate;
      if (!stackPushResult((INTERP_TYPE)ST_TEMPLATE, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Get a pointer to a droid based on a component stat - returns NULL if cannot find one
BOOL scrGetDroid(void)
{
      SDWORD                        player;
      DROID                   *psDroid;
      BOOL                    found;
      INTERP_VAL              sVal;
      UDWORD                        i;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrGetUnit:player number is too high" );
            return false;
      }

      if (!stackPop(&sVal))
      {
            return false;
      }

      //search the players' list of droid to see if one exists
      found = false;
      for (psDroid = apsDroidLists[player]; psDroid != NULL; psDroid =
            psDroid->psNext)
      {
            switch( sVal.type)
            {
            case ST_BODY:
                  if (psDroid->asBits[COMP_BODY].nStat == (UDWORD)sVal.v.ival)
                  {
                        found = true;
                  }
                  break;
            case ST_PROPULSION:
                  if (psDroid->asBits[COMP_PROPULSION].nStat == (UDWORD)sVal.v.ival)
                  {
                        found = true;
                  }
                  break;
            case ST_ECM:
                  if (psDroid->asBits[COMP_ECM].nStat == (UDWORD)sVal.v.ival)
                  {
                        found = true;
                  }
                  break;
            case ST_SENSOR:
                  if (psDroid->asBits[COMP_SENSOR].nStat == (UDWORD)sVal.v.ival)
                  {
                        found = true;
                  }
                  break;
            case ST_CONSTRUCT:
                  if (psDroid->asBits[COMP_CONSTRUCT].nStat == (UDWORD)sVal.v.ival)
                  {
                        found = true;
                  }
                  break;
            case ST_REPAIR:
                  if (psDroid->asBits[COMP_REPAIRUNIT].nStat == (UDWORD)sVal.v.ival)
                  {
                        found = true;
                  }
                  break;
            case ST_WEAPON:
                  for (i=0; i < DROID_MAXWEAPS; i++)
                  {
                        if (psDroid->asWeaps[i].nStat == (UDWORD)sVal.v.ival)
                        {
                              found = true;
                              break;
                        }
                  }
                  break;
            default:
                  ASSERT( false, "scrGetUnit: unknown type" );
                  return false;
            }

            if (found)
            {
                  break;
            }
      }

      //make sure pass NULL back if not got one
      if (!found)
      {
            psDroid = NULL;
      }

      scrFunctionResult.v.oval = psDroid;
      if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// Sets all the scroll params for the map
BOOL scrSetScrollParams(void)
{
      SDWORD            minX, minY, maxX, maxY, prevMinX, prevMinY, prevMaxX, prevMaxY;

      if (!stackPopParams(4, VAL_INT, &minX, VAL_INT, &minY, VAL_INT, &maxX, VAL_INT, &maxY))
      {
            return false;
      }

      // check that the values entered are valid
      ASSERT(minX >= 0, "Minimum scroll x value %d is less than zero - ", minX);
      ASSERT(minY >= 0, "Minimum scroll y value %d is less than zero - ", minY);
      ASSERT(maxX <= mapWidth, "Maximum scroll x value %d is greater than mapWidth %d", maxX, (int)mapWidth);
      ASSERT(maxY <= mapHeight, "Maximum scroll y value %d is greater than mapHeight %d", maxY, (int)mapHeight);

      prevMinX = scrollMinX;
      prevMinY = scrollMinY;
      prevMaxX = scrollMaxX;
      prevMaxY = scrollMaxY;

      scrollMinX = minX;
      scrollMaxX = maxX;
      scrollMinY = minY;
      scrollMaxY = maxY;

      // When the scroll limits change midgame - need to redo the lighting
      initLighting(prevMinX < scrollMinX ? prevMinX : scrollMinX,
                   prevMinY < scrollMinY ? prevMinY : scrollMinY,
                   prevMaxX < scrollMaxX ? prevMaxX : scrollMaxX,
                   prevMaxY < scrollMaxY ? prevMaxY : scrollMaxY);

      return true;
}

// -----------------------------------------------------------------------------------------
// Sets the scroll minX separately for the map
BOOL scrSetScrollMinX(void)
{
      SDWORD                        minX, prevMinX;

      if (!stackPopParams(1, VAL_INT, &minX))
      {
            return false;
      }

      //check the value entered are valid
      if (minX < 0)
      {
            ASSERT( false, "Minimum scroll x value %d is less than zero - ", minX );
            return false;
      }

    prevMinX = scrollMinX;

    scrollMinX = minX;

    //when the scroll limits change midgame - need to redo the lighting
    initLighting(prevMinX < scrollMinX ? prevMinX : scrollMinX,
        scrollMinY, scrollMaxX, scrollMaxY);

    return true;
}

// -----------------------------------------------------------------------------------------
// Sets the scroll minY separately for the map
BOOL scrSetScrollMinY(void)
{
      SDWORD                        minY, prevMinY;

      if (!stackPopParams(1, VAL_INT, &minY))
      {
            return false;
      }

      //check the value entered are valid
      if (minY < 0)
      {
            ASSERT( false, "Minimum scroll y value %d is less than zero - ", minY );
            return false;
      }

    prevMinY = scrollMinY;

      scrollMinY = minY;

    //when the scroll limits change midgame - need to redo the lighting
    initLighting(scrollMinX,
        prevMinY < scrollMinY ? prevMinY : scrollMinY,
        scrollMaxX, scrollMaxY);

    return true;
}

// -----------------------------------------------------------------------------------------
// Sets the scroll maxX separately for the map
BOOL scrSetScrollMaxX(void)
{
      SDWORD                        maxX, prevMaxX;

      if (!stackPopParams(1, VAL_INT, &maxX))
      {
            return false;
      }

      //check the value entered are valid
      if (maxX > (SDWORD)mapWidth)
      {
            ASSERT( false, "Maximum scroll x value %d is greater than mapWidth - ", maxX );
            return false;
      }

    prevMaxX = scrollMaxX;

      scrollMaxX = maxX;

    //when the scroll limits change midgame - need to redo the lighting
    initLighting(scrollMinX,  scrollMinY,
        prevMaxX < scrollMaxX ? prevMaxX : scrollMaxX,
        scrollMaxY);

    return true;
}

// -----------------------------------------------------------------------------------------
// Sets the scroll maxY separately for the map
BOOL scrSetScrollMaxY(void)
{
      SDWORD                        maxY, prevMaxY;

      if (!stackPopParams(1, VAL_INT, &maxY))
      {
            return false;
      }

      //check the value entered are valid
      if (maxY > (SDWORD)mapHeight)
      {
            ASSERT( false, "Maximum scroll y value %d is greater than mapWidth - ", maxY );
            return false;
      }

    prevMaxY = scrollMaxY;

      scrollMaxY = maxY;

    //when the scroll limits change midgame - need to redo the lighting
    initLighting(scrollMinX, scrollMinY, scrollMaxX,
        prevMaxY < scrollMaxY ? prevMaxY : scrollMaxY);

    return true;
}

// -----------------------------------------------------------------------------------------
// Sets which sensor will be used as the default for a player
BOOL scrSetDefaultSensor(void)
{
      SDWORD                        player;
      UDWORD                        sensorInc;

      if (!stackPopParams(2, ST_SENSOR, &sensorInc, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetDefaultSensor:player number is too high" );
            return false;
      }

      //check is a valid sensor Inc
      if (sensorInc > numSensorStats)
      {
            ASSERT( false, "scrSetDefaultSensor: Sensor Inc is too high - %d", sensorInc );
            return false;
      }

      //check that this sensor is a default sensor
      if (asSensorStats[sensorInc].location != LOC_DEFAULT)
      {

            ASSERT( false, "scrSetDefaultSensor: This sensor is not a default one - %s",
                  getStatName(&asSensorStats[sensorInc]) );
            return false;
      }

      //assign since OK!
      aDefaultSensor[player] = sensorInc;

      return true;
}

// -----------------------------------------------------------------------------------------
// Sets which ECM will be used as the default for a player
BOOL scrSetDefaultECM(void)
{
      SDWORD                        player;
      UDWORD                        ecmInc;

      if (!stackPopParams(2, ST_ECM, &ecmInc, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetDefaultECM:player number is too high" );
            return false;
      }

      //check is a valid ecmInc
      if (ecmInc > numECMStats)
      {
            ASSERT( false, "scrSetDefaultECM: ECM Inc is too high - %d", ecmInc );
            return false;
      }

      //check that this ecm is a default ecm
      if (asECMStats[ecmInc].location != LOC_DEFAULT)
      {
            ASSERT( false, "scrSetDefaultECM: This ecm is not a default one - %s",
                  getStatName(&asECMStats[ecmInc]) );
            return false;
      }

      //assign since OK!
      aDefaultECM[player] = ecmInc;

      return true;
}

// -----------------------------------------------------------------------------------------
// Sets which RepairUnit will be used as the default for a player
BOOL scrSetDefaultRepair(void)
{
      SDWORD                        player;
      UDWORD                        repairInc;

      if (!stackPopParams(2, ST_REPAIR, &repairInc, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetDefaultRepair:player number is too high" );
            return false;
      }

      //check is a valid repairInc
      if (repairInc > numRepairStats)
      {
            ASSERT( false, "scrSetDefaultRepair: Repair Inc is too high - %d", repairInc );
            return false;
      }

      //check that this repair is a default repair
      if (asRepairStats[repairInc].location != LOC_DEFAULT)
      {
            ASSERT( false, "scrSetDefaultRepair: This repair is not a default one - %s",
                  getStatName(&asRepairStats[repairInc]) );
            return false;
      }

      //assign since OK!
      aDefaultRepair[player] = repairInc;

      return true;
}

// -----------------------------------------------------------------------------------------
// Sets the structure limits for a player
BOOL scrSetStructureLimits(void)
{
      SDWORD                        player, limit;
      UDWORD                        structInc;
      STRUCTURE_LIMITS  *psStructLimits;

      if (!stackPopParams(3, ST_STRUCTURESTAT, &structInc, VAL_INT, &limit, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetStructureLimits:player number is too high" );
            return false;
      }

      if (structInc > numStructureStats)
      {
            ASSERT( false, "scrSetStructureLimits: Structure stat is too high - %d", structInc );
            return false;
      }

      if (limit < 0)
      {
            ASSERT( false, "scrSetStructureLimits: limit is less than zero - %d", limit );
            return false;
      }

      if (limit > LOTS_OF)
      {
            ASSERT( false, "scrSetStructureLimits: limit is too high - %d - must be less than %d",
                  limit, LOTS_OF );
            return false;
      }

      psStructLimits = asStructLimits[player];
      psStructLimits[structInc].limit = (UBYTE)limit;

      psStructLimits[structInc].globalLimit = (UBYTE)limit;

      return true;
}



// -----------------------------------------------------------------------------------------
// multiplayer limit handler.
BOOL scrApplyLimitSet(void)
{
      applyLimitSet();
      return true;
}



// -----------------------------------------------------------------------------------------
// plays a sound for the specified player - only plays the sound if the
// specified player = selectedPlayer
BOOL scrPlaySound(void)
{
      SDWORD      player, soundID;

      if (!stackPopParams(2, ST_SOUND, &soundID, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrPlaySound:player number is too high" );
            return false;
      }

      if (player == (SDWORD)selectedPlayer)
      {
            audio_QueueTrack(soundID);
            if(bInTutorial)
            {
                  audio_QueueTrack(ID_SOUND_OF_SILENCE);
            }
      }
      return true;
}

// -----------------------------------------------------------------------------------------
// plays a sound for the specified player - only plays the sound if the
// specified player = selectedPlayer - saves position
BOOL scrPlaySoundPos(void)
{
      SDWORD      player, soundID, iX, iY, iZ;

      if (!stackPopParams(5, ST_SOUND, &soundID, VAL_INT, &player,
                                          VAL_INT, &iX, VAL_INT, &iY, VAL_INT, &iZ))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrPlaySoundPos:player number is too high" );
            return false;
      }

      if (player == (SDWORD)selectedPlayer)
      {
            audio_QueueTrackPos(soundID, iX, iY, iZ);
      }
      return true;
}

// -----------------------------------------------------------------------------------------

/* add a text message to the top of the screen for the selected player*/
BOOL scrShowConsoleText(void)
{
      char                    *pText;
      SDWORD                        player;

      if (!stackPopParams(2, ST_TEXTSTRING, &pText, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAddConsoleText:player number is too high" );
            return false;
      }

      if (player == (SDWORD)selectedPlayer)
      {
            permitNewConsoleMessages(true);
            addConsoleMessage(pText, CENTRE_JUSTIFY, SYSTEM_MESSAGE);
      }

      return true;
}

// -----------------------------------------------------------------------------------------
/* add a text message to the top of the screen for the selected player*/
BOOL scrAddConsoleText(void)
{
      char                    *pText;
      SDWORD                        player;

      if (!stackPopParams(2, ST_TEXTSTRING, &pText, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAddConsoleText:player number is too high" );
            return false;
      }

      if (player == (SDWORD)selectedPlayer)
      {
            permitNewConsoleMessages(true);
            setConsolePermanence(true,true);
            addConsoleMessage(pText, CENTRE_JUSTIFY, SYSTEM_MESSAGE);
            permitNewConsoleMessages(false);
      }

      return true;
}



// -----------------------------------------------------------------------------------------
/* add a text message to the top of the screen for the selected player - without clearing whats there*/
BOOL scrTagConsoleText(void)
{
      char                    *pText;
      SDWORD                        player;

      if (!stackPopParams(2, ST_TEXTSTRING, &pText, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAddConsoleText:player number is too high" );
            return false;
      }

      if (player == (SDWORD)selectedPlayer)
      {
            permitNewConsoleMessages(true);
            setConsolePermanence(true,false);
            addConsoleMessage(pText, CENTRE_JUSTIFY, SYSTEM_MESSAGE);
            permitNewConsoleMessages(false);
      }

      return true;
}


// -----------------------------------------------------------------------------------------

BOOL  scrClearConsole(void)
{
      flushConsoleMessages();
      return(true);
}

// -----------------------------------------------------------------------------------------
//demo functions for turning the power on
BOOL scrTurnPowerOff(void)
{
      //powerCalculated = false;
      powerCalc(false);

      return true;
}

// -----------------------------------------------------------------------------------------
//demo functions for turning the power off
BOOL scrTurnPowerOn(void)
{

      //powerCalculated = true;
      powerCalc(true);

      return true;
}

// -----------------------------------------------------------------------------------------
//flags when the tutorial is over so that console messages can be turned on again
BOOL scrTutorialEnd(void)
{
      initConsoleMessages();
      return true;
}

// -----------------------------------------------------------------------------------------
//function to play a full-screen video in the middle of the game for the selected player
BOOL scrPlayVideo(void)
{
      char                    *pVideo, *pText;

      if (!stackPopParams(2, ST_TEXTSTRING, &pVideo, ST_TEXTSTRING, &pText))
      {
            return false;
      }

            seq_ClearSeqList();
            seq_AddSeqToList(pVideo, NULL, pText, false);         // Arpzzzzzzzzzzzzzzzlksht!
            seq_StartNextFullScreenVideo();

      return true;
}

// -----------------------------------------------------------------------------------------
//checks to see if there are any droids for the specified player
BOOL scrAnyDroidsLeft(void)
{
      SDWORD            player;
      BOOL        droidsLeft;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAnyUnitsLeft:player number is too high" );
            return false;
      }

      //check the players list for any droid
      droidsLeft = true;
      if (apsDroidLists[player] == NULL)
      {
            droidsLeft = false;
      }

      scrFunctionResult.v.bval = droidsLeft;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
//function to call when the game is over, plays a message then does game over stuff.
//
BOOL scrGameOverMessage(void)
{
      BOOL              gameWon;
      MESSAGE                 *psMessage;
      MESSAGE_TYPE            msgType;
      SDWORD                  player;
      VIEWDATA          *psViewData;


      if (!stackPopParams(4, ST_INTMESSAGE, &psViewData , VAL_INT, &msgType,
                        VAL_INT, &player, VAL_BOOL, &gameWon))
      {
            return false;
      }


      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrGameOverMessage:player number is too high" );
            return false;
      }

      if(gameWon)
      {
            addConsoleMessage(_("YOU ARE VICTORIOUS!"),DEFAULT_JUSTIFY, SYSTEM_MESSAGE);
      }
      else
      {
            addConsoleMessage(_("YOU WERE DEFEATED!"),DEFAULT_JUSTIFY, SYSTEM_MESSAGE);
      }

      //create the message
      psMessage = addMessage(msgType, false, player);

      ASSERT( msgType != MSG_PROXIMITY, "scrGameOverMessage: Bad message type (MSG_PROXIMITY)" );

      if (psMessage)
      {
            //we need to set this here so the VIDEO_QUIT callback is not called
            setScriptWinLoseVideo((UBYTE)(gameWon ? PLAY_WIN : PLAY_LOSE));

            //set the data
            psMessage->pViewData = (MSG_VIEWDATA *)psViewData;
            displayImmediateMessage(psMessage);
            stopReticuleButtonFlash(IDRET_INTEL_MAP);

        // Can't do this cos won't process windows stuff
        // Wait for the video to finish.
            /*while (loop_GetVideoStatus())
            {
                  videoLoop();
            }*/
      }
      debug(LOG_MSG, "Game over message");

    // this should be called when the video Quit is processed
    // not always is tough, so better be sure
      displayGameOver(gameWon);

      return true;
}




// -----------------------------------------------------------------------------------------
//function to call when the game is over
BOOL scrGameOver(void)
{
      BOOL  gameOver;

      if (!stackPopParams(1, VAL_BOOL, &gameOver))
      {
            return false;
      }

    /*this function will only be called with gameOver = true when at the end of
    the game so we'll just hard-code what happens!*/

    //don't want this in multiplayer...
    if (!bMultiPlayer)

    {
        if (gameOver == true && !bInTutorial)
        {
            //we need to set this here so the VIDEO_QUIT callback is not called
                setScriptWinLoseVideo(PLAY_WIN);

          seq_ClearSeqList();

              seq_AddSeqToList("outro.rpl", NULL, "outro.txa", false);
              seq_StartNextFullScreenVideo();

        }
    }

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL scrAnyFactoriesLeft(void)
{
      SDWORD            player;
      BOOL        bResult;
      STRUCTURE   *psCurr;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAnyFactorysLeft:player number is too high" );
            return false;
      }

      //check the players list for any structures
      bResult = false;
      if(apsStructLists[player])
      {
            for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext)
            {
//                if (psCurr->pStructureType->type    == REF_FACTORY ||
//                      psCurr->pStructureType->type == REF_CYBORG_FACTORY ||
//                      psCurr->pStructureType->type == REF_VTOL_FACTORY )
                  if(StructIsFactory(psCurr))
                  {
                        bResult = true;
                        break;
                  }
            }
      }

      scrFunctionResult.v.bval = bResult;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
//checks to see if there are any structures (except walls) for the specified player
BOOL scrAnyStructButWallsLeft(void)
{
      SDWORD            player;
      BOOL        structuresLeft;
      STRUCTURE   *psCurr;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAnyStructuresButWallsLeft:player number is too high" );
            return false;
      }

      //check the players list for any structures
      structuresLeft = true;
      if (apsStructLists[player] == NULL)
      {
            structuresLeft = false;
      }
      else
      {
            structuresLeft = false;
            for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext)
            {
                  if (psCurr->pStructureType->type != REF_WALL && psCurr->pStructureType->
                        type != REF_WALLCORNER)
                  {
                        structuresLeft = true;
                        break;
                  }
            }
      }

      scrFunctionResult.v.bval = structuresLeft;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
//defines the background audio to play
BOOL scrPlayBackgroundAudio(void)
{
      char  *pText;
      SDWORD      iVol;

      if (!stackPopParams(2, ST_TEXTSTRING, &pText, VAL_INT, &iVol))
      {
            return false;
      }


      audio_PlayStream(pText, (float)iVol / 100.f, NULL, NULL);


      return true;

}

BOOL scrPlayIngameCDAudio(void)
{
      if (war_GetPlayAudioCDs())
      {
            cdAudio_PlayTrack(playlist_ingame);
      }

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL scrStopCDAudio(void)
{
      if (war_GetPlayAudioCDs()) {
            cdAudio_Stop();
      }

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL scrPauseCDAudio(void)
{
      if (war_GetPlayAudioCDs()) {
            cdAudio_Pause();
      }
      return true;
}

// -----------------------------------------------------------------------------------------
BOOL scrResumeCDAudio(void)
{
      if (war_GetPlayAudioCDs()) {
            cdAudio_Resume();
      }
      return true;
}

// -----------------------------------------------------------------------------------------
// set the retreat point for a player
BOOL scrSetRetreatPoint(void)
{
      SDWORD      player, x,y;

      if (!stackPopParams(3, VAL_INT, &player, VAL_INT, &x, VAL_INT, &y))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetRetreatPoint: player out of range" );
            return false;
      }
      if (x < 0 || x >= (SDWORD)mapWidth*TILE_UNITS ||
            y < 0 || y >= (SDWORD)mapHeight*TILE_UNITS)
      {
            ASSERT( false, "scrSetRetreatPoint: coords off map" );
            return false;
      }

      asRunData[player].sPos.x = x;
      asRunData[player].sPos.y = y;

      return true;
}

// -----------------------------------------------------------------------------------------
// set the retreat force level
BOOL scrSetRetreatForce(void)
{
      SDWORD      player, level, numDroids;
      DROID *psCurr;

      if (!stackPopParams(2, VAL_INT, &player, VAL_INT, &level))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetRetreatForce: player out of range" );
            return false;
      }

      if (level > 100 || level < 0)
      {
            ASSERT( false, "scrSetRetreatForce: level out of range" );
            return false;
      }

      // count up the current number of droids
      numDroids = 0;
      for(psCurr = apsDroidLists[player]; psCurr; psCurr=psCurr->psNext)
      {
            numDroids += 1;
      }

      asRunData[player].forceLevel = (UBYTE)(level * numDroids / 100);

      return true;
}

// -----------------------------------------------------------------------------------------
// set the retreat leadership
BOOL scrSetRetreatLeadership(void)
{
      SDWORD      player, level;

      if (!stackPopParams(2, VAL_INT, &player, VAL_INT, &level))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetRetreatLeadership: player out of range" );
            return false;
      }

      if (level > 100 || level < 0)
      {
            ASSERT( false, "scrSetRetreatLeadership: level out of range" );
            return false;
      }

      asRunData[player].leadership = (UBYTE)level;

      return true;
}

// -----------------------------------------------------------------------------------------
// set the retreat point for a group
BOOL scrSetGroupRetreatPoint(void)
{
      SDWORD            x,y;
      DROID_GROUP *psGroup;

      if (!stackPopParams(3, ST_GROUP, &psGroup, VAL_INT, &x, VAL_INT, &y))
      {
            return false;
      }

      if (x < 0 || x >= (SDWORD)mapWidth*TILE_UNITS ||
            y < 0 || y >= (SDWORD)mapHeight*TILE_UNITS)
      {
            ASSERT( false, "scrSetRetreatPoint: coords off map" );
            return false;
      }

      psGroup->sRunData.sPos.x = x;
      psGroup->sRunData.sPos.y = y;

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL scrSetGroupRetreatForce(void)
{
      SDWORD            level, numDroids;
      DROID_GROUP *psGroup;
      DROID       *psCurr;

      if (!stackPopParams(2, ST_GROUP, &psGroup, VAL_INT, &level))
      {
            return false;
      }

      if (level > 100 || level < 0)
      {
            ASSERT( false, "scrSetRetreatForce: level out of range" );
            return false;
      }

      // count up the current number of droids
      numDroids = 0;
      for(psCurr = psGroup->psList; psCurr; psCurr=psCurr->psGrpNext)
      {
            numDroids += 1;
      }

      psGroup->sRunData.forceLevel = (UBYTE)(level * numDroids / 100);

      return true;
}

// -----------------------------------------------------------------------------------------
// set the retreat health level
BOOL scrSetRetreatHealth(void)
{
      SDWORD      player, health;

      if (!stackPopParams(2, VAL_INT, &player, VAL_INT, &health))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetHealthForce: player out of range" );
            return false;
      }

      if (health > 100 || health < 0)
      {
            ASSERT( false, "scrSetHealthForce: health out of range" );
            return false;
      }

      asRunData[player].healthLevel = (UBYTE)health;

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL scrSetGroupRetreatHealth(void)
{
      SDWORD            health;
      DROID_GROUP *psGroup;

      if (!stackPopParams(2, ST_GROUP, &psGroup, VAL_INT, &health))
      {
            return false;
      }

      if (health > 100 || health < 0)
      {
            ASSERT( false, "scrSetGroupRetreatHealth: health out of range" );
            return false;
      }

      psGroup->sRunData.healthLevel = (UBYTE)health;

      return true;
}

// -----------------------------------------------------------------------------------------
// set the retreat leadership
BOOL scrSetGroupRetreatLeadership(void)
{
      SDWORD            level;
      DROID_GROUP *psGroup;

      if (!stackPopParams(2, ST_GROUP, &psGroup, VAL_INT, &level))
      {
            return false;
      }

      if (level > 100 || level < 0)
      {
            ASSERT( false, "scrSetRetreatLeadership: level out of range" );
            return false;
      }

      psGroup->sRunData.leadership = (UBYTE)level;

      return true;
}

// -----------------------------------------------------------------------------------------
//start a Mission - the missionType is ignored now - gets it from the level data ***********
BOOL scrStartMission(void)
{
      char                    *pGame;
      SDWORD                        missionType;
      LEVEL_DATASET           *psNewLevel;

      if (!stackPopParams(2, VAL_INT, &missionType, ST_LEVEL, &pGame))
      {
            return false;
      }

      //if (missionType > MISSION_NONE)
      if (missionType > LDS_NONE)
      {
            ASSERT( false, "Invalid Mission Type" );
            return false;
      }

      // check the last mission got finished
      /*if (mission.type != MISSION_NONE)
      {
            DBMB(("scrStartMission: old mission incomplete\n   ending mission with success"));
            endMission(true);
      }*/

      // tell the loop that a new level has to be loaded up - not yet!
      //loopNewLevel = true;
      sstrcpy(aLevelName, pGame);

      // find the level dataset
      psNewLevel = levFindDataSet(pGame);
      if (psNewLevel == NULL)
      {
            debug( LOG_ERROR, "scrStartMission: couldn't find level data" );
            abort();
            return false;
      }

      //set the mission rolling...
      //nextMissionType = missionType;
      nextMissionType = psNewLevel->type;
//    loopMissionState = LMS_SETUPMISSION;
      loopMissionState = LMS_CLEAROBJECTS;

/*    if (!setUpMission(missionType))
      {
            ASSERT( false, "Unable to start mission - %s", pGame );
            return false;
      }*/

      return true;
}

//end a mission - NO LONGER CALLED FROM SCRIPT
/*BOOL scrEndMission(void)
{
      BOOL  status;

      if (!stackPopParams(1, VAL_BOOL, &status))
      {
            return false;
      }

      endMission(status);
      return true;
}*/
// -----------------------------------------------------------------------------------------
//set Snow (enable disable snow)
BOOL scrSetSnow(void)
{
      BOOL bState;

      if (!stackPopParams(1, VAL_BOOL, &bState))
      {
            return false;
      }


      if(bState)
      {
            atmosSetWeatherType(WT_SNOWING);
      }
      else
      {
            atmosSetWeatherType(WT_NONE);
      }

      return true;
}

// -----------------------------------------------------------------------------------------
//set Rain (enable disable Rain)
BOOL scrSetRain(void)
{
      BOOL bState;

      if (!stackPopParams(1, VAL_BOOL, &bState))
      {
            return false;
      }


      if(bState)
      {
            atmosSetWeatherType(WT_RAINING);
      }
      else
      {
            atmosSetWeatherType(WT_NONE);
      }

      return true;
}

// -----------------------------------------------------------------------------------------
//set Background Fog (replace fade out with fog)
BOOL scrSetBackgroundFog(void)
{
      BOOL bState;

      if (!stackPopParams(1, VAL_BOOL, &bState))
      {
            return false;
      }

      //jps 17 feb 99 just set the status let other code worry about fogEnable/reveal
      if (bState)//true, so go to false
      {
            //restart fog if it was off
            if ((fogStatus == 0) && war_GetFog() && !(bMultiPlayer && game.fog))
            {
                  pie_EnableFog(true);
            }
            fogStatus |= FOG_BACKGROUND;//set lowest bit of 3
      }
      else
      {
            fogStatus &= FOG_FLAGS-FOG_BACKGROUND;//clear middle bit of 3
            //disable fog if it longer used
            if (fogStatus == 0)
            {
                  pie_SetFogStatus(false);
                  pie_EnableFog(false);
            }
      }

/* jps 17 feb 99
      if(getRevealStatus())         // fog'o war enabled
      {
            pie_SetFogStatus(false);
            pie_EnableFog(false);
//          fogStatus = 0;
            return true;
      }

      if (bState)//true, so go to false
      {
            if (war_GetFog())
            {
                  //restart fog if it was off
                  if (fogStatus == 0)
                  {
                        pie_EnableFog(true);
                  }
                  fogStatus |= FOG_BACKGROUND;//set lowest bit of 3
            }
      }
      else
      {
            if (war_GetFog())
            {
                  fogStatus &= FOG_FLAGS-FOG_BACKGROUND;//clear middle bit of 3
                  //disable fog if it longer used
                  if (fogStatus == 0)
                  {
                        pie_SetFogStatus(false);
                        pie_EnableFog(false);
                  }
            }
      }
*/

      return true;
}

// -----------------------------------------------------------------------------------------
//set Depth Fog (gradual fog from mid range to edge of world)
BOOL scrSetDepthFog(void)
{
      BOOL bState;

      if (!stackPopParams(1, VAL_BOOL, &bState))
      {
            return false;
      }
      // ffs am
//jps 17 feb 99 just set the status let other code worry about fogEnable/reveal
      if (bState)//true, so go to false
      {
            //restart fog if it was off
            if ((fogStatus == 0) && war_GetFog() )
            {
                  pie_EnableFog(true);
            }
            fogStatus |= FOG_DISTANCE;//set lowest bit of 3
      }
      else
      {
            fogStatus &= FOG_FLAGS-FOG_DISTANCE;//clear middle bit of 3
            //disable fog if it longer used
            if (fogStatus == 0)
            {
                  pie_SetFogStatus(false);
                  pie_EnableFog(false);
            }
      }

/* jps 17 feb 99  if(getRevealStatus())         // fog'o war enabled
      {
            pie_SetFogStatus(false);
            pie_EnableFog(false);
//          fogStatus = 0;
            return true;
      }

      if (bState)//true, so go to false
      {
            if (war_GetFog())
            {
                  //restart fog if it was off
                  if (fogStatus == 0)
                  {
                        pie_EnableFog(true);
                  }
                  fogStatus |= FOG_DISTANCE;//set lowest bit of 3
            }
      }
      else
      {
            if (war_GetFog())
            {
                  fogStatus &= FOG_FLAGS-FOG_DISTANCE;//clear middle bit of 3
                  //disable fog if it longer used
                  if (fogStatus == 0)
                  {
                        pie_SetFogStatus(false);
                        pie_EnableFog(false);
                  }
            }
      }
*/

      return true;
}

// -----------------------------------------------------------------------------------------
//set Mission Fog colour, may be modified by weather effects
BOOL scrSetFogColour(void)
{
      SDWORD      red,green,blue;
      PIELIGHT scrFogColour;

      if (!stackPopParams(3, VAL_INT, &red, VAL_INT, &green, VAL_INT, &blue))
      {
            return false;
      }

      if (war_GetFog())
      {
            scrFogColour.byte.r = red;
            scrFogColour.byte.g = green;
            scrFogColour.byte.b = blue;
            scrFogColour.byte.a = 255;

            pie_SetFogColour(scrFogColour);
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// test function to test variable references
BOOL scrRefTest(void)
{
      SDWORD            Num = 0;

      if (!stackPopParams(1,VAL_INT, Num))
      {
            return false;
      }

      debug( LOG_NEVER, "scrRefTest: num: %d \n", Num );

      return true;
}

// -----------------------------------------------------------------------------------------
// is player a human or computer player? (multiplayer only)

BOOL scrIsHumanPlayer(void)
{
      SDWORD      player;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }

      scrFunctionResult.v.bval = isHumanPlayer(player);
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
// Set an alliance between two players
BOOL scrCreateAlliance(void)
{
      SDWORD      player1,player2;

      if (!stackPopParams(2, VAL_INT, &player1, VAL_INT, &player2))
      {
            return false;
      }

      if (player1 < 0 || player1 >= MAX_PLAYERS ||
            player2 < 0 || player2 >= MAX_PLAYERS)
      {
            ASSERT( false, "scrCreateAlliance: player out of range" );
            return false;
      }

      if(bMultiPlayer)
      {
            if(game.alliance==NO_ALLIANCES || game.alliance==ALLIANCES_TEAMS
                  || player1 >= game.maxPlayers || player2>=game.maxPlayers)
            {
                  return true;
            }
      }

      formAlliance((UBYTE)player1, (UBYTE)player2,true,false,true);

/*
      if(bMultiPlayer)
      {

            if(game.alliance==NO_ALLIANCES || player1 >= game.maxPlayers || player2>=game.maxPlayers)
            {
                  return true;
            }

            if(alliances[player1][player2] != ALLIANCE_FORMED)
            {
#ifdef DEBUG
                  CONPRINTF(ConsoleString,(ConsoleString,"%d and %d form an alliance.",player1,player2));
#endif
                  sendAlliance((UBYTE)player1,(UBYTE)player2,ALLIANCE_FORMED,0);
            }
      }

      alliances[player1][player2] = ALLIANCE_FORMED;
      alliances[player2][player1] = ALLIANCE_FORMED;
*/
      return true;
}



// -----------------------------------------------------------------------------------------
// offer an alliance
BOOL scrOfferAlliance(void)
{
      SDWORD      player1,player2;
      if (!stackPopParams(2, VAL_INT, &player1, VAL_INT, &player2))
      {
            return false;
      }
      if (game.alliance==NO_ALLIANCES || game.alliance==ALLIANCES_TEAMS ||
            player1 < 0 || player1 >= MAX_PLAYERS ||
            player2 < 0 || player2 >= MAX_PLAYERS)
      {
            ASSERT( false, "scrCreateAlliance: player out of range" );
            return false;
      }


      requestAlliance((UBYTE)player1,(UBYTE)player2,true,true);
      return true;
}


// -----------------------------------------------------------------------------------------
// Break an alliance between two players
BOOL scrBreakAlliance(void)
{
      SDWORD      player1,player2;

      if (!stackPopParams(2, VAL_INT, &player1, VAL_INT, &player2))
      {
            return false;
      }

      if (
            player1 < 0 || player1 >= MAX_PLAYERS ||
            player2 < 0 || player2 >= MAX_PLAYERS)
      {
            ASSERT( false, "scrCreateAlliance: player out of range" );
            return false;
      }
/*
if(bMultiPlayer)
      {


            if(alliances[player1][player2] != ALLIANCE_BROKEN)
            {
                  CONPRINTF(ConsoleString,(ConsoleString,"%d and %d break alliance.",player1,player2));
                  sendAlliance((UBYTE)player1,(UBYTE)player2,ALLIANCE_BROKEN,0);
            }
}
*/


      if(bMultiPlayer)
      {
            if(game.alliance==NO_ALLIANCES || game.alliance==ALLIANCES_TEAMS
                  || player1 >= game.maxPlayers || player2>=game.maxPlayers)
            {
                  return true;
            }
            breakAlliance(player1,player2,true,true);
      }
      else
      {
            breakAlliance(player1,player2,false,true);
      }
/*
      alliances[player1][player2] = ALLIANCE_BROKEN;
      alliances[player2][player1] = ALLIANCE_BROKEN;
*/
      return true;
}


// -----------------------------------------------------------------------------------------
// Multiplayer relevant scriptfuncs
// returns true if 2 or more players are in alliance.
BOOL scrAllianceExists(void)
{

      UDWORD i,j;
      for(i=0;i<MAX_PLAYERS;i++)
      {
            for(j=0;j<MAX_PLAYERS;j++)
            {
                  if(alliances[i][j] == ALLIANCE_FORMED)
                  {
                        scrFunctionResult.v.bval = true;
                        if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
                        {
                              return false;
                        }
                        return true;
                  }
            }
      }

      scrFunctionResult.v.bval = false;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

BOOL scrAllianceExistsBetween(void)
{
      UDWORD i,j;


      if (!stackPopParams(2, VAL_INT, &i,VAL_INT, &j))
      {
            return false;
      }
      if(alliances[i][j] == ALLIANCE_FORMED)
      {
            scrFunctionResult.v.bval = true;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
            return true;
      }

      scrFunctionResult.v.bval = false;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
BOOL scrPlayerInAlliance(void)
{
      UDWORD player,j;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }

      for(j=0;j<MAX_PLAYERS;j++)
      {
            if(alliances[player][j] == ALLIANCE_FORMED)
            {
                  scrFunctionResult.v.bval = true;
                  if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
                  {
                        return false;
                  }
                  return true;
            }
      }
      scrFunctionResult.v.bval = false;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// returns true if a single alliance is dominant.
BOOL scrDominatingAlliance(void)
{
      UDWORD i,j;

      for(i=0;i<MAX_PLAYERS;i++)
      {
            for(j=0;j<MAX_PLAYERS;j++)
            {
                  if(   isHumanPlayer(j)
                     && isHumanPlayer(i)
                     && i != j
                     && alliances[i][j] != ALLIANCE_FORMED)
                  {
                        scrFunctionResult.v.bval = false;
                        if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
                        {
                              return false;
                        }
                        return true;
                  }
            }
// -----------------------------------------------------------------------------------------
      }


      scrFunctionResult.v.bval = true;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


BOOL scrMyResponsibility(void)
{
      SDWORD player;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }

      if(   myResponsibility(player) )
      {
            scrFunctionResult.v.bval = true;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
      }
      else
      {
            scrFunctionResult.v.bval = false;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
      }


      return true;
}

// -----------------------------------------------------------------------------------------
/*checks to see if a structure of the type specified exists within the
specified range of an XY location */
BOOL scrStructureBuiltInRange(void)
{
      SDWORD            player, index, x, y, range;
      SDWORD            rangeSquared;
      STRUCTURE   *psCurr;
      BOOL        found;
      SDWORD            xdiff, ydiff;
      STRUCTURE_STATS *psTarget;

      if (!stackPopParams(5, ST_STRUCTURESTAT, &index, VAL_INT, &x, VAL_INT, &y,
            VAL_INT, &range, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrStructureBuiltInRange:player number is too high" );
            return false;
      }

      if (x < 0
       || map_coord(x) > (SDWORD)mapWidth)
      {
            ASSERT( false, "scrStructureBuiltInRange : invalid X coord" );
            return false;
      }
      if (y < 0
       || map_coord(y) > (SDWORD)mapHeight)
      {
            ASSERT( false,"scrStructureBuiltInRange : invalid Y coord" );
            return false;
      }
      if (index < (SDWORD)0 || index > (SDWORD)numStructureStats)
      {
            ASSERT( false, "scrStructureBuiltInRange : Invalid structure stat" );
            return false;
      }
      if (range < (SDWORD)0)
      {
            ASSERT( false, "scrStructureBuiltInRange : Rnage is less than zero" );
            return false;
      }

      //now look through the players list of structures to see if this type
      //exists within range
      psTarget = &asStructureStats[index];
      rangeSquared = range * range;
      found = false;
      for(psCurr = apsStructLists[player]; psCurr; psCurr = psCurr->psNext)
      {
            xdiff = (SDWORD)psCurr->pos.x - x;
            ydiff = (SDWORD)psCurr->pos.y - y;
            if (xdiff*xdiff + ydiff*ydiff <= rangeSquared)
            {

                  if( strcmp(psCurr->pStructureType->pName,psTarget->pName) == 0 )
                  {
                        if (psCurr->status == SS_BUILT)
                        {
                              found = true;
                              break;
                        }
                  }
            }
      }
      //make sure pass NULL back if not got one
      if (!found)
      {
            psCurr = NULL;
      }

      scrFunctionResult.v.oval = psCurr;
      if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


// -----------------------------------------------------------------------------------------
// generate a random number
BOOL scrRandom(void)
{
      SDWORD            range, iResult;

      if (!stackPopParams(1, VAL_INT, &range))
      {
            return false;
      }

      if (range == 0)
      {
            iResult = 0;
      }
      else if (range > 0)
      {
            iResult = rand() % range;
      }
      else
      {
            iResult = rand() % (-range);
      }

      scrFunctionResult.v.ival = iResult;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// -----------------------------------------------------------------------------------------
// randomise the random number seed
BOOL scrRandomiseSeed(void)
{
      srand((UDWORD)clock());

      return true;
}

// -----------------------------------------------------------------------------------------
//explicitly enables a research topic
BOOL scrEnableResearch(void)
{
      SDWORD            player;
      RESEARCH    *psResearch;

      if (!stackPopParams(2, ST_RESEARCH, &psResearch, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrEnableResearch:player number is too high" );
            return false;
      }

      if (!enableResearch(psResearch, player))
      {
            return false;
      }
      return true;
}

// -----------------------------------------------------------------------------------------
//acts as if the research topic was completed - used to jump into the tree
BOOL scrCompleteResearch(void)
{
      SDWORD            player;
      RESEARCH    *psResearch;
      UDWORD            researchIndex;

      if (!stackPopParams(2, ST_RESEARCH, &psResearch, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrCompleteResearch:player number is too high" );
            return false;
      }


      if(psResearch == NULL)
      {
            ASSERT( false, "scrCompleteResearch: no such research topic" );
            return false;
      }

      researchIndex = psResearch - asResearch;  //TODO: fix if needed
      if (researchIndex > numResearch)
      {
            ASSERT( false, "scrCompleteResearch: invalid research index" );
            return false;
      }

      researchResult(researchIndex, (UBYTE)player, false, NULL);


      if(bMultiPlayer && (gameTime > 2 ))
      {
            SendResearch((UBYTE)player,researchIndex );
      }


      return true;
}

// -----------------------------------------------------------------------------------------
// This routine used to start just a reticule button flashing
//   .. now it starts any button flashing (awaiting implmentation from widget library)
BOOL scrFlashOn(void)
{
      SDWORD            button;

      if (!stackPopParams(1, VAL_INT, &button))
      {
            return false;
      }

      // For the time being ... we will perform the old code for the reticule ...
      if (button >= IDRET_OPTIONS && button <= IDRET_CANCEL)
      {
            flashReticuleButton((UDWORD)button);
            return true;
      }


      if(widgGetFromID(psWScreen,button) != NULL)
      {
            widgSetButtonFlash(psWScreen,button);
      }
      return true;
}


// -----------------------------------------------------------------------------------------
// stop a generic button flashing
BOOL scrFlashOff(void)
{
      SDWORD            button;

      if (!stackPopParams(1, VAL_INT, &button))
      {
            return false;
      }

      if (button >= IDRET_OPTIONS && button <= IDRET_CANCEL)
      {
            stopReticuleButtonFlash((UDWORD)button);
            return true;
      }


      if(widgGetFromID(psWScreen,button) != NULL)
      {
            widgClearButtonFlash(psWScreen,button);
      }
      return true;
}

// -----------------------------------------------------------------------------------------
//set the initial power level settings for a player
BOOL scrSetPowerLevel(void)
{
      SDWORD            player, power;

      if (!stackPopParams(2, VAL_INT, &power, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetPowerLevel:player number is too high" );
            return false;
      }

      setPlayerPower(power, player);

      return true;
}

// -----------------------------------------------------------------------------------------
//add some power for a player
BOOL scrAddPower(void)
{
      SDWORD            player, power;

      if (!stackPopParams(2, VAL_INT, &power, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAddPower:player number is too high" );
            return false;
      }

      addPower(player, power);

      return true;
}

// -----------------------------------------------------------------------------------------
/*set the landing Zone position for the map - this is for player 0. Can be
scrapped and replaced by setNoGoAreas, left in for compatibility*/
BOOL scrSetLandingZone(void)
{
      SDWORD            x1, x2, y1, y2;

      if (!stackPopParams(4, VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      //check the values - check against max possible since can set in one mission for the next
      //if (x1 > (SDWORD)mapWidth)
      if (x1 > (SDWORD)MAP_MAXWIDTH)
      {
            ASSERT( false, "scrSetLandingZone: x1 is greater than max mapWidth" );
            return false;
      }
      //if (x2 > (SDWORD)mapWidth)
      if (x2 > (SDWORD)MAP_MAXWIDTH)
      {
            ASSERT( false, "scrSetLandingZone: x2 is greater than max mapWidth" );
            return false;
      }
      //if (y1 > (SDWORD)mapHeight)
      if (y1 > (SDWORD)MAP_MAXHEIGHT)
      {
            ASSERT( false, "scrSetLandingZone: y1 is greater than max mapHeight" );
            return false;
      }
      //if (y2 > (SDWORD)mapHeight)
      if (y2 > (SDWORD)MAP_MAXHEIGHT)
      {
            ASSERT( false, "scrSetLandingZone: y2 is greater than max mapHeight" );
            return false;
      }
      //check won't overflow!
      if (x1 > UBYTE_MAX || y1 > UBYTE_MAX || x2 > UBYTE_MAX || y2 > UBYTE_MAX)
      {
            ASSERT( false, "scrSetLandingZone: one coord is greater than %d", UBYTE_MAX );
            return false;
      }

      setLandingZone((UBYTE)x1, (UBYTE)y1, (UBYTE)x2, (UBYTE)y2);

      return true;
}

/*set the landing Zone position for the Limbo droids and adds the Limbo droids
to the world at the location*/
BOOL scrSetLimboLanding(void)
{
      SDWORD            x1, x2, y1, y2;

      if (!stackPopParams(4, VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      //check the values - check against max possible since can set in one mission for the next
      //if (x1 > (SDWORD)mapWidth)
      if (x1 > (SDWORD)MAP_MAXWIDTH)
      {
            ASSERT( false, "scrSetLimboLanding: x1 is greater than max mapWidth" );
            return false;
      }
      //if (x2 > (SDWORD)mapWidth)
      if (x2 > (SDWORD)MAP_MAXWIDTH)
      {
            ASSERT( false, "scrSetLimboLanding: x2 is greater than max mapWidth" );
            return false;
      }
      //if (y1 > (SDWORD)mapHeight)
      if (y1 > (SDWORD)MAP_MAXHEIGHT)
      {
            ASSERT( false, "scrSetLimboLanding: y1 is greater than max mapHeight" );
            return false;
      }
      //if (y2 > (SDWORD)mapHeight)
      if (y2 > (SDWORD)MAP_MAXHEIGHT)
      {
            ASSERT( false, "scrSetLimboLanding: y2 is greater than max mapHeight" );
            return false;
      }
      //check won't overflow!
      if (x1 > UBYTE_MAX || y1 > UBYTE_MAX || x2 > UBYTE_MAX || y2 > UBYTE_MAX)
      {
            ASSERT( false, "scrSetLimboLanding: one coord is greater than %d", UBYTE_MAX );
            return false;
      }

      setNoGoArea((UBYTE)x1, (UBYTE)y1, (UBYTE)x2, (UBYTE)y2, LIMBO_LANDING);

    //this calls the Droids from the Limbo list onto the map
    placeLimboDroids();

      return true;
}

// -----------------------------------------------------------------------------------------
//initialises all the no go areas
BOOL scrInitAllNoGoAreas(void)
{
      initNoGoAreas();

      return true;
}

// -----------------------------------------------------------------------------------------
//set a no go area for the map - landing zones for the enemy, or player 0
BOOL scrSetNoGoArea(void)
{
      SDWORD            x1, x2, y1, y2, area;

      if (!stackPopParams(5, VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2,
            VAL_INT, &area))
      {
            return false;
      }

    if (area == LIMBO_LANDING)
    {
        ASSERT( false, "scrSetNoGoArea: Cannot set the Limbo Landing area with this function" );
        return false;
    }

      //check the values - check against max possible since can set in one mission for the next
      //if (x1 > (SDWORD)mapWidth)
      if (x1 > (SDWORD)MAP_MAXWIDTH)
      {
            ASSERT( false, "scrSetNoGoArea: x1 is greater than max mapWidth" );
            return false;
      }
      //if (x2 > (SDWORD)mapWidth)
      if (x2 > (SDWORD)MAP_MAXWIDTH)
      {
            ASSERT( false, "scrSetNoGoArea: x2 is greater than max mapWidth" );
            return false;
      }
      //if (y1 > (SDWORD)mapHeight)
      if (y1 > (SDWORD)MAP_MAXHEIGHT)
      {
            ASSERT( false, "scrSetNoGoArea: y1 is greater than max mapHeight" );
            return false;
      }
      //if (y2 > (SDWORD)mapHeight)
      if (y2 > (SDWORD)MAP_MAXHEIGHT)
      {
            ASSERT( false, "scrSetNoGoArea: y2 is greater than max mapHeight" );
            return false;
      }
      //check won't overflow!
      if (x1 > UBYTE_MAX || y1 > UBYTE_MAX || x2 > UBYTE_MAX || y2 > UBYTE_MAX)
      {
            ASSERT( false, "scrSetNoGoArea: one coord is greater than %d", UBYTE_MAX );
            return false;
      }

      if (area >= MAX_NOGO_AREAS)
      {
            ASSERT( false, "scrSetNoGoArea: max num of areas is %d", MAX_NOGO_AREAS );
            return false;
      }

      setNoGoArea((UBYTE)x1, (UBYTE)y1, (UBYTE)x2, (UBYTE)y2, (UBYTE)area);

      return true;
}


// -----------------------------------------------------------------------------------------
// set the zoom level for the radar
BOOL scrSetRadarZoom(void)
{
      SDWORD      level;

      if (!stackPopParams(1, VAL_INT, &level))
      {
            return true;
      }

      if (level < 0 || level > 2)
      {
            ASSERT( false, "scrSetRadarZoom: zoom level out of range" );
            return false;
      }



      SetRadarZoom((UWORD)level);

      return true;
}

// -----------------------------------------------------------------------------------------
//set how long an offworld mission can last -1 = no limit
BOOL scrSetMissionTime(void)
{
      SDWORD            time;

      if (!stackPopParams(1, VAL_INT, &time))
      {
            return false;
      }

      time *= 100;

      //check not more than one hour - the mission timers cannot cope at present! - (visually)
      //if (time > 60*60*GAME_TICKS_PER_SEC)
      //check not more than 99 mins - the mission timers cannot cope at present! - (visually)
    //we're allowing up to 5 hours now!
    if (time > 5*60*60*GAME_TICKS_PER_SEC)
      {
            ASSERT( false,"The mission timer cannot be set to more than 99!" );
            time = -1;
      }
      //store the value
      mission.time = time;
            // ffs ab    ... but shouldn't this be on the psx ?
    setMissionCountDown();


      //add the timer to the interface
      if (mission.time >= 0)
      {
            mission.startTime = gameTime;
            addMissionTimerInterface();
      }
    else
    {
        //make sure its not up if setting to -1
        intRemoveMissionTimer();
        //make sure the cheat time is not set
        mission.cheatTime = 0;
    }

      return true;
}

// this returns how long is left for the current mission time is 1/100th sec - same units as passed in
BOOL scrMissionTimeRemaining(void)
{
    SDWORD      timeRemaining;

      timeRemaining = mission.time - (gameTime - mission.startTime);

    if (timeRemaining < 0)
    {
        timeRemaining = 0;
    }
    else
    {
        timeRemaining /= 100;
    }

      scrFunctionResult.v.ival = timeRemaining;
      if(!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return(false);
      }
      return(true);
}

// -----------------------------------------------------------------------------------------
//set the time delay for reinforcements for an offworld mission
BOOL scrSetReinforcementTime(void)
{
      SDWORD            time;
    DROID       *psDroid;

      if (!stackPopParams(1, VAL_INT, &time))
      {
            return false;
      }

    time *= 100;

      //check not more than one hour - the mission timers cannot cope at present!
      if (time != LZ_COMPROMISED_TIME && time > 60*60*GAME_TICKS_PER_SEC)
      {
            ASSERT( false,"The transport timer cannot be set to more than 1 hour!" );
            time = -1;
      }

    //not interseted in this check any more -  AB 28/01/99
    //quick check of the value - don't check if time has not been set
      /*if (mission.time > 0 && time != LZ_COMPROMISED_TIME && time > mission.time)
      {
            DBMB(("scrSetReinforcementTime: reinforcement time greater than mission time!"));
      }*/
      //store the value
      mission.ETA = time;

      //if offworld or campaign change mission, then add the timer
      //if (mission.type == LDS_MKEEP || mission.type == LDS_MCLEAR ||
    //    mission.type == LDS_CAMCHANGE)
    if (missionCanReEnforce())
      {
            addTransporterTimerInterface();
      }

    //make sure the timer is not there if the reinforcement time has been set to < 0
    if (time < 0)
    {

        intRemoveTransporterTimer();

        /*only remove the launch if haven't got a transporter droid since the
        scripts set the time to -1 at the between stage if there are not going
        to be reinforcements on the submap  */
        for (psDroid = apsDroidLists[selectedPlayer]; psDroid != NULL; psDroid =
            psDroid->psNext)
        {
            if (psDroid->droidType == DROID_TRANSPORTER)
            {
                break;
            }
        }
        //if not found a transporter, can remove the launch button
        if (psDroid ==  NULL)
        {
            intRemoveTransporterLaunch();
        }
    }

      return true;
}

// -----------------------------------------------------------------------------------------
// Sets all structure limits for a player to a specified value
BOOL scrSetAllStructureLimits(void)
{
      SDWORD                        player, limit;
      STRUCTURE_LIMITS  *psStructLimits;
      UDWORD                        i;

      if (!stackPopParams(2, VAL_INT, &limit, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetStructureLimits:player number is too high" );
            return false;
      }

      if (limit < 0)
      {
            ASSERT( false, "scrSetStructureLimits: limit is less than zero - %d", limit );
            return false;
      }

      if (limit > LOTS_OF)
      {
            ASSERT( false, "scrSetStructureLimits: limit is too high - %d - must be less than %d",
                  limit, LOTS_OF );
            return false;
      }

      //set all the limits to the value specified
      psStructLimits = asStructLimits[player];
      for (i = 0; i < numStructureStats; i++)
      {
            psStructLimits[i].limit = (UBYTE)limit;

            psStructLimits[i].globalLimit = (UBYTE)limit;

      }

      return true;
}


// -----------------------------------------------------------------------------------------
// clear all the console messages
BOOL scrFlushConsoleMessages(void)
{
      flushConsoleMessages();

      return true;
}

// -----------------------------------------------------------------------------------------
// Establishes the distance between two points - uses an approximation
BOOL scrDistanceTwoPts( void )
{
      SDWORD      x1,y1,x2,y2;

      if(!stackPopParams(4,VAL_INT,&x1,VAL_INT,&y1,VAL_INT,&x2,VAL_INT,&y2))
      {
            ASSERT( false,"SCRIPT : Distance between two points - cannot get parameters" );
            return(false);
      }

      /* Approximate the distance */
      scrFunctionResult.v.ival = (SDWORD)dirtySqrt(x1,y1,x2,y2);
      if(!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            ASSERT( false,"SCRIPT : Distance between two points - cannot return scrFunctionResult" );
            return(false);
      }
      return(true);
}

// -----------------------------------------------------------------------------------------
// Returns whether two objects can see each other
BOOL  scrLOSTwoBaseObjects( void )
{
BASE_OBJECT *psSource,*psDest;
BOOL        bWallsBlock;
BOOL        retVal;

      if(!stackPopParams(3,ST_BASEOBJECT,&psSource,ST_BASEOBJECT,&psDest,VAL_BOOL,&bWallsBlock))
      {
            ASSERT( false,"SCRIPT : scrLOSTwoBaseObjects - cannot get parameters" );
            return(false);
      }

      if(bWallsBlock)
      {
            retVal = visibleObjWallBlock(psSource,psDest);
      }
      else
      {
            retVal = visibleObject(psSource,psDest);
      }

      scrFunctionResult.v.bval = retVal;
      if(!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            ASSERT( false,"SCRIPT : scrLOSTwoBaseObjects - cannot return scrFunctionResult" );
            return(false);
      }
      return(true);
}

// -----------------------------------------------------------------------------------------
// Destroys all structures within a certain bounding area.
BOOL  scrDestroyStructuresInArea( void )
{
SDWORD            x1,y1,x2,y2;
UDWORD            typeRef;
UDWORD            player;
STRUCTURE   *psStructure,*psNextS;
FEATURE           *psFeature,*psNextF;
BOOL        bVisible,bTakeFeatures;
SDWORD            sX,sY;

      if(!stackPopParams(8, VAL_INT, &player, VAL_INT, &typeRef, VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2,
                                    VAL_INT, &y2, VAL_BOOL, &bVisible, VAL_BOOL, &bTakeFeatures))
      {
            ASSERT( false,"SCRIPT : scrDestroyStructuresInArea - Cannot get parameters" );
            return(false);
      }

      if(player>=MAX_PLAYERS)
      {
            ASSERT( false,"Player number too high in scrDestroyStructuresInArea" );
      }

      for(psStructure = apsStructLists[player]; psStructure; psStructure = psNextS)
      {
            /* Keep a copy */
            psNextS = psStructure->psNext;

            sX = psStructure->pos.x;
            sY = psStructure->pos.y;

            if(psStructure->pStructureType->type == typeRef)
            {
                  if(sX >= x1 && sX <=x2 && sY >= y1 && sY <= y2)
                  {
                        if(bVisible)
                        {
                              destroyStruct(psStructure);
                        }
                        else
                        {
                              removeStruct(psStructure, true);
                        }
                  }
            }
      }

      if(bTakeFeatures)
      {
            for(psFeature = apsFeatureLists[0]; psFeature; psFeature = psNextF)
            {
                  /* Keep a copy */
                  psNextF = psFeature->psNext;

                  sX = psFeature->pos.x;
                  sY = psFeature->pos.y;

                  if( psFeature->psStats->subType == FEAT_BUILDING)
              //        (psFeature->psStats->subType != FEAT_OIL_DRUM) &&
              //        (psFeature->psStats->subType != FEAT_OIL_RESOURCE) )

                  {
                        if(sX >= x1 && sX <=x2 && sY >= y1 && sY <= y2)
                        {
                              if(bVisible)
                              {
                                    destroyFeature(psFeature);
                              }
                              else
                              {
                                    removeFeature(psFeature);
                              }
                        }
                  }
            }
      }
      return(true);
}
// -----------------------------------------------------------------------------------------
// Returns a value representing the threat from droids in a given area
BOOL  scrThreatInArea( void )
{
SDWORD      x1,y1,x2,y2;
SDWORD      ldThreat,mdThreat,hdThreat;
UDWORD      playerLooking,playerTarget;
SDWORD      totalThreat;
DROID *psDroid;
SDWORD      dX,dY;
BOOL  bVisible;

      if(!stackPopParams(10,VAL_INT,&playerLooking,VAL_INT,&playerTarget,VAL_INT,&x1,VAL_INT,&y1,VAL_INT,&x2,VAL_INT,&y2,
            VAL_INT,&ldThreat,VAL_INT,&mdThreat,VAL_INT,&hdThreat, VAL_BOOL, &bVisible))
      {
            ASSERT( false,"SCRIPT : scrThreatInArea - Cannot get parameters" );
            return(false);
      }

      if(playerLooking>=MAX_PLAYERS || playerTarget >= MAX_PLAYERS)
      {
            ASSERT( false,"Player number too high in scrThreatInArea" );
            return(false);
      }

      totalThreat = 0;

      for(psDroid = apsDroidLists[playerTarget]; psDroid; psDroid = psDroid->psNext)
      {
            if (!objHasWeapon((BASE_OBJECT *)psDroid))
            {
                  continue;
            }

            dX = psDroid->pos.x;
            dY = psDroid->pos.y;
            /* Do we care if the droid is visible or not */
            if(bVisible ? psDroid->visible[playerLooking] : true)
            {
                  /* Have we found a droid in this area */
                  if(dX >= x1 && dX <=x2 && dY >= y1 && dY <= y2)
                  {
                        switch ((asBodyStats + psDroid->asBits[COMP_BODY].nStat)->size)
                        {
                        case SIZE_LIGHT:
                              totalThreat += ldThreat;
                              break;
                        case SIZE_MEDIUM:
                              totalThreat += mdThreat;
                              break;
                        case SIZE_HEAVY:
                        case SIZE_SUPER_HEAVY:
                              totalThreat += hdThreat;
                              break;
                        default:
                              ASSERT( false, "Weird droid size in threat assessment" );
                              break;
                        }
                  }
            }
      }
//    DBPRINTF(("scrThreatInArea: returning %d\n", totalThreat));
      scrFunctionResult.v.ival = totalThreat;
      if(!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            ASSERT( false,"SCRIPT : Cannot push scrFunctionResult in scrThreatInArea" );
            return(false);
      }
      return(true);
}
// -----------------------------------------------------------------------------------------
// returns the nearest gateway bottleneck to a specified point
BOOL scrGetNearestGateway( void )
{
      SDWORD      x,y;
      SDWORD      gX,gY;
      UDWORD      nearestSoFar;
      UDWORD      dist;
      GATEWAY     *psGateway;
      SDWORD      retX,retY;
      SDWORD      *rX,*rY;
      BOOL  success;

      if(!stackPopParams(4, VAL_INT, &x, VAL_INT, &y, VAL_REF|VAL_INT, &rX, VAL_REF|VAL_INT, &rY))
      {
            ASSERT( false,"SCRIPT : Cannot get parameters for scrGetNearestGateway" );
            return(false);
      }

      if(x<0 || x>(SDWORD)mapWidth || y<0 || y>(SDWORD)mapHeight)
      {
            ASSERT( false,"SCRIPT : Invalid coordinates in getNearestGateway" );
            return(false);
      }

      if(psGateways == NULL)
      {
            ASSERT( false,"SCRIPT : No gateways found in getNearestGatway" );
            return(false);
      }

      nearestSoFar = UDWORD_MAX;
      retX = retY = 0;
      success = false;
      for(psGateway = psGateways; psGateway; psGateway = psGateway->psNext)
      {
            /* Get gateway midpoint */
            gX = (psGateway->x1 + psGateway->x2)/2;
            gY = (psGateway->y1 + psGateway->y2)/2;

            /* Estimate the distance to it */
            dist = dirtySqrt(x,y,gX,gY);

            /* Is it best we've found? */
            if(dist<nearestSoFar)
            {
                  success = true;
                  /* Yes, then keep a record of it */
                  nearestSoFar = dist;
                  retX = gX;
                  retY = gY;
            }
      }

      *rX = retX;
      *rY = retY;

      scrFunctionResult.v.bval = success;
      if(!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            ASSERT( false,"SCRIPT : Cannot return scrFunctionResult for stackPushResult" );
            return(false);
      }


      return(true);
}
// -----------------------------------------------------------------------------------------
BOOL  scrSetWaterTile(void)
{
UDWORD      tileNum;

      if(!stackPopParams(1,VAL_INT, &tileNum))
      {
            ASSERT( false,"SCRIPT : Cannot get parameter for scrSetWaterTile" );
            return(false);
      }


      if(tileNum > 96)
      {
            ASSERT( false,"SCRIPT : Water tile number too high in scrSetWaterTile" );
            return(false);
      }

      setUnderwaterTile(tileNum);

      return(true);
}
// -----------------------------------------------------------------------------------------
BOOL  scrSetRubbleTile(void)
{
UDWORD      tileNum;

      if(!stackPopParams(1,VAL_INT, &tileNum))
      {
            ASSERT( false,"SCRIPT : Cannot get parameter for scrSetRubbleTile" );
            return(false);
      }


      if(tileNum > 96)
      {
            ASSERT( false,"SCRIPT : Rubble tile number too high in scrSetWaterTile" );
            return(false);
      }

      setRubbleTile(tileNum);

      return(true);
}
// -----------------------------------------------------------------------------------------
BOOL  scrSetCampaignNumber(void)
{
UDWORD      campaignNumber;

      if(!stackPopParams(1,VAL_INT, &campaignNumber))
      {
            ASSERT( false,"SCRIPT : Cannot get parameter for scrSetCampaignNumber" );
            return(false);
      }


      setCampaignNumber(campaignNumber);

      return(true);
}

// -----------------------------------------------------------------------------------------
// Tests whether a structure has a certain module for a player. Tests whether any structure
// has this module if structure is null
BOOL  scrTestStructureModule(void)
{
SDWORD      player,refId;
STRUCTURE   *psStructure,*psStruct;
BOOL  bFound;

      if(!stackPopParams(3,VAL_INT,&player,ST_STRUCTURE,&psStructure,VAL_INT,&refId))
      {
            ASSERT( false,"SCRIPT : Cannot get parameters in scrTestStructureModule" );
            return(false);
      }

      if(player>=MAX_PLAYERS)
      {
            ASSERT( false,"SCRIPT : Player number too high in scrTestStructureModule" );
            return(false);

      }

      /* Nothing yet */
      bFound = false;

      /* Check the specified case first */
      if(psStructure)
      {
            if(structHasModule(psStructure))
            {
                  bFound = true;
            }
      }
      /* psStructure was NULL - so test the general case */
      else
      {
            // Search them all, but exit if we get one!!
            for(psStruct = apsStructLists[player],bFound = false;
                  psStruct && !bFound; psStruct = psStruct->psNext)
            {
                  if(structHasModule(psStruct))
                  {
                        bFound = true;
                  }
            }
      }

      /* Send back the scrFunctionResult */
      scrFunctionResult.v.bval = bFound;
      if(!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            ASSERT( false,"SCRIPT : Cannot push scrFunctionResult for scrTestStructureModule" );
            return(false);
      }

      return(true);
}


// -----------------------------------------------------------------------------------------
BOOL  scrForceDamage( void )
{
DROID       *psDroid;
STRUCTURE   *psStructure;
FEATURE           *psFeature;
BASE_OBJECT *psObj;
UDWORD            damagePercent;
float       divisor;
UDWORD            newVal;

      /* OK - let's get the vars */
      if(!stackPopParams(2,ST_BASEOBJECT,&psObj,VAL_INT,&damagePercent))
      {
            ASSERT( false,"Cannot pop params for scrForceDamage" );
            return(false);
      }

      /* Got to be a percent, so must be less than or equal to 100 */
      if(damagePercent > 100)
      {
            ASSERT( false,"scrForceDamage : You're supposed to be passing in a PERCENTAGE VALUE, \
                  instead I got given %d, which is clearly no good, now is it!?", damagePercent );
            return(false);
      }

      /* Get percentage in range [0.1] */
      divisor =  (float)damagePercent / 100.f;

      /* See what we're dealing with */
      switch(psObj->type)
      {
      case OBJ_DROID:
            psDroid = (DROID *) psObj;
            newVal = divisor * psDroid->originalBody;
            psDroid->body = newVal;
            break;
      case OBJ_STRUCTURE:
            psStructure = (STRUCTURE *) psObj;
            newVal = divisor * structureBody(psStructure);
            psStructure->body = (UWORD)newVal;
            break;
      case OBJ_FEATURE:
            psFeature = (FEATURE *) psObj;
            /* Some features cannot be damaged */
            if(psFeature->psStats->damageable)
            {
                  newVal = divisor * psFeature->psStats->body;
                  psFeature->body = newVal;
            }
            break;
      default:
            ASSERT( false,"Unsupported base object type in scrForceDamage" );
            return(false);
            break;
      }

      return(true);

}
// Kills of a droid without spawning any explosion effects.
// -----------------------------------------------------------------------------------------
BOOL  scrDestroyUnitsInArea( void )
{
DROID *psDroid,*psNext;
SDWORD      x1,y1,x2,y2;
UDWORD      player;
UDWORD      count=0;

      if(!stackPopParams(5,VAL_INT,&x1,VAL_INT,&y1,VAL_INT,&x2,VAL_INT,&y2,VAL_INT, &player))
      {
            ASSERT( false,"Cannot get params for scrDestroyUnitsInArea" );
            return(false);
      }

      if(player>=MAX_PLAYERS)
      {
            ASSERT( false,"Invalid player number in scrKillDroidsInArea" );
      }

      for(psDroid = apsDroidLists[player]; psDroid; psDroid = psNext)
      {
            psNext = psDroid->psNext;     // get a copy cos pointer will be lost
            if( (psDroid->pos.x > x1) && (psDroid->pos.x < x2) &&
                  (psDroid->pos.y > y1) && (psDroid->pos.y < y2) )
            {
                  /* then it's inside the area */
                  destroyDroid(psDroid);
                  count++;
            }
      }

      scrFunctionResult.v.ival = count;
      if(!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return(false);
      }

      return(true);
}
// -----------------------------------------------------------------------------------------
BOOL  scrRemoveDroid( void )
{
DROID *psDroid;

      if(!stackPopParams(1,ST_DROID,&psDroid))
      {           ASSERT( false,"Cannot get vars for scrRemoveDroid!" );
            return(false);
      }

      if(psDroid)
      {
            vanishDroid(psDroid);
      }

      return(true);
}
// -----------------------------------------------------------------------------------------
static BOOL structHasModule(STRUCTURE *psStruct)
{
STRUCTURE_STATS   *psStats;
BOOL              bFound;

      /* Fail if the structure isn't built yet */
      if(psStruct->status != SS_BUILT)
      {
            return(false);
      }

      /* Not found yet */
      bFound = false;


      if(psStruct==NULL)
      {
            ASSERT( psStruct!=NULL,"structHasModule - Testing for a module from a NULL struct - huh!?" );
            return(false);
      }

      if(psStruct)
      {
            /* Grab a stats pointer */
            psStats = psStruct->pStructureType;
            if(StructIsFactory(psStruct)
                  || psStats->type == REF_POWER_GEN || psStats->type == REF_RESEARCH)
            {
                  switch(psStats->type)
                  {
                        case REF_POWER_GEN:
                              if (((POWER_GEN *)psStruct->pFunctionality)->capacity)
                              {
                                    bFound = true;
                              }
                              break;
                        case REF_FACTORY:
                        case REF_VTOL_FACTORY:
                              if (((FACTORY *)psStruct->pFunctionality)->capacity)
                              {
                                    bFound = true;
                              }
                              break;
                        case REF_RESEARCH:
                              if (((RESEARCH_FACILITY *)psStruct->pFunctionality)->capacity)

                              {
                                    bFound = true;
                              }
                              break;
                        default:
                              //no other structures can have modules attached
                              break;
                  }
            }
            else
            {
                  /* Wrong type of building - cannot have a module */
                  bFound = false;
            }

      }
      return(bFound);
}

// -----------------------------------------------------------------------------------------
// give player a template belonging to another.
BOOL scrAddTemplate(void)
{
      DROID_TEMPLATE *psTemplate;
      UDWORD                  player;

      if (!stackPopParams(2, ST_TEMPLATE, &psTemplate, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrAddTemplate:player number is too high" );
            return false;
      }

      ASSERT( psTemplate != NULL, "scrAddTemplate: Invalid template pointer" );

      if(   addTemplate(player,psTemplate))
      {
            scrFunctionResult.v.bval = true;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
      }
      else
      {
            scrFunctionResult.v.bval = false;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
      }

      return true;
}



// -----------------------------------------------------------------------------------------

// additional structure check
static BOOL structDoubleCheck(BASE_STATS *psStat,UDWORD xx,UDWORD yy, SDWORD maxBlockingTiles)
{
      UDWORD x,y,xTL,yTL,xBR,yBR;
      UBYTE count =0;

      STRUCTURE_STATS *psBuilding = (STRUCTURE_STATS *)psStat;

      xTL = xx-1;
      yTL = yy-1;
      xBR = (xx + psBuilding->baseWidth );
      yBR = (yy + psBuilding->baseBreadth );
      // can you get past it?

      y = yTL;    // top
      for(x = xTL;x!=xBR+1;x++)
      {
            if(fpathGroundBlockingTile(x,y))
            {
                  count++;
                  break;
            }}

      y = yBR;    // bottom
      for(x = xTL;x!=xBR+1;x++)
      {
            if(fpathGroundBlockingTile(x,y))
            {
                  count++;
                  break;
            }}

      x = xTL;    // left
      for(y = yTL+1; y!=yBR; y++)
      {
            if(fpathGroundBlockingTile(x,y))
            {
                  count++;
                  break;
            }}

      x = xBR;    // right
      for(y = yTL+1; y!=yBR; y++)
      {
            if(fpathGroundBlockingTile(x,y))
            {
                  count++;
                  break;
            }}

      //make sure this location is not blocked from too many sides
      if((count <= maxBlockingTiles) || (maxBlockingTiles == -1))
      {
            return true;
      }
      return false;

}

static BOOL pickStructLocation(int index, int *pX, int *pY, int player, int maxBlockingTiles)
{
      STRUCTURE_STATS   *psStat;
      UDWORD                  numIterations = 30;
      BOOL              found = false;
      UDWORD                  startX, startY, incX, incY;
      SDWORD                  x=0, y=0;

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "pickStructLocation:player number is too high" );
            return false;
      }

    // check for wacky coords.
      if(         *pX < 0
            ||    *pX > world_coord(mapWidth)
            ||    *pY < 0
            ||    *pY > world_coord(mapHeight)
        )
      {
            goto failedstructloc;
      }

      psStat = &asStructureStats[index];              // get stat.
      startX = map_coord(*pX);                              // change to tile coords.
      startY = map_coord(*pY);

      x = startX;
      y = startY;

      // first try the original location
      if ( validLocation((BASE_STATS*)psStat, startX, startY, player, false) )
      {
            if(structDoubleCheck((BASE_STATS*)psStat,startX,startY,maxBlockingTiles))
            {
                  found = true;
            }
      }

      // try some locations nearby
      if(!found)
      {
            for (incX = 1, incY = 1; incX < numIterations; incX++, incY++)
            {
                  if (!found){                  //top
                        y = startY - incY;
                        for(x = startX - incX; x < (SDWORD)(startX + incX); x++){
                              if ( validLocation((BASE_STATS*)psStat, x, y, player, false)
                                     && structDoubleCheck((BASE_STATS*)psStat,x,y,maxBlockingTiles)
                                    ){
                                    found = true;
                                    break;
                              }}}

                  if (!found) {                 //right
                        x = startX + incX;
                        for(y = startY - incY; y < (SDWORD)(startY + incY); y++){
                              if(validLocation((BASE_STATS*)psStat, x, y, player, false)
                                     && structDoubleCheck((BASE_STATS*)psStat,x,y,maxBlockingTiles)
                                    ){
                                    found = true;
                                    break;
                              }}}

                  if (!found){                  //bot
                        y = startY + incY;
                        for(x = startX + incX; x > (SDWORD)(startX - incX); x--){
                              if(validLocation((BASE_STATS*)psStat, x, y, player, false)
                                     && structDoubleCheck((BASE_STATS*)psStat,x,y,maxBlockingTiles)
                                     ){
                                    found = true;
                                    break;
                              }}}

                  if (!found){                  //left
                        x = startX - incX;
                        for(y = startY + incY; y > (SDWORD)(startY - incY); y--){
                              if(validLocation((BASE_STATS*)psStat, x, y, player, false)
                                     && structDoubleCheck((BASE_STATS*)psStat,x,y,maxBlockingTiles)
                                     ){
                                    found = true;
                                    break;
                              }}}

                  if (found)
                  {
                        break;
                  }
            }
      }

      if(found)   // did It!
      {
            // back to world coords.
            *pX = world_coord(x) + (psStat->baseWidth * (TILE_UNITS / 2));
            *pY = world_coord(y) + (psStat->baseBreadth * (TILE_UNITS / 2));

            scrFunctionResult.v.bval = true;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))         // success!
            {
                  return false;
            }

            return true;
      }
      else
      {
failedstructloc:
            scrFunctionResult.v.bval = false;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))         // failed!
            {
                  return false;
            }
      }
      return true;
}

// pick a structure location(only used in skirmish game at 27Aug) ajl.
BOOL scrPickStructLocation(void)
{
      SDWORD                  *pX,*pY;
      SDWORD                  index;
      UDWORD                  player;

      if (!stackPopParams(4, ST_STRUCTURESTAT, &index, VAL_REF|VAL_INT, &pX ,
        VAL_REF|VAL_INT, &pY, VAL_INT, &player))
      {
            return false;
      }
      return pickStructLocation(index, pX, pY, player, MAX_BLOCKING_TILES);
}

// pick a structure location(only used in skirmish game at 27Aug) ajl.
// Max number of blocking tiles is passed as parameter for this one
BOOL scrPickStructLocationB(void)
{
      SDWORD                  *pX,*pY;
      SDWORD                  index;
      UDWORD                  player;
      SDWORD                  maxBlockingTiles;

      if (!stackPopParams(5, ST_STRUCTURESTAT, &index, VAL_REF|VAL_INT, &pX ,
        VAL_REF|VAL_INT, &pY, VAL_INT, &player, VAL_INT, &maxBlockingTiles))
      {
            return false;
      }
      return pickStructLocation(index, pX, pY, player, maxBlockingTiles);
}

// -----------------------------------------------------------------------------------------
// Sets the transporter entry and exit points for the map
BOOL scrSetTransporterExit(void)
{
      SDWORD      iPlayer, iExitTileX, iExitTileY;

      if (!stackPopParams(3, VAL_INT, &iPlayer, VAL_INT, &iExitTileX, VAL_INT, &iExitTileY))
      {
            return false;
      }

      missionSetTransporterExit( iPlayer, iExitTileX, iExitTileY );

      return true;
}

// -----------------------------------------------------------------------------------------
// Fly transporters in at start of map
BOOL scrFlyTransporterIn(void)
{
      SDWORD      iPlayer, iEntryTileX, iEntryTileY;
      BOOL  bTrackTransporter;

      if (!stackPopParams(4, VAL_INT, &iPlayer, VAL_INT, &iEntryTileX, VAL_INT, &iEntryTileY,
                                          VAL_BOOL, &bTrackTransporter))
      {
            return false;
      }

      missionSetTransporterEntry( iPlayer, iEntryTileX, iEntryTileY );
      missionFlyTransportersIn( iPlayer, bTrackTransporter );

      return true;
}

// -----------------------------------------------------------------------------------------



/*
 ** scrGetGameStatus
 *
 *  PARAMETERS: The parameter passed must be one of the STATUS_ variable
 *
 *  DESCRIPTION: Returns various BOOL options in the game   e.g. If the reticule is open
 *      - You should use the externed variable intMode for other game mode options
 *        e.g. in the intelligence screen or desgin screen)
 *
 *  RETURNS:
 *
 */
BOOL scrGetGameStatus(void)
{
      SDWORD GameChoice;
      BOOL bResult;

      if (!stackPopParams(1, VAL_INT, &GameChoice))
      {
            return false;
      }

//    DBPRINTF(("getgamestatus choice=%d\n",GameChoice));

      bResult=false;          // the default scrFunctionResult is false

      switch (GameChoice)
      {

            case STATUS_ReticuleIsOpen:
                  if(widgGetFromID(psWScreen,IDRET_FORM) != NULL) bResult=true;
                  break;

            case STATUS_BattleMapViewEnabled:
//                if (driveTacticalActive()==true) scrFunctionResult=true;


                  if (bResult==true)
                  {
                        debug( LOG_NEVER, "battle map active" );
                  }
                  else
                  {
                        debug( LOG_NEVER, "battle map not active" );
                  }


                  break;
            case STATUS_DeliveryReposInProgress:
                  if (DeliveryReposValid()==true) bResult=true;
                  break;

            default:
            ASSERT( false,"ScrGetGameStatus. Invalid STATUS_ variable" );
            break;
      }

      scrFunctionResult.v.bval = bResult;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }
      return true;
}

//get the colour number used by a player
BOOL scrGetPlayerColour(void)
{
      SDWORD            player;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrGetPlayerColour: player number is too high" );
            return false;
      }

      scrFunctionResult.v.ival = (SDWORD)getPlayerColour(player);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

//get the colour name of the player ("green", "black" etc)
BOOL scrGetPlayerColourName(void)
{
      SDWORD            player;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS || player < 0)
      {
            ASSERT( false, "scrGetPlayerColourName: wrong player index" );
            return false;
      }

      /* Casting away constness because stackPushResult doesn't modify it's
       * value (i.e. in this case it's not const correct).
       */
      scrFunctionResult.v.sval = (char*)getPlayerColourName(player);
      if (!stackPushResult(VAL_STRING, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrGetPlayerColourName(): failed to push result");
            return false;
      }

      return true;
}

//set the colour number to use for a player
BOOL scrSetPlayerColour(void)
{
      SDWORD            player, colour;

      if (!stackPopParams(2, VAL_INT, &colour, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetPlayerColour:player number is too high" );
            return false;
      }

      if (colour >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetPlayerColour:colour number is too high" );
            return false;
      }

    //not the end of the world if this doesn't work so don't check the return code
    (void)setPlayerColour(player, colour);

      return true;
}

//set all droids in an area to belong to a different player - returns the number of droids changed
BOOL scrTakeOverDroidsInArea(void)
{
      SDWORD            fromPlayer, toPlayer, x1, x2, y1, y2, numChanged;
    DROID       *psDroid, *psNext;

      if (!stackPopParams(6, VAL_INT, &fromPlayer, VAL_INT, &toPlayer,
        VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      if (fromPlayer >= MAX_PLAYERS || toPlayer >= MAX_PLAYERS)
      {
            ASSERT( false, "scrTakeOverUnitsInArea:player number is too high" );
            return false;
      }

      if (x1 > world_coord(MAP_MAXWIDTH))
      {
            ASSERT( false, "scrTakeOverUnitsInArea: x1 is greater than max mapWidth" );
            return false;
      }

    if (x2 > world_coord(MAP_MAXWIDTH))
      {
            ASSERT( false, "scrTakeOverUnitsInArea: x2 is greater than max mapWidth" );
            return false;
      }

    if (y1 > world_coord(MAP_MAXHEIGHT))
      {
            ASSERT( false, "scrTakeOverUnitsInArea: y1 is greater than max mapHeight" );
            return false;
      }

    if (y2 > world_coord(MAP_MAXHEIGHT))
      {
            ASSERT( false, "scrTakeOverUnitsInArea: y2 is greater than max mapHeight" );
            return false;
      }

    numChanged = 0;
    for (psDroid = apsDroidLists[fromPlayer]; psDroid != NULL; psDroid = psNext)
    {
        psNext = psDroid->psNext;
        //check if within area specified
        if (psDroid->pos.x >= x1 && psDroid->pos.x <= x2 &&
            psDroid->pos.y >= y1 && psDroid->pos.y <= y2)
        {
            //give the droid away
            if (giftSingleDroid(psDroid, toPlayer))
            {
                numChanged++;
            }
        }
    }

      scrFunctionResult.v.ival = numChanged;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

    return true;
}

/*this takes over a single droid and passes a pointer back to the new one*/
BOOL scrTakeOverSingleDroid(void)
{
      SDWORD                  playerToGain;
    DROID           *psDroidToTake, *psNewDroid;

    if (!stackPopParams(2, ST_DROID, &psDroidToTake, VAL_INT, &playerToGain))
    {
            return false;
    }

      if (playerToGain >= MAX_PLAYERS)
      {
            ASSERT( false, "scrTakeOverSingleUnit:player number is too high" );
            return false;
      }

    if (psDroidToTake == NULL)
    {
        ASSERT( false, "scrTakeOverSingleUnit: Null unit" );
        return false;
    }

      ASSERT( psDroidToTake != NULL,
            "scrTakeOverSingleUnit: Invalid unit pointer" );

    psNewDroid = giftSingleDroid(psDroidToTake, playerToGain);

      scrFunctionResult.v.oval = psNewDroid;
      if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))
      {
            return false;
    }
      return true;
}

// set all droids in an area of a certain experience level or less to belong to
// a different player - returns the number of droids changed
BOOL scrTakeOverDroidsInAreaExp(void)
{
      SDWORD            fromPlayer, toPlayer, x1, x2, y1, y2, numChanged, level, maxUnits;
    DROID       *psDroid, *psNext;

      if (!stackPopParams(8, VAL_INT, &fromPlayer, VAL_INT, &toPlayer,
        VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2, VAL_INT, &level, VAL_INT, &maxUnits))
      {
            return false;
      }

      if (fromPlayer >= MAX_PLAYERS || toPlayer >= MAX_PLAYERS)
      {
            ASSERT( false, "scrTakeOverUnitsInArea:player number is too high" );
            return false;
      }

      if (x1 > world_coord(MAP_MAXWIDTH))
      {
            ASSERT( false, "scrTakeOverUnitsInArea: x1 is greater than max mapWidth" );
            return false;
      }

    if (x2 > world_coord(MAP_MAXWIDTH))
      {
            ASSERT( false, "scrTakeOverUnitsInArea: x2 is greater than max mapWidth" );
            return false;
      }

    if (y1 > world_coord(MAP_MAXHEIGHT))
      {
            ASSERT( false, "scrTakeOverUnitsInArea: y1 is greater than max mapHeight" );
            return false;
      }

    if (y2 > world_coord(MAP_MAXHEIGHT))
      {
            ASSERT( false, "scrTakeOverUnitsInArea: y2 is greater than max mapHeight" );
            return false;
      }

    numChanged = 0;
    for (psDroid = apsDroidLists[fromPlayer]; psDroid != NULL; psDroid = psNext)
    {
        psNext = psDroid->psNext;
        //check if within area specified
        if ((psDroid->droidType != DROID_CONSTRUCT) &&
                  (psDroid->droidType != DROID_REPAIR) &&
            (psDroid->droidType != DROID_CYBORG_CONSTRUCT) &&
            (psDroid->droidType != DROID_CYBORG_REPAIR) &&
//                ((SDWORD)getDroidLevel(psDroid) <= level) &&
                  ((SDWORD)psDroid->experience <= level) &&
                  psDroid->pos.x >= x1 && psDroid->pos.x <= x2 &&
            psDroid->pos.y >= y1 && psDroid->pos.y <= y2)
        {
            //give the droid away
            if (giftSingleDroid(psDroid, toPlayer))
            {
                numChanged++;
            }
        }

            if (numChanged >= maxUnits)
            {
                  break;
            }
    }

      scrFunctionResult.v.ival = numChanged;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

    return true;
}

/*this takes over a single structure and passes a pointer back to the new one*/
BOOL scrTakeOverSingleStructure(void)
{
      SDWORD                  playerToGain;
    STRUCTURE       *psStructToTake, *psNewStruct;
    UDWORD          structureInc;

    if (!stackPopParams(2, ST_STRUCTURE, &psStructToTake, VAL_INT, &playerToGain))
    {
            return false;
    }

      if (playerToGain >= MAX_PLAYERS)
      {
            ASSERT( false, "scrTakeOverSingleStructure:player number is too high" );
            return false;
      }

    if (psStructToTake == NULL)
    {
        ASSERT( false, "scrTakeOverSingleStructure: Null structure" );
        return false;
    }

      ASSERT( psStructToTake != NULL,
            "scrTakeOverSingleStructure: Invalid structure pointer" );

    structureInc = psStructToTake->pStructureType->ref - REF_STRUCTURE_START;
    if (playerToGain == (SDWORD)selectedPlayer && StructIsFactory(psStructToTake) &&
        asStructLimits[playerToGain][structureInc].currentQuantity >= MAX_FACTORY)
    {
            debug( LOG_NEVER, "scrTakeOverSingleStructure - factory ignored for selectedPlayer\n" );
        psNewStruct = NULL;
    }
    else
    {
        psNewStruct = giftSingleStructure(psStructToTake, (UBYTE)playerToGain, true);
        if (psNewStruct)
        {
            //check the structure limits aren't compromised
            if (asStructLimits[playerToGain][structureInc].currentQuantity >
                asStructLimits[playerToGain][structureInc].limit)
            {
                asStructLimits[playerToGain][structureInc].limit = asStructLimits[
                    playerToGain][structureInc].currentQuantity;
            }
            //for each structure taken - add graphical effect if the selectedPlayer
            if (playerToGain == (SDWORD)selectedPlayer)
            {
                assignSensorTarget((BASE_OBJECT *)psNewStruct);
            }
        }
    }

      scrFunctionResult.v.oval = psNewStruct;
      if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
      {
            return false;
    }
      return true;
}

//set all structures in an area to belong to a different player - returns the number of droids changed
//will not work on factories for the selectedPlayer
BOOL scrTakeOverStructsInArea(void)
{
      SDWORD            fromPlayer, toPlayer, x1, x2, y1, y2, numChanged;
    STRUCTURE   *psStruct, *psNext, *psNewStruct;
    UDWORD      structureInc;

      if (!stackPopParams(6, VAL_INT, &fromPlayer, VAL_INT, &toPlayer,
        VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
      {
            return false;
      }

      if (fromPlayer >= MAX_PLAYERS || toPlayer >= MAX_PLAYERS)
      {
            ASSERT( false, "scrTakeOverStructsInArea:player number is too high" );
            return false;
      }

      if (x1 > world_coord(MAP_MAXWIDTH))
      {
            ASSERT( false, "scrTakeOverStructsInArea: x1 is greater than max mapWidth" );
            return false;
      }

    if (x2 > world_coord(MAP_MAXWIDTH))
      {
            ASSERT( false, "scrTakeOverStructsInArea: x2 is greater than max mapWidth" );
            return false;
      }

    if (y1 > world_coord(MAP_MAXHEIGHT))
      {
            ASSERT( false, "scrTakeOverStructsInArea: y1 is greater than max mapHeight" );
            return false;
      }

    if (y2 > world_coord(MAP_MAXHEIGHT))
      {
            ASSERT( false, "scrTakeOverStructsInArea: y2 is greater than max mapHeight" );
            return false;
      }

    numChanged = 0;
    for (psStruct = apsStructLists[fromPlayer]; psStruct != NULL; psStruct = psNext)
    {
        psNext = psStruct->psNext;
        //check if within area specified
        if (psStruct->pos.x >= x1 && psStruct->pos.x <= x2 &&
            psStruct->pos.y >= y1 && psStruct->pos.y <= y2)
        {
            //changed this so allows takeOver is have less than 5 factories
            //don't work on factories for the selectedPlayer
            structureInc = psStruct->pStructureType->ref - REF_STRUCTURE_START;
            if (toPlayer == (SDWORD)selectedPlayer && StructIsFactory(psStruct) &&
                asStructLimits[toPlayer][structureInc].currentQuantity >= MAX_FACTORY)
            {
                        debug( LOG_NEVER, "scrTakeOverStructsInArea - factory ignored for selectedPlayer\n" );
            }
            else
            {
                //give the structure away
                psNewStruct = giftSingleStructure(psStruct, (UBYTE)toPlayer, true);
                if (psNewStruct)
                {
                    numChanged++;
                    //check the structure limits aren't compromised
                    //structureInc = psNewStruct->pStructureType->ref - REF_STRUCTURE_START;
                    if (asStructLimits[toPlayer][structureInc].currentQuantity >
                        asStructLimits[toPlayer][structureInc].limit)
                    {
                        asStructLimits[toPlayer][structureInc].limit = asStructLimits[
                            toPlayer][structureInc].currentQuantity;
                    }
                    //for each structure taken - add graphical effect if the selectedPlayer
                    if (toPlayer == (SDWORD)selectedPlayer)
                    {
                        assignSensorTarget((BASE_OBJECT *)psNewStruct);
                    }
                }
            }
        }
    }

      scrFunctionResult.v.ival = numChanged;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

    return true;
}

//set Flag for defining what happens to the droids in a Transporter
BOOL scrSetDroidsToSafetyFlag(void)
{
      BOOL bState;

      if (!stackPopParams(1, VAL_BOOL, &bState))
      {
            return false;
      }

    setDroidsToSafetyFlag(bState);

      return true;
}

//set Flag for defining whether the coded countDown is called
BOOL scrSetPlayCountDown(void)
{
      BOOL bState;

      if (!stackPopParams(1, VAL_BOOL, &bState))
      {
            return false;
      }


    setPlayCountDown((UBYTE)bState);


      return true;
}

//get the number of droids currently onthe map for a player
BOOL scrGetDroidCount(void)
{
      SDWORD            player;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrGetUnitCount:player number is too high" );
            return false;
      }

      scrFunctionResult.v.ival = getNumDroids(player);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


// fire a weapon stat at an object
BOOL scrFireWeaponAtObj(void)
{
      SDWORD                  wIndex;
      BASE_OBJECT       *psTarget;
      WEAPON                  sWeapon;

      if (!stackPopParams(2, ST_WEAPON, &wIndex, ST_BASEOBJECT, &psTarget))
      {
            return false;
      }

      if (psTarget == NULL)
      {
            ASSERT( false,"scrFireWeaponAtObj: Null target pointer" );
            return false;
      }

      memset(&sWeapon, 0, sizeof(WEAPON));
      sWeapon.nStat = wIndex;

      // send the projectile using the selectedPlayer so that it can always be seen
      proj_SendProjectile(&sWeapon, NULL, selectedPlayer, psTarget->pos.x,psTarget->pos.y,psTarget->pos.z, psTarget, true, false, 0);

      return true;
}

// fire a weapon stat at a location
BOOL scrFireWeaponAtLoc(void)
{
      SDWORD                  wIndex, x,y;
      WEAPON                  sWeapon;

      if (!stackPopParams(3, ST_WEAPON, &wIndex, VAL_INT, &x, VAL_INT, &y))
      {
            return false;
      }

      memset(&sWeapon, 0, sizeof(WEAPON));
      sWeapon.nStat = wIndex;

      // send the projectile using the selectedPlayer so that it can always be seen
      proj_SendProjectile(&sWeapon, NULL, selectedPlayer, x,y,map_Height(x,y), NULL, true, false, 0);

      return true;
}

// set the number of kills for a droid
BOOL scrSetDroidKills(void)
{
      DROID *psDroid;
      SDWORD      kills;

      if (!stackPopParams(2, ST_DROID, &psDroid, VAL_INT, &kills))
      {
            return true;
      }

      if ((psDroid == NULL) ||
            (psDroid->type != OBJ_DROID))
      {
            ASSERT( false, "scrSetUnitKills: NULL/invalid unit pointer" );
            return false;
      }

      psDroid->experience = (UWORD)kills * 100;

      return true;
}

// get the number of kills for a droid
BOOL scrGetDroidKills(void)
{
      DROID *psDroid;

      if (!stackPopParams(1, ST_DROID, &psDroid))
      {
            return true;
      }

      if ((psDroid == NULL) ||
            (psDroid->type != OBJ_DROID))
      {
            ASSERT( false, "scrGetDroidKills: NULL/invalid unit pointer" );
            return false;
      }

      scrFunctionResult.v.ival = psDroid->experience;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// reset the visibility for a player
BOOL scrResetPlayerVisibility(void)
{
      SDWORD                  player, i;
      BASE_OBJECT       *psObj;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            return false;
      }

      if (player < 0 || player > MAX_PLAYERS)
      {
            ASSERT( false, "scrResetPlayerVisibility: invalid player" );
            return false;
      }

      for(i=0; i< MAX_PLAYERS; i++)
      {
            if (i == player)
            {
                  continue;
            }

            for(psObj = (BASE_OBJECT *)apsDroidLists[i]; psObj; psObj = psObj->psNext)
            {
                  psObj->visible[player] = 0;
            }

            for(psObj = (BASE_OBJECT *)apsStructLists[i]; psObj; psObj = psObj->psNext)
            {
                  psObj->visible[player] = 0;
            }
      }

      for(psObj = (BASE_OBJECT *)apsFeatureLists[0]; psObj; psObj = psObj->psNext)
      {
            psObj->visible[player] = 0;
      }

      clustResetVisibility(player);

      return true;
}


// set the vtol return pos for a player
BOOL scrSetVTOLReturnPos(void)
{
      SDWORD            player, tx,ty;

      if (!stackPopParams(3, VAL_INT, &player, VAL_INT, &tx, VAL_INT, &ty))
      {
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetVTOLReturnPos: invalid player" );
                  return false;
      }

      asVTOLReturnPos[player].x = (tx * TILE_UNITS) + TILE_UNITS/2;
      asVTOLReturnPos[player].y = (ty * TILE_UNITS) + TILE_UNITS/2;

      return true;
}

//called via the script in a Limbo Expand level to set the level to plain ol' expand
BOOL scrResetLimboMission(void)
{
    //check currently on a Limbo expand mission
    if (!missionLimboExpand())
    {
        ASSERT( false, "scrResetLimboMission: current mission type invalid" );
        return false;
    }

    //turn it into an expand mission
    resetLimboMission();

    return true;
}



// skirmish only.
BOOL scrIsVtol(void)
{
      DROID *psDroid;

      if (!stackPopParams(1, ST_DROID, &psDroid))
      {
            return true;
      }

      if(psDroid == NULL)
      {
            ASSERT( false,"scrIsVtol: null droid passed in." );
      }

      scrFunctionResult.v.bval = isVtolDroid(psDroid) ;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }
      return true;
}




// do the setting up of the template list for the tutorial.
BOOL scrTutorialTemplates(void)
{
      DROID_TEMPLATE    *psCurr, *psPrev;
      char              pName[MAX_STR_LENGTH];

      // find ViperLtMGWheels
      sstrcpy(pName, "ViperLtMGWheels");
      if (!getResourceName(pName))
      {
            debug( LOG_ERROR, "tutorial template setup failed" );
            abort();
            return false;
      }

      getDroidResourceName(pName);


      for (psCurr = apsDroidTemplates[selectedPlayer],psPrev = NULL;
                  psCurr != NULL;
                  psCurr = psCurr->psNext)
      {
            if (strcmp(pName,psCurr->aName)==0)
            {
                  if (psPrev)
                  {
                        psPrev->psNext = psCurr->psNext;
                  }
                  else
                  {
                        apsDroidTemplates[selectedPlayer] = psCurr->psNext;
                  }
                  //quit looking cos found
                  break;
            }
            psPrev = psCurr;
      }

      // Delete the template.
      if(psCurr)
      {
            free(psCurr);
      }
      else
      {
            debug( LOG_ERROR, "tutorial template setup failed" );
            abort();
            return false;
      }
      return true;
}







//-----------------------------------------
//New functions
//-----------------------------------------

//compare two strings (0 means they are different)
BOOL scrStrcmp(void)
{
      if (!stackPopParams(2, VAL_STRING, &strParam1, VAL_STRING, &strParam2))
      {
            debug(LOG_ERROR, "scrStrcmp(): stack failed");
            return false;
      }

      scrFunctionResult.v.bval = !strcmp(strParam1, strParam2);
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrStrcmp: failed to push result");
            return false;
      }

      return true;
}

/* Output a string to console */
BOOL scrConsole(void)
{
      if (!stackPopParams(1, VAL_STRING, &strParam1))
      {
            debug(LOG_ERROR, "scrConsole(): stack failed");
            return false;
      }

      addConsoleMessage(strParam1,DEFAULT_JUSTIFY, SYSTEM_MESSAGE);

      return true;
}

BOOL scrDebug[MAX_PLAYERS];

//turn on debug messages
BOOL scrDbgMsgOn(void)
{
      BOOL  bOn;
      SDWORD      player;

      if (!stackPopParams(2, VAL_INT, &player, VAL_BOOL, &bOn))
      {
            debug(LOG_ERROR, "scrDbgMsgOn(): stack failed");
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            debug(LOG_ERROR, "scrDbgMsgOn(): wrong player number");
            return false;
      }

      scrDebug[player] = bOn;

      return true;
}

BOOL scrMsg(void)
{
      SDWORD      playerTo,playerFrom;
      char tmp[255];

      if (!stackPopParams(3, VAL_STRING, &strParam1, VAL_INT, &playerFrom, VAL_INT, &playerTo))
      {
            debug(LOG_ERROR, "scrMsg(): stack failed");
            return false;
      }

      if(playerFrom < 0 || playerFrom >= MAX_PLAYERS)
      {
            debug(LOG_ERROR, "scrMsg(): playerFrom out of range");
            return false;
      }

      if(playerTo < 0 || playerTo >= MAX_PLAYERS)
      {
            debug(LOG_ERROR, "scrMsg(): playerTo out of range");
            return false;
      }

      sendAIMessage(strParam1, playerFrom, playerTo);


      //show the message we sent on our local console as well (even in skirmish, if player plays as this AI)
      if(playerFrom == selectedPlayer)
      {
            sprintf(tmp,"[%d-%d] : %s",playerFrom, playerTo, strParam1);                                                                  // add message
            addConsoleMessage(tmp, RIGHT_JUSTIFY, playerFrom);
      }

      return true;
}

BOOL scrDbg(void)
{
      SDWORD      player;

      if (!stackPopParams(2, VAL_STRING, &strParam1, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrDbg(): stack failed");
            return false;
      }

      if(scrDebug[player])
      {
            char  sTmp[255];
            sprintf(sTmp,"%d) %s",player,strParam1);
            addConsoleMessage(sTmp,DEFAULT_JUSTIFY, player);
      }

      return true;
}

BOOL scrDebugFile(void)
{
      if (!stackPopParams(1, VAL_STRING, &strParam1))
      {
            debug(LOG_ERROR, "scrDebugFile(): stack failed");
            return false;
      }

      debug(LOG_SCRIPT, strParam1);

      return true;
}

static      UDWORD                  playerToEnumDroid;
static      UDWORD                  playerVisibleDroid;
static      UDWORD                  enumDroidCount;

/* Prepare the droid iteration */
BOOL scrInitEnumDroids(void)
{
      SDWORD      targetplayer,playerVisible;

      if ( !stackPopParams(2,  VAL_INT, &targetplayer, VAL_INT, &playerVisible) )
      {
            //DbgMsg("scrInitEnumDroids() - failed to pop params");
            return false;
      }

      playerToEnumDroid = (UDWORD)targetplayer;
      playerVisibleDroid      = (UDWORD)playerVisible;
      enumDroidCount = 0;           //returned 0 droids so far
      return true;
}

/* Get next droid */
BOOL scrEnumDroid(void)
{
      UDWORD                  count;
      DROID        *psDroid;
      BOOL              found;

      count = 0;
      for(psDroid=apsDroidLists[playerToEnumDroid];psDroid && count<enumDroidCount;count++)
      {
            psDroid = psDroid->psNext;
      }


      //search the players' list of droid to see if one exists and is visible
      found = false;
      while(psDroid)
      {
            if(psDroid->visible[playerVisibleDroid])
            {
                  scrFunctionResult.v.oval = psDroid;
                  if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))              //    push scrFunctionResult
                  {
                        return false;
                  }

                  enumDroidCount++;
                  return true;
            }

            enumDroidCount++;
            psDroid = psDroid->psNext;
      }

      // push NULLDROID, since didn't find any
      scrFunctionResult.v.oval = NULL;
      if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrEnumDroid() - push failed");
            return false;
      }

      return true;
}

//Return the template factory is currently building
BOOL scrFactoryGetTemplate(void)
{
      STRUCTURE         *psStructure = NULL;
      DROID_TEMPLATE    *psTemplate = NULL;

      if (!stackPopParams(1, ST_STRUCTURE, &psStructure))
      {
            debug(LOG_ERROR, "scrFactoryGetTemplate() - stackPopParams failed");
            return false;
      }

      if (psStructure == NULL)
      {
            debug(LOG_ERROR, "scrFactoryGetTemplate() - NULL factory object");
            ASSERT( false, "scrFactoryGetTemplate: NULL factory object" );
            return false;
      }

      ASSERT( psStructure != NULL,
            "scrFactoryGetTemplate: Invalid structure pointer" );
      ASSERT( (psStructure->pStructureType->type == REF_FACTORY ||
            psStructure->pStructureType->type == REF_CYBORG_FACTORY ||
            psStructure->pStructureType->type == REF_VTOL_FACTORY),
            "scrFactoryGetTemplate: structure is not a factory" );

      if(!StructIsFactory(psStructure))
      {
            debug(LOG_ERROR, "scrFactoryGetTemplate: structure not a factory.");
            return false;
      }

      psTemplate = (DROID_TEMPLATE *)((FACTORY*)psStructure->pFunctionality)->psSubject;

      ASSERT( psTemplate != NULL,
            "scrFactoryGetTemplate: Invalid template pointer" );

      scrFunctionResult.v.oval = psTemplate;
      if (!stackPushResult((INTERP_TYPE)ST_TEMPLATE, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrFactoryGetTemplate: stackPushResult failed");
            return false;
      }

      return true;
}

BOOL scrNumTemplatesInProduction(void)
{
      SDWORD                  player,numTemplates = 0;
      DROID_TEMPLATE    *psTemplate;
    STRUCTURE           *psStruct;
      STRUCTURE         *psList;
      BASE_STATS        *psBaseStats;

      if (!stackPopParams(2, ST_TEMPLATE, &psTemplate, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrNumTemplatesInProduction: stackPopParams failed");
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            debug(LOG_ERROR, "scrNumTemplatesInProduction: player number is too high");
            ASSERT( false, "scrNumTemplatesInProduction: player number is too high" );
            return false;
      }

      ASSERT( psTemplate != NULL,
            "scrNumTemplatesInProduction: Invalid template pointer" );

      psBaseStats = (BASE_STATS *)psTemplate; //Convert

      psList = apsStructLists[player];

      for (psStruct = psList; psStruct != NULL; psStruct = psStruct->psNext)
      {
            if (StructIsFactory(psStruct))
            {
                  FACTORY *psFactory = (FACTORY *)psStruct->pFunctionality;

                  //if this is the template currently being worked on
                  if (psBaseStats == psFactory->psSubject)
                  {
                        numTemplates++;
                  }
            }
      }

      scrFunctionResult.v.ival = numTemplates;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrNumTemplatesInProduction: stackPushResult failed");
            return false;
      }

      return true;
}

// Returns number of units based on a component a certain player has
BOOL scrNumDroidsByComponent(void)
{
      SDWORD                        player,lookingPlayer,comp;
      UDWORD                        numFound;
      INTERP_VAL              sVal;
      DROID                   *psDroid;

      if (!stackPopParams(2, VAL_INT, &player, VAL_INT, &lookingPlayer))
      {
            debug(LOG_ERROR, "scrNumDroidsByComponent(): stack failed");
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            debug(LOG_ERROR, "scrNumDroidsByComponent(): player number is too high");
            ASSERT( false, "scrNumDroidsByComponent:player number is too high" );
            return false;
      }

      if (!stackPop(&sVal))
      {
            debug(LOG_ERROR, "scrNumDroidsByComponent(): failed to pop component");
            return false;
      }

      numFound = 0;

      comp = (SDWORD)sVal.v.ival;    //cache access

      //check droids
      for(psDroid = apsDroidLists[player]; psDroid; psDroid = psDroid->psNext)
      {
            if(psDroid->visible[lookingPlayer])       //can see this droid?
            {
                  switch(sVal.type)
                  {
                  case ST_BODY:
                        if (psDroid->asBits[COMP_BODY].nStat == comp)
                        {
                              numFound++;
                        }
                        break;
                  case ST_PROPULSION:
                        if (psDroid->asBits[COMP_PROPULSION].nStat == comp)
                        {
                              numFound++;
                        }
                        break;
                  case ST_ECM:
                        if (psDroid->asBits[COMP_ECM].nStat == comp)
                        {
                              numFound++;
                        }
                        break;
                  case ST_SENSOR:
                        if (psDroid->asBits[COMP_SENSOR].nStat == comp)
                        {
                              numFound++;
                        }
                        break;
                  case ST_CONSTRUCT:
                        if (psDroid->asBits[COMP_CONSTRUCT].nStat == comp)
                        {
                              numFound++;
                        }
                        break;
                  case ST_REPAIR:
                        if (psDroid->asBits[COMP_REPAIRUNIT].nStat == comp)
                        {
                              numFound++;
                        }
                        break;
                  case ST_WEAPON:
                        if (psDroid->asWeaps[0].nStat == comp)
                        {
                              numFound++;
                              break;
                        }
                        break;
                  case ST_BRAIN:
                        if (psDroid->asBits[COMP_BRAIN].nStat == comp)
                        {
                              numFound++;
                        }
                        break;
                  default:
                        debug(LOG_ERROR, "scrNumDroidsByComponent(): unknown component type");
                        ASSERT( false, "scrNumDroidsByComponent: unknown component type" );
                        return false;
                  }
            }
      }

      scrFunctionResult.v.ival = numFound;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrNumDroidsByComponent(): stackPushResult failed");
            return false;
      }

      return true;
}

BOOL scrGetStructureLimit(void)
{
      SDWORD                        player,limit;
      UDWORD                        structInc;
      STRUCTURE_LIMITS  *psStructLimits;

      if (!stackPopParams(2, ST_STRUCTURESTAT, &structInc, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrGetStructureLimit(): stackPopParams failed");
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            debug(LOG_ERROR, "scrGetStructureLimit(): player number is too high");
            ASSERT( false, "scrSetStructureLimits: player number is too high" );
            return false;}

      if (structInc > numStructureStats)
      {
            debug(LOG_ERROR, "scrGetStructureLimit(): tructure stat is too high - %d", structInc);
            ASSERT( false, "scrSetStructureLimits: Structure stat is too high - %d", structInc );
            return false;}

      psStructLimits = asStructLimits[player];
      limit = (SDWORD)psStructLimits[structInc].limit;

      scrFunctionResult.v.ival = limit;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrGetStructureLimit(): stackPushResult failed");
            return false;
      }

      return true;
}

// Returns true if limit for the passed structurestat is reached, otherwise returns false
BOOL scrStructureLimitReached(void)
{
      SDWORD                        player;
      BOOL                    bLimit = false;
      UDWORD                        structInc;
      STRUCTURE_LIMITS  *psStructLimits;

      if (!stackPopParams(2, ST_STRUCTURESTAT, &structInc, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrStructureLimitReached(): stackPopParams failed");
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            debug(LOG_ERROR, "scrStructureLimitReached(): player number is too high");
            ASSERT( false, "scrSetStructureLimits: player number is too high" );
            return false;
      }

      if (structInc > numStructureStats)
      {
            debug(LOG_ERROR, "scrStructureLimitReached(): Structure stat is too high - %d", structInc);
            ASSERT( false, "scrSetStructureLimits: Structure stat is too high - %d", structInc );
            return false;}

      psStructLimits = asStructLimits[player];

      if(psStructLimits[structInc].currentQuantity >= psStructLimits[structInc].limit) bLimit = true;

      scrFunctionResult.v.bval = bLimit;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrStructureLimitReached(): stackPushResult failed");
            return false;
      }

      return true;
}

// How many structures of a given type a player has
BOOL scrGetNumStructures(void)
{
      SDWORD                        player,numStructures;
      UDWORD                        structInc;
      STRUCTURE_LIMITS  *psStructLimits;

      if (!stackPopParams(2, ST_STRUCTURESTAT, &structInc, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrSetStructureLimits: failed to pop");
            return false;}

      if (player >= MAX_PLAYERS)
      {
            debug(LOG_ERROR, "scrSetStructureLimits:player number is too high");
            return false;}

      if (structInc > numStructureStats)
      {
            debug(LOG_ERROR, "scrSetStructureLimits: Structure stat is too high");
            return false;}

      psStructLimits = asStructLimits[player];
      numStructures = (SDWORD)psStructLimits[structInc].currentQuantity;

      scrFunctionResult.v.ival = numStructures;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// Return player's unit limit
BOOL scrGetUnitLimit(void)
{
      SDWORD                        player;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrGetUnitLimit: failed to pop");
            return false;}

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetStructureLimits:player number is too high" );
            return false;}

      scrFunctionResult.v.ival = getMaxDroids(player);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// Return minimum of 2 vals
BOOL scrMin(void)
{
      SDWORD                        val1,val2;

      if (!stackPopParams(2, VAL_INT, &val1, VAL_INT, &val2))
      {
            return false;
      }

      scrFunctionResult.v.ival = MIN(val2, val1);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// Return maximum of 2 vals
BOOL scrMax(void)
{
      SDWORD                        val1,val2;

      if (!stackPopParams(2, VAL_INT, &val1, VAL_INT, &val2))
      {
            return false;
      }

      scrFunctionResult.v.ival = MAX(val1, val2);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

BOOL scrFMin(void)
{
      float                   fval1,fval2;

      if (!stackPopParams(2, VAL_FLOAT, &fval1, VAL_FLOAT, &fval2))
      {
            return false;
      }

      scrFunctionResult.v.fval = MIN(fval2, fval1);
      if (!stackPushResult(VAL_FLOAT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// Return maximum of 2 floats
BOOL scrFMax(void)
{
      float                   fval1,fval2;

      if (!stackPopParams(2, VAL_FLOAT, &fval1, VAL_FLOAT, &fval2))
      {
            return false;
      }

      scrFunctionResult.v.fval = MAX(fval1, fval2);
      if (!stackPushResult(VAL_FLOAT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

BOOL ThreatInRange(SDWORD player, SDWORD range, SDWORD rangeX, SDWORD rangeY, BOOL bVTOLs)
{
      UDWORD                        i,structType,tx,ty;
      STRUCTURE               *psStruct;
      DROID                   *psDroid;

      tx = map_coord(rangeX);
      ty = map_coord(rangeY);

      for(i=0;i<MAX_PLAYERS;i++)
      {
            if((alliances[player][i] == ALLIANCE_FORMED) || (i == player))
            {
                  continue;
            }

            //check structures
            for(psStruct = apsStructLists[i]; psStruct; psStruct=psStruct->psNext)
            {
                  if(psStruct->visible[player]) //if can see it
                  {
                        if(psStruct->status == SS_BUILT)
                        {
                              structType = psStruct->pStructureType->type;

                              switch(structType)            //dangerous to get near these structures
                              {
                                    case REF_DEFENSE:
                                    case REF_CYBORG_FACTORY:
                                    case REF_FACTORY:
                                    case REF_VTOL_FACTORY:
                                    case REF_REARM_PAD:

                                    if (range < 0
                                     || world_coord(dirtySqrt(tx, ty, map_coord(psStruct->pos.x), map_coord(psStruct->pos.y))) < range)   //enemy in range
                                    {
                                          return true;
                                    }

                                    break;
                              }
                        }
                  }
            }

            //check droids
            for(psDroid = apsDroidLists[i]; psDroid; psDroid = psDroid->psNext)
            {
                  if(psDroid->visible[player])        //can see this droid?
                  {
                        if (!objHasWeapon((BASE_OBJECT *)psDroid))
                        {
                              continue;
                        }

                        //if VTOLs are excluded, skip them
                        if(!bVTOLs && ((asPropulsionStats[psDroid->asBits[COMP_PROPULSION].nStat].propulsionType == LIFT) || (psDroid->droidType == DROID_TRANSPORTER)))
                        {
                              continue;
                        }

                        if (range < 0
                         || world_coord(dirtySqrt(tx, ty , map_coord(psDroid->pos.x), map_coord(psDroid->pos.y))) < range)    //enemy in range
                        {
                              return true;
                        }
                  }
            }
      }

      return false;
}

//find unrevealed tile closest to pwLooker within the range of wRange
BOOL scrFogTileInRange(void)
{
      SDWORD            pwLookerX,pwLookerY,tBestX,tBestY,threadRange;
      SDWORD            wRangeX,wRangeY,tRangeX,tRangeY,wRange,player;
      UDWORD            tx,ty,i,j,wDist,wBestDist;
      MAPTILE           *psTile;
      BOOL        ok = false;
      SDWORD            *wTileX,*wTileY;

      if (!stackPopParams(9, VAL_REF|VAL_INT, &wTileX, VAL_REF|VAL_INT, &wTileY,
            VAL_INT, &pwLookerX, VAL_INT, &pwLookerY, VAL_INT, &wRangeX, VAL_INT, &wRangeY,
            VAL_INT, &wRange, VAL_INT, &player, VAL_INT, &threadRange))
      {
            debug(LOG_ERROR, "scrFogTileInRange: failed to pop");
            return false;}

    //Check coords
      if(         pwLookerX < 0
            ||    pwLookerX > world_coord(mapWidth)
            ||    pwLookerY < 0
            ||    pwLookerY > world_coord(mapHeight))
      {
            debug(LOG_ERROR, "scrFogTileInRange: coords off map");
            return false;
      }

      tRangeX = map_coord(wRangeX);                   //cache to tile coords, for faster calculations
      tRangeY = map_coord(wRangeY);

      tx = map_coord(pwLookerX);                            // change to tile coords.
      ty = map_coord(pwLookerY);

      wBestDist = 99999;
      tBestX = -1; tBestY = -1;

      for(i=0; i<mapWidth;i++)
      {
            for(j=0; j<mapHeight; j++)
            {
                  psTile = mapTile(i,j);
                  if(!TEST_TILE_VISIBLE(player,psTile))     //not vis
                  {
                        //within base range
                        if (wRange <= 0
                         || world_coord(dirtySqrt(tRangeX, tRangeY, i, j)) < wRange)            //dist in world units between baseX/baseY and the tile
                        {
                              //calc dist between this tile and looker
                              wDist = world_coord(dirtySqrt(tx, ty, i, j));

                              //closer than last one?
                              if(wDist < wBestDist)
                              {
                                    //tmpX = i;
                                    //tmpY = j;
                                    //if(pickATileGen(&tmpX, &tmpY, 4,zonedPAT))    //can reach (don't need many passes)
                                    if(zonedPAT(i,j)) //Can reach this tile
                                    {
                                          //if((tmpX == i) && (tmpY == j))    //can't allow to change coords, otherwise might send to the same unrevealed tile next time
                                                                                          //and units will stuck forever
                                          //{
                                          if((threadRange <= 0) || (!ThreatInRange(player, threadRange, world_coord(i), world_coord(j), false)))
                                          {
                                                      wBestDist = wDist;
                                                      tBestX = i;
                                                      tBestY = j;
                                                      ok = true;
                                          }
                                          //}
                                    }
                              }
                        }
                  }
            }
      }

      if(ok)      //something found
      {
            *wTileX = world_coord(tBestX);
            *wTileY = world_coord(tBestY);

            scrFunctionResult.v.bval = true;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  debug(LOG_ERROR, "scrFogTileInRange: stackPushResult failed (found)");
                  return false;
            }
      }
      else
      {
            scrFunctionResult.v.bval = false;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  debug(LOG_ERROR, "scrFogTileInRange: stackPushResult failed (not found)");
                  return false;
            }
      }

      return true;
}

BOOL scrMapRevealedInRange(void)
{
      SDWORD            wRangeX,wRangeY,tRangeX,tRangeY,wRange,tRange,player;
      UDWORD            i,j;

      if (!stackPopParams(4, VAL_INT, &wRangeX, VAL_INT, &wRangeY,
            VAL_INT, &wRange, VAL_INT, &player))
      {
            debug(LOG_ERROR,  "scrMapRevealedInRange: failed to pop");
            return false;
      }

    //Check coords
      if (wRangeX < 0
       || wRangeX > world_coord(mapWidth)
       || wRangeY < 0
       || wRangeY > world_coord(mapHeight))
      {
            debug(LOG_ERROR,  "scrMapRevealedInRange: coords off map");
            return false;
      }

      // convert to tile coords
      tRange = map_coord(wRange);
      tRangeX = map_coord(wRangeX);                   //cache to tile coords, for faster calculations
      tRangeY = map_coord(wRangeY);

      for(i=0; i<mapWidth;i++)
      {
            for(j=0; j<mapHeight; j++)
            {
                  // don't bother checking if out of range
                  if(abs(tRangeX-i) < tRange && abs(tRangeY-j) < tRange)
                  {
                        //within range
                        if ((world_coord(dirtySqrt(tRangeX, tRangeY, i, j)) < wRange) &&        //dist in world units between x/y and the tile
                              TEST_TILE_VISIBLE( player,mapTile(i,j) ))       //not visible
                        {
                              scrFunctionResult.v.bval = true;
                              if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
                              {
                                    return false;
                              }

                              return true;
                        }
                  }
            }
      }

      //nothing found
      scrFunctionResult.v.bval = false;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Returns true if a certain map tile was revealed, ie fog of war was removed */
BOOL scrMapTileVisible(void)
{
      SDWORD            tileX,tileY,player;

      if (!stackPopParams(3, VAL_INT, &tileX, VAL_INT, &tileY, VAL_INT, &player))
      {
            debug(LOG_ERROR,  "scrMapTileVisible: failed to pop");
            return false;
      }

    //Check coords
      if (tileX < 0
       || tileX > world_coord(mapWidth)
       || tileY < 0
       || tileY > world_coord(mapHeight))
      {
            debug(LOG_ERROR,  "scrMapTileVisible: coords off map");
            return false;
      }

      if(TEST_TILE_VISIBLE( player,mapTile(tileX,tileY) ))
      {
            scrFunctionResult.v.bval = true;

            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
      }
      else
      {
            //not visible
            scrFunctionResult.v.bval = false;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
      }

      return true;
}

//return number of reserach topics that are left to be researched
//for a certain technology to become available
BOOL scrNumResearchLeft(void)
{
      RESEARCH                *psResearch;
      SDWORD                        player,iResult;
      UWORD                   cur,index,tempIndex;
      SWORD                   top;

      UWORD                   Stack[400];

      BOOL                    found;
      PLAYER_RESEARCH         *pPlayerRes;


      if (!stackPopParams(2, VAL_INT, &player, ST_RESEARCH, &psResearch ))
      {
            debug(LOG_ERROR,  "scrNumResearchLeft(): stack failed");
            return false;
      }

      if(psResearch == NULL)
      {
            ASSERT( false, "scrNumResearchLeft(): no such research topic" );
            return false;
      }

      pPlayerRes = asPlayerResList[player];
      index = psResearch - asResearch;    //TODO: fix if needed

      if (index >= numResearch)
      {
            ASSERT( false, "scrNumResearchLeft(): invalid research index" );
            return false;
      }

      found = false;

      if(beingResearchedByAlly(index, player))
      {
            iResult = 1;
      }
      else if(IsResearchCompleted(&pPlayerRes[index]))
      {
            iResult = 0;
      }
      else if(IsResearchStarted(&pPlayerRes[index]))
      {
            iResult = 1;
      }
      else if(IsResearchPossible(&pPlayerRes[index]) || IsResearchCancelled(&pPlayerRes[index]))
      {
            iResult = 1;
      }
      else if(skTopicAvail(index,player))
      {
            iResult = 1;
      }
      else
      {
            iResult = 1;            //init, count the top research topic as 1
            top = -1;

            cur = 0;                      //start with first index's PR
            tempIndex = -1;
            while(true)             //do
            {
                  if(cur >= asResearch[index].numPRRequired)            //this one has no PRs or end of PRs reached
                  {
                        top = top - 2;
                        if(top < (-1))
                        {
                              break;            //end of stack
                        }
                        index = Stack[top + 2]; //if index = -1, then exit
                        cur = Stack[top + 1];         //go to next PR of the last node

                  }
                  else        //end of PRs not reached
                  {
                        iResult += asResearch[index].numPRRequired;           //add num of PRs this topic has

                        tempIndex = asResearch[index].pPRList[cur];           //get cur node's index

                        //decide if has to check its PRs
                        if(!IsResearchCompleted(&pPlayerRes[tempIndex]) &&    //don't touch if completed already
                              !skTopicAvail(index,player) &&                              //has no unresearched PRs left if available
                              !beingResearchedByAlly(index, player))                //will become available soon anyway
                        {
                              if(asResearch[tempIndex].numPRRequired > 0)     //node has any nodes itself
                              {
                                    Stack[top+1] = cur;                                               //so can go back to it further
                                    Stack[top+2] = index;
                                    top = top + 2;

                                    index = tempIndex;            //go 1 level further
                                    cur = -1;                                                   //start with first PR of this PR next time
                              }
                        }
                  }

                  cur++;                        //try next node of the main node
                  if((cur >= asResearch[index].numPRRequired) && (top <= (-1)))     //nothing left
                  {
                        break;
                  }

            }
      }

      scrFunctionResult.v.ival = iResult;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

//check if any of the ally is researching this topic
BOOL beingResearchedByAlly(SDWORD resIndex, SDWORD player)
{
      STRUCTURE *psOtherStruct;
      SDWORD      i;
      BASE_STATS *Stat;

      Stat = (BASE_STATS*)(asResearch + resIndex);

      for(i=0;i<MAX_PLAYERS;i++)
      {
            if(i != player && aiCheckAlliances(player,i))
            {
                  //check each research facility to see if they are doing this topic.
                  for(psOtherStruct=apsStructLists[i];psOtherStruct;psOtherStruct=psOtherStruct->psNext)
                  {
                        if(   psOtherStruct->pStructureType->type == REF_RESEARCH
                                    && psOtherStruct->status == SS_BUILT
                                    && ((RESEARCH_FACILITY *)psOtherStruct->pFunctionality)->psSubject
                                     )
                        {

                              if(((RESEARCH_FACILITY *)psOtherStruct->pFunctionality)->psSubject->ref == Stat->ref)
                              {
                                    return true;
                              }
                        }
                  }

            }
      }

      return false;
}

// true if player has completed this research
BOOL scrResearchCompleted(void)
{
      RESEARCH                *psResearch;
      SDWORD                        player;
      UWORD                   index;
      PLAYER_RESEARCH         *pPlayerRes;

      if (!stackPopParams(2,ST_RESEARCH, &psResearch, VAL_INT, &player ))
      {
            debug(LOG_ERROR,   "scrResearchCompleted: stack failed");
            return false;
      }

      if(psResearch == NULL)
      {
            debug( LOG_ERROR, "scrResearchCompleted: no such research topic" );
            return false;
      }

      pPlayerRes = asPlayerResList[player];
      index = psResearch - asResearch;    //TODO: fix if needed

      if (index >= numResearch)
      {
            debug( LOG_ERROR, "scrResearchCompleted: invalid research index" );
            return false;
      }

      if(IsResearchCompleted(&pPlayerRes[index]))
      {
            scrFunctionResult.v.bval = true;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
      }
      else
      {
            scrFunctionResult.v.bval = false;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
      }

      return true;
}

// true if player has already started researching it
BOOL scrResearchStarted(void)
{
      RESEARCH                *psResearch;
      SDWORD                        player;
      UWORD                   index;
      PLAYER_RESEARCH         *pPlayerRes;

      if (!stackPopParams(2,ST_RESEARCH, &psResearch, VAL_INT, &player ))
      {
            debug(LOG_ERROR,  "scrResearchStarted(): stack failed");
            return false;
      }

      if(psResearch == NULL)
      {
            ASSERT( false, ": no such research topic" );
            return false;
      }

      pPlayerRes = asPlayerResList[player];
      index = psResearch - asResearch;    //TODO: fix if needed

      if (index >= numResearch)
      {
            ASSERT( false, "scrResearchCompleted: invalid research index" );
            return false;
      }

      if(IsResearchStarted(&pPlayerRes[index]))
      {
            scrFunctionResult.v.bval = true;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
      }
      else
      {
            scrFunctionResult.v.bval = false;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
      }

      return true;
}

//returns true if location is dangerous
BOOL scrThreatInRange(void)
{
      SDWORD                        player,range,rangeX,rangeY;
      BOOL                          bVTOLs;

      if (!stackPopParams(5, VAL_INT, &player, VAL_INT, &rangeX,
            VAL_INT, &rangeY, VAL_INT, &range, VAL_BOOL, &bVTOLs))
      {
            debug(LOG_ERROR,  "scrThreatInRange(): stack failed");
            return false;
      }

      scrFunctionResult.v.bval = ThreatInRange(player, range, rangeX, rangeY, bVTOLs);
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


BOOL scrNumEnemyWeapObjInRange(void)
{
      SDWORD                        lookingPlayer,range,rangeX,rangeY,i;
      UDWORD                        numEnemies = 0;
      BOOL                    bVTOLs,bFinished;

      if (!stackPopParams(6, VAL_INT, &lookingPlayer, VAL_INT, &rangeX,
            VAL_INT, &rangeY, VAL_INT, &range, VAL_BOOL, &bVTOLs, VAL_BOOL, &bFinished))
      {
            debug(LOG_ERROR,  "scrNumEnemyWeapObjInRange(): stack failed");
            return false;
      }


      for(i=0;i<MAX_PLAYERS;i++)
      {
            if((alliances[lookingPlayer][i] == ALLIANCE_FORMED) || (i == lookingPlayer))  //skip allies and myself
            {
                  continue;
            }

            numEnemies += numPlayerWeapDroidsInRange(i, lookingPlayer, range, rangeX, rangeY, bVTOLs);
            numEnemies += numPlayerWeapStructsInRange(i, lookingPlayer, range, rangeX, rangeY, bFinished);
      }

      scrFunctionResult.v.ival = numEnemies;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrNumEnemyWeapObjInRange(): failed to push result");
            return false;
      }

      return true;
}

/* Calculates the total cost of enemy weapon objects in a certain area */
BOOL scrEnemyWeapObjCostInRange(void)
{
      SDWORD                        lookingPlayer,range,rangeX,rangeY,i;
      UDWORD                        enemyCost = 0;
      BOOL                    bVTOLs,bFinished;

      if (!stackPopParams(6, VAL_INT, &lookingPlayer, VAL_INT, &rangeX,
            VAL_INT, &rangeY, VAL_INT, &range, VAL_BOOL, &bVTOLs, VAL_BOOL, &bFinished))
      {
            debug(LOG_ERROR,  "scrEnemyWeapObjCostInRange(): stack failed");
            return false;
      }

      for(i=0;i<MAX_PLAYERS;i++)
      {
            if((alliances[lookingPlayer][i] == ALLIANCE_FORMED) || (i == lookingPlayer))  //skip allies and myself
            {
                  continue;
            }

            enemyCost += playerWeapDroidsCostInRange(i, lookingPlayer, range, rangeX, rangeY, bVTOLs);
            enemyCost += playerWeapStructsCostInRange(i, lookingPlayer, range, rangeX, rangeY, bFinished);
      }

      scrFunctionResult.v.ival = enemyCost;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrEnemyWeapObjCostInRange(): failed to push result");
            return false;
      }

      return true;
}

/* Calculates the total cost of ally (+ looking player)
 * weapon objects in a certain area
 */
BOOL scrFriendlyWeapObjCostInRange(void)
{
      SDWORD                        player,range,rangeX,rangeY,i;
      UDWORD                        friendlyCost = 0;
      BOOL                    bVTOLs,bFinished;

      if (!stackPopParams(6, VAL_INT, &player, VAL_INT, &rangeX,
            VAL_INT, &rangeY, VAL_INT, &range, VAL_BOOL, &bVTOLs, VAL_BOOL, &bFinished))
      {
            debug(LOG_ERROR,  "scrFriendlyWeapObjCostInRange(): stack failed");
            return false;
      }

      for(i=0;i<MAX_PLAYERS;i++)
      {
            if((alliances[player][i] == ALLIANCE_FORMED) || (i == player))    //skip enemies
            {
                  friendlyCost += numPlayerWeapDroidsInRange(i, player, range, rangeX, rangeY, bVTOLs);
                  friendlyCost += numPlayerWeapStructsInRange(i, player, range, rangeX, rangeY,bFinished);
            }
      }

      scrFunctionResult.v.ival = friendlyCost;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/**
 * Helper function for numPlayerWeapDroidsInRange and playerWeapDroidsCostInRange.
 * Will either count the number of droids or calculate the total costs.
 */
static UDWORD costOrAmountInRange(SDWORD player, SDWORD lookingPlayer, SDWORD range,
                                                   SDWORD rangeX, SDWORD rangeY, BOOL bVTOLs, BOOL justCount)
{
      UDWORD                        droidCost;
      DROID                   *psDroid;

      droidCost = 0;

      //check droids
      for(psDroid = apsDroidLists[player]; psDroid; psDroid = psDroid->psNext)
      {
            if(psDroid->visible[lookingPlayer])       //can see this droid?
            {
                  if (!objHasWeapon((BASE_OBJECT *)psDroid))
                  {
                        continue;
                  }

                  //if VTOLs are excluded, skip them
                  if(!bVTOLs && ((asPropulsionStats[psDroid->asBits[COMP_PROPULSION].nStat].propulsionType == LIFT) || (psDroid->droidType == DROID_TRANSPORTER)))
                  {
                        continue;
                  }

                  if((range < 0) || (dirtySqrt(rangeX, rangeY , psDroid->pos.x, psDroid->pos.y) < range))   //enemy in range
                  {
                        if (justCount)
                        {
                              droidCost++;
                        }
                        else
                        {
                              droidCost += calcDroidPower(psDroid);
                        }
                  }
            }
      }

      return droidCost;
}

UDWORD numPlayerWeapDroidsInRange(SDWORD player, SDWORD lookingPlayer, SDWORD range, SDWORD rangeX, SDWORD rangeY, BOOL bVTOLs)
{
      return costOrAmountInRange(player, lookingPlayer, range, rangeX, rangeY, bVTOLs, true /*only count*/);
}

UDWORD playerWeapDroidsCostInRange(SDWORD player, SDWORD lookingPlayer, SDWORD range,
                                                   SDWORD rangeX, SDWORD rangeY, BOOL bVTOLs)
{
      return costOrAmountInRange(player, lookingPlayer, range, rangeX, rangeY, bVTOLs, false /*total cost*/);
}



UDWORD numPlayerWeapStructsInRange(SDWORD player, SDWORD lookingPlayer, SDWORD range,
                                                   SDWORD rangeX, SDWORD rangeY, BOOL bFinished)
{
      UDWORD                        tx,ty,numStructs;
      STRUCTURE               *psStruct;

      tx = map_coord(rangeX);
      ty = map_coord(rangeY);

      numStructs = 0;

      //check structures
      for(psStruct = apsStructLists[player]; psStruct; psStruct=psStruct->psNext)
      {
            if(psStruct->visible[lookingPlayer])      //if can see it
            {
                  if(objHasWeapon((BASE_OBJECT *) psStruct))      //make sure structure is dangerous
                  {
                        if(!bFinished || psStruct->status == SS_BUILT)
                        {
                              if (range < 0
                               || world_coord(dirtySqrt(tx, ty, map_coord(psStruct->pos.x), map_coord(psStruct->pos.y))) < range)   //enemy in range
                              {
                                    numStructs++;
                              }
                        }
                  }
            }
      }

      return numStructs;
}

UDWORD playerWeapStructsCostInRange(SDWORD player, SDWORD lookingPlayer, SDWORD range,
                                                   SDWORD rangeX, SDWORD rangeY, BOOL bFinished)
{
      UDWORD                        tx,ty,structsCost;
      STRUCTURE               *psStruct;

      tx = rangeX >> TILE_SHIFT;
      ty = rangeY >> TILE_SHIFT;

      structsCost = 0;

      //check structures
      for(psStruct = apsStructLists[player]; psStruct; psStruct=psStruct->psNext)
      {
            if(psStruct->visible[lookingPlayer])      //if can see it
            {
                  if(objHasWeapon((BASE_OBJECT *) psStruct))
                  {
                        if(!bFinished || psStruct->status == SS_BUILT)
                        {
                              if((range < 0) || ((dirtySqrt(tx, ty, psStruct->pos.x >> TILE_SHIFT, psStruct->pos.y >> TILE_SHIFT)
                                    << TILE_SHIFT) < range))      //enemy in range
                              {
                                    structsCost += structPowerToBuild(psStruct);
                              }
                        }
                  }
            }
      }

      return structsCost;
}

BOOL scrNumEnemyWeapDroidsInRange(void)
{
      SDWORD                        lookingPlayer,range,rangeX,rangeY,i;
      UDWORD                        numEnemies = 0;
      BOOL                    bVTOLs;

      if (!stackPopParams(5, VAL_INT, &lookingPlayer, VAL_INT, &rangeX,
            VAL_INT, &rangeY, VAL_INT, &range, VAL_BOOL, &bVTOLs))
      {
            debug(LOG_ERROR,  "scrNumEnemyWeapDroidsInRange(): stack failed");
            return false;
      }

      for(i=0;i<MAX_PLAYERS;i++)
      {
            if((alliances[lookingPlayer][i] == ALLIANCE_FORMED) || (i == lookingPlayer))  //skip allies and myself
            {
                  continue;
            }

            numEnemies += numPlayerWeapDroidsInRange(i, lookingPlayer, range, rangeX, rangeY, bVTOLs);
      }

      scrFunctionResult.v.ival = numEnemies;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR,  "scrNumEnemyWeapDroidsInRange(): failed to push result");
            return false;
      }

      return true;
}



BOOL scrNumEnemyWeapStructsInRange(void)
{
      SDWORD                        lookingPlayer,range,rangeX,rangeY,i;
      UDWORD                        numEnemies = 0;
      BOOL                    bFinished;

      if (!stackPopParams(5, VAL_INT, &lookingPlayer, VAL_INT, &rangeX,
            VAL_INT, &rangeY, VAL_INT, &range, VAL_BOOL, &bFinished))
      {
            debug(LOG_ERROR,  "scrNumEnemyWeapStructsInRange(): stack failed");
            return false;
      }

      for(i=0;i<MAX_PLAYERS;i++)
      {
            if((alliances[lookingPlayer][i] == ALLIANCE_FORMED) || (i == lookingPlayer))  //skip allies and myself
            {
                  continue;
            }

            numEnemies += numPlayerWeapStructsInRange(i, lookingPlayer, range, rangeX, rangeY, bFinished);
      }

      scrFunctionResult.v.ival = numEnemies;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrNumEnemyWeapStructsInRange(): failed to push result");
            return false;
      }

      return true;
}

BOOL scrNumFriendlyWeapObjInRange(void)
{
      SDWORD                        player,range,rangeX,rangeY,i;
      UDWORD                        numFriends = 0;
      BOOL                    bVTOLs,bFinished;

      if (!stackPopParams(6, VAL_INT, &player, VAL_INT, &rangeX,
            VAL_INT, &rangeY, VAL_INT, &range, VAL_BOOL, &bVTOLs, VAL_BOOL, &bFinished))
      {
            debug(LOG_ERROR,  "scrNumFriendlyWeapObjInRange(): stack failed");
            return false;
      }

      for(i=0;i<MAX_PLAYERS;i++)
      {
            if((alliances[player][i] == ALLIANCE_FORMED) || (i == player))    //skip enemies
            {
                  numFriends += numPlayerWeapDroidsInRange(i, player, range, rangeX, rangeY, bVTOLs);
                  numFriends += numPlayerWeapStructsInRange(i, player, range, rangeX, rangeY,bFinished);
            }
      }

      scrFunctionResult.v.ival = numFriends;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

BOOL scrNumFriendlyWeapDroidsInRange(void)
{
      SDWORD                        lookingPlayer,range,rangeX,rangeY,i;
      UDWORD                        numEnemies = 0;
      BOOL                    bVTOLs;

      if (!stackPopParams(5, VAL_INT, &lookingPlayer, VAL_INT, &rangeX,
            VAL_INT, &rangeY, VAL_INT, &range, VAL_BOOL, &bVTOLs))
      {
            debug(LOG_ERROR,  "scrNumFriendlyWeapDroidsInRange(): stack failed");
            return false;
      }

      for(i=0;i<MAX_PLAYERS;i++)
      {
            if((alliances[lookingPlayer][i] == ALLIANCE_FORMED) || (i == lookingPlayer))
            {
                  numEnemies += numPlayerWeapDroidsInRange(i, lookingPlayer, range, rangeX, rangeY, bVTOLs);
            }
      }

      //numEnemies = numEnemyWeapObjInRange(player, range, rangeX, rangeY, bVTOLs);
      scrFunctionResult.v.ival = numEnemies;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrNumFriendlyWeapDroidsInRange(): failed to push result");
            return false;
      }

      return true;
}



BOOL scrNumFriendlyWeapStructsInRange(void)
{
      SDWORD                        lookingPlayer,range,rangeX,rangeY,i;
      UDWORD                        numEnemies = 0;
      BOOL                    bFinished;

      if (!stackPopParams(5, VAL_INT, &lookingPlayer, VAL_INT, &rangeX,
            VAL_INT, &rangeY, VAL_INT, &range, VAL_BOOL, &bFinished))
      {
            debug(LOG_ERROR, "scrNumFriendlyWeapStructsInRange(): stack failed");
            return false;
      }

      for(i=0; i<MAX_PLAYERS; i++)
      {
            if((alliances[lookingPlayer][i] == ALLIANCE_FORMED) || (i == lookingPlayer))  //skip enemies
            {
                  numEnemies += numPlayerWeapStructsInRange(i, lookingPlayer, range, rangeX, rangeY, bFinished);
            }
      }

      scrFunctionResult.v.ival = numEnemies;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR,"scrNumFriendlyWeapStructsInRange(): failed to push result");
            return false;
      }

      return true;
}

BOOL scrNumPlayerWeapDroidsInRange(void)
{
      SDWORD            targetPlayer,lookingPlayer,range,rangeX,rangeY;
      BOOL        bVTOLs;

      if (!stackPopParams(6, VAL_INT, &targetPlayer, VAL_INT, &lookingPlayer,
            VAL_INT, &rangeX, VAL_INT, &rangeY, VAL_INT, &range, VAL_BOOL, &bVTOLs))
      {
            debug(LOG_ERROR,"scrNumPlayerWeapDroidsInRange(): stack failed");
            return false;
      }

      scrFunctionResult.v.ival = numPlayerWeapDroidsInRange(targetPlayer, lookingPlayer, range, rangeX, rangeY, bVTOLs);

      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrNumPlayerWeapDroidsInRange(): failed to push result");
            return false;
      }

      return true;
}

BOOL scrNumPlayerWeapStructsInRange(void)
{
      SDWORD            targetPlayer,lookingPlayer,range,rangeX,rangeY;
      BOOL        bFinished;

      if (!stackPopParams(6, VAL_INT, &targetPlayer, VAL_INT, &lookingPlayer,
            VAL_INT, &rangeX, VAL_INT, &rangeY, VAL_INT, &range, VAL_BOOL, &bFinished))
      {
            debug(LOG_ERROR,"scrNumPlayerWeapStructsInRange(): stack failed");
            return false;
      }

      scrFunctionResult.v.ival = numPlayerWeapStructsInRange(targetPlayer, lookingPlayer, range, rangeX, rangeY, bFinished);

      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrNumPlayerWeapStructsInRange(): failed to push result");
            return false;
      }

      return true;
}

BOOL scrNumPlayerWeapObjInRange(void)
{
      SDWORD                        targetPlayer,lookingPlayer,range,rangeX,rangeY;
      UDWORD                        numEnemies = 0;
      BOOL                    bVTOLs,bFinished;

      if (!stackPopParams(7, VAL_INT, &targetPlayer, VAL_INT, &lookingPlayer,
                                    VAL_INT, &rangeX, VAL_INT, &rangeY, VAL_INT, &range,
                                    VAL_BOOL, &bVTOLs, VAL_BOOL, &bFinished))
      {
            debug(LOG_ERROR,"scrNumPlayerWeapObjInRange(): stack failed");
            return false;
      }

      numEnemies += numPlayerWeapDroidsInRange(targetPlayer, lookingPlayer, range, rangeX, rangeY, bVTOLs);
      numEnemies += numPlayerWeapStructsInRange(targetPlayer, lookingPlayer, range, rangeX, rangeY, bFinished);

      scrFunctionResult.v.ival = numEnemies;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrNumPlayerWeapObjInRange(): failed to push result");
            return false;
      }

      return true;
}

BOOL scrNumEnemyObjInRange(void)
{
      SDWORD                        lookingPlayer,range,rangeX,rangeY;
      BOOL                    bVTOLs,bFinished;

      if (!stackPopParams(6, VAL_INT, &lookingPlayer, VAL_INT, &rangeX,
            VAL_INT, &rangeY, VAL_INT, &range, VAL_BOOL, &bVTOLs, VAL_BOOL, &bFinished))
      {
            debug(LOG_ERROR, "scrNumEnemyObjInRange(): stack failed");
            return false;
      }

      scrFunctionResult.v.ival = numEnemyObjInRange(lookingPlayer, range, rangeX, rangeY, bVTOLs, bFinished);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrNumEnemyObjInRange(): failed to push result");
            return false;
      }

      return true;
}

UDWORD numEnemyObjInRange(SDWORD player, SDWORD range, SDWORD rangeX, SDWORD rangeY,
                                      BOOL bVTOLs, BOOL bFinished)
{
      UDWORD                        i,tx,ty,numEnemies;
      STRUCTURE               *psStruct;
      DROID                   *psDroid;

      tx = map_coord(rangeX);
      ty = map_coord(rangeY);

      numEnemies = 0;

      for(i=0;i<MAX_PLAYERS;i++)
      {
            if((alliances[player][i] == ALLIANCE_FORMED) || (i == player))
            {
                  continue;
            }

            //check structures
            for(psStruct = apsStructLists[i]; psStruct; psStruct=psStruct->psNext)
            {
                  if(psStruct->visible[player]) //if can see it
                  {
                        if(!bFinished || psStruct->status == SS_BUILT)
                        {
                              if (range < 0
                               || world_coord(dirtySqrt(tx, ty, map_coord(psStruct->pos.x), map_coord(psStruct->pos.y))) < range)   //enemy in range
                              {
                                    numEnemies++;
                              }
                        }
                  }
            }

            //check droids
            for(psDroid = apsDroidLists[i]; psDroid; psDroid = psDroid->psNext)
            {
                  if(psDroid->visible[player])        //can see this droid?
                  {
                        //if VTOLs are excluded, skip them
                        if(!bVTOLs && ((asPropulsionStats[psDroid->asBits[COMP_PROPULSION].nStat].propulsionType == LIFT) || (psDroid->droidType == DROID_TRANSPORTER)))
                        {
                              continue;
                        }

                        if (range < 0
                         || world_coord(dirtySqrt(tx, ty , map_coord(psDroid->pos.x), map_coord(psDroid->pos.y))) < range)    //enemy in range
                        {
                              numEnemies++;
                        }
                  }
            }
      }

      return numEnemies;
}

/* Similiar to structureBuiltInRange(), but also returns true if structure is not finished */
BOOL scrNumStructsByStatInRange(void)
{
      SDWORD            player, lookingPlayer, index, x, y, range;
      SDWORD            rangeSquared,NumStruct;
      STRUCTURE   *psCurr;
      SDWORD            xdiff, ydiff;
      STRUCTURE_STATS *psTarget;

      if (!stackPopParams(6, ST_STRUCTURESTAT, &index, VAL_INT, &x, VAL_INT, &y,
            VAL_INT, &range, VAL_INT, &lookingPlayer, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrNumStructsByStatInRange(): stack failed");
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrNumStructsByStatInRange:player number is too high" );
            return false;
      }

      if (x < 0
       || map_coord(x) > (SDWORD)mapWidth)
      {
            ASSERT( false, "scrNumStructsByStatInRange : invalid X coord" );
            return false;
      }
      if (y < 0
       || map_coord(y) > (SDWORD)mapHeight)
      {
            ASSERT( false,"scrNumStructsByStatInRange : invalid Y coord" );
            return false;
      }
      if (index < (SDWORD)0 || index > (SDWORD)numStructureStats)
      {
            ASSERT( false, "scrNumStructsByStatInRange : Invalid structure stat" );
            return false;
      }
      if (range < (SDWORD)0)
      {
            ASSERT( false, "scrNumStructsByStatInRange : Rnage is less than zero" );
            return false;
      }

      NumStruct = 0;

      //now look through the players list of structures to see if this type
      //exists within range
      psTarget = &asStructureStats[index];
      rangeSquared = range * range;
      for(psCurr = apsStructLists[player]; psCurr; psCurr = psCurr->psNext)
      {
            xdiff = (SDWORD)psCurr->pos.x - x;
            ydiff = (SDWORD)psCurr->pos.y - y;
            if (xdiff*xdiff + ydiff*ydiff <= rangeSquared)
            {
                  if( strcmp(psCurr->pStructureType->pName,psTarget->pName) == 0 )
                  {
                        if(psCurr->visible[lookingPlayer])        //can we see it?
                        {
                              NumStruct++;
                        }
                  }
            }
      }

      scrFunctionResult.v.ival = NumStruct;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

BOOL scrNumStructsByStatInArea(void)
{
      SDWORD            player, lookingPlayer, index, x1, y1, x2, y2;
      SDWORD            NumStruct;
      STRUCTURE   *psCurr;

      STRUCTURE_STATS         *psStats;

      if (!stackPopParams(7, ST_STRUCTURESTAT, &index, VAL_INT, &x1, VAL_INT, &y1,
            VAL_INT, &x2, VAL_INT, &y2, VAL_INT, &lookingPlayer, VAL_INT, &player))
      {
            debug(LOG_ERROR,"scrNumStructsByStatInArea: failed to pop");
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            debug(LOG_ERROR,"scrNumStructsByStatInArea: player number too high");
            ASSERT( false, "scrStructureBuiltInRange:player number is too high" );
            return false;
      }


      if (index < (SDWORD)0 || index > (SDWORD)numStructureStats)
      {
            debug(LOG_ERROR, "scrNumStructsByStatInArea: invalid structure stat");
            ASSERT( false, "scrStructureBuiltInRange : Invalid structure stat" );
            return false;
      }

      psStats = (STRUCTURE_STATS *)(asStructureStats + index);

      ASSERT( psStats != NULL,
                  "scrNumStructsByStatInArea: Invalid structure pointer" );

      NumStruct = 0;

      for (psCurr = apsStructLists[player]; psCurr != NULL;
            psCurr = psCurr->psNext)
      {
            if (psCurr->pStructureType == psStats)
            {
                  if(psCurr->visible[lookingPlayer])        //can we see it?
                  {
                        if(psCurr->pos.x < x1) continue;          //not in bounds
                        if(psCurr->pos.y < y1) continue;          //not in bounds
                        if(psCurr->pos.x > x2) continue;          //not in bounds
                        if(psCurr->pos.y > y2) continue;          //not in bounds
                        NumStruct++;
                  }
            }
      }

      scrFunctionResult.v.ival = NumStruct;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

BOOL scrNumStructsByTypeInRange(void)
{
      SDWORD            targetPlayer, lookingPlayer, type, x, y, range;
      SDWORD            rangeSquared,NumStruct;
      STRUCTURE   *psCurr;
      SDWORD            xdiff, ydiff;

      if (!stackPopParams(6, VAL_INT, &lookingPlayer, VAL_INT, &targetPlayer,
            VAL_INT, &type, VAL_INT, &x, VAL_INT, &y, VAL_INT, &range))
      {
            debug(LOG_ERROR,"scrNumStructsByTypeInRange: failed to pop");
            return false;
      }

      if (lookingPlayer >= MAX_PLAYERS || targetPlayer >= MAX_PLAYERS)
      {
            ASSERT( false, "scrNumStructsByTypeInRange:player number is too high" );
            return false;
      }

      if (x < 0
       || map_coord(x) > (SDWORD)mapWidth)
      {
            ASSERT( false, "scrNumStructsByTypeInRange : invalid X coord" );
            return false;
      }

      if (y < 0
       || map_coord(y) > (SDWORD)mapHeight)
      {
            ASSERT( false,"scrNumStructsByTypeInRange : invalid Y coord" );
            return false;
      }

      if (range < (SDWORD)0)
      {
            ASSERT( false, "scrNumStructsByTypeInRange : Rnage is less than zero" );
            return false;
      }

      NumStruct = 0;

      //now look through the players list of structures to see if this type
      //exists within range
      rangeSquared = range * range;
      for(psCurr = apsStructLists[targetPlayer]; psCurr; psCurr = psCurr->psNext)
      {
            xdiff = (SDWORD)psCurr->pos.x - x;
            ydiff = (SDWORD)psCurr->pos.y - y;
            if (xdiff*xdiff + ydiff*ydiff <= rangeSquared)
            {
                  if((type < 0) ||(psCurr->pStructureType->type == type))
                  {
                        if(psCurr->visible[lookingPlayer])        //can we see it?
                        {
                              NumStruct++;
                        }
                  }
            }
      }

      scrFunctionResult.v.ival = NumStruct;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

BOOL scrNumFeatByTypeInRange(void)
{
      SDWORD            lookingPlayer, type, x, y, range;
      SDWORD            rangeSquared,NumFeat;
      FEATURE           *psCurr;
      SDWORD            xdiff, ydiff;

      if (!stackPopParams(5, VAL_INT, &lookingPlayer,
            VAL_INT, &type, VAL_INT, &x, VAL_INT, &y, VAL_INT, &range))
      {
            debug(LOG_ERROR, "scrNumFeatByTypeInRange(): failed to pop");
            return false;
      }

      if (lookingPlayer >= MAX_PLAYERS)
      {
            ASSERT( false, "scrNumFeatByTypeInRange:player number is too high" );
            return false;
      }

      if (x < 0
       || map_coord(x) > (SDWORD)mapWidth)
      {
            ASSERT( false, "scrNumFeatByTypeInRange : invalid X coord" );
            return false;
      }

      if (y < 0
       || map_coord(y) > (SDWORD)mapHeight)
      {
            ASSERT( false,"scrNumFeatByTypeInRange : invalid Y coord" );
            return false;
      }

      if (range < (SDWORD)0)
      {
            ASSERT( false, "scrNumFeatByTypeInRange : Rnage is less than zero" );
            return false;
      }

      NumFeat = 0;

      //now look through the players list of structures to see if this type
      //exists within range
      rangeSquared = range * range;
      for(psCurr = apsFeatureLists[0]; psCurr; psCurr = psCurr->psNext)
      {
            xdiff = (SDWORD)psCurr->pos.x - x;
            ydiff = (SDWORD)psCurr->pos.y - y;
            if (xdiff*xdiff + ydiff*ydiff <= rangeSquared)
            {
                  if((type < 0) ||(psCurr->psStats->subType == type))   //like FEAT_OIL_RESOURCE
                  {
                        if(psCurr->visible[lookingPlayer])        //can we see it?
                        {
                              NumFeat++;
                        }
                  }
            }
      }

      scrFunctionResult.v.ival = NumFeat;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

//returns num of visible structures of a certain player in range (only visible ones)
BOOL scrNumStructsButNotWallsInRangeVis(void)
{
      SDWORD            player, lookingPlayer, x, y, range;
      SDWORD            rangeSquared,NumStruct;
      STRUCTURE   *psCurr;
      SDWORD            xdiff, ydiff;

      if (!stackPopParams(5, VAL_INT, &x, VAL_INT, &y,
            VAL_INT, &range, VAL_INT, &lookingPlayer, VAL_INT, &player))
      {
            debug(LOG_ERROR,"scrNumStructsButNotWallsInRangeVis: failed to pop");
            return false;
      }

      if ((player >= MAX_PLAYERS) || (lookingPlayer >= MAX_PLAYERS))
      {
            ASSERT( false, "scrNumStructsButNotWallsInRangeVis:player number is too high" );
            return false;
      }

      if (x < 0
       || map_coord(x) > (SDWORD)mapWidth)
      {
            ASSERT( false, "scrNumStructsButNotWallsInRangeVis : invalid X coord" );
            return false;
      }
      if (y < 0
       || map_coord(y) > (SDWORD)mapHeight)
      {
            ASSERT( false,"scrNumStructsButNotWallsInRangeVis : invalid Y coord" );
            return false;
      }
      if (range < (SDWORD)0)
      {
            ASSERT( false, "scrNumStructsButNotWallsInRangeVis : Rnage is less than zero" );
            return false;
      }

      NumStruct = 0;

      //now look through the players list of structures
      rangeSquared = range * range;
      for(psCurr = apsStructLists[player]; psCurr; psCurr = psCurr->psNext)
      {
            if ((psCurr->pStructureType->type != REF_WALL) &&
            (psCurr->pStructureType->type != REF_WALLCORNER))
            {
                  if(psCurr->visible[lookingPlayer])        //can we see it?
                  {
                        xdiff = (SDWORD)psCurr->pos.x - x;
                        ydiff = (SDWORD)psCurr->pos.y - y;
                        if (xdiff*xdiff + ydiff*ydiff <= rangeSquared)
                        {
                              NumStruct++;
                        }
                  }
            }
      }

      scrFunctionResult.v.ival = NumStruct;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// Only returns structure if it is visible
BOOL scrGetStructureVis(void)
{
      SDWORD                        player, lookingPlayer, index;
      STRUCTURE               *psStruct;
      UDWORD                        structType;
      BOOL                    found;

      if (!stackPopParams(3, ST_STRUCTURESTAT, &index, VAL_INT, &player, VAL_INT, &lookingPlayer))
      {
            debug(LOG_ERROR,"scrGetStructureVis: failed to pop");
            return false;
      }

      if ((player >= MAX_PLAYERS) || (lookingPlayer >= MAX_PLAYERS))
      {
            ASSERT( false, "scrGetStructureVis:player number is too high" );
            return false;
      }

      structType = asStructureStats[index].ref;

      //search the players' list of built structures to see if one exists
      found = false;
      for (psStruct = apsStructLists[player]; psStruct != NULL; psStruct =
            psStruct->psNext)
      {
            if (psStruct->pStructureType->ref == structType)
            {
                  if(psStruct->visible[lookingPlayer])
                  {
                        found = true;
                        break;
                  }
            }
      }

      //make sure pass NULL back if not got one
      if (!found)
      {
            psStruct = NULL;
      }

      scrFunctionResult.v.oval = psStruct;
      if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

//returns num of visible structures of a certain player in range
BOOL scrChooseValidLoc(void)
{
      SDWORD sendY, sendX, *x, *y, player, threatRange;
      UDWORD tx,ty;

      if (!stackPopParams(6, VAL_REF|VAL_INT, &x, VAL_REF|VAL_INT, &y,
            VAL_INT, &sendX, VAL_INT, &sendY, VAL_INT, &player, VAL_INT, &threatRange))
      {
            debug(LOG_ERROR,"scrChooseValidLoc: failed to pop");
            return false;
      }

    //Check coords
      if (sendX < 0
       || sendX > world_coord(mapWidth)
       || sendY < 0
       || sendY > world_coord(mapHeight))
      {
            debug(LOG_ERROR, "scrChooseValidLoc: coords off map");
            return false;
      }

      tx = map_coord(sendX);
      ty = map_coord(sendY);

      if(pickATileGenThreat(&tx, &ty, LOOK_FOR_EMPTY_TILE, threatRange, player, zonedPAT))
      {
            *x = world_coord(tx);
            *y = world_coord(ty);
            scrFunctionResult.v.bval = true;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
      }
      else
      {
            scrFunctionResult.v.bval = false;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
      }

      return true;
}

//returns closest enemy object
BOOL scrGetClosestEnemy(void)
{
      SDWORD                        x,y,tx,ty, player, range,i;
      UDWORD                        dist, bestDist;
      BOOL                    weaponOnly, bVTOLs, bFound = false; //only military objects?
      BASE_OBJECT             *psObj = NULL;
      STRUCTURE               *psStruct = NULL;
      DROID                   *psDroid = NULL;

      if (!stackPopParams(6, VAL_INT, &x, VAL_INT, &y,
             VAL_INT, &range,  VAL_BOOL, &weaponOnly, VAL_BOOL, &bVTOLs, VAL_INT, &player))
      {
            debug(LOG_ERROR,"scrGetClosestEnemy: stack failed");
            return false;
      }

    //Check coords
      if (x < 0
       || x > world_coord(mapWidth)
       || y < 0
       || y > world_coord(mapHeight))
      {
            debug(LOG_ERROR,"scrGetClosestEnemy: coords off map");
            return false;
      }

      tx = map_coord(x);
      ty = map_coord(y);

      bestDist = 99999;

      for(i=0;i<MAX_PLAYERS;i++)
      {
            if((alliances[player][i] == ALLIANCE_FORMED) || (i == player))
            {
                  continue;
            }


            //check droids
            for(psDroid = apsDroidLists[i]; psDroid; psDroid = psDroid->psNext)
            {
                  if(psDroid->visible[player])        //can see this droid?
                  {
                        //if only weapon droids and don't have it, then skip
                        if (weaponOnly && !objHasWeapon((BASE_OBJECT *)psDroid))
                        {
                              continue;
                        }

                        //if VTOLs are excluded, skip them
                        if(!bVTOLs && ((asPropulsionStats[psDroid->asBits[COMP_PROPULSION].nStat].propulsionType == LIFT) || (psDroid->droidType == DROID_TRANSPORTER)))
                        {
                              continue;
                        }

                        dist = world_coord(dirtySqrt(tx, ty , map_coord(psDroid->pos.x), map_coord(psDroid->pos.y)));
                        if(dist < bestDist)
                        {
                              if((range < 0) || (dist < range))   //enemy in range
                              {
                                    bestDist = dist;
                                    bFound = true;
                                    psObj = (BASE_OBJECT*)psDroid;
                              }
                        }
                  }
            }


            //check structures
            for(psStruct = apsStructLists[i]; psStruct; psStruct=psStruct->psNext)
            {
                  if(psStruct->visible[player]) //if can see it
                  {
                        //only need defenses?
                        if(weaponOnly && (!objHasWeapon((BASE_OBJECT *) psStruct) || (psStruct->status != SS_BUILT) ))  //non-weapon-structures or not finished
                        {
                              continue;
                        }

                        dist = world_coord(dirtySqrt(tx, ty, map_coord(psStruct->pos.x), map_coord(psStruct->pos.y)));
                        if(dist < bestDist)
                        {
                              if((range < 0) || (dist < range))   //in range
                              {
                                    bestDist = dist;
                                    bFound = true;
                                    psObj = (BASE_OBJECT*)psStruct;
                              }
                        }
                  }
            }

      }

      if(bFound)
      {
            scrFunctionResult.v.oval = psObj;
            if (!stackPushResult((INTERP_TYPE)ST_BASEOBJECT, &scrFunctionResult))
            {
                  return false;
            }
      }
      else
      {
            scrFunctionResult.v.oval = NULL;
            if (!stackPushResult((INTERP_TYPE)ST_BASEOBJECT, &scrFunctionResult))
            {
                  return false;
            }
      }

      return true;
}

//How many droids can it still fit?
BOOL scrTransporterCapacity(void)
{
      DROID             *psDroid;

      if (!stackPopParams(1, ST_DROID, &psDroid))
      {
            debug(LOG_ERROR, "scrTransporterCapacity(): failed to pop params");
            return false;
      }

      if(psDroid == NULL)
      {
            debug(LOG_ERROR,"scrTransporterCapacity(): NULLOBJECT passed");
            return false;
      }

      if(psDroid->droidType != DROID_TRANSPORTER)
      {
            debug(LOG_ERROR, "scrTransporterCapacity(): passed droid is not a transporter");
            return false;
      }

      scrFunctionResult.v.ival = calcRemainingCapacity(psDroid);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrHasIndirectWeapon(): failed to push result");
            return false;
      }

      return true;
}

//is it?
BOOL scrTransporterFlying(void)
{
      DROID             *psDroid;

      if (!stackPopParams(1, ST_DROID, &psDroid))
      {
            debug(LOG_ERROR, "scrTransporterFlying(): failed to pop params");
            return false;
      }

      if(psDroid == NULL)
      {
            debug(LOG_ERROR,"scrTransporterFlying(): NULLOBJECT passed");
            return false;
      }

      if(psDroid->droidType != DROID_TRANSPORTER)
      {
            debug(LOG_ERROR,"scrTransporterFlying(): passed droid is not a transporter");
            return false;
      }

      scrFunctionResult.v.bval = transporterFlying(psDroid);
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            debug(LOG_ERROR,"scrTransporterFlying(): failed to push result");
            return false;
      }

      return true;
}

BOOL scrUnloadTransporter(void)
{
      DROID             *psDroid;
      SDWORD                  x,y;

      if (!stackPopParams(3, ST_DROID, &psDroid, VAL_INT, &x, VAL_INT, &y))
      {
            debug(LOG_ERROR,"scrUnloadTransporter(): failed to pop params");
            return false;
      }

      if(psDroid == NULL)
      {
            debug(LOG_ERROR,"scrUnloadTransporter(): NULLOBJECT passed");
            return false;
      }

      if(psDroid->droidType != DROID_TRANSPORTER)
      {
            debug(LOG_ERROR,"scrUnloadTransporter(): passed droid is not a transporter");
            return false;
      }

      unloadTransporter(psDroid,x,y, false);

      return true;
}

//return true if droid is a member of any group
BOOL scrHasGroup(void)
{
      DROID             *psDroid;
      BOOL              retval;

      if (!stackPopParams(1, ST_DROID, &psDroid))
      {
            debug( LOG_ERROR,"scrHasGroup: failed to pop" );
            return false;
      }

      if (psDroid == NULL)
      {
            debug( LOG_ERROR, "scrHasGroup: droid is NULLOBJECT" );
            return false;
      }

      if (psDroid->psGroup != NULL)
      {
            retval = true;
      }
      else
      {
            retval = false;
      }

      scrFunctionResult.v.bval = retval;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Range is in world units! */
BOOL scrObjWeaponMaxRange(void)
{
      BASE_OBJECT             *psObj;
      WEAPON_STATS            *psStats;
      DROID                   *psDroid;
      STRUCTURE               *psStruct;

      if (!stackPopParams(1, ST_BASEOBJECT, &psObj))
      {
            debug(LOG_ERROR, "scrObjWeaponMaxRange: stack failed");
            return false;
      }

      //check if valid type
      if(psObj->type == OBJ_DROID)
      {
            psDroid = (DROID*)psObj;
            if (psDroid->asWeaps[0].nStat != 0)
            {
                  psStats = asWeaponStats + psDroid->asWeaps[0].nStat;
                  scrFunctionResult.v.ival = psStats->longRange;
                  if (!stackPushResult(VAL_INT, &scrFunctionResult))
                  {
                        return false;
                  }

                  return true;
            }
      }
      else if(psObj->type == OBJ_STRUCTURE)
      {
            psStruct = (STRUCTURE*)psObj;
            if (psStruct->asWeaps[0].nStat != 0)
            {
                  psStats = asWeaponStats + psStruct->asWeaps[0].nStat;
                  scrFunctionResult.v.ival = psStats->longRange;
                  if (!stackPushResult(VAL_INT, &scrFunctionResult))
                  {
                        return false;
                  }

                  return true;
            }
      }

      scrFunctionResult.v.ival = 0;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR,"scrObjWeaponMaxRange: wrong object type");
            return false;
      }

      return true;
}

BOOL scrObjHasWeapon(void)
{
      BASE_OBJECT             *psObj;

      if (!stackPopParams(1, ST_BASEOBJECT, &psObj))
      {
            debug(LOG_ERROR, "scrObjHasWeapon: stack failed");
            return false;
      }

      //check if valid type
      if(objHasWeapon(psObj))
      {
            scrFunctionResult.v.bval = true;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }

            return true;
      }


      scrFunctionResult.v.bval = false;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

BOOL scrObjectHasIndirectWeapon(void)
{
      WEAPON_STATS      *psWeapStats;
      BOOL              bIndirect;
      BASE_OBJECT       *psObj;

      if (!stackPopParams(1, ST_BASEOBJECT, &psObj))
      {
            debug(LOG_ERROR, "scrHasIndirectWeapon(): failed to pop params");
            return false;
      }

      if (psObj == NULL)
      {
            debug(LOG_ERROR,"scrHasIndirectWeapon(): NULLOBJECT passed");
            return false;
      }

      bIndirect = false;
      if(psObj->type == OBJ_DROID)
      {
            if (((DROID *)psObj)->asWeaps[0].nStat > 0)
            {
                  psWeapStats = asWeaponStats + ((DROID *)psObj)->asWeaps[0].nStat;
                  bIndirect = !proj_Direct(psWeapStats);
            }
      }
      else if(psObj->type == OBJ_STRUCTURE)
      {
            if (((STRUCTURE *)psObj)->asWeaps[0].nStat > 0)
            {
                  psWeapStats = asWeaponStats + ((STRUCTURE *)psObj)->asWeaps[0].nStat;
                  bIndirect = !proj_Direct(psWeapStats);
            }
      }

      scrFunctionResult.v.bval = bIndirect;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            debug(LOG_ERROR,"scrHasIndirectWeapon(): failed to push result");
            return false;
      }

      return true;
}

//returns closest droid by type
BOOL scrGetClosestEnemyDroidByType(void)
{
      SDWORD                        x,y,tx,ty, player, range,i,type;
      UDWORD                        dist,bestDist;
      BOOL                    bFound = false;   //only military objects?
      BOOL                    bVTOLs;
      DROID                   *psDroid = NULL, *foundDroid = NULL;

      if (!stackPopParams(6, VAL_INT, &x, VAL_INT, &y,
             VAL_INT, &range,  VAL_INT, &type, VAL_BOOL, &bVTOLs, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrGetClosestEnemyDroidByType: stack failed");
            return false;
      }

    //Check coords
      if (x < 0
       || x > world_coord(mapWidth)
       || y < 0
       || y > world_coord(mapHeight))
      {
            debug(LOG_ERROR,"scrGetClosestEnemyDroidByType: coords off map");
            return false;
      }

      tx = map_coord(x);
      ty = map_coord(y);

      bestDist = 99999;

      for(i=0;i<MAX_PLAYERS;i++)
      {
            if((alliances[player][i] == ALLIANCE_FORMED) || (i == player))
            {
                  continue;
            }

            //check droids
            for(psDroid = apsDroidLists[i]; psDroid; psDroid = psDroid->psNext)
            {
                  //if VTOLs are excluded, skip them (don't check for transporter this time)
                  if(!bVTOLs && (asPropulsionStats[psDroid->asBits[COMP_PROPULSION].nStat].propulsionType == LIFT) )
                  {
                        continue;
                  }

                  if(psDroid->visible[player])        //can see this droid?
                  {
                        //skip?
                        if ((type != (-1)) && (psDroid->droidType != type))
                        {
                              continue;
                        }

                        dist = world_coord(dirtySqrt(tx, ty , map_coord(psDroid->pos.x), map_coord(psDroid->pos.y)));
                        if(dist < bestDist)
                        {
                              if(dist < range)  //enemy in range
                              {
                                    bestDist = dist;
                                    bFound = true;
                                    foundDroid = psDroid;
                              }
                        }
                  }
            }
      }

      if(bFound)
      {
            scrFunctionResult.v.oval = foundDroid;
            if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))
            {
                  return false;
            }
      }
      else
      {
            scrFunctionResult.v.oval = NULL;
            if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))
            {
                  return false;
            }
      }

      return true;
}

//returns closest structure by type
BOOL scrGetClosestEnemyStructByType(void)
{
      SDWORD                        x,y,tx,ty, player, range,i,type,dist;
      UDWORD                        bestDist;
      BOOL                    bFound = false;   //only military objects?
      STRUCTURE               *psStruct = NULL, *foundStruct = NULL;

      if (!stackPopParams(5, VAL_INT, &x, VAL_INT, &y,
             VAL_INT, &range,  VAL_INT, &type, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrGetClosestEnemyStructByType: stack failed");
            return false;
      }

    //Check coords
      if (x < 0
       || x > world_coord(mapWidth)
       || y < 0
       || y > world_coord(mapHeight))
      {
            debug(LOG_ERROR,"scrGetClosestEnemyStructByType: coords off map");
            return false;
      }

      tx = map_coord(x);
      ty = map_coord(y);

      bestDist = 99999;

      for(i=0;i<MAX_PLAYERS;i++)
      {
            if((alliances[player][i] == ALLIANCE_FORMED) || (i == player))
            {
                  continue;
            }

            //check structures
            for(psStruct = apsStructLists[i]; psStruct; psStruct=psStruct->psNext)
            {
                  if(psStruct->visible[player]) //if can see it
                  {
                        //only need defenses?
                        if((type != (-1)) && (psStruct->pStructureType->type != type))    //non-weapon-structures
                        {
                              continue;
                        }

                        dist = world_coord(dirtySqrt(tx, ty, map_coord(psStruct->pos.x), map_coord(psStruct->pos.y)));
                        if(dist < bestDist)
                        {
                              if((range < 0) || (dist < range))   //in range or no range check
                              {
                                    bestDist = dist;
                                    bFound = true;
                                    foundStruct = psStruct;
                              }
                        }
                  }
            }

      }

      if(bFound)
      {
            scrFunctionResult.v.oval = foundStruct;
            if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
            {
                  return false;
            }
      }
      else
      {
            scrFunctionResult.v.oval = NULL;
            if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
            {
                  return false;
            }
      }

      return true;
}



//Approx point of intersection of a circle and a line with start loc being circle's center point
BOOL scrCirclePerimPoint(void)
{
      SDWORD                        basex,basey,*grx,*gry,radius;
      UDWORD                        dist;
      float                   factor,tempx,tempy;

      if (!stackPopParams(5, VAL_INT, &basex, VAL_INT, &basey, VAL_REF|VAL_INT, &grx,
            VAL_REF|VAL_INT, &gry, VAL_INT, &radius))
      {
            debug(LOG_ERROR,"scrCirclePerimPoint(): stack failed");
            return false;
      }

      if(radius == 0)
      {
            debug(LOG_ERROR,"scrCirclePerimPoint: radius == 0.");
            return true;
      }

      tempx = (float)(*grx - basex);      //x len (signed!)
      tempy = (float)(*gry - basey);

      dist = dirtySqrt(basex,basey,*grx,*gry);        //len

      factor =  (float)((float)dist / (float)radius);             //by what factor is dist > radius?

      //if point was inside of the circle, don't modify passed parameter
      if(factor == 0)
      {
            printf_console("scrCirclePerimPoint: division by zero.");
            return true;
      }

      //calc new len
      tempx = tempx / factor;
      tempy = tempy / factor;

      //now add new len to the center coords
      *grx = basex + (SDWORD)tempx;
      *gry = basey + (SDWORD)tempy;

      return true;
}

//send my vision to AI
BOOL scrGiftRadar(void)
{
      SDWORD      playerFrom, playerTo;
      BOOL  playMsg;

      if (!stackPopParams(3, VAL_INT, &playerFrom, VAL_INT, &playerTo, VAL_BOOL, &playMsg))
      {
            debug(LOG_ERROR,"scrGiftRadar(): stack failed");
            return false;
      }

      if (playerFrom >= MAX_PLAYERS || playerTo >= MAX_PLAYERS)
      {
            debug(LOG_ERROR,"scrGiftRadar: player out of range");
            return false;
      }

      giftRadar(playerFrom,playerTo,true);

      if(playMsg)
            audio_QueueTrack(ID_SENSOR_DOWNLOAD);

      return true;
}

BOOL scrNumAllies(void)
{
      SDWORD                  player,numAllies,i;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrNumAllies: failed to pop");
            return false;
      }

      if (player < 0)
      {
            debug(LOG_ERROR, "scrNumAllies: player < 0");
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            debug(LOG_ERROR,"scrNumAllies: player index too high");
            return false;
      }

      numAllies = 0;
      for(i=0;i<MAX_PLAYERS;i++)
      {
            if(i != player)
            {
                  if(alliances[i][player] == ALLIANCE_FORMED)
                  {
                        numAllies++;
                  }
            }
      }

      scrFunctionResult.v.ival = numAllies;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


//num aa defenses in range
BOOL scrNumAAinRange(void)
{
      SDWORD                        targetPlayer,lookingPlayer,range,rangeX,rangeY;
      SDWORD                        tx,ty;
      UDWORD                        numFound = 0;
      STRUCTURE   *psStruct;

      if (!stackPopParams(5, VAL_INT, &targetPlayer, VAL_INT, &lookingPlayer,
            VAL_INT, &rangeX, VAL_INT, &rangeY, VAL_INT, &range))
      {
            debug(LOG_ERROR,"scrNumAAinRange(): stack failed");
            return false;
      }

      tx = map_coord(rangeX);
      ty = map_coord(rangeY);

      numFound = 0;

      //check structures
      for(psStruct = apsStructLists[targetPlayer]; psStruct; psStruct=psStruct->psNext)
      {
            if(psStruct->visible[lookingPlayer])      //if can see it
            {
                  if(objHasWeapon((BASE_OBJECT *) psStruct) &&
                        (asWeaponStats[psStruct->asWeaps[0].nStat].surfaceToAir == SHOOT_IN_AIR) )
                  {
                        if (range < 0
                         || world_coord(dirtySqrt(tx, ty, map_coord(psStruct->pos.x), map_coord(psStruct->pos.y))) < range)   //enemy in range
                        {
                              numFound++;
                        }
                  }
            }
      }

      scrFunctionResult.v.ival = numFound;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR,"scrNumAAinRange(): failed to push result");
            return false;
      }

      return true;
}

//select droid
BOOL scrSelectDroid(void)
{
      BOOL  bSelect;
      DROID *psDroid;

      if (!stackPopParams(2, ST_DROID, &psDroid, VAL_BOOL, &bSelect))
      {
            debug(LOG_ERROR, "scrSelectDroid(): stack failed");
            return false;
      }

      if(psDroid == NULL)
      {
            debug(LOG_ERROR,"scrSelectDroid(): droid is NULLOBJECT");
            return false;
      }

      psDroid->selected = bSelect;

      return true;
}

//select droid group
BOOL scrSelectGroup(void)
{
      BOOL        bSelect;
      DROID_GROUP *psGroup;
      DROID       *psCurr;

      if (!stackPopParams(2, ST_GROUP, &psGroup, VAL_BOOL, &bSelect))
      {
            debug(LOG_ERROR, "scrSelectGroup(): stack failed");
            return false;
      }

      for(psCurr = psGroup->psList; psCurr; psCurr=psCurr->psGrpNext)
      {
            psCurr->selected = bSelect;
      }

      return true;
}

BOOL scrModulo(void)
{
      SDWORD                        num1,num2;

      if (!stackPopParams(2, VAL_INT, &num1, VAL_INT, &num2))
      {
            debug(LOG_ERROR,"scrModulo(): stack failed");
            return false;
      }

      scrFunctionResult.v.ival =  (num1 % num2);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR,"scrModulo(): failed to push result");
            return false;
      }

      return true;
}

BOOL scrPlayerLoaded(void)
{
      SDWORD                  player;
      BOOL              bPlayerHasFactories=false;
      STRUCTURE         *psCurr;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrPlayerLoaded(): stack failed");
            return false;
      }

      /* see if there are any player factories left */
      if(apsStructLists[player])
      {
            for (psCurr = apsStructLists[player]; psCurr != NULL; psCurr = psCurr->psNext)
            {
                  if(StructIsFactory(psCurr))
                  {
                        bPlayerHasFactories = true;
                        break;
                  }
            }
      }

      /* player is active if he has at least a unit or some factory */
      scrFunctionResult.v.bval = (apsDroidLists[player] != NULL || bPlayerHasFactories);

      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            debug(LOG_ERROR,"scrPlayerLoaded(): failed to push result");
            return false;
      }

      return true;
}


            /********************************/
            /*          AI Experience Stuff           */
            /********************************/

//Returns enemy base x and y for a certain player
BOOL scrLearnPlayerBaseLoc(void)
{
      SDWORD                        playerStoring,enemyPlayer, x, y;

      if (!stackPopParams(4, VAL_INT, &playerStoring, VAL_INT, &enemyPlayer,
                                    VAL_INT, &x, VAL_INT, &y))
      {
            debug(LOG_ERROR, "scrLearnPlayerBaseLoc(): stack failed");
            return false;
      }

      if((playerStoring >= MAX_PLAYERS) || (enemyPlayer >= MAX_PLAYERS))
      {
            debug(LOG_ERROR, "scrLearnPlayerBaseLoc: player index too high.");
            return false;
      }

      if((playerStoring < 0) || (enemyPlayer < 0))
      {
            debug(LOG_ERROR, "scrLearnPlayerBaseLoc: player index too low.");
            return false;
      }

      if (x < 0
       || x >= world_coord(mapWidth)
       || y < 0
       || y >= world_coord(mapHeight))
      {
            debug(LOG_ERROR, "scrLearnPlayerBaseLoc: coords off map");
            return false;
      }

      baseLocation[playerStoring][enemyPlayer][0] = x;
      baseLocation[playerStoring][enemyPlayer][1] = y;

#ifdef DEBUG
      printf_console("Learned player base.");
#endif

      scrFunctionResult.v.bval = true;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

//Saves enemy base x and y for a certain player
BOOL scrRecallPlayerBaseLoc(void)
{
      SDWORD                        playerStoring,enemyPlayer, *x, *y;

      if (!stackPopParams(4, VAL_INT, &playerStoring, VAL_INT, &enemyPlayer,
                                    VAL_REF|VAL_INT, &x, VAL_REF|VAL_INT, &y))
      {
            debug(LOG_ERROR, "scrRecallPlayerBaseLoc(): stack failed");
            return false;
      }

      if((playerStoring >= MAX_PLAYERS) || (enemyPlayer >= MAX_PLAYERS))
      {
            debug(LOG_ERROR, "scrRecallPlayerBaseLoc: player index too high.");
            return false;
      }

      if((playerStoring < 0) || (enemyPlayer < 0))
      {
            debug(LOG_ERROR, "scrRecallPlayerBaseLoc: player index too low.");
            return false;
      }

      if(!CanRememberPlayerBaseLoc(playerStoring, enemyPlayer))         //return false if this one not set yet
      {
            scrFunctionResult.v.bval = false;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }

            return true;
      }

      *x = baseLocation[playerStoring][enemyPlayer][0];
      *y = baseLocation[playerStoring][enemyPlayer][1];

      scrFunctionResult.v.bval = true;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Checks if player base loc is stored */
BOOL scrCanRememberPlayerBaseLoc(void)
{
      SDWORD                        playerStoring,enemyPlayer;

      if (!stackPopParams(2, VAL_INT, &playerStoring, VAL_INT, &enemyPlayer))
      {
            debug(LOG_ERROR, "scrCanRememberPlayerBaseLoc(): stack failed");
            return false;
      }

      if((playerStoring >= MAX_PLAYERS) || (enemyPlayer >= MAX_PLAYERS))
      {
            debug(LOG_ERROR,"scrCanRememberPlayerBaseLoc: player index too high.");
            return false;
      }

      if((playerStoring < 0) || (enemyPlayer < 0))
      {
            debug(LOG_ERROR,"scrCanRememberPlayerBaseLoc: player index too low.");
            return false;
      }

      scrFunctionResult.v.bval = CanRememberPlayerBaseLoc(playerStoring, enemyPlayer);
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Stores the place where we were attacked at */
BOOL scrLearnBaseDefendLoc(void)
{
      SDWORD                        playerStoring,enemyPlayer, x, y;

      if (!stackPopParams(4, VAL_INT, &playerStoring, VAL_INT, &enemyPlayer,
                                    VAL_INT, &x, VAL_INT, &y))
      {
            debug(LOG_ERROR, "scrLearnBaseDefendLoc(): stack failed");
            return false;
      }

      if((playerStoring >= MAX_PLAYERS) || (enemyPlayer >= MAX_PLAYERS))
      {
            debug(LOG_ERROR,"scrLearnBaseDefendLoc: player index too high.");
            return false;
      }

      if((playerStoring < 0) || (enemyPlayer < 0))
      {
            debug(LOG_ERROR,"scrLearnBaseDefendLoc: player index too low.");
            return false;
      }

      if (x < 0
       || x >= world_coord(mapWidth)
       || y < 0
       || y >= world_coord(mapHeight))
      {
            debug(LOG_ERROR,"scrLearnBaseDefendLoc: coords off map");
            return false;
      }

      StoreBaseDefendLoc(x, y, playerStoring);

      scrFunctionResult.v.bval = true;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Stores the place where we were attacked at */
BOOL scrLearnOilDefendLoc(void)
{
      SDWORD                        playerStoring,enemyPlayer, x, y;

      if (!stackPopParams(4, VAL_INT, &playerStoring, VAL_INT, &enemyPlayer,
                                    VAL_INT, &x, VAL_INT, &y))
      {
            debug(LOG_ERROR, "scrLearnOilDefendLoc(): stack failed");
            return false;
      }

      if((playerStoring >= MAX_PLAYERS) || (enemyPlayer >= MAX_PLAYERS))
      {
            debug(LOG_ERROR,"scrLearnOilDefendLoc: player index too high.");
            return false;
      }

      if((playerStoring < 0) || (enemyPlayer < 0))
      {
            debug(LOG_ERROR,"scrLearnOilDefendLoc: player index too low.");
            return false;
      }

      if (x < 0
       || x >= world_coord(mapWidth)
       || y < 0
       || y >= world_coord(mapHeight))
      {
            debug(LOG_ERROR,"scrLearnOilDefendLoc: coords off map");
            return false;
      }

      StoreOilDefendLoc(x, y, playerStoring);

      scrFunctionResult.v.bval = true;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Returns -1 if this location is not stored yet, otherwise returns index */
BOOL scrGetBaseDefendLocIndex(void)
{
      SDWORD                        playerStoring, x, y;

      if (!stackPopParams(3, VAL_INT, &playerStoring, VAL_INT, &x, VAL_INT, &y))
      {
            debug(LOG_ERROR, "scrGetBaseDefendLocIndex(): stack failed");
            return false;
      }

      if(playerStoring >= MAX_PLAYERS)
      {
            debug(LOG_ERROR, "scrGetBaseDefendLocIndex: player index too high.");
            return false;
      }

      if(playerStoring < 0)
      {
            debug(LOG_ERROR, "scrGetBaseDefendLocIndex: player index too low.");
            return false;
      }

      if (x < 0
       || x >= world_coord(mapWidth)
       || y < 0
       || y >= world_coord(mapHeight))
      {
            debug(LOG_ERROR, "scrGetBaseDefendLocIndex: coords off map");
            return false;
      }

      scrFunctionResult.v.ival = GetBaseDefendLocIndex(x,y,playerStoring);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Returns -1 if this location is not stored yet, otherwise returns index */
BOOL scrGetOilDefendLocIndex(void)
{
      SDWORD                        playerStoring, x, y;

      if (!stackPopParams(3, VAL_INT, &playerStoring, VAL_INT, &x, VAL_INT, &y))
      {
            debug(LOG_ERROR, "scrGetOilDefendLocIndex(): stack failed");
            return false;
      }

      if(playerStoring >= MAX_PLAYERS)
      {
            debug(LOG_ERROR, "scrGetOilDefendLocIndex: player index too high.");

            return false;
      }

      if(playerStoring < 0)
      {
            debug(LOG_ERROR, "scrGetOilDefendLocIndex: player index too low.");
            return false;
      }

      if (x < 0
       || x >= world_coord(mapWidth)
       || y < 0
       || y >= world_coord(mapHeight))
      {
            debug(LOG_ERROR, "scrGetOilDefendLocIndex: coords off map");
            return false;
      }

      scrFunctionResult.v.ival = GetOilDefendLocIndex(x,y,playerStoring);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Returns number of available locations */
BOOL scrGetBaseDefendLocCount(void)
{
      scrFunctionResult.v.ival = MAX_BASE_DEFEND_LOCATIONS;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrGetBaseDefendLocCount: push failed");
            return false;
      }

      return true;
}

/* Returns number of available locations*/
BOOL scrGetOilDefendLocCount(void)
{
      scrFunctionResult.v.ival = MAX_OIL_DEFEND_LOCATIONS;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrGetOilDefendLocCount: push failed");
            return false;
      }

      return true;
}

/* Returns a locations and its priority */
BOOL scrRecallBaseDefendLoc(void)
{
      SDWORD                        player, *x, *y, *prior,index;

      if (!stackPopParams(5, VAL_INT, &player, VAL_INT, &index,
                                    VAL_REF|VAL_INT, &x, VAL_REF|VAL_INT, &y, VAL_REF|VAL_INT, &prior))
      {
            debug(LOG_ERROR, "scrRecallBaseDefendLoc(): stack failed");
            return false;
      }

      if(player >= MAX_PLAYERS)
      {
            debug(LOG_ERROR,"scrRecallBaseDefendLoc: player index too high.");
            return false;
      }

      if(index < 0 || index >= MAX_BASE_DEFEND_LOCATIONS)
      {
            debug(LOG_ERROR,"scrRecallBaseDefendLoc: wrong index.");
            return false;
      }

      if(player < 0)
      {
            debug(LOG_ERROR,"scrRecallBaseDefendLoc: player index too low.");
            return false;
      }

      //check if can recall at this location
      if(!CanRememberPlayerBaseDefenseLoc(player, index))
      {
            scrFunctionResult.v.bval = false;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }

            return true;
      }

      *x = baseDefendLocation[player][index][0];
      *y = baseDefendLocation[player][index][1];

      *prior = baseDefendLocPrior[player][index];

      scrFunctionResult.v.bval = true;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Returns number of available locations */
BOOL scrRecallOilDefendLoc(void)
{
      SDWORD                        player, *x, *y, *prior,index;

      if (!stackPopParams(5, VAL_INT, &player, VAL_INT, &index,
                                    VAL_REF|VAL_INT, &x, VAL_REF|VAL_INT, &y, VAL_REF|VAL_INT, &prior))
      {
            debug(LOG_ERROR, "scrRecallOilDefendLoc(): stack failed");
            return false;
      }

      if(player >= MAX_PLAYERS)
      {
            debug(LOG_ERROR,"scrRecallOilDefendLoc: player index too high.");
            return false;
      }

      if(index < 0 || index >= MAX_OIL_DEFEND_LOCATIONS)
      {
            debug(LOG_ERROR,"scrRecallOilDefendLoc: wrong index: %d.", index);
            return false;
      }

      if(player < 0)
      {
            debug(LOG_ERROR,"scrRecallOilDefendLoc: player index too low.");
            return false;
      }

      //check if can recall at this location
      if(!CanRememberPlayerOilDefenseLoc(player, index))
      {
            scrFunctionResult.v.bval= false;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }

            return true;
      }

      *x = oilDefendLocation[player][index][0];
      *y = oilDefendLocation[player][index][1];

      *prior = oilDefendLocPrior[player][index];

      scrFunctionResult.v.bval = true;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Restores vilibility (fog of war) */
BOOL scrRecallPlayerVisibility(void)
{
      SDWORD                        player;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrRecallPlayerVisibility(): stack failed");
            return false;
      }

      if(player >= MAX_PLAYERS)
      {
            debug(LOG_ERROR,"scrRecallPlayerVisibility: player index too high.");
            return false;
      }



      scrFunctionResult.v.bval = true;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

BOOL scrSavePlayerAIExperience(void)
{
      SDWORD                        player;
      BOOL                    bNotify;

      if (!stackPopParams(2, VAL_INT, &player, VAL_BOOL, &bNotify))
      {
            debug(LOG_ERROR, "scrSavePlayerAIExperience(): stack failed");
            return false;
      }

      scrFunctionResult.v.bval = SavePlayerAIExperience(player, bNotify);
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

BOOL scrLoadPlayerAIExperience(void)
{
      SDWORD                        player;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrLoadPlayerAIExperience(): stack failed");
            return false;
      }

      scrFunctionResult.v.ival = LoadPlayerAIExperience(player);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}


/* Add a beacon (blip) */
BOOL addBeaconBlip(SDWORD locX, SDWORD locY, SDWORD forPlayer, SDWORD sender, char * textMsg)
{
      MESSAGE                 *psMessage;
      VIEWDATA          *pTempData;

      if (forPlayer >= MAX_PLAYERS)
      {
            debug(LOG_ERROR, "addBeaconBlip: player number is too high");
            return false;
      }

      //find the message if was already added previously
      psMessage = findBeaconMsg(forPlayer, sender);
      if (psMessage)
      {
            //remove it
            removeMessage(psMessage, forPlayer);
      }

      //create new message
      psMessage = addBeaconMessage(MSG_PROXIMITY, false, forPlayer);
      if (psMessage)
      {
            pTempData = CreateBeaconViewData(sender, locX, locY);

            ASSERT(pTempData != NULL, "Empty help data for radar beacon");

            psMessage->pViewData = (MSG_VIEWDATA *)pTempData;

            debug(LOG_MSG, "blip added, pViewData=%p", psMessage->pViewData);
      }
      else
      {
            debug(LOG_MSG, "call failed");
      }

      //Received a blip message from a player callback
      //store and call later
      //-------------------------------------------------
      //call beacon callback only if not adding for ourselves
      if(forPlayer != sender)
      {
            if(!msgStackPush(CALL_BEACON,sender,forPlayer,textMsg,locX,locY,NULL))
            {
                  debug(LOG_ERROR, "addBeaconBlip() - msgStackPush - stack failed");
                  return false;
            }

            if(selectedPlayer == forPlayer)
            {
                  // show console message
                  CONPRINTF(ConsoleString,(ConsoleString, _("Beacon received from %s!"),
                        getPlayerName(sender)));

                  // play audio
                  audio_QueueTrackPos( ID_SOUND_BEACON, locX, locY, 0);
            }
      }

      return true;
}

BOOL sendBeaconToPlayer(SDWORD locX, SDWORD locY, SDWORD forPlayer, SDWORD sender, char * beaconMsg)
{
      if(sender == forPlayer || myResponsibility(forPlayer))      //if destination player is on this machine
      {
            debug(LOG_WZ,"sending beacon to player %d (local player) from %d", forPlayer, sender);
            return addBeaconBlip(locX, locY, forPlayer, sender, beaconMsg);
      }
      else
      {
            debug(LOG_WZ,"sending beacon to player %d (remote player) from %d", forPlayer, sender);
            return sendBeacon(locX, locY, forPlayer, sender, beaconMsg);
      }
}

//prepare viewdata for help blip
VIEWDATA *CreateBeaconViewData(SDWORD sender, UDWORD LocX, UDWORD LocY)
{
      UDWORD                        height;
      VIEWDATA                *psViewData;
      SDWORD                        audioID;
      char                    name[MAXSTRLEN];

      //allocate message space
      psViewData = (VIEWDATA *)malloc(sizeof(VIEWDATA));
      if (psViewData == NULL)
      {
            ASSERT(false, "prepairHelpViewData() - Unable to allocate memory for viewdata");
            return NULL;
      }

      memset(psViewData, 0, sizeof(VIEWDATA));

      psViewData->numText = 1;

      //store name
      sprintf(name, _("Beacon %d"), sender);
      psViewData->pName = name;

      //allocate space for text strings
      psViewData->ppTextMsg = (char **) malloc(sizeof(char *));

      //store text message, hardcoded for now
      psViewData->ppTextMsg[0] = strdup(getPlayerName(sender));

      //store message type
      psViewData->type = VIEW_BEACON;

      //allocate memory for blip location etc
      psViewData->pData = (VIEW_PROXIMITY *) malloc(sizeof(VIEW_PROXIMITY));
      if (psViewData->pData == NULL)
      {
            ASSERT(false, "prepairHelpViewData() - Unable to allocate memory");
            return NULL;
      }

      //store audio
      audioID = NO_SOUND;
      ((VIEW_PROXIMITY *)psViewData->pData)->audioID = audioID;

      //store blip location
      if (LocX < 0)
      {
            ASSERT(false, "prepairHelpViewData() - Negative X coord for prox message");
            return NULL;
      }

      if (LocY < 0)
      {
            ASSERT(false, "prepairHelpViewData() - Negative X coord for prox message");
            return NULL;
      }

      ((VIEW_PROXIMITY *)psViewData->pData)->x = (UDWORD)LocX;
      ((VIEW_PROXIMITY *)psViewData->pData)->y = (UDWORD)LocY;

      //check the z value is at least the height of the terrain
      height = map_Height(LocX, LocY);

      ((VIEW_PROXIMITY *)psViewData->pData)->z = height;

      //store prox message type
      ((VIEW_PROXIMITY *)psViewData->pData)->proxType = PROX_ENEMY; //PROX_ENEMY for now

      //remember who sent this msg, so we could remove this one, when same player sends a new help-blip msg
      ((VIEW_PROXIMITY *)psViewData->pData)->sender = sender;

      //remember when the message was created so can remove it after some time
      ((VIEW_PROXIMITY *)psViewData->pData)->timeAdded = gameTime;
      debug(LOG_MSG, "Added message");

      return psViewData;
}

/* Looks through the players list of messages to find VIEW_BEACON (one per player!) pointer */
MESSAGE * findBeaconMsg(UDWORD player, SDWORD sender)
{
      MESSAGE                             *psCurr;

      for (psCurr = apsMessages[player]; psCurr != NULL; psCurr = psCurr->psNext)
      {
            //look for VIEW_BEACON, should only be 1 per player
            if (psCurr->dataType == MSG_DATA_BEACON)
            {
                  if(((VIEWDATA *)psCurr->pViewData)->type == VIEW_BEACON)
                  {
                        debug(LOG_WZ, "findBeaconMsg: %d ALREADY HAS A MESSAGE STORED", player);
                        if(((VIEW_PROXIMITY *)((VIEWDATA *)psCurr->pViewData)->pData)->sender == sender)
                        {
                              debug(LOG_WZ, "findBeaconMsg: %d ALREADY HAS A MESSAGE STORED from %d", player, sender);
                              return psCurr;
                        }
                  }
            }
      }

      //not found the message so return NULL
      return NULL;
}

/* Add beacon (radar blip) */
BOOL scrDropBeacon(void)
{
      SDWORD                  forPlayer,sender;
      char                          ssval2[255];
      UDWORD                  locX,locY,locZ;

      if (!stackPopParams(6, VAL_STRING, &strParam1 , VAL_INT, &forPlayer,
                        VAL_INT, &sender, VAL_INT, &locX, VAL_INT, &locY, VAL_INT, &locZ))
      {
            debug(LOG_ERROR,"scrDropBeacon failed to pop parameters");
            return false;
      }

      ssprintf(ssval2, "%s : %s", getPlayerName(sender), strParam1);    //temporary solution

      return sendBeaconToPlayer(locX, locY, forPlayer, sender, ssval2);
}

/* Remove beacon from the map */
BOOL scrRemoveBeacon(void)
{
      MESSAGE                 *psMessage;
      SDWORD                  player, sender;

      if (!stackPopParams(2, VAL_INT, &player, VAL_INT, &sender))
      {
            debug(LOG_ERROR,"scrRemoveBeacon: failed to pop parameters");
            return false;
      }

      if (player >= MAX_PLAYERS)
      {
            debug(LOG_ERROR,"scrRemoveBeacon:player number is too high");
            return false;
      }

      if (sender >= MAX_PLAYERS)
      {
            debug(LOG_ERROR,"scrRemoveBeacon:sender number is too high");
            return false;
      }

      //find the message
      psMessage = findBeaconMsg(player, sender);
      if (psMessage)
      {
            //delete it
            removeMessage(psMessage, player);
      }

      return true;
}

BOOL scrClosestDamagedGroupDroid(void)
{
      DROID_GROUP *psGroup;
      DROID       *psDroid,*psClosestDroid;
      SDWORD            x,y,healthLeft,wBestDist,wDist,maxRepairedBy,player;

      if (!stackPopParams(6, VAL_INT, &player, ST_GROUP, &psGroup, VAL_INT, &healthLeft,
            VAL_INT, &x, VAL_INT, &y, VAL_INT, &maxRepairedBy))
      {
            debug(LOG_ERROR, "scrClosestDamagedGroupDroid: failed to pop");
            return false;
      }

      wBestDist = 999999;
      psClosestDroid = NULL;
      for(psDroid = psGroup->psList;psDroid; psDroid = psDroid->psGrpNext)
      {
            if((psDroid->body * 100 / psDroid->originalBody) <= healthLeft)   //in%
            {
                  wDist = map_coord(dirtySqrt(psDroid->pos.x, psDroid->pos.y, x, y));     //in tiles
                  if(wDist < wBestDist)
                  {
                        if((maxRepairedBy < 0) || (getNumRepairedBy(psDroid, player) <= maxRepairedBy))
                        {
                              psClosestDroid = psDroid;
                              wBestDist = wDist;
                        }
                  }
            }
      }

      scrFunctionResult.v.oval = psClosestDroid;
      if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

SDWORD getNumRepairedBy(DROID *psDroidToCheck, SDWORD player)
{
      DROID       *psDroid;
      SDWORD            numRepaired = 0;

      for(psDroid = apsDroidLists[player]; psDroid; psDroid = psDroid->psNext)
      {
            if((psDroid->droidType != DROID_REPAIR) && (psDroid->droidType != DROID_CYBORG_REPAIR))
            {
                  continue;
            }

            if (psDroid->psTarget != NULL && psDroid->psTarget->type == OBJ_DROID)
            {
                  if ((DROID *)psDroid->psTarget == psDroidToCheck)
                  {
                        numRepaired++;
                  }
            }
      }

      return numRepaired;
}

/* Uses printf_console() for console debug output right now */
BOOL scrMsgBox(void)
{
      if (!stackPopParams(1, VAL_STRING, &strParam1))
      {
            debug(LOG_ERROR, "scrMsgBox(): stack failed");
            return false;
      }

      printf_console("DEBUG: %s",strParam1);

      return true;
}


// Check for a struct being within a certain range of a position (must be visible)
BOOL scrStructInRangeVis(void)
{
      SDWORD            range, player,lookingPlayer, x,y;
      BOOL        found;

      if (!stackPopParams(5, VAL_INT, &lookingPlayer, VAL_INT, &player , VAL_INT, &x, VAL_INT, &y, VAL_INT, &range))
      {
            debug(LOG_ERROR, "scrStructInRangeVis: failed to pop");
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT(false, "scrStructInRange: invalid player number");
            return false;
      }

      found = objectInRangeVis((BASE_OBJECT *)apsStructLists[player], x,y, range, lookingPlayer);

      scrFunctionResult.v.bval = found;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

// check for a base object being in range of a point
BOOL objectInRangeVis(BASE_OBJECT *psList, SDWORD x, SDWORD y, SDWORD range, SDWORD lookingPlayer)
{
      BASE_OBJECT       *psCurr;
      SDWORD                  xdiff, ydiff, rangeSq;

      // See if there is a droid in range
      rangeSq = range * range;
      for(psCurr = psList; psCurr; psCurr = psCurr->psNext)
      {
            if(psCurr->type == OBJ_STRUCTURE)
            {
                  if(!((STRUCTURE *)psCurr)->visible[lookingPlayer])
                        continue;
            }

            if(psCurr->type == OBJ_DROID)
            {
                  if(!((DROID *)psCurr)->visible[lookingPlayer])
                        continue;
            }

            // skip partially build structures
            //if ( (psCurr->type == OBJ_STRUCTURE) &&
            //     (((STRUCTURE *)psCurr)->status != SS_BUILT) )
            //{
            //    continue;
            //}

            // skip flying vtols
            if ( (psCurr->type == OBJ_DROID) &&
                  isVtolDroid((DROID *)psCurr) &&
                  ((DROID *)psCurr)->sMove.Status != MOVEINACTIVE )
            {
                  continue;
            }

            xdiff = (SDWORD)psCurr->pos.x - x;
            ydiff = (SDWORD)psCurr->pos.y - y;
            if (xdiff*xdiff + ydiff*ydiff < rangeSq)
            {
                  return true;
            }
      }

      return false;
}

/* Go after a certain research */
BOOL scrPursueResearch(void)
{
      RESEARCH                *psResearch;
      SDWORD                        foundIndex = 0,player,cur,tempIndex,Stack[400];
      UDWORD                        index;
      SWORD                   top;

      BOOL                    found;
      PLAYER_RESEARCH         *pPlayerRes;

      char                    sTemp[128];
      STRUCTURE               *psBuilding;
      RESEARCH_FACILITY *psResFacilty;

      RESEARCH                *pResearch;

      if (!stackPopParams(3,ST_STRUCTURE, &psBuilding, VAL_INT, &player, ST_RESEARCH, &psResearch ))
      {
            debug(LOG_ERROR, "scrPursueResearch(): stack failed");
            return false;
      }

      if(psResearch == NULL)
      {
            ASSERT(false, ": no such research topic");
            return false;
      }

      psResFacilty =    (RESEARCH_FACILITY*)psBuilding->pFunctionality;

      if(psResFacilty->psSubject != NULL)       // not finished yet
      {
            scrFunctionResult.v.bval = false;
            if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
            {
                  return false;
            }
            return true;
      }

      pPlayerRes = asPlayerResList[player];
      index = psResearch - asResearch;

      if (index >= numResearch)
      {
            ASSERT(false, "scrPursueResearch: invalid research index");
            return false;
      }

      found = false;

      if(beingResearchedByAlly(index, player))        //an ally is already researching it
      {
            found = false;
      }
      else if(IsResearchCompleted(&pPlayerRes[index]))
      {
            found = false;
            //DbgMsg("Research already completed: %d", index);
      }
      else if(IsResearchStarted(&pPlayerRes[index]))
      {
            found = false;
            //DbgMsg("Research already in progress, %d", index);
      }
      else if(IsResearchPossible(&pPlayerRes[index])
           || IsResearchCancelled(&pPlayerRes[index]))
      {
            foundIndex = index;
            found = true;
            //DbgMsg("Research possible or cancelled: %d", index);
      }
      else if(skTopicAvail(index,player))
      {
            foundIndex = index;
            found = true;
            //DbgMsg("Research available: %d",index);
      }
      else
      {
            //DbgMsg("starting to search for: %d, %s", index, asResearch[index].pName);
            top = -1;

            cur = 0;                      //start with first index's PR
            tempIndex = -1;
            while(true) //do
            {
                  //DbgMsg("Going on with %d, numPR: %d, %s", index, asResearch[index].numPRRequired, asResearch[index].pName);

                  if(cur >= asResearch[index].numPRRequired)            //node has nodes?
                  {
                        //DbgMsg("cur >= numPRRequired : %d (%d >= %d)", index, cur, asResearch[index].numPRRequired);

                        top = top - 2;
                        if(top < (-1))
                        {
                              //DbgMsg("Nothing on stack");
                              break;            //end of stack
                        }
                        index = Stack[top + 2]; //if index = -1, then exit
                        cur = Stack[top + 1];         //go to next PR of the last node, since this one didn't work

                  }
                  else        //end of nodes not reached
                  {
                        tempIndex = asResearch[index].pPRList[cur];           //get cur node's index
                        //DbgMsg("evaluating node: %d, (cur = %d), %s", tempIndex, cur, asResearch[tempIndex].pName);

                        if(skTopicAvail(tempIndex,player) && (!beingResearchedByAlly(tempIndex, player)))   //<NEW> - ally check added
                        {
                              //DbgMsg("avail: %d (cur=%d), %s", tempIndex, cur, asResearch[tempIndex].pName);
                              found = true;
                              foundIndex = tempIndex;       //done
                              break;
                        }
                        else if((IsResearchCompleted(&pPlayerRes[tempIndex])==false) && (IsResearchStarted(&pPlayerRes[tempIndex])==false))           //not avail and not busy with it, can check this PR's PR
                        {
                              //DbgMsg("node not complete, not started: %d, (cur=%d), %s", tempIndex,cur, asResearch[tempIndex].pName);
                              if(asResearch[tempIndex].numPRRequired > 0)     //node has any nodes itself
                              {
                                    //DbgMsg("node has nodes, so selecting as main node: %d, %s", tempIndex, asResearch[tempIndex].pName);

                                    Stack[top+1] = cur;                                               //so can go back to it further
                                    Stack[top+2] = index;
                                    top = top + 2;

                                    index = tempIndex;            //go 1 level further
                                    cur = -1;                                                   //start with first PR of this PR next time
                              }
                              else        //has no PRs, choose it (?)
                              {
                                    if(!beingResearchedByAlly(tempIndex, player))   //<NEW> ally check added
                                    {
                                          //DbgMsg("PR has no PRs, choosing it: %d (cur=%d), %s", tempIndex, cur, asResearch[tempIndex].pName);
                                          found = true;
                                          foundIndex = tempIndex; //done
                                          break;
                                    }
                              }
                        }
                  }

                  cur++;                        //try next node of the main node
                  if((cur >= asResearch[index].numPRRequired) && (top <= (-1)))     //nothing left
                  {
                        //DbgMsg("END");
                        break;
                  }

            } // while(true)
      }

      if(found
       && foundIndex < numResearch)
      {
            pResearch = (asResearch + foundIndex);
            pPlayerRes                    = asPlayerResList[player]+ foundIndex;
            psResFacilty->psSubject = (BASE_STATS*)pResearch;             //set the subject up

            if (IsResearchCancelled(pPlayerRes))
            {
                  psResFacilty->powerAccrued = pResearch->researchPower;//set up as if all power available for cancelled topics
            }
            else
            {
                  psResFacilty->powerAccrued = 0;
            }

            MakeResearchStarted(pPlayerRes);
            psResFacilty->timeStarted = ACTION_START_TIME;
            psResFacilty->timeStartHold = 0;
            psResFacilty->timeToResearch = pResearch->researchPoints /  psResFacilty->researchPoints;
            if (psResFacilty->timeToResearch == 0)
            {
                  psResFacilty->timeToResearch = 1;
            }

            sprintf(sTemp,"player:%d starts topic: %s",player, asResearch[foundIndex].pName );
            NETlogEntry(sTemp,0,0);
      }

      scrFunctionResult.v.bval = found;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

BOOL scrGetStructureType(void)
{
      STRUCTURE               *psStruct;

      if (!stackPopParams(1, ST_STRUCTURE, &psStruct))
      {
            debug(LOG_ERROR, "scrGetStructureType(): stack failed");
            return false;
      }

      scrFunctionResult.v.ival = psStruct->pStructureType->type;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrGetStructureType(): failed to push result");
            return false;
      }

      return true;
}

/* Get player name from index */
BOOL scrGetPlayerName(void)
{
      SDWORD      player;

      if (!stackPopParams(1, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrGetPlayerName(): stack failed");
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrGetPlayerName: invalid player number" );
            return false;
      }

      /* Casting away constness because stackPushResult doesn't modify it's
       * value (i.e. in this case it's not const correct).
       */
      scrFunctionResult.v.sval = (char*)getPlayerName((UDWORD)player);
      if (!stackPushResult(VAL_STRING, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrGetPlayerName(): failed to push result");
            return false;
      }

      return true;
}

/* Set player name */
BOOL scrSetPlayerName(void)
{
      SDWORD      player;

      if (!stackPopParams(2, VAL_INT, &player, VAL_STRING, &strParam1))
      {
            debug(LOG_ERROR, "scrSetPlayerName(): stack failed");
            return false;
      }

      if (player < 0 || player >= MAX_PLAYERS)
      {
            ASSERT( false, "scrSetPlayerName: invalid player number" );
            return false;
      }

      scrFunctionResult.v.bval = setPlayerName(player, strParam1);
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrSetPlayerName(): failed to push result");
            return false;
      }

      return true;
}

SDWORD getPlayerFromString(char *playerName)
{
      UDWORD      playerIndex;
      char  sPlayerNumber[255];

      for( playerIndex=0; playerIndex<MAX_PLAYERS; playerIndex++ )
      {
            /* check name */
            //debug(LOG_SCRIPT, "checking  (%s,%s)",getPlayerName(playerIndex), playerName);
            if (strncasecmp(getPlayerName(playerIndex), playerName, 255) == 0)
            {
                  //debug(LOG_SCRIPT, "matched, returning %d", playerIndex);
                  return playerIndex;
            }

            /* check color */
            //debug(LOG_SCRIPT, "checking (%s,%s)",getPlayerColourName(playerIndex), playerName);
            if (strncasecmp(getPlayerColourName(playerIndex), playerName, 255) == 0)
            {
                  //debug(LOG_SCRIPT, "matched, returning %d", playerIndex);
                  return playerIndex;
            }

            /* check player number */
            sprintf(sPlayerNumber,"%d",playerIndex);
            //debug(LOG_SCRIPT, "checking (%s,%s)",sPlayerNumber, playerName);
            if (strncasecmp(sPlayerNumber,playerName, 255) == 0)
            {
                  //debug(LOG_SCRIPT, "matched, returning %d", playerIndex);
                  return playerIndex;
            }

      }

      return -1;
}

/* Checks if a particular bit is set in an integer */
BOOL scrGetBit(void)
{
      SDWORD                        val1,val2;

      if (!stackPopParams(2, VAL_INT, &val1, VAL_INT, &val2))
      {
            debug(LOG_ERROR, "scrGetBit(): failed to pop");
            return false;
      }

      ASSERT(val2 < MAX_PLAYERS && val2 >= 0, "scrGetBit(): wrong player index (%d)", val2);

      scrFunctionResult.v.bval = ((val1 & bitMask[val2]) != 0);
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Sets a particular bit in an integer */
BOOL scrSetBit(void)
{
      SDWORD                        base,position;
      BOOL                    bSet;

      if (!stackPopParams(3, VAL_INT, &base,
            VAL_INT, &position, VAL_BOOL, &bSet))
      {
            debug(LOG_ERROR, "scrSetBit(): failed to pop");
            return false;
      }

      ASSERT(position < MAX_PLAYERS && position >= 0, "scrSetBit(): wrong position index (%d)", position);

      if(bSet)
      {
            base |= bitMask[position];
      }
      else
      {
            base &= bitMask[position];
      }

      scrFunctionResult.v.ival = base;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Can we create and break alliances? */
BOOL scrAlliancesLocked(void)
{
      BOOL        bResult = true;

      if(bMultiPlayer && (game.alliance == ALLIANCES))
            bResult = false;

      scrFunctionResult.v.bval = bResult;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrAlliancesLocked(): failed to push result");
            return false;
      }

      return true;
}

BOOL scrASSERT(void)
{
      BOOL                    bExpression;
      SDWORD                  player;
      char              sTmp[255];

      if (!stackPopParams(3, VAL_BOOL, &bExpression, VAL_STRING, &strParam1, VAL_INT, &player))
      {
            debug(LOG_ERROR, "scrASSERT(): stack failed");
            return false;
      }

#ifdef DEBUG
      /* Just pass the expression and message from script */
      sprintf(sTmp,"%d) %s",player,strParam1);
      ASSERT(bExpression, sTmp);
#else
      if(scrDebug[player])
      {
            if(!bExpression)
            {
                  sprintf(sTmp,"%d) %s",player,strParam1);
                  addConsoleMessage(sTmp,RIGHT_JUSTIFY,player);
            }
      }
#endif

      return true;
}

/* Visualize radius at position */
BOOL scrShowRangeAtPos(void)
{
      SDWORD            x,y,radius;

      if (!stackPopParams(3, VAL_INT, &x, VAL_INT, &y, VAL_INT, &radius))
      {
            debug(LOG_ERROR, "scrShowRangeAtPos(): stack failed");
            return false;
      }

      //Turn on/off drawing
      showRangeAtPos(x,y,radius);

      return true;
}

BOOL scrToPow(void)
{
      float       x,y;

      if (!stackPopParams(2, VAL_FLOAT, &x, VAL_FLOAT, &y))
      {
            debug(LOG_ERROR, "scrToPow(): stack failed");
            return false;
      }

      scrFunctionResult.v.fval = (float)pow(x,y);
      if (!stackPushResult(VAL_FLOAT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrToPow(): failed to push result");
            return false;
      }

      return true;
}

/* Exponential function */
BOOL scrExp(void)
{
      float       fArg;

      if (!stackPopParams(1, VAL_FLOAT, &fArg))
      {
            return false;
      }

      scrFunctionResult.v.fval = exp(fArg);
      if (!stackPushResult(VAL_FLOAT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Square root */
BOOL scrSqrt(void)
{
      float       fArg;

      if (!stackPopParams(1, VAL_FLOAT, &fArg))
      {
            return false;
      }

      scrFunctionResult.v.fval = sqrtf(fArg);
      if (!stackPushResult(VAL_FLOAT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Natural logarithm */
BOOL scrLog(void)
{
      float       fArg;

      if (!stackPopParams(1, VAL_FLOAT, &fArg))
      {
            return false;
      }

      scrFunctionResult.v.fval = log(fArg);
      if (!stackPushResult(VAL_FLOAT, &scrFunctionResult))
      {
            return false;
      }

      return true;
}

/* Show/Hide multiplayer debug menu */
BOOL scrDebugMenu(void)
{
      SDWORD            menuUp;

      if (!stackPopParams(1, VAL_BOOL, &menuUp))
      {
            debug(LOG_ERROR, "scrDebugMenu(): stack failed");
            return false;
      }

      (void)addDebugMenu(menuUp);

      return true;
}

/* Set debug menu output string */
BOOL scrSetDebugMenuEntry(void)
{
      SDWORD            index;

      if (!stackPopParams(2, VAL_STRING, &strParam1, VAL_INT, &index))
      {
            debug(LOG_ERROR, "scrSetDebugMenuEntry(): stack failed");
            return false;
      }

      setDebugMenuEntry(strParam1, index);

      return true;
}

/* Parse chat message and return number of commands that could be extracted */
BOOL scrProcessChatMsg(void)
{
      if (!stackPopParams(1, VAL_STRING, &strParam1))
      {
            debug(LOG_ERROR, "scrProcessChatMsg(): stack failed");
            return false;
      }

      debug(LOG_NEVER, "Now preparing to parse '%s'", strParam1);

      if (!chatLoad(strParam1, strlen(strParam1)))
      {
            ASSERT(false, "Couldn't process chat message: %s", strParam1);
            return false;
      }

      scrFunctionResult.v.ival = chat_msg.numCommands;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrProcessChatMsg(): failed to push result");
            return false;
      }

      return true;
}

/* Returns number of command arguments for a certain
 * chat command that could be extracted
 */
BOOL scrGetNumArgsInCmd(void)
{
      SDWORD            cmdIndex;

      if (!stackPopParams(1, VAL_INT, &cmdIndex))
      {
            debug(LOG_ERROR, "scrGetNumArgsInCmd(): stack failed");
            return false;
      }

      /* Check command bounds */
      if(cmdIndex < 0 || cmdIndex >= chat_msg.numCommands)
      {
            ASSERT(false, "scrGetNumArgsInCmd: command inxed out of bounds: %d (num commands: %d)",
                  cmdIndex, chat_msg.numCommands);
            return false;
      }


      scrFunctionResult.v.ival = chat_msg.cmdData[cmdIndex].numCmdParams;
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrGetNumArgsInCmd(): failed to push result");
            return false;
      }

      return true;
}

/* Returns a string representing a certain chat command,
 * based on the command index provided
 */
BOOL scrGetChatCmdDescription(void)
{
      SDWORD                  cmdIndex;
      char              *pChatCommand=NULL;

      if (!stackPopParams(1, VAL_INT, &cmdIndex))
      {
            debug(LOG_ERROR, "scrGetCommandDescription(): stack failed");
            return false;
      }

      /* Check command bounds */
      if(cmdIndex < 0 || cmdIndex >= chat_msg.numCommands)
      {
            ASSERT(false, "scrGetCommandDescription: command inxed out of bounds: %d (num commands: %d)",
                  cmdIndex, chat_msg.numCommands);
            return false;
      }

      /* Allocate memory for the comamnd string */
      pChatCommand = (char*)malloc(MAXSTRLEN);
      if (pChatCommand == NULL)
      {
            debug(LOG_ERROR, "scrGetCmdDescription: Out of memory!");
            abort();
            return false;
      }

      /* Copy command */
      strlcpy(pChatCommand, chat_msg.cmdData[cmdIndex].pCmdDescription, MAXSTRLEN);

      /* Make scrFunctionResult point to the valid command */
      scrFunctionResult.v.sval = pChatCommand;

      if (!stackPushResult(VAL_STRING, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrGetCommandDescription(): failed to push result");
            free(pChatCommand);
            return false;
      }

      free(pChatCommand);

      return true;
}

/* Returns a certain parameter of a certain chat command
 * Returns false if failed
 */
BOOL scrGetChatCmdParam(void)
{
      SDWORD                  cmdIndex, argIndex;
      void              *pArgument=NULL;
      INTERP_TYPE       argType=VAL_VOID;
      BOOL              bSuccess=true;          //failure on type mismatch

      //if (!stackPopParams(3, VAL_INT, &cmdIndex, VAL_INT, &argIndex, VAL_REF | VAL_VOID, &pArgument))
      //{
      //    debug(LOG_ERROR, "scrGetChatCmdParam(): stack failed");
      //    return false;
      //}

      if (!stackPopParams(2, VAL_INT, &cmdIndex, VAL_INT, &argIndex))
      {
            debug(LOG_ERROR, "scrGetChatCmdParam(): stack failed");
            return false;
      }

      if(cmdIndex < 0 || cmdIndex >= chat_msg.numCommands)
      {
            ASSERT(false, "scrGetChatCmdParam: command index out of bounds: %d", cmdIndex);
            return false;
      }

      if(argIndex < 0 || argIndex >= chat_msg.cmdData[cmdIndex].numCmdParams )
      {
            ASSERT(false, "scrGetChatCmdParam: argument index for command %d is out of bounds: %d", cmdIndex, argIndex);
            return false;
      }

      /* Find out the type of the argument we are going to pass to the script */
      argType = chat_msg.cmdData[cmdIndex].parameter[argIndex].type;

      if (!stackPopParams(1, VAL_REF | argType, &pArgument))
      {
            debug(LOG_ERROR, "scrGetChatCmdParam(): stack failed or argument mismatch (expected type of argument: %d)", argType);
            bSuccess = false;       //return type mismatch
            //return false;
      }

      if(pArgument == NULL)
      {
            ASSERT(false, "scrGetChatCmdParam: nullpointer check failed");
            bSuccess = false;
            //return false;
      }

      /* Return command argument to the script */
      if(bSuccess){
            memcpy(pArgument, &(chat_msg.cmdData[cmdIndex].parameter[argIndex].v), sizeof(chat_msg.cmdData[cmdIndex].parameter[argIndex].v));
      }

      scrFunctionResult.v.bval = bSuccess;
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrGetChatCmdParam(): failed to push result");
            return false;
      }
      return true;
}

/* Returns true if a certain command was addressed to a certain player */
BOOL scrChatCmdIsPlayerAddressed(void)
{
      SDWORD            cmdIndex,playerInQuestion;

      if (!stackPopParams(2, VAL_INT, &cmdIndex, VAL_INT, &playerInQuestion))
      {
            debug(LOG_ERROR, "scrChatCmdIsPlayerAddressed(): stack failed");
            return false;
      }

      /* Check command bounds */
      if(cmdIndex < 0 || cmdIndex >= chat_msg.numCommands)
      {
            ASSERT(false, "scrChatCmdIsPlayerAddressed: command inxed out of bounds: %d (num commands: %d)",
                  cmdIndex, chat_msg.numCommands);
            return false;
      }

      /* Check player bounds */
      if(playerInQuestion < 0 || playerInQuestion >= MAX_PLAYERS)
      {
            ASSERT(false, "scrChatCmdIsPlayerAddressed: player inxed out of bounds: %d", playerInQuestion);
            return false;
      }

      scrFunctionResult.v.bval = chat_msg.cmdData[cmdIndex].bPlayerAddressed[playerInQuestion];
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrChatCmdIsPlayerAddressed(): failed to push result");
            return false;
      }

      return true;
}

/* Modifies height of a tile */
BOOL scrSetTileHeight(void)
{
      UDWORD            tileX,tileY,newHeight;
      MAPTILE           *psTile;

      if (!stackPopParams(3, VAL_INT, &tileX, VAL_INT, &tileY, VAL_INT, &newHeight))
      {
            debug(LOG_ERROR, "scrSetTileHeight(): stack failed");
            return false;
      }

      ASSERT(newHeight <= 255, "scrSetTileHeight: height out of bounds");

      psTile = mapTile(tileX,tileY);

      psTile->height = (UBYTE)newHeight;

      return true;
}

/* Returns structure which placed on provided coordinates.
 * Returns NULL (NULLOBJECT) if there's no structure.
 */
BOOL scrGetTileStructure(void)
{
      SDWORD            structureX,structureY;

      if (!stackPopParams(2, VAL_INT, &structureX, VAL_INT, &structureY))
      {
            debug(LOG_ERROR, "scrGetTileStructure(): stack failed");
            return false;
      }

      scrFunctionResult.v.oval = getTileStructure(structureX, structureY);
      if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrGetTileStructure(): failed to push result");
            return false;
      }

      return true;
}

/* Outputs script call stack
 */
BOOL scrPrintCallStack(void)
{
      scrOutputCallTrace(LOG_SCRIPT);

      return true;
}

/*
 * Returns true if game debug mode is on
 */
BOOL scrDebugModeEnabled(void)
{

      scrFunctionResult.v.bval = getDebugMappingStatus();
      if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrDebugModeEnabled(): failed to push result");
            return false;
      }

      return true;
}

/*
 * Returns the cost of a droid
 */
BOOL scrCalcDroidPower(void)
{
      DROID *psDroid;

      if (!stackPopParams(1, ST_DROID, &psDroid))
      {
            return false;
      }

      ASSERT(psDroid != NULL,
            "scrCalcDroidPower: can't calculate cost of a null-droid");

      scrFunctionResult.v.ival = (SDWORD)calcDroidPower(psDroid);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrCalcDroidPower(): failed to push result");
            return false;
      }

      return true;
}

/*
 * Returns experience level of a droid
 */
BOOL scrGetDroidLevel(void)
{
      DROID *psDroid;

      if (!stackPopParams(1, ST_DROID, &psDroid))
      {
            return false;
      }

      ASSERT(psDroid != NULL,
            "scrGetDroidLevel: null-pointer passed");

      scrFunctionResult.v.ival = (SDWORD)getDroidLevel(psDroid);
      if (!stackPushResult(VAL_INT, &scrFunctionResult))
      {
            debug(LOG_ERROR, "scrGetDroidLevel(): failed to push result");
            return false;
      }

      return true;
}

Generated by  Doxygen 1.6.0   Back to index