Skip to content

Commit

Permalink
qtscript: Add new functions addSpotter() and removeSpotter() to add a…
Browse files Browse the repository at this point in the history
…nd remove

temporary areas of vision on the map.
  • Loading branch information
perim committed May 17, 2013
1 parent db5b44c commit 371ab04
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/init.cpp
Expand Up @@ -1232,6 +1232,8 @@ bool stageThreeShutDown(void)
{
debug(LOG_WZ, "== stageThreeShutDown ==");

removeSpotters();

// There is an assymetry in scripts initialization and destruction, due
// the many different ways scripts get loaded.
if (!shutdownScripts())
Expand Down
28 changes: 28 additions & 0 deletions src/qtscriptfuncs.cpp
Expand Up @@ -3764,6 +3764,32 @@ static QScriptValue js_cameraTrack(QScriptContext *context, QScriptEngine *)
return QScriptValue();
}

//-- \subsection{addSpotter(x, y, player, range, type, expiry)}
//-- Add an invisible viewer at a given position for given player that shows map in given range. \emph{type}
//-- is zero for vision reveal, or one for radar reveal. The difference is that a radar reveal can be obstructed
//-- by ECM jammers. \emph{expiry}, if non-zero, is the game time at which the spotter shall automatically be
//-- removed. The function returns a unique ID that can be used to remove the spotter with \emph{removeSpotter}.
static QScriptValue js_addSpotter(QScriptContext *context, QScriptEngine *)
{
int x = context->argument(0).toInt32();
int y = context->argument(1).toInt32();
int player = context->argument(2).toInt32();
int range = context->argument(3).toInt32();
bool radar = context->argument(4).toBool();
uint32_t expiry = context->argument(5).toUInt32();
uint32_t id = addSpotter(x, y, player, range, radar, expiry);
return QScriptValue(id);
}

//-- \subsection{removeSpotter(id)}
//-- Remove a spotter given its unique ID.
static QScriptValue js_removeSpotter(QScriptContext *context, QScriptEngine *)
{
uint32_t id = context->argument(0).toUInt32();
removeSpotter(id);
return QScriptValue();
}

//-- \subsection{syncRandom(limit)}
//-- Generate a synchronized random number in range 0...(limit - 1) that will be the same if this function is
//-- run on all network peers in the same game frame. If it is called on just one peer (such as would be
Expand Down Expand Up @@ -4355,6 +4381,8 @@ bool registerFunctions(QScriptEngine *engine, QString scriptName)
engine->globalObject().setProperty("cameraTrack", engine->newFunction(js_cameraTrack));
engine->globalObject().setProperty("cameraZoom", engine->newFunction(js_cameraZoom));
engine->globalObject().setProperty("resetArea", engine->newFunction(js_resetArea));
engine->globalObject().setProperty("addSpotter", engine->newFunction(js_addSpotter));
engine->globalObject().setProperty("removeSpotter", engine->newFunction(js_removeSpotter));

// horrible hacks follow -- do not rely on these being present!
engine->globalObject().setProperty("hackNetOff", engine->newFunction(js_hackNetOff));
Expand Down
114 changes: 114 additions & 0 deletions src/visibility.cpp
Expand Up @@ -55,6 +55,25 @@ static const int VIS_LEVEL_DEC = 50;
// integer amount to change visiblility this turn
static SDWORD visLevelInc, visLevelDec;

class SPOTTER
{
public:
SPOTTER(int x, int y, int plr, int radius, int type, uint32_t expiry = 0)
: pos(x, y, 0), player(plr), sensorRadius(radius), sensorType(type), expiryTime(expiry), numWatchedTiles(0), watchedTiles(NULL)
{ id = generateSynchronisedObjectId(); }
~SPOTTER();

Position pos;
int player;
int sensorRadius;
int sensorType; // 0 - vision, 1 - radar
uint32_t expiryTime; // when to self-destruct, zero if never
int numWatchedTiles;
TILEPOS *watchedTiles;
uint32_t id;
};
static std::vector<SPOTTER *> apsInvisibleViewers;

// horrible hack because this code is full of them and I ain't rewriting it all - Per
#define MAX_SEEN_TILES (29*29 * 355/113) // Increased hack to support 28 tile sensor radius. - Cyp

Expand All @@ -63,6 +82,8 @@ static SDWORD visLevelInc, visLevelDec;
static int *gNumWalls = NULL;
static Vector2i *gWall = NULL;

// forward declarations
static void setSeenBy(BASE_OBJECT *psObj, unsigned viewer, int val);

