2142 lines
47 KiB
C++
2142 lines
47 KiB
C++
/*
|
|
This file is part of Warzone 2100.
|
|
Copyright (C) 1999-2004 Eidos Interactive
|
|
Copyright (C) 2005-2013 Warzone 2100 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
|
|
*/
|
|
/*
|
|
* ScriptAI.c
|
|
*
|
|
* Script functions to support the AI system
|
|
*
|
|
*/
|
|
|
|
#include "lib/framework/frame.h"
|
|
#include "objects.h"
|
|
#include "group.h"
|
|
#include "lib/script/script.h"
|
|
#include "scripttabs.h"
|
|
#include "scriptai.h"
|
|
#include "order.h"
|
|
#include "map.h"
|
|
#include "cluster.h"
|
|
#include "lib/netplay/netplay.h"
|
|
#include "cmddroid.h"
|
|
#include "projectile.h"
|
|
#include "research.h"
|
|
#include "gateway.h"
|
|
#include "multiplay.h"
|
|
#include "action.h" //because of .action
|
|
#include "power.h"
|
|
#include "geometry.h"
|
|
#include "src/scriptfuncs.h"
|
|
#include "fpath.h"
|
|
#include "multigifts.h"
|
|
|
|
static INTERP_VAL scrFunctionResult; //function return value to be pushed to stack
|
|
|
|
// Add a droid to a group
|
|
bool scrGroupAddDroid(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
DROID *psDroid;
|
|
|
|
if (!stackPopParams(2, ST_GROUP, &psGroup, ST_DROID, &psDroid))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psGroup != NULL,
|
|
"scrGroupAdd: Invalid group pointer");
|
|
ASSERT(psDroid != NULL,
|
|
"scrGroupAdd: Invalid droid pointer");
|
|
if (psDroid == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
if (psDroid->droidType == DROID_COMMAND)
|
|
{
|
|
debug(LOG_ERROR,
|
|
"scrGroupAdd: cannot add a command droid to a group");
|
|
return false;
|
|
}
|
|
if (psDroid->droidType == DROID_TRANSPORTER || psDroid->droidType == DROID_SUPERTRANSPORTER)
|
|
{
|
|
debug(LOG_ERROR,
|
|
"scrGroupAdd: cannot add a transporter to a group");
|
|
return false;
|
|
}
|
|
|
|
psGroup->add(psDroid);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Add droids in an area to a group
|
|
bool scrGroupAddArea(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
DROID *psDroid;
|
|
SDWORD x1, y1, x2, y2, player;
|
|
|
|
if (!stackPopParams(6, ST_GROUP, &psGroup, VAL_INT, &player,
|
|
VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psGroup != NULL,
|
|
"scrGroupAdd: Invalid group pointer");
|
|
|
|
if (player < 0 || player >= MAX_PLAYERS)
|
|
{
|
|
ASSERT(false, "scrGroupAddArea: invalid player");
|
|
return false;
|
|
}
|
|
|
|
for (psDroid = apsDroidLists[player]; psDroid; psDroid = psDroid->psNext)
|
|
{
|
|
if (((SDWORD)psDroid->pos.x >= x1) && ((SDWORD)psDroid->pos.x <= x2) &&
|
|
((SDWORD)psDroid->pos.y >= y1) && ((SDWORD)psDroid->pos.y <= y2) &&
|
|
psDroid->droidType != DROID_COMMAND &&
|
|
(psDroid->droidType != DROID_TRANSPORTER && psDroid->droidType != DROID_SUPERTRANSPORTER))
|
|
|
|
{
|
|
psGroup->add(psDroid);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Add groupless droids in an area to a group
|
|
bool scrGroupAddAreaNoGroup(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
DROID *psDroid;
|
|
SDWORD x1, y1, x2, y2, player;
|
|
|
|
if (!stackPopParams(6, ST_GROUP, &psGroup, VAL_INT, &player,
|
|
VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psGroup != NULL,
|
|
"scrGroupAddNoGroup: Invalid group pointer");
|
|
|
|
if (player < 0 || player >= MAX_PLAYERS)
|
|
{
|
|
ASSERT(false, "scrGroupAddAreaNoGroup: invalid player");
|
|
return false;
|
|
}
|
|
|
|
for (psDroid = apsDroidLists[player]; psDroid; psDroid = psDroid->psNext)
|
|
{
|
|
if (((SDWORD)psDroid->pos.x >= x1) && ((SDWORD)psDroid->pos.x <= x2) &&
|
|
((SDWORD)psDroid->pos.y >= y1) && ((SDWORD)psDroid->pos.y <= y2) &&
|
|
psDroid->droidType != DROID_COMMAND &&
|
|
(psDroid->droidType != DROID_TRANSPORTER && psDroid->droidType != DROID_SUPERTRANSPORTER) &&
|
|
psDroid->psGroup == NULL)
|
|
{
|
|
psGroup->add(psDroid);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Move the droids from one group to another
|
|
bool scrGroupAddGroup(void)
|
|
{
|
|
DROID_GROUP *psTo, *psFrom;
|
|
DROID *psDroid, *psNext;
|
|
|
|
if (!stackPopParams(2, ST_GROUP, &psTo, ST_GROUP, &psFrom))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psTo != NULL,
|
|
"scrGroupAddGroup: Invalid group pointer");
|
|
ASSERT(psFrom != NULL,
|
|
"scrGroupAddGroup: Invalid group pointer");
|
|
|
|
for (psDroid = psFrom->psList; psDroid; psDroid = psNext)
|
|
{
|
|
psNext = psDroid->psGrpNext;
|
|
psTo->add(psDroid);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// check if a droid is a member of a group
|
|
bool scrGroupMember(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
DROID *psDroid;
|
|
bool retval;
|
|
|
|
if (!stackPopParams(2, ST_GROUP, &psGroup, ST_DROID, &psDroid))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psGroup != NULL,
|
|
"scrGroupMember: Invalid group pointer");
|
|
ASSERT(psDroid != NULL,
|
|
"scrGroupMember: Invalid droid pointer");
|
|
if (psDroid == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (psDroid->psGroup == psGroup)
|
|
{
|
|
retval = true;
|
|
}
|
|
else
|
|
{
|
|
retval = false;
|
|
}
|
|
|
|
scrFunctionResult.v.bval = retval;
|
|
if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// returns number of idle droids in a group.
|
|
bool scrIdleGroup(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
DROID *psDroid;
|
|
UDWORD count = 0;
|
|
|
|
if (!stackPopParams(1, ST_GROUP, &psGroup))
|
|
{
|
|
return false;
|
|
}
|
|
ASSERT_OR_RETURN(false, psGroup != NULL, "Invalid group pointer");
|
|
|
|
for (psDroid = psGroup->psList; psDroid; psDroid = psDroid->psGrpNext)
|
|
{
|
|
if (psDroid->order.type == DORDER_NONE || (psDroid->order.type == DORDER_GUARD && psDroid->order.psObj == NULL))
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
|
|
scrFunctionResult.v.ival = (SDWORD)count;
|
|
if (!stackPushResult(VAL_INT, &scrFunctionResult))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// variables for the group iterator
|
|
static DROID_GROUP *psScrIterateGroup;
|
|
static DROID *psScrIterateGroupDroid;
|
|
|
|
// initialise iterating a groups members
|
|
bool scrInitIterateGroup(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
|
|
if (!stackPopParams(1, ST_GROUP, &psGroup))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psGroup != NULL,
|
|
"scrInitGroupIterate: invalid group pointer");
|
|
|
|
psScrIterateGroup = psGroup;
|
|
psScrIterateGroupDroid = psGroup->psList;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// iterate through a groups members
|
|
bool scrIterateGroup(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
DROID *psDroid;
|
|
|
|
if (!stackPopParams(1, ST_GROUP, &psGroup))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (psGroup != psScrIterateGroup)
|
|
{
|
|
debug(LOG_ERROR, "scrIterateGroup: invalid group, InitGroupIterate not called?");
|
|
return false;
|
|
}
|
|
|
|
if (psScrIterateGroupDroid != NULL)
|
|
{
|
|
psDroid = psScrIterateGroupDroid;
|
|
psScrIterateGroupDroid = psScrIterateGroupDroid->psGrpNext;
|
|
}
|
|
else
|
|
{
|
|
psDroid = NULL;
|
|
}
|
|
|
|
scrFunctionResult.v.oval = psDroid;
|
|
if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// remove a droid from a group
|
|
bool scrDroidLeaveGroup(void)
|
|
{
|
|
DROID *psDroid;
|
|
|
|
if (!stackPopParams(1, ST_DROID, &psDroid))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (psDroid->psGroup != NULL)
|
|
{
|
|
psDroid->psGroup->remove(psDroid);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Give a group an order
|
|
bool scrOrderGroup(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
DROID_ORDER order;
|
|
|
|
if (!stackPopParams(2, ST_GROUP, &psGroup, VAL_INT, &order))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psGroup != NULL,
|
|
"scrOrderGroup: Invalid group pointer");
|
|
|
|
if (order != DORDER_STOP &&
|
|
order != DORDER_RETREAT &&
|
|
order != DORDER_DESTRUCT &&
|
|
order != DORDER_RTR &&
|
|
order != DORDER_RTB &&
|
|
order != DORDER_RUN)
|
|
{
|
|
ASSERT(false,
|
|
"scrOrderGroup: Invalid order");
|
|
return false;
|
|
}
|
|
|
|
debug(LOG_NEVER, "group %p (%u) order %d", psGroup, psGroup->getNumMembers(), order);
|
|
psGroup->orderGroup(order);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Give a group an order to a location
|
|
bool scrOrderGroupLoc(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
DROID_ORDER order;
|
|
SDWORD x, y;
|
|
|
|
if (!stackPopParams(4, ST_GROUP, &psGroup, VAL_INT, &order, VAL_INT, &x, VAL_INT, &y))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psGroup != NULL,
|
|
"scrOrderGroupLoc: Invalid group pointer");
|
|
|
|
if (order != DORDER_MOVE &&
|
|
order != DORDER_SCOUT)
|
|
{
|
|
ASSERT(false,
|
|
"scrOrderGroupLoc: Invalid order");
|
|
return false;
|
|
}
|
|
if (x < 0
|
|
|| x > world_coord(mapWidth)
|
|
|| y < 0
|
|
|| y > world_coord(mapHeight))
|
|
{
|
|
ASSERT(false, "Invalid map location (%d, %d), max is (%d, %d)", x, y, world_coord(mapWidth), world_coord(mapHeight));
|
|
return false;
|
|
}
|
|
|
|
debug(LOG_NEVER, "group %p (%u) order %d (%d,%d)",
|
|
psGroup, psGroup->getNumMembers(), order, x, y);
|
|
psGroup->orderGroup(order, (UDWORD)x, (UDWORD)y);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Give a group an order to an object
|
|
bool scrOrderGroupObj(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
DROID_ORDER order;
|
|
BASE_OBJECT *psObj;
|
|
|
|
if (!stackPopParams(3, ST_GROUP, &psGroup, VAL_INT, &order, ST_BASEOBJECT, &psObj))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psGroup != NULL,
|
|
"scrOrderGroupObj: Invalid group pointer");
|
|
ASSERT(psObj != NULL,
|
|
"scrOrderGroupObj: Invalid object pointer");
|
|
|
|
if (order != DORDER_ATTACK &&
|
|
order != DORDER_HELPBUILD &&
|
|
order != DORDER_DEMOLISH &&
|
|
order != DORDER_REPAIR &&
|
|
order != DORDER_OBSERVE &&
|
|
order != DORDER_EMBARK &&
|
|
order != DORDER_FIRESUPPORT &&
|
|
order != DORDER_DROIDREPAIR)
|
|
{
|
|
ASSERT(false,
|
|
"scrOrderGroupObj: Invalid order");
|
|
return false;
|
|
}
|
|
|
|
debug(LOG_NEVER, "group %p (%u) order %d, obj type %d player %d id %d",
|
|
psGroup, psGroup->getNumMembers(), order, psObj->type, psObj->player, psObj->id);
|
|
|
|
psGroup->orderGroup(order, psObj);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Give a droid an order
|
|
bool scrOrderDroid(void)
|
|
{
|
|
DROID *psDroid;
|
|
DROID_ORDER order;
|
|
|
|
if (!stackPopParams(2, ST_DROID, &psDroid, VAL_INT, &order))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psDroid != NULL,
|
|
"scrOrderUnit: Invalid unit pointer");
|
|
if (psDroid == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (order != DORDER_STOP &&
|
|
order != DORDER_RETREAT &&
|
|
order != DORDER_DESTRUCT &&
|
|
order != DORDER_RTR &&
|
|
order != DORDER_RTB &&
|
|
order != DORDER_RUN &&
|
|
order != DORDER_NONE)
|
|
{
|
|
ASSERT(false,
|
|
"scrOrderUnit: Invalid order %d", order);
|
|
return false;
|
|
}
|
|
|
|
orderDroid(psDroid, order, ModeQueue);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Give a Droid an order to a location
|
|
bool scrOrderDroidLoc(void)
|
|
{
|
|
DROID *psDroid;
|
|
DROID_ORDER order;
|
|
SDWORD x, y;
|
|
|
|
if (!stackPopParams(4, ST_DROID, &psDroid, VAL_INT, &order, VAL_INT, &x, VAL_INT, &y))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psDroid != NULL,
|
|
"scrOrderUnitLoc: Invalid unit pointer");
|
|
if (psDroid == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (order != DORDER_MOVE &&
|
|
order != DORDER_SCOUT)
|
|
{
|
|
ASSERT(false,
|
|
"scrOrderUnitLoc: Invalid order");
|
|
return false;
|
|
}
|
|
if (x < 0
|
|
|| x > world_coord(mapWidth)
|
|
|| y < 0
|
|
|| y > world_coord(mapHeight))
|
|
{
|
|
ASSERT(false,
|
|
"scrOrderUnitLoc: Invalid location");
|
|
return false;
|
|
}
|
|
|
|
orderDroidLoc(psDroid, order, x, y, ModeQueue);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Give a Droid an order to an object
|
|
bool scrOrderDroidObj(void)
|
|
{
|
|
DROID *psDroid;
|
|
DROID_ORDER order;
|
|
BASE_OBJECT *psObj;
|
|
|
|
if (!stackPopParams(3, ST_DROID, &psDroid, VAL_INT, &order, ST_BASEOBJECT, &psObj))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psDroid != NULL,
|
|
"scrOrderUnitObj: Invalid unit pointer");
|
|
ASSERT(psObj != NULL,
|
|
"scrOrderUnitObj: Invalid object pointer");
|
|
if (psDroid == NULL || psObj == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (order != DORDER_ATTACK &&
|
|
order != DORDER_HELPBUILD &&
|
|
order != DORDER_DEMOLISH &&
|
|
order != DORDER_REPAIR &&
|
|
order != DORDER_OBSERVE &&
|
|
order != DORDER_EMBARK &&
|
|
order != DORDER_FIRESUPPORT &&
|
|
order != DORDER_DROIDREPAIR)
|
|
{
|
|
ASSERT(false,
|
|
"scrOrderUnitObj: Invalid order");
|
|
return false;
|
|
}
|
|
|
|
orderDroidObj(psDroid, order, psObj, ModeQueue);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Give a Droid an order with a stat
|
|
bool scrOrderDroidStatsLoc(void)
|
|
{
|
|
DROID *psDroid;
|
|
DROID_ORDER order;
|
|
SDWORD x, y, statIndex;
|
|
|
|
if (!stackPopParams(5, ST_DROID, &psDroid, VAL_INT, &order, ST_STRUCTURESTAT, &statIndex,
|
|
VAL_INT, &x, VAL_INT, &y))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (statIndex < 0 || statIndex >= (SDWORD)numStructureStats)
|
|
{
|
|
ASSERT(false, "Invalid structure stat");
|
|
return false;
|
|
}
|
|
|
|
ASSERT_OR_RETURN(false, statIndex < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", statIndex, numStructureStats);
|
|
STRUCTURE_STATS *psStats = asStructureStats + statIndex;
|
|
|
|
ASSERT_OR_RETURN(false, psDroid != NULL, "Invalid Unit pointer");
|
|
ASSERT_OR_RETURN(false, psStats != NULL, "Invalid object pointer");
|
|
|
|
if ((x < 0) || (x > (SDWORD)mapWidth * TILE_UNITS) ||
|
|
(y < 0) || (y > (SDWORD)mapHeight * TILE_UNITS))
|
|
{
|
|
ASSERT(false, "Invalid location");
|
|
return false;
|
|
}
|
|
|
|
if (order != DORDER_BUILD)
|
|
{
|
|
ASSERT(false, "Invalid order");
|
|
return false;
|
|
}
|
|
// HACK: FIXME: Looks like a script error in the player*.slo files
|
|
// buildOnExactLocation() which references previously destroyed buildings from
|
|
// _stat = rebuildStructStat[_count] causes this.
|
|
if (psStats->id.compare("A0ADemolishStructure") == 0)
|
|
{
|
|
// I don't feel like spamming a ASSERT here, we *know* it is a issue.
|
|
return true;
|
|
}
|
|
|
|
orderDroidStatsLocDir(psDroid, order, psStats, x, y, 0, ModeQueue);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// set the secondary state for a droid
|
|
bool scrSetDroidSecondary(void)
|
|
{
|
|
DROID *psDroid;
|
|
SECONDARY_ORDER sec;
|
|
SECONDARY_STATE state;
|
|
|
|
if (!stackPopParams(3, ST_DROID, &psDroid, VAL_INT, &sec, VAL_INT, &state))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psDroid != NULL,
|
|
"scrSetUnitSecondary: invalid unit pointer");
|
|
if (psDroid == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
secondarySetState(psDroid, sec, state);
|
|
|
|
return true;
|
|
}
|
|
|
|
// set the secondary state for a droid
|
|
bool scrSetGroupSecondary(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
SECONDARY_ORDER sec;
|
|
SECONDARY_STATE state;
|
|
|
|
if (!stackPopParams(3, ST_GROUP, &psGroup, VAL_INT, &sec, VAL_INT, &state))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psGroup != NULL,
|
|
"scrSetGroupSecondary: invalid group pointer");
|
|
|
|
psGroup->setSecondary(sec, state);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// add a droid to a commander
|
|
bool scrCmdDroidAddDroid(void)
|
|
{
|
|
DROID *psDroid, *psCommander;
|
|
|
|
if (!stackPopParams(2, ST_DROID, &psCommander, ST_DROID, &psDroid))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
cmdDroidAddDroid(psCommander, psDroid);
|
|
|
|
return true;
|
|
}
|
|
|
|
// returns max number of droids in a commander group
|
|
bool scrCmdDroidMaxGroup(void)
|
|
{
|
|
DROID *psCommander;
|
|
|
|
if (!stackPopParams(1, ST_DROID, &psCommander))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psCommander != NULL,
|
|
"scrCmdDroidMaxGroup: NULL pointer passed");
|
|
|
|
scrFunctionResult.v.ival = cmdDroidMaxGroup(psCommander);
|
|
if (!stackPushResult(VAL_INT, &scrFunctionResult))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// store the prefered and ignore targets for the target functions
|
|
UDWORD scrStructPref, scrStructIgnore;
|
|
UDWORD scrDroidPref, scrDroidIgnore;
|
|
|
|
|
|
// reset the structure preferences
|
|
bool scrResetStructTargets(void)
|
|
{
|
|
scrStructPref = 0;
|
|
scrStructIgnore = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// reset the droid preferences
|
|
bool scrResetDroidTargets(void)
|
|
{
|
|
scrDroidPref = 0;
|
|
scrDroidIgnore = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// set prefered structure target types
|
|
bool scrSetStructTarPref(void)
|
|
{
|
|
UDWORD pref;
|
|
|
|
if (!stackPopParams(1, VAL_INT, &pref))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT((SCR_ST_HQ == pref) ||
|
|
(SCR_ST_FACTORY == pref) ||
|
|
(SCR_ST_POWER_GEN == pref) ||
|
|
(SCR_ST_RESOURCE_EXTRACTOR == pref) ||
|
|
(SCR_ST_WALL == pref) ||
|
|
(SCR_ST_RESEARCH == pref) ||
|
|
(SCR_ST_REPAIR_FACILITY == pref) ||
|
|
(SCR_ST_COMMAND_CONTROL == pref) ||
|
|
(SCR_ST_CYBORG_FACTORY == pref) ||
|
|
(SCR_ST_VTOL_FACTORY == pref) ||
|
|
(SCR_ST_REARM_PAD == pref) ||
|
|
(SCR_ST_SENSOR == pref) ||
|
|
(SCR_ST_DEF_GROUND == pref) ||
|
|
(SCR_ST_DEF_AIR == pref) ||
|
|
(SCR_ST_DEF_IDF == pref) ||
|
|
(SCR_ST_DEF_ALL == pref) ,
|
|
"scrSetStructTarPref: unknown target preference");
|
|
|
|
scrStructPref |= pref;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// set structure target ignore types
|
|
bool scrSetStructTarIgnore(void)
|
|
{
|
|
UDWORD pref;
|
|
|
|
if (!stackPopParams(1, VAL_INT, &pref))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT((SCR_ST_HQ == pref) ||
|
|
(SCR_ST_FACTORY == pref) ||
|
|
(SCR_ST_POWER_GEN == pref) ||
|
|
(SCR_ST_RESOURCE_EXTRACTOR == pref) ||
|
|
(SCR_ST_WALL == pref) ||
|
|
(SCR_ST_RESEARCH == pref) ||
|
|
(SCR_ST_REPAIR_FACILITY == pref) ||
|
|
(SCR_ST_COMMAND_CONTROL == pref) ||
|
|
(SCR_ST_CYBORG_FACTORY == pref) ||
|
|
(SCR_ST_VTOL_FACTORY == pref) ||
|
|
(SCR_ST_REARM_PAD == pref) ||
|
|
(SCR_ST_SENSOR == pref) ||
|
|
(SCR_ST_DEF_GROUND == pref) ||
|
|
(SCR_ST_DEF_AIR == pref) ||
|
|
(SCR_ST_DEF_IDF == pref) ||
|
|
(SCR_ST_DEF_ALL == pref) ,
|
|
"scrSetStructTarIgnore: unknown ignore target");
|
|
|
|
scrStructIgnore |= pref;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// set prefered droid target types
|
|
bool scrSetDroidTarPref(void)
|
|
{
|
|
UDWORD pref;
|
|
|
|
if (!stackPopParams(1, VAL_INT, &pref))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT((SCR_DT_COMMAND == pref) ||
|
|
(SCR_DT_SENSOR == pref) ||
|
|
(SCR_DT_CONSTRUCT == pref) ||
|
|
(SCR_DT_REPAIR == pref) ||
|
|
(SCR_DT_WEAP_GROUND == pref) ||
|
|
(SCR_DT_WEAP_AIR == pref) ||
|
|
(SCR_DT_WEAP_IDF == pref) ||
|
|
(SCR_DT_WEAP_ALL == pref) ||
|
|
(SCR_DT_LIGHT == pref) ||
|
|
(SCR_DT_MEDIUM == pref) ||
|
|
(SCR_DT_HEAVY == pref) ||
|
|
(SCR_DT_SUPER_HEAVY == pref) ||
|
|
(SCR_DT_TRACK == pref) ||
|
|
(SCR_DT_HTRACK == pref) ||
|
|
(SCR_DT_WHEEL == pref) ||
|
|
(SCR_DT_LEGS == pref) ||
|
|
(SCR_DT_GROUND == pref) ||
|
|
(SCR_DT_VTOL == pref) ||
|
|
(SCR_DT_HOVER == pref) ,
|
|
"scrSetUnitTarPref: unknown target preference");
|
|
|
|
|
|
scrDroidPref |= pref;
|
|
|
|
return true;
|
|
}
|
|
|
|
// set droid target ignore types
|
|
bool scrSetDroidTarIgnore(void)
|
|
{
|
|
UDWORD pref;
|
|
|
|
if (!stackPopParams(1, VAL_INT, &pref))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ASSERT((SCR_DT_COMMAND == pref) ||
|
|
(SCR_DT_SENSOR == pref) ||
|
|
(SCR_DT_CONSTRUCT == pref) ||
|
|
(SCR_DT_REPAIR == pref) ||
|
|
(SCR_DT_WEAP_GROUND == pref) ||
|
|
(SCR_DT_WEAP_AIR == pref) ||
|
|
(SCR_DT_WEAP_IDF == pref) ||
|
|
(SCR_DT_WEAP_ALL == pref) ||
|
|
(SCR_DT_LIGHT == pref) ||
|
|
(SCR_DT_MEDIUM == pref) ||
|
|
(SCR_DT_HEAVY == pref) ||
|
|
(SCR_DT_SUPER_HEAVY == pref) ||
|
|
(SCR_DT_TRACK == pref) ||
|
|
(SCR_DT_HTRACK == pref) ||
|
|
(SCR_DT_WHEEL == pref) ||
|
|
(SCR_DT_LEGS == pref) ||
|
|
(SCR_DT_GROUND == pref) ||
|
|
(SCR_DT_VTOL == pref) ||
|
|
(SCR_DT_HOVER == pref) ,
|
|
"scrSetUnitTarIgnore: unknown ignore target");
|
|
|
|
scrDroidIgnore |= pref;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// get the correct type mask for a structure target
|
|
static UDWORD scrStructTargetMask(STRUCTURE *psStruct)
|
|
{
|
|
UDWORD mask = 0;
|
|
STRUCTURE_STATS *psStats;
|
|
WEAPON_STATS *psWStats;
|
|
|
|
psStats = psStruct->pStructureType;
|
|
switch (psStats->type)
|
|
{
|
|
case REF_HQ:
|
|
mask = SCR_ST_HQ;
|
|
break;
|
|
case REF_FACTORY:
|
|
case REF_FACTORY_MODULE:
|
|
mask = SCR_ST_FACTORY;
|
|
break;
|
|
case REF_POWER_GEN:
|
|
case REF_POWER_MODULE:
|
|
mask = SCR_ST_POWER_GEN;
|
|
break;
|
|
case REF_RESOURCE_EXTRACTOR:
|
|
mask = SCR_ST_RESOURCE_EXTRACTOR;
|
|
break;
|
|
case REF_DEFENSE:
|
|
//if (psStats->numWeaps == 0 && psStats->pSensor != NULL)
|
|
if (psStats->psWeapStat[0] == NULL && psStats->pSensor != NULL)
|
|
{
|
|
mask = SCR_ST_SENSOR;
|
|
}
|
|
//else if (psStats->numWeaps > 0)
|
|
else if (psStats->psWeapStat[0] != NULL)
|
|
{
|
|
psWStats = psStats->psWeapStat[0];
|
|
if (!proj_Direct(psWStats))
|
|
{
|
|
mask = SCR_ST_DEF_IDF;
|
|
}
|
|
else if (psWStats->surfaceToAir & SHOOT_IN_AIR)
|
|
{
|
|
mask = SCR_ST_DEF_AIR;
|
|
}
|
|
else
|
|
{
|
|
mask = SCR_ST_DEF_GROUND;
|
|
}
|
|
}
|
|
break;
|
|
case REF_WALL:
|
|
case REF_WALLCORNER:
|
|
mask = 0; // ignore walls for now
|
|
break;
|
|
case REF_RESEARCH:
|
|
case REF_RESEARCH_MODULE:
|
|
mask = SCR_ST_RESEARCH;
|
|
break;
|
|
case REF_REPAIR_FACILITY:
|
|
mask = SCR_ST_REPAIR_FACILITY;
|
|
break;
|
|
case REF_COMMAND_CONTROL:
|
|
mask = SCR_ST_COMMAND_CONTROL;
|
|
break;
|
|
case REF_CYBORG_FACTORY:
|
|
mask = SCR_ST_CYBORG_FACTORY;
|
|
break;
|
|
case REF_VTOL_FACTORY:
|
|
mask = SCR_ST_VTOL_FACTORY;
|
|
break;
|
|
case REF_REARM_PAD:
|
|
mask = SCR_ST_REARM_PAD;
|
|
break;
|
|
case REF_MISSILE_SILO:
|
|
//don't want the assert!
|
|
mask = 0;
|
|
break;
|
|
case REF_LAB:
|
|
case REF_BRIDGE:
|
|
case REF_DEMOLISH:
|
|
case REF_GENERIC:
|
|
case REF_GATE:
|
|
default:
|
|
ASSERT(false,
|
|
"scrStructTargetMask: unknown or invalid target structure type");
|
|
mask = 0;
|
|
break;
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
|
|
|
|
// prioritise structure targets
|
|
static void scrStructTargetPriority(STRUCTURE **ppsTarget, STRUCTURE *psCurr)
|
|
{
|
|
// skip walls unless they are explicitly asked for
|
|
if ((scrStructPref & SCR_ST_WALL) ||
|
|
((psCurr->pStructureType->type != REF_WALL) &&
|
|
(psCurr->pStructureType->type != REF_WALLCORNER)))
|
|
{
|
|
*ppsTarget = psCurr;
|
|
}
|
|
}
|
|
|
|
|
|
static UDWORD scrDroidTargetMask(DROID *psDroid)
|
|
{
|
|
UDWORD mask = 0;
|
|
WEAPON_STATS *psWStats;
|
|
BODY_STATS *psBStats;
|
|
PROPULSION_STATS *psPStats;
|
|
|
|
// get the turret type
|
|
switch (psDroid->droidType)
|
|
{
|
|
case DROID_PERSON:
|
|
case DROID_CYBORG:
|
|
case DROID_CYBORG_SUPER:
|
|
case DROID_WEAPON:
|
|
//if (psDroid->numWeaps > 0)
|
|
if (psDroid->asWeaps[0].nStat > 0)
|
|
{
|
|
psWStats = asWeaponStats + psDroid->asWeaps[0].nStat;
|
|
if (!proj_Direct(psWStats))
|
|
{
|
|
mask |= SCR_DT_WEAP_IDF;
|
|
}
|
|
else if (psWStats->surfaceToAir & SHOOT_IN_AIR)
|
|
{
|
|
mask |= SCR_DT_WEAP_AIR;
|
|
}
|
|
else
|
|
{
|
|
mask |= SCR_DT_WEAP_GROUND;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mask |= SCR_DT_SENSOR;
|
|
}
|
|
break;
|
|
case DROID_SENSOR:
|
|
case DROID_ECM:
|
|
mask |= SCR_DT_SENSOR;
|
|
break;
|
|
case DROID_CONSTRUCT:
|
|
case DROID_CYBORG_CONSTRUCT:
|
|
mask |= SCR_DT_CONSTRUCT;
|
|
break;
|
|
case DROID_COMMAND:
|
|
mask |= SCR_DT_COMMAND;
|
|
break;
|
|
case DROID_REPAIR:
|
|
case DROID_CYBORG_REPAIR:
|
|
mask |= SCR_DT_REPAIR;
|
|
break;
|
|
case DROID_TRANSPORTER:
|
|
case DROID_SUPERTRANSPORTER:
|
|
break;
|
|
case DROID_DEFAULT:
|
|
case DROID_ANY:
|
|
default:
|
|
ASSERT(false,
|
|
"scrUnitTargetMask: unknown or invalid target unit type");
|
|
break;
|
|
}
|
|
|
|
// get the body type
|
|
psBStats = asBodyStats + psDroid->asBits[COMP_BODY];
|
|
switch (psBStats->size)
|
|
{
|
|
case SIZE_LIGHT:
|
|
mask |= SCR_DT_LIGHT;
|
|
break;
|
|
case SIZE_MEDIUM:
|
|
mask |= SCR_DT_MEDIUM;
|
|
break;
|
|
case SIZE_HEAVY:
|
|
mask |= SCR_DT_HEAVY;
|
|
break;
|
|
case SIZE_SUPER_HEAVY:
|
|
mask |= SCR_DT_SUPER_HEAVY;
|
|
break;
|
|
default:
|
|
ASSERT(false,
|
|
"scrUnitTargetMask: unknown or invalid target body size");
|
|
break;
|
|
}
|
|
|
|
// get the propulsion type
|
|
psPStats = asPropulsionStats + psDroid->asBits[COMP_PROPULSION];
|
|
switch (psPStats->propulsionType)
|
|
{
|
|
case PROPULSION_TYPE_WHEELED:
|
|
mask |= SCR_DT_WHEEL;
|
|
break;
|
|
case PROPULSION_TYPE_TRACKED:
|
|
mask |= SCR_DT_TRACK;
|
|
break;
|
|
case PROPULSION_TYPE_LEGGED:
|
|
mask |= SCR_DT_LEGS;
|
|
break;
|
|
case PROPULSION_TYPE_HOVER:
|
|
mask |= SCR_DT_HOVER;
|
|
break;
|
|
case PROPULSION_TYPE_LIFT:
|
|
mask |= SCR_DT_VTOL;
|
|
break;
|
|
case PROPULSION_TYPE_HALF_TRACKED:
|
|
mask |= SCR_DT_HTRACK;
|
|
break;
|
|
case PROPULSION_TYPE_PROPELLOR:
|
|
mask |= SCR_DT_PROPELLOR;
|
|
break;
|
|
default:
|
|
ASSERT(false,
|
|
"scrUnitTargetMask: unknown or invalid target unit propulsion type");
|
|
break;
|
|
}
|
|
|
|
|
|
return mask;
|
|
}
|
|
|
|
// prioritise droid targets
|
|
static void scrDroidTargetPriority(DROID **ppsTarget, DROID *psCurr)
|
|
{
|
|
// priority to things with weapons
|
|
if (((*ppsTarget) == NULL) ||
|
|
((*ppsTarget)->asWeaps[0].nStat == 0))
|
|
{
|
|
*ppsTarget = psCurr;
|
|
}
|
|
}
|
|
|
|
|
|
// what type of object for scrTargetInArea
|
|
enum
|
|
{
|
|
SCR_TAR_STRUCT,
|
|
SCR_TAR_DROID,
|
|
};
|
|
|
|
// get target mask function for scrTargetInArea
|
|
typedef UDWORD(*TARGET_MASK)(BASE_OBJECT *);
|
|
|
|
// see if one target is preferable to another
|
|
typedef void (*TARGET_PREF)(BASE_OBJECT **, BASE_OBJECT *);
|
|
|
|
// generic find a target in an area given preference data
|
|
static BASE_OBJECT *scrTargetInArea(SDWORD tarPlayer, SDWORD visPlayer, SDWORD tarType, SDWORD cluster,
|
|
SDWORD x1, SDWORD y1, SDWORD x2, SDWORD y2)
|
|
{
|
|
BASE_OBJECT *psTarget, *psCurr;
|
|
SDWORD temp;
|
|
UDWORD tarMask;
|
|
TARGET_MASK getTargetMask;
|
|
TARGET_PREF targetPriority;
|
|
UDWORD prefer, ignore;
|
|
|
|
if (tarPlayer < 0 || tarPlayer >= MAX_PLAYERS)
|
|
{
|
|
ASSERT(false,
|
|
"scrTargetInArea: invalid target player number");
|
|
return NULL;
|
|
}
|
|
|
|
if (x1 > x2)
|
|
{
|
|
temp = x2;
|
|
x2 = x1;
|
|
x1 = temp;
|
|
}
|
|
if (y1 > y2)
|
|
{
|
|
temp = y2;
|
|
y2 = y1;
|
|
y1 = temp;
|
|
}
|
|
|
|
// see which target type to use
|
|
switch (tarType)
|
|
{
|
|
case SCR_TAR_STRUCT:
|
|
getTargetMask = (TARGET_MASK)scrStructTargetMask;
|
|
targetPriority = (TARGET_PREF)scrStructTargetPriority;
|
|
prefer = scrStructPref;
|
|
ignore = scrStructIgnore;
|
|
psCurr = apsStructLists[tarPlayer];
|
|
break;
|
|
case SCR_TAR_DROID:
|
|
getTargetMask = (TARGET_MASK)scrDroidTargetMask;
|
|
targetPriority = (TARGET_PREF)scrDroidTargetPriority;
|
|
prefer = scrDroidPref;
|
|
ignore = scrDroidIgnore;
|
|
psCurr = apsDroidLists[tarPlayer];
|
|
break;
|
|
default:
|
|
ASSERT(false, "scrTargetInArea: invalid target type");
|
|
return NULL;
|
|
}
|
|
|
|
psTarget = NULL;
|
|
for (; psCurr; psCurr = psCurr->psNext)
|
|
{
|
|
if ((cluster == 0 || psCurr->cluster == cluster) &&
|
|
((SDWORD)psCurr->pos.x >= x1) &&
|
|
((SDWORD)psCurr->pos.x <= x2) &&
|
|
((SDWORD)psCurr->pos.y >= y1) &&
|
|
((SDWORD)psCurr->pos.y <= y2))
|
|
{
|
|
tarMask = getTargetMask(psCurr);
|
|
if (tarMask & ignore)
|
|
{
|
|
// skip any that match ignore
|
|
continue;
|
|
}
|
|
else if (tarMask & prefer)
|
|
{
|
|
// got a prefered target - go with that
|
|
psTarget = psCurr;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// see if the current target has priority over the previous one
|
|
targetPriority(&psTarget, psCurr);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return psTarget;
|
|
}
|
|
|
|
// get a structure target in an area using the preferences
|
|
bool scrStructTargetInArea(void)
|
|
{
|
|
SDWORD x1, y1, x2, y2;
|
|
SDWORD tarPlayer, visPlayer;
|
|
|
|
if (!stackPopParams(6, VAL_INT, &tarPlayer, VAL_INT, &visPlayer,
|
|
VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
scrFunctionResult.v.oval = (STRUCTURE *)scrTargetInArea(tarPlayer, visPlayer, SCR_TAR_STRUCT, 0, x1, y1, x2, y2);
|
|
|
|
if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// get a structure target on the map using the preferences
|
|
bool scrStructTargetOnMap(void)
|
|
{
|
|
SDWORD tarPlayer, visPlayer;
|
|
STRUCTURE *psTarget;
|
|
|
|
if (!stackPopParams(2, VAL_INT, &tarPlayer, VAL_INT, &visPlayer))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psTarget = (STRUCTURE *)scrTargetInArea(tarPlayer, visPlayer, SCR_TAR_STRUCT, 0,
|
|
scrollMinX * TILE_UNITS, scrollMinY * TILE_UNITS,
|
|
scrollMaxX * TILE_UNITS, scrollMaxY * TILE_UNITS);
|
|
|
|
scrFunctionResult.v.oval = psTarget;
|
|
if (!stackPushResult((INTERP_TYPE)ST_STRUCTURE, &scrFunctionResult))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// get a droid target in an area using the preferences
|
|
bool scrDroidTargetInArea(void)
|
|
{
|
|
SDWORD x1, y1, x2, y2;
|
|
SDWORD tarPlayer, visPlayer;
|
|
DROID *psTarget;
|
|
|
|
if (!stackPopParams(6, VAL_INT, &tarPlayer, VAL_INT, &visPlayer,
|
|
VAL_INT, &x1, VAL_INT, &y1, VAL_INT, &x2, VAL_INT, &y2))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psTarget = (DROID *)scrTargetInArea(tarPlayer, visPlayer, SCR_TAR_DROID, 0, x1, y1, x2, y2);
|
|
|
|
scrFunctionResult.v.oval = psTarget;
|
|
if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// get a droid target on the map using the preferences
|
|
bool scrDroidTargetOnMap(void)
|
|
{
|
|
SDWORD tarPlayer, visPlayer;
|
|
DROID *psTarget;
|
|
|
|
if (!stackPopParams(2, VAL_INT, &tarPlayer, VAL_INT, &visPlayer))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psTarget = (DROID *)scrTargetInArea(tarPlayer, visPlayer, SCR_TAR_DROID, 0,
|
|
scrollMinX * TILE_UNITS, scrollMinY * TILE_UNITS,
|
|
scrollMaxX * TILE_UNITS, scrollMaxY * TILE_UNITS);
|
|
|
|
scrFunctionResult.v.oval = psTarget;
|
|
if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// get a target from a cluster using the preferences
|
|
bool scrTargetInCluster(void)
|
|
{
|
|
SDWORD tarPlayer, tarType, visPlayer, clusterID, cluster;
|
|
BASE_OBJECT *psTarget;
|
|
|
|
if (!stackPopParams(2, VAL_INT, &clusterID, VAL_INT, &visPlayer))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (clusterID < 0 || clusterID >= CLUSTER_MAX)
|
|
{
|
|
ASSERT(false, "scrTargetInCluster: invalid clusterID");
|
|
return false;
|
|
}
|
|
|
|
cluster = aClusterMap[clusterID];
|
|
tarPlayer = aClusterInfo[cluster] & CLUSTER_PLAYER_MASK;
|
|
tarType = (aClusterInfo[cluster] & CLUSTER_DROID) ? SCR_TAR_DROID : SCR_TAR_STRUCT;
|
|
|
|
psTarget = scrTargetInArea(tarPlayer, visPlayer, tarType, cluster,
|
|
scrollMinX * TILE_UNITS, scrollMinY * TILE_UNITS,
|
|
scrollMaxX * TILE_UNITS, scrollMaxY * TILE_UNITS);
|
|
|
|
scrFunctionResult.v.oval = psTarget;
|
|
if (!stackPushResult((INTERP_TYPE)ST_BASEOBJECT, &scrFunctionResult))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// ********************************************************************************************
|
|
// Additional Skirmish Code for superduper skirmish games may99
|
|
// ********************************************************************************************
|
|
// ********************************************************************************************
|
|
|
|
bool scrSkCanBuildTemplate(void)
|
|
{
|
|
STRUCTURE *psStructure;
|
|
DROID_TEMPLATE *psTempl;
|
|
|
|
SDWORD player;
|
|
|
|
if (!stackPopParams(3, VAL_INT, &player, ST_STRUCTURE, &psStructure, ST_TEMPLATE, &psTempl))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// is factory big enough?
|
|
if (!validTemplateForFactory(psTempl, psStructure, false))
|
|
{
|
|
goto failTempl;
|
|
}
|
|
|
|
if ((asBodyStats + psTempl->asParts[COMP_BODY])->size > psStructure->capacity)
|
|
{
|
|
goto failTempl;
|
|
}
|
|
|
|
// is every component from template available?
|
|
// body available.
|
|
if (apCompLists[player][COMP_BODY][psTempl->asParts[COMP_BODY]] != AVAILABLE)
|
|
{
|
|
goto failTempl;
|
|
}
|
|
|
|
// propulsion method available.
|
|
if (apCompLists[player][COMP_PROPULSION][psTempl->asParts[COMP_PROPULSION]] != AVAILABLE)
|
|
{
|
|
goto failTempl;
|
|
}
|
|
|
|
// weapon/sensor
|
|
|
|
switch (droidTemplateType(psTempl))
|
|
{
|
|
case DROID_CYBORG: // cyborg-type thang.. no need to check weapon.
|
|
case DROID_CYBORG_SUPER: // super cyborg-type thang
|
|
break;
|
|
case DROID_WEAPON:
|
|
if (apCompLists[player][COMP_WEAPON][ psTempl->asWeaps[0] ] != AVAILABLE)
|
|
{
|
|
goto failTempl;
|
|
}
|
|
break;
|
|
case DROID_SENSOR:
|
|
if (apCompLists[player][COMP_SENSOR][psTempl->asParts[COMP_SENSOR]] != AVAILABLE)
|
|
{
|
|
goto failTempl;
|
|
}
|
|
break;
|
|
case DROID_ECM:
|
|
if (apCompLists[player][COMP_ECM][psTempl->asParts[COMP_ECM]] != AVAILABLE)
|
|
{
|
|
goto failTempl;
|
|
}
|
|
break;
|
|
case DROID_REPAIR:
|
|
if (apCompLists[player][COMP_REPAIRUNIT][psTempl->asParts[COMP_REPAIRUNIT]] != AVAILABLE)
|
|
{
|
|
goto failTempl;
|
|
}
|
|
break;
|
|
case DROID_CYBORG_REPAIR:
|
|
if (apCompLists[player][COMP_REPAIRUNIT][psTempl->asParts[COMP_REPAIRUNIT]] != AVAILABLE)
|
|
{
|
|
goto failTempl;
|
|
}
|
|
break;
|
|
case DROID_COMMAND:
|
|
if (apCompLists[player][COMP_BRAIN][psTempl->asParts[COMP_BRAIN]] != AVAILABLE)
|
|
{
|
|
goto failTempl;
|
|
}
|
|
break;
|
|
case DROID_CONSTRUCT:
|
|
if (apCompLists[player][COMP_CONSTRUCT][psTempl->asParts[COMP_CONSTRUCT]] != AVAILABLE)
|
|
{
|
|
goto failTempl;
|
|
}
|
|
break;
|
|
case DROID_CYBORG_CONSTRUCT:
|
|
if (apCompLists[player][COMP_CONSTRUCT][psTempl->asParts[COMP_CONSTRUCT]] != AVAILABLE)
|
|
{
|
|
goto failTempl;
|
|
}
|
|
break;
|
|
|
|
case DROID_PERSON: // person
|
|
case DROID_TRANSPORTER: // guess what this is!
|
|
case DROID_SUPERTRANSPORTER:
|
|
case DROID_DEFAULT: // Default droid
|
|
case DROID_ANY:
|
|
default:
|
|
debug(LOG_FATAL, "scrSkCanBuildTemplate: Unhandled template type");
|
|
abort();
|
|
break;
|
|
}
|
|
|
|
scrFunctionResult.v.bval = true;
|
|
if (!stackPushResult(VAL_BOOL, &scrFunctionResult)) // yes
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
|
|
failTempl:
|
|
scrFunctionResult.v.bval = false;
|
|
if (!stackPushResult(VAL_BOOL, &scrFunctionResult)) // no
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// locate the enemy
|
|
// gives a target location given a player to attack.
|
|
bool scrSkLocateEnemy(void)
|
|
{
|
|
SDWORD player;//,*x,*y;
|
|
STRUCTURE *psStruct;
|
|
|
|
if (!stackPopParams(1, VAL_INT, &player))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// find where the player has some structures.. // factories or hq.
|
|
for (psStruct = apsStructLists[player];
|
|
psStruct &&
|
|
!(psStruct->pStructureType->type == REF_HQ
|
|
|| psStruct->pStructureType->type == REF_FACTORY
|
|
|| psStruct->pStructureType->type == REF_CYBORG_FACTORY
|
|
|| psStruct->pStructureType->type == REF_VTOL_FACTORY
|
|
);
|
|
psStruct = psStruct->psNext) {}
|
|
|
|
// set the x and y accordingly..
|
|
if (psStruct)
|
|
{
|
|
scrFunctionResult.v.oval = psStruct;
|
|
if (!stackPushResult((INTERP_TYPE)ST_BASEOBJECT, &scrFunctionResult)) // success!
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
scrFunctionResult.v.oval = NULL;
|
|
if (!stackPushResult((INTERP_TYPE)ST_BASEOBJECT, &scrFunctionResult)) // part success
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
|
|
bool skTopicAvail(UWORD inc, UDWORD player)
|
|
{
|
|
UDWORD incPR, incS;
|
|
bool bPRFound, bStructFound;
|
|
|
|
|
|
//if the topic is possible and has not already been researched - add to list
|
|
if ((IsResearchPossible(&asPlayerResList[player][inc])))
|
|
{
|
|
if (!IsResearchCompleted(&asPlayerResList[player][inc])
|
|
&& !IsResearchStartedPending(&asPlayerResList[player][inc]))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// make sure that the research is not completed or started by another researchfac
|
|
if (!IsResearchCompleted(&asPlayerResList[player][inc])
|
|
&& !IsResearchStartedPending(&asPlayerResList[player][inc]))
|
|
{
|
|
// Research is not completed ... also it has not been started by another researchfac
|
|
|
|
//if there aren't any PR's - go to next topic
|
|
if (asResearch[inc].pPRList.empty())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//check for pre-requisites
|
|
bPRFound = true;
|
|
for (incPR = 0; incPR < asResearch[inc].pPRList.size(); incPR++)
|
|
{
|
|
if (IsResearchCompleted(&asPlayerResList[player][asResearch[inc].pPRList[incPR]]) == 0)
|
|
{
|
|
//if haven't pre-requisite - quit checking rest
|
|
bPRFound = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!bPRFound)
|
|
{
|
|
//if haven't pre-requisites, skip the rest of the checks
|
|
return false;
|
|
}
|
|
|
|
//check for structure effects
|
|
bStructFound = true;
|
|
for (incS = 0; incS < asResearch[inc].pStructList.size(); incS++)
|
|
{
|
|
//if (!checkStructureStatus(asStructureStats + asResearch[inc].
|
|
// pStructList[incS], playerID, SS_BUILT))
|
|
if (!checkSpecificStructExists(asResearch[inc].pStructList[incS],
|
|
player))
|
|
{
|
|
//if not built, quit checking
|
|
bStructFound = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!bStructFound)
|
|
{
|
|
//if haven't all structs built, skip to next topic
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
bool scrSkDoResearch(void)
|
|
{
|
|
SDWORD player, bias;
|
|
UWORD i;
|
|
STRUCTURE *psBuilding;
|
|
RESEARCH_FACILITY *psResFacilty;
|
|
|
|
if (!stackPopParams(3, ST_STRUCTURE, &psBuilding, VAL_INT, &player, VAL_INT, &bias))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
psResFacilty = (RESEARCH_FACILITY *)psBuilding->pFunctionality;
|
|
|
|
if (psResFacilty->psSubject != NULL)
|
|
{
|
|
// not finshed yet..
|
|
return true;
|
|
}
|
|
|
|
// choose a topic to complete.
|
|
for (i = 0; i < asResearch.size(); i++)
|
|
{
|
|
if (skTopicAvail(i, player) && (!bMultiPlayer || !beingResearchedByAlly(i, player)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i != asResearch.size())
|
|
{
|
|
sendResearchStatus(psBuilding, i, player, true); // inform others, I'm researching this.
|
|
#if defined (DEBUG)
|
|
{
|
|
char sTemp[128];
|
|
sprintf(sTemp, "[debug]player:%d starts topic: %s", player, getName(&asResearch[i]));
|
|
NETlogEntry(sTemp, SYNC_FLAG, 0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
bool scrSkVtolEnableCheck(void)
|
|
{
|
|
SDWORD player;
|
|
UDWORD i;
|
|
|
|
if (!stackPopParams(1, VAL_INT, &player))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
// vtol factory
|
|
for (i = 0; (i < numStructureStats) && (asStructureStats[i].type != REF_VTOL_FACTORY); i++) {}
|
|
if ((i < numStructureStats) && (apStructTypeLists[player][i] == AVAILABLE))
|
|
{
|
|
// vtol propulsion
|
|
for (i = 0; (i < numPropulsionStats); i++)
|
|
{
|
|
if ((asPropulsionStats[i].propulsionType == PROPULSION_TYPE_LIFT)
|
|
&& apCompLists[player][COMP_PROPULSION][i] == AVAILABLE)
|
|
{
|
|
scrFunctionResult.v.bval = true;
|
|
if (!stackPushResult(VAL_BOOL, &scrFunctionResult)) // success!
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
scrFunctionResult.v.bval = false;
|
|
if (!stackPushResult(VAL_BOOL, &scrFunctionResult)) // success!
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
bool scrSkGetFactoryCapacity(void)
|
|
{
|
|
SDWORD count = 0;
|
|
STRUCTURE *psStructure;
|
|
|
|
if (!stackPopParams(1, ST_STRUCTURE, &psStructure))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (psStructure && StructIsFactory(psStructure))
|
|
{
|
|
count = psStructure->capacity;
|
|
}
|
|
|
|
scrFunctionResult.v.ival = count;
|
|
if (!stackPushResult(VAL_INT, &scrFunctionResult))
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
// ********************************************************************************************
|
|
bool scrSkDifficultyModifier(void)
|
|
{
|
|
int player;
|
|
RESEARCH_FACILITY *psResFacility;
|
|
STRUCTURE *psStr;
|
|
PLAYER_RESEARCH *pPlayerRes;
|
|
|
|
if (!stackPopParams(1, VAL_INT, &player))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/* Skip cheats if difficulty modifier slider is set to minimum or this is a true multiplayer game.
|
|
* (0 - player disabled, 20 - max value, UBYTE_MAX - autogame)
|
|
*/
|
|
if (game.skDiff[player] <= 1 || game.skDiff[player] == UBYTE_MAX || NetPlay.bComms)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// power modifier, range: 0-1000
|
|
giftPower(ANYPLAYER, player, game.skDiff[player] * 50, true);
|
|
|
|
//research modifier
|
|
for (psStr = apsStructLists[player]; psStr; psStr = psStr->psNext)
|
|
{
|
|
if (psStr->pStructureType->type == REF_RESEARCH)
|
|
{
|
|
psResFacility = (RESEARCH_FACILITY *)psStr->pFunctionality;
|
|
|
|
// subtract 0 - 80% off the time to research.
|
|
if (psResFacility->psSubject)
|
|
{
|
|
RESEARCH *psResearch = (RESEARCH *)psResFacility->psSubject;
|
|
|
|
pPlayerRes = &asPlayerResList[player][psResearch->ref - REF_RESEARCH_START];
|
|
pPlayerRes->currentPoints += (psResearch->researchPoints * 4 * game.skDiff[player]) / 100;
|
|
syncDebug("AI %d is cheating with research time.", player);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
|
|
// not a direct script function but a helper for scrSkDefenseLocation and scrSkDefenseLocationB
|
|
static bool defenseLocation(bool variantB)
|
|
{
|
|
SDWORD *pX, *pY, statIndex, statIndex2;
|
|
UDWORD x, y, gX, gY, dist, player, nearestSoFar, count;
|
|
GATEWAY *psGate, *psChosenGate;
|
|
DROID *psDroid;
|
|
UDWORD x1, x2, x3, x4, y1, y2, y3, y4;
|
|
bool noWater;
|
|
UDWORD minCount;
|
|
UDWORD offset;
|
|
|
|
if (!stackPopParams(6,
|
|
VAL_REF | VAL_INT, &pX,
|
|
VAL_REF | VAL_INT, &pY,
|
|
ST_STRUCTURESTAT, &statIndex,
|
|
ST_STRUCTURESTAT, &statIndex2,
|
|
ST_DROID, &psDroid,
|
|
VAL_INT, &player))
|
|
{
|
|
debug(LOG_ERROR, "defenseLocation: failed to pop");
|
|
return false;
|
|
}
|
|
|
|
if (player >= MAX_PLAYERS)
|
|
{
|
|
ASSERT(false, "defenseLocation:player number is too high");
|
|
return false;
|
|
}
|
|
|
|
ASSERT_OR_RETURN(false, statIndex < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", statIndex, numStructureStats);
|
|
|
|
ASSERT_OR_RETURN(false, statIndex2 < numStructureStats, "Invalid range referenced for numStructureStats, %d > %d", statIndex2, numStructureStats);
|
|
STRUCTURE_STATS *psWStats = (asStructureStats + statIndex2);
|
|
|
|
// check for wacky coords.
|
|
if (*pX < 0
|
|
|| *pX > world_coord(mapWidth)
|
|
|| *pY < 0
|
|
|| *pY > world_coord(mapHeight)
|
|
)
|
|
{
|
|
goto failed;
|
|
}
|
|
|
|
x = map_coord(*pX); // change to tile coords.
|
|
y = map_coord(*pY);
|
|
|
|
// go down the gateways, find the nearest gateway with >1 empty tiles
|
|
nearestSoFar = UDWORD_MAX;
|
|
psChosenGate = NULL;
|
|
for (psGate = gwGetGateways(); psGate; psGate = psGate->psNext)
|
|
{
|
|
if (auxTile(psGate->x1, psGate->y1, player) & AUXBITS_THREAT)
|
|
{
|
|
continue; // enemy can shoot there, not safe to build
|
|
}
|
|
count = 0;
|
|
noWater = true;
|
|
// does it have >1 tile unoccupied.
|
|
if (psGate->x1 == psGate->x2)
|
|
{
|
|
// vert
|
|
//skip gates that are too short
|
|
if (variantB && (psGate->y2 - psGate->y1) <= 2)
|
|
{
|
|
continue;
|
|
}
|
|
gX = psGate->x1;
|
|
for (gY = psGate->y1; gY <= psGate->y2; gY++)
|
|
{
|
|
if (! TileIsOccupied(mapTile(gX, gY)))
|
|
{
|
|
count++;
|
|
}
|
|
if (terrainType(mapTile(gX, gY)) == TER_WATER)
|
|
{
|
|
noWater = false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// horiz
|
|
//skip gates that are too short
|
|
if (variantB && (psGate->x2 - psGate->x1) <= 2)
|
|
{
|
|
continue;
|
|
}
|
|
gY = psGate->y1;
|
|
for (gX = psGate->x1; gX <= psGate->x2; gX++)
|
|
{
|
|
if (! TileIsOccupied(mapTile(gX, gY)))
|
|
{
|
|
count++;
|
|
}
|
|
if (terrainType(mapTile(gX, gY)) == TER_WATER)
|
|
{
|
|
noWater = false;
|
|
}
|
|
}
|
|
}
|
|
if (variantB)
|
|
{
|
|
minCount = 2;
|
|
}
|
|
else
|
|
{
|
|
minCount = 1;
|
|
}
|
|
if (count > minCount && noWater) //<NEW> min 2 tiles
|
|
{
|
|
// ok it's free. Is it the nearest one yet?
|
|
/* Get gateway midpoint */
|
|
gX = (psGate->x1 + psGate->x2) / 2;
|
|
gY = (psGate->y1 + psGate->y2) / 2;
|
|
/* Estimate the distance to it */
|
|
dist = iHypot(x - gX, y - gY);
|
|
/* Is it best we've found? */
|
|
if (dist < nearestSoFar && dist < 30)
|
|
{
|
|
/* Yes, then keep a record of it */
|
|
nearestSoFar = dist;
|
|
psChosenGate = psGate;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!psChosenGate) // we have a gateway.
|
|
{
|
|
goto failed;
|
|
}
|
|
|
|
// find an unnocupied tile on that gateway.
|
|
if (psChosenGate->x1 == psChosenGate->x2)
|
|
{
|
|
// vert
|
|
gX = psChosenGate->x1;
|
|
for (gY = psChosenGate->y1; gY <= psChosenGate->y2; gY++)
|
|
{
|
|
if (! TileIsOccupied(mapTile(gX, gY)))
|
|
{
|
|
y = gY;
|
|
x = gX;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// horiz
|
|
gY = psChosenGate->y1;
|
|
for (gX = psChosenGate->x1; gX <= psChosenGate->x2; gX++)
|
|
{
|
|
if (! TileIsOccupied(mapTile(gX, gY)))
|
|
{
|
|
y = gY;
|
|
x = gX;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// back to world coords and store result.
|
|
*pX = world_coord(x) + (TILE_UNITS / 2); // return centre of tile.
|
|
*pY = world_coord(y) + (TILE_UNITS / 2);
|
|
|
|
scrFunctionResult.v.bval = true;
|
|
if (!stackPushResult(VAL_BOOL, &scrFunctionResult)) // success
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
// order the droid to build two walls, one either side of the gateway.
|
|
// or one in the case of a 2 size gateway.
|
|
|
|
//find center of the gateway
|
|
x = (psChosenGate->x1 + psChosenGate->x2) / 2;
|
|
y = (psChosenGate->y1 + psChosenGate->y2) / 2;
|
|
|
|
//find start pos of the gateway
|
|
x1 = world_coord(psChosenGate->x1) + (TILE_UNITS / 2);
|
|
y1 = world_coord(psChosenGate->y1) + (TILE_UNITS / 2);
|
|
|
|
if (variantB)
|
|
{
|
|
offset = 2;
|
|
}
|
|
else
|
|
{
|
|
offset = 1;
|
|
}
|
|
if (psChosenGate->x1 == psChosenGate->x2) //vert
|
|
{
|
|
x2 = x1; //vert: end x pos of the first section = start x pos
|
|
y2 = world_coord(y - 1) + TILE_UNITS / 2; //start y loc of the first sec
|
|
x3 = x1;
|
|
y3 = world_coord(y + offset) + TILE_UNITS / 2;
|
|
}
|
|
else //hor
|
|
{
|
|
x2 = world_coord(x - 1) + TILE_UNITS / 2;
|
|
y2 = y1;
|
|
x3 = world_coord(x + offset) + TILE_UNITS / 2;
|
|
y3 = y1;
|
|
|
|
}
|
|
//end coords of the second section
|
|
x4 = world_coord(psChosenGate->x2) + TILE_UNITS / 2;
|
|
y4 = world_coord(psChosenGate->y2) + TILE_UNITS / 2;
|
|
|
|
// first section.
|
|
if (x1 == x2 && y1 == y2) //first sec is 1 tile only: ((2 tile gate) or (3 tile gate and first sec))
|
|
{
|
|
orderDroidStatsLocDir(psDroid, DORDER_BUILD, psWStats, x1, y1, 0, ModeQueue);
|
|
}
|
|
else
|
|
{
|
|
orderDroidStatsTwoLocDir(psDroid, DORDER_LINEBUILD, psWStats, x1, y1, x2, y2, 0, ModeQueue);
|
|
}
|
|
|
|
// second section
|
|
if (x3 == x4 && y3 == y4)
|
|
{
|
|
orderDroidStatsLocDirAdd(psDroid, DORDER_BUILD, psWStats, x3, y3, 0);
|
|
}
|
|
else
|
|
{
|
|
orderDroidStatsTwoLocDirAdd(psDroid, DORDER_LINEBUILD, psWStats, x3, y3, x4, y4, 0);
|
|
}
|
|
|
|
return true;
|
|
|
|
failed:
|
|
scrFunctionResult.v.bval = false;
|
|
if (!stackPushResult(VAL_BOOL, &scrFunctionResult)) // failed!
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// return a good place to build a defence, given a starting point
|
|
bool scrSkDefenseLocation(void)
|
|
{
|
|
return defenseLocation(false);
|
|
}
|
|
|
|
// return a good place to build a defence with a min number of clear tiles
|
|
bool scrSkDefenseLocationB(void)
|
|
{
|
|
return defenseLocation(true);
|
|
}
|
|
|
|
|
|
bool scrSkFireLassat(void)
|
|
{
|
|
SDWORD player;
|
|
BASE_OBJECT *psObj;
|
|
|
|
if (!stackPopParams(2, VAL_INT, &player, ST_BASEOBJECT, &psObj))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (psObj)
|
|
{
|
|
orderStructureObj(player, psObj);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------
|
|
// New functions
|
|
//-----------------------
|
|
bool scrActionDroidObj(void)
|
|
{
|
|
DROID *psDroid;
|
|
DROID_ACTION action;
|
|
BASE_OBJECT *psObj;
|
|
|
|
if (!stackPopParams(3, ST_DROID, &psDroid, VAL_INT, &action, ST_BASEOBJECT, &psObj))
|
|
{
|
|
debug(LOG_ERROR, "scrActionDroidObj: failed to pop");
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psDroid != NULL,
|
|
"scrOrderUnitObj: Invalid unit pointer");
|
|
ASSERT(psObj != NULL,
|
|
"scrOrderUnitObj: Invalid object pointer");
|
|
|
|
if (psDroid == NULL || psObj == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (action != DACTION_DROIDREPAIR)
|
|
{
|
|
debug(LOG_ERROR, "scrActionDroidObj: this action is not supported");
|
|
return false;
|
|
}
|
|
|
|
syncDebug("TODO: Synchronise this!");
|
|
actionDroid(psDroid, action, psObj);
|
|
|
|
return true;
|
|
}
|
|
|
|
//<script function - improved version
|
|
// variables for the group iterator
|
|
static DROID_GROUP *psScrIterateGroupB[MAX_PLAYERS];
|
|
static DROID *psScrIterateGroupDroidB[MAX_PLAYERS];
|
|
|
|
// initialise iterating a groups members
|
|
bool scrInitIterateGroupB(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
SDWORD bucket;
|
|
|
|
if (!stackPopParams(2, ST_GROUP, &psGroup, VAL_INT, &bucket))
|
|
{
|
|
debug(LOG_ERROR, "scrInitIterateGroupB: stackPopParams failed");
|
|
return false;
|
|
}
|
|
|
|
ASSERT(psGroup != NULL,
|
|
"scrInitIterateGroupB: invalid group pointer");
|
|
|
|
ASSERT(bucket < MAX_PLAYERS,
|
|
"scrInitIterateGroupB: invalid bucket");
|
|
|
|
psScrIterateGroupB[bucket] = psGroup;
|
|
psScrIterateGroupDroidB[bucket] = psGroup->psList;
|
|
|
|
return true;
|
|
}
|
|
|
|
//script function - improved version
|
|
// iterate through a groups members
|
|
bool scrIterateGroupB(void)
|
|
{
|
|
DROID_GROUP *psGroup;
|
|
DROID *psDroid;
|
|
SDWORD bucket;
|
|
|
|
if (!stackPopParams(2, ST_GROUP, &psGroup, VAL_INT, &bucket))
|
|
{
|
|
debug(LOG_ERROR, "scrIterateGroupB: stackPopParams failed");
|
|
return false;
|
|
}
|
|
|
|
ASSERT(bucket < MAX_PLAYERS,
|
|
"scrIterateGroupB: invalid bucket");
|
|
|
|
if (psGroup != psScrIterateGroupB[bucket])
|
|
{
|
|
ASSERT(false, "scrIterateGroupB: invalid group, InitGroupIterateB not called?");
|
|
return false;
|
|
}
|
|
|
|
if (psScrIterateGroupDroidB[bucket] != NULL)
|
|
{
|
|
psDroid = psScrIterateGroupDroidB[bucket];
|
|
psScrIterateGroupDroidB[bucket] = psScrIterateGroupDroidB[bucket]->psGrpNext;
|
|
}
|
|
else
|
|
{
|
|
psDroid = NULL;
|
|
}
|
|
|
|
scrFunctionResult.v.oval = psDroid;
|
|
if (!stackPushResult((INTERP_TYPE)ST_DROID, &scrFunctionResult))
|
|
{
|
|
debug(LOG_ERROR, "scrIterateGroupB: stackPushResult failed");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool scrDroidCanReach(void)
|
|
{
|
|
DROID *psDroid;
|
|
int x, y;
|
|
|
|
if (!stackPopParams(3, ST_DROID, &psDroid, VAL_INT, &x, VAL_INT, &y))
|
|
{
|
|
debug(LOG_ERROR, "Failed to pop parameters");
|
|
return false;
|
|
}
|
|
if (psDroid)
|
|
{
|
|
const PROPULSION_STATS *psPropStats = asPropulsionStats + psDroid->asBits[COMP_PROPULSION];
|
|
const Vector3i rPos(x, y, 0);
|
|
|
|
scrFunctionResult.v.bval = fpathCheck(psDroid->pos, rPos, psPropStats->propulsionType);
|
|
if (!stackPushResult(VAL_BOOL, &scrFunctionResult))
|
|
{
|
|
debug(LOG_ERROR, "stackPushResult failed");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// ********************************************************************************************
|
|
// ********************************************************************************************
|