// initialise the visibility stuff
bool visInitialise(void)
Expand Down Expand Up @@ -98,6 +119,98 @@ static inline void updateTileVis(MAPTILE *psTile)
}
}

uint32_t addSpotter(int x, int y, int player, int radius, bool radar, uint32_t expiry)
{
SPOTTER *psSpot = new SPOTTER(x, y, player, radius, (int)radar, expiry);
size_t size;
const WavecastTile *tiles = getWavecastTable(radius, &size);
psSpot->watchedTiles = (TILEPOS *)malloc(size * sizeof(*psSpot->watchedTiles));
for (int i = 0; i < size; ++i)
{
const int mapX = x + tiles[i].dx;
const int mapY = y + tiles[i].dy;
if (mapX < 0 || mapX >= mapWidth || mapY < 0 || mapY >= mapHeight)
{
continue;
}
MAPTILE *psTile = mapTile(mapX, mapY);
psTile->tileExploredBits |= alliancebits[player];
uint8_t *visionType = (!radar) ? psTile->watchers : psTile->sensors;
if (visionType[player] < UBYTE_MAX)
{
TILEPOS tilePos = {uint8_t(mapX), uint8_t(mapY), uint8_t(radar)};
visionType[player]++; // we observe this tile
updateTileVis(psTile);
psSpot->watchedTiles[psSpot->numWatchedTiles++] = tilePos; // record having seen it
}
}
apsInvisibleViewers.push_back(psSpot);
return psSpot->id;
}

bool removeSpotter(uint32_t id)
{
for (int i = 0; i < apsInvisibleViewers.size(); i++)
{
SPOTTER *psSpot = apsInvisibleViewers.at(i);
if (psSpot->id == id)
{
delete psSpot;
apsInvisibleViewers.erase(apsInvisibleViewers.begin() + i);
return true;
}
}
return false;
}

void removeSpotters()
{
while (!apsInvisibleViewers.empty())
{
SPOTTER *psSpot = apsInvisibleViewers.back();
delete psSpot;
apsInvisibleViewers.pop_back();
}
}

static void updateSpotters()
{
static GridList gridList; // static to avoid allocations.
for (int i = 0; i < apsInvisibleViewers.size(); i++)
{
SPOTTER *psSpot = apsInvisibleViewers.at(i);
if (psSpot->expiryTime != 0 && psSpot->expiryTime < gameTime)
{
delete psSpot;
apsInvisibleViewers.erase(apsInvisibleViewers.begin() + i);
continue;
}
// else, ie if not expired, show objects around it
gridList = gridStartIterateUnseen(world_coord(psSpot->pos.x), world_coord(psSpot->pos.y), psSpot->sensorRadius, psSpot->player);
for (GridIterator gi = gridList.begin(); gi != gridList.end(); ++gi)
{
BASE_OBJECT *psObj = *gi;

// Tell system that this side can see this object
setSeenBy(psObj, psSpot->player, UBYTE_MAX);
}
}
}

SPOTTER::~SPOTTER()
{
for (int i = 0; i < numWatchedTiles; i++)
{
const TILEPOS pos = watchedTiles[i];
MAPTILE *psTile = mapTile(pos.x, pos.y);
uint8_t *visionType = (pos.type == 0) ? psTile->watchers : psTile->sensors;
ASSERT(visionType[player] > 0, "Not watching watched tile (%d, %d)", (int)pos.x, (int)pos.y);
visionType[player]--;
updateTileVis(psTile);
}
free(watchedTiles);
}

/* Record all tiles that some object confers visibility to. Only record each tile
* once. Note that there is both a limit to how many objects can watch any given
* tile, and a limit to how many tiles each object can watch. Strange but non fatal
Expand Down Expand Up @@ -651,6 +764,7 @@ static void processVisibilityLevel(BASE_OBJECT *psObj)

void processVisibility()
{
updateSpotters();
for (int player = 0; player < MAX_PLAYERS; ++player)
{
BASE_OBJECT *lists[] = {apsDroidLists[player], apsStructLists[player], apsFeatureLists[player]};
Expand Down
4 changes: 4 additions & 0 deletions src/visibility.h
Expand Up @@ -100,4 +100,8 @@ static inline int objJammerPower(const BASE_OBJECT* psObj)
return 0;
}

void removeSpotters();
bool removeSpotter(uint32_t id);
uint32_t addSpotter(int x, int y, int player, int radius, bool radar, uint32_t expiry = 0);

#endif // __INCLUDED_SRC_VISIBILITY__

0 comments on commit 371ab04

Please sign in to comment.