223 changes: 195 additions & 28 deletions src/qtscriptfuncs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ struct labeltype
int id;
int type;
int player;
int subscriber;
QList<int> idlist;
int triggered;

bool operator==(const labeltype &o) const { return id == o.id && type == o.type && player == o.player; }
};
Expand Down Expand Up @@ -162,7 +164,7 @@ static void updateLabelModel()
int nextRow = 0;
for (LABELMAP::iterator i = labels.begin(); i != labels.end(); i++)
{
labeltype &l = (*i);
const labeltype &l = i.value();
labelModel->setItem(nextRow, 0, new QStandardItem(i.key()));
const char *c = "?";
switch (l.type)
Expand All @@ -172,6 +174,7 @@ static void updateLabelModel()
case OBJ_STRUCTURE: c = "STRUCTURE"; break;
case SCRIPT_POSITION: c = "POSITION"; break;
case SCRIPT_AREA: c = "AREA"; break;
case SCRIPT_RADIUS: c = "RADIUS"; break;
case SCRIPT_GROUP: c = "GROUP"; break;
case SCRIPT_PLAYER:
case SCRIPT_RESEARCH:
Expand All @@ -180,15 +183,40 @@ static void updateLabelModel()
case SCRIPT_COUNT: c = "ERROR"; break;
}
labelModel->setItem(nextRow, 1, new QStandardItem(QString(c)));
switch (l.triggered)
{
case -1: labelModel->setItem(nextRow, 2, new QStandardItem("N/A")); break;
case 0: labelModel->setItem(nextRow, 2, new QStandardItem("Active")); break;
default: labelModel->setItem(nextRow, 2, new QStandardItem("Done")); break;
}
if (l.player == ALL_PLAYERS)
{
labelModel->setItem(nextRow, 3, new QStandardItem("ALL"));
}
else
{
labelModel->setItem(nextRow, 3, new QStandardItem(QString::number(l.player)));
}
if (l.subscriber == ALL_PLAYERS)
{
labelModel->setItem(nextRow, 4, new QStandardItem("ALL"));
}
else
{
labelModel->setItem(nextRow, 4, new QStandardItem(QString::number(l.subscriber)));
}
nextRow++;
}
}

QStandardItemModel *createLabelModel()
{
labelModel = new QStandardItemModel(0, 2);
labelModel = new QStandardItemModel(0, 5);
labelModel->setHeaderData(0, Qt::Horizontal, QString("Label"));
labelModel->setHeaderData(1, Qt::Horizontal, QString("Type"));
labelModel->setHeaderData(2, Qt::Horizontal, QString("Trigger"));
labelModel->setHeaderData(3, Qt::Horizontal, QString("Owner"));
labelModel->setHeaderData(4, Qt::Horizontal, QString("Subscriber"));
updateLabelModel();
return labelModel;
}
Expand Down Expand Up @@ -233,6 +261,38 @@ void showLabel(const QString &key)
}
}
}
else if (l.type == SCRIPT_RADIUS)
{
setViewPos(map_coord(l.p1.x), map_coord(l.p1.y), false); // move camera position
clearMarks();
for (int x = MAX(map_coord(l.p1.x - l.p2.x), 0); x < MIN(map_coord(l.p1.x + l.p2.x), mapWidth); x++)
{
for (int y = MAX(map_coord(l.p1.y - l.p2.x), 0); y < MIN(map_coord(l.p1.y + l.p2.x), mapHeight); y++) // l.p2.x is radius, not a bug
{
if (iHypot(map_coord(l.p1) - Vector2i(x, y)) < map_coord(l.p2.x))
{
MAPTILE *psTile = mapTile(x, y);
psTile->tileInfoBits |= BITS_MARKED;
}
}
}
}
else if (l.type == SCRIPT_RADIUS)
{
setViewPos(map_coord(l.p1.x), map_coord(l.p1.y), false); // move camera position
clearMarks();
for (int x = MAX(map_coord(l.p1.x - l.p2.x), 0); x < MIN(map_coord(l.p1.x + l.p2.x), mapWidth); x++)
{
for (int y = MAX(map_coord(l.p1.y - l.p2.x), 0); y < MIN(map_coord(l.p1.y + l.p2.x), mapHeight); y++) // l.p2.x is radius, not a bug
{
if (iHypot(map_coord(l.p1) - Vector2i(x, y)) < map_coord(l.p2.x))
{
MAPTILE *psTile = mapTile(x, y);
psTile->tileInfoBits |= BITS_MARKED;
}
}
}
}
else if (l.type == OBJ_DROID || l.type == OBJ_FEATURE || l.type == OBJ_STRUCTURE)
{
clearMarks();
Expand Down Expand Up @@ -268,24 +328,52 @@ void showLabel(const QString &key)
}
}

// Returns zero for no action, SCRIPT_OBJECT_SEEN for object seen callback, positive for group callback
int seenLabelCheck(QScriptEngine *engine, BASE_OBJECT *seen, BASE_OBJECT *viewer)
{
GROUPMAP *psMap = groups.value(engine);
int groupId = psMap->value(seen);
for (LABELMAP::iterator i = labels.begin(); i != labels.end(); i++)
{
labeltype &l = (*i);
if (l.id == seen->id && l.triggered == 0)
{
l.triggered = viewer->id; // record who made the discovery
return SCRIPT_OBJECT_SEEN;
}
else if (l.type == SCRIPT_GROUP && l.id == groupId && l.triggered == 0
&& (l.subscriber == ALL_PLAYERS || l.subscriber == viewer->player))
{
l.triggered = viewer->id; // record who made the discovery
return groupId;
}
}
return 0;
}

bool areaLabelCheck(DROID *psDroid)
{
int x = psDroid->pos.x;
int y = psDroid->pos.y;
bool activated = false;
for (LABELMAP::iterator i = labels.begin(); i != labels.end(); i++)
{
labeltype &l = (*i);
if (l.id == -1 && l.p1.x < x && l.p1.y < y && l.p2.x > x && l.p2.y > y
&& (l.player == ALL_PLAYERS || l.player == psDroid->player)
&& l.type == SCRIPT_AREA)
labeltype &l = i.value();
if (l.triggered == 0 && (l.subscriber == ALL_PLAYERS || l.subscriber == psDroid->player)
&& ((l.type == SCRIPT_AREA && l.p1.x < x && l.p1.y < y && l.p2.x > x && l.p2.y > y)
|| (l.type == SCRIPT_RADIUS && iHypot(l.p1 - removeZ(psDroid->pos)) < l.p2.x)))
{
// We're inside an untriggered area
l.id = 1; // deactivate it
activated = true;
l.triggered = psDroid->id;
triggerEventArea(i.key(), psDroid);
return true;
}
}
return false;
if (activated)
{
updateLabelModel();
}
return activated;
}

static void removeFromGroup(QScriptEngine *engine, GROUPMAP *psMap, BASE_OBJECT *psObj)
Expand Down Expand Up @@ -794,23 +882,42 @@ bool loadLabels(const char *filename)
p.type = SCRIPT_POSITION;
p.player = ALL_PLAYERS;
p.id = -1;
p.triggered = -1; // always deactivated
labels.insert(label, p);
p.triggered = ini.value("triggered", -1).toInt(); // deactivated by default
}
else if (list[i].startsWith("area"))
{
p.p1 = ini.vector2i("pos1");
p.p2 = ini.vector2i("pos2");
p.type = SCRIPT_AREA;
p.player = ini.value("player", ALL_PLAYERS).toInt();
p.id = ini.value("triggered", -1).toInt();
p.triggered = ini.value("triggered", 0).toInt(); // activated by default
p.id = -1;
p.subscriber = ini.value("subscriber", ALL_PLAYERS).toInt();
labels.insert(label, p);
}
else if (list[i].startsWith("radius"))
{
p.p1 = ini.vector2i("pos");
p.p2.x = ini.value("radius").toInt();
p.p2.y = 0; // unused
p.type = SCRIPT_RADIUS;
p.player = ini.value("player", ALL_PLAYERS).toInt();
p.triggered = ini.value("triggered", 0).toInt(); // activated by default
p.subscriber = ini.value("subscriber", ALL_PLAYERS).toInt();
p.id = -1;
labels.insert(label, p);
p.triggered = ini.value("triggered", -1).toInt(); // deactivated by default
}
else if (list[i].startsWith("object"))
{
p.id = ini.value("id").toInt();
p.type = ini.value("type").toInt();
p.player = ini.value("player").toInt();
labels.insert(label, p);
p.triggered = ini.value("triggered", -1).toInt(); // deactivated by default
p.subscriber = ini.value("subscriber", ALL_PLAYERS).toInt();
}
else if (list[i].startsWith("group"))
{
Expand All @@ -827,6 +934,7 @@ bool loadLabels(const char *filename)
p.idlist += id;
}
labels.insert(label, p);
p.triggered = ini.value("triggered", -1).toInt(); // deactivated by default
}
else
{
Expand All @@ -839,13 +947,14 @@ bool loadLabels(const char *filename)

bool writeLabels(const char *filename)
{
int c[4]; // make unique, incremental section names
int c[5]; // make unique, incremental section names
memset(c, 0, sizeof(c));
WzConfig ini(filename);
for (LABELMAP::const_iterator i = labels.constBegin(); i != labels.constEnd(); i++)
{
QString key = i.key();
labeltype l = i.value();
ini.setValue("triggered", l.triggered);
if (l.type == SCRIPT_POSITION)
{
ini.beginGroup("position_" + QString::number(c[0]++));
Expand All @@ -860,25 +969,39 @@ bool writeLabels(const char *filename)
ini.setVector2i("pos2", l.p2);
ini.setValue("label", key);
ini.setValue("player", l.player);
ini.setValue("triggered", l.id);
ini.setValue("triggered", l.triggered);
ini.setValue("subscriber", l.subscriber);
ini.endGroup();
}
else if (l.type == SCRIPT_RADIUS)
{
ini.beginGroup("radius_" + QString::number(c[2]++));
ini.setVector2i("pos", l.p1);
ini.setValue("radius", l.p2.x);
ini.setValue("label", key);
ini.setValue("player", l.player);
ini.setValue("triggered", l.triggered);
ini.setValue("subscriber", l.subscriber);
ini.endGroup();
}
else if (l.type == SCRIPT_GROUP)
{
ini.beginGroup("group_" + QString::number(c[2]++));
ini.beginGroup("group_" + QString::number(c[3]++));
ini.setValue("player", l.player);
ini.setValue("triggered", l.triggered);
QStringList list;
for (int i = 0; i < l.idlist.size(); i++)
{
list += QString::number(l.idlist[i]);
}
ini.setValue("members", list);
ini.setValue("label", key);
ini.setValue("subscriber", l.subscriber);
ini.endGroup();
}
else
{
ini.beginGroup("object_" + QString::number(c[3]++));
ini.beginGroup("object_" + QString::number(c[4]++));
ini.setValue("id", l.id);
ini.setValue("player", l.player);
ini.setValue("type", l.type);
Expand Down Expand Up @@ -912,20 +1035,26 @@ static QScriptValue js_getWeaponInfo(QScriptContext *context, QScriptEngine *eng
return QScriptValue(info);
}

//-- \subsection{resetLabel(label[, filter])}
//-- Reset the trigger on an label. Next time a unit enters the area, it will trigger
//-- an area event. Next time an object or a group is seen, it will trigger a seen event.
//-- Optionally add a filter on it in the second parameter, which can
//-- be a specific player to watch for, or ALL_PLAYERS by default.
//-- This is a fast operation of O(log n) algorithmic complexity. (3.2+ only)
//-- \subsection{resetArea(label[, filter])}
//-- Reset the trigger on an area. Next time a unit enters the area, it will trigger
//-- an area event. Optionally add a filter on it in the second parameter, which can
//-- be a specific player to watch for, or ALL_PLAYERS by default.
//-- This is a fast operation of O(log n) algorithmic complexity. (3.2+ only)
static QScriptValue js_resetArea(QScriptContext *context, QScriptEngine *)
//-- This is a fast operation of O(log n) algorithmic complexity. DEPRECATED - use resetLabel instead. (3.2+ only)
static QScriptValue js_resetLabel(QScriptContext *context, QScriptEngine *)
{
QString labelName = context->argument(0).toString();
SCRIPT_ASSERT(context, labels.contains(labelName), "Label %s not found", labelName.toUtf8().constData());
labeltype &l = labels[labelName];
l.id = -1; // make active again
l.triggered = 0; // make active again
if (context->argumentCount() > 1)
{
l.player = context->argument(1).toInt32();
l.subscriber = context->argument(1).toInt32();
}
return QScriptValue();
}
Expand Down Expand Up @@ -972,8 +1101,19 @@ static QScriptValue js_addLabel(QScriptContext *context, QScriptEngine *engine)
value.player = qval.property("player").toInt32();
value.p1.x = world_coord(qval.property("x").toInt32());
value.p1.y = world_coord(qval.property("y").toInt32());
value.p2.x = world_coord(qval.property("x2").toInt32());
value.p2.y = world_coord(qval.property("y2").toInt32());
if (value.type == SCRIPT_AREA) {
value.p2.x = world_coord(qval.property("x2").toInt32());
value.p2.y = world_coord(qval.property("y2").toInt32());
} else if (value.type == SCRIPT_RADIUS) {
value.p2.x = world_coord(qval.property("radius").toInt32());
}
value.triggered = -1; // default off
QScriptValue triggered = qval.property("triggered");
if (triggered.isNumber())
{
SCRIPT_ASSERT(context, value.type != SCRIPT_POSITION, "Cannot assign a trigger to a position");
value.triggered = triggered.toInt32();
}
if (value.type == OBJ_DROID || value.type == OBJ_STRUCTURE || value.type == OBJ_FEATURE)
{
BASE_OBJECT *psObj = IdToObject((OBJECT_TYPE)value.type, value.id, value.player);
Expand Down Expand Up @@ -1058,21 +1198,30 @@ static QScriptValue js_getObject(QScriptContext *context, QScriptEngine *engine)
{
ret = engine->newObject();
labeltype p = labels.value(label);
ret.setProperty("type", p.type, QScriptValue::ReadOnly);
switch (p.type)
{
case SCRIPT_RADIUS:
ret.setProperty("x", map_coord(p.p1.x), QScriptValue::ReadOnly);
ret.setProperty("y", map_coord(p.p1.y), QScriptValue::ReadOnly);
ret.setProperty("radius", map_coord(p.p2.x), QScriptValue::ReadOnly);
ret.setProperty("triggered", p.triggered);
ret.setProperty("subscriber", p.subscriber);
break;
case SCRIPT_AREA:
ret.setProperty("x2", map_coord(p.p2.x), QScriptValue::ReadOnly);
ret.setProperty("y2", map_coord(p.p2.y), QScriptValue::ReadOnly);
ret.setProperty("type", p.type, QScriptValue::ReadOnly);
ret.setProperty("subscriber", p.subscriber);
// fall through
case SCRIPT_POSITION:
ret.setProperty("triggered", p.triggered);
ret.setProperty("x", map_coord(p.p1.x), QScriptValue::ReadOnly);
ret.setProperty("y", map_coord(p.p1.y), QScriptValue::ReadOnly);
ret.setProperty("type", p.type, QScriptValue::ReadOnly);
break;
case SCRIPT_GROUP:
ret.setProperty("triggered", p.triggered);
ret.setProperty("subscriber", p.subscriber);
ret.setProperty("id", p.id, QScriptValue::ReadOnly);
ret.setProperty("type", p.type, QScriptValue::ReadOnly);
break;
case OBJ_DROID:
case OBJ_FEATURE:
Expand Down Expand Up @@ -3780,12 +3929,28 @@ static QScriptValue js_hackMarkTiles(QScriptContext *context, QScriptEngine *)
QString label = context->argument(0).toString();
SCRIPT_ASSERT(context, labels.contains(label), "Label %s not found", label.toUtf8().constData());
labeltype &l = labels[label];
for (int x = map_coord(l.p1.x); x < map_coord(l.p2.x); x++)
if (l.type == SCRIPT_AREA)
{
for (int y = map_coord(l.p1.y); y < map_coord(l.p2.y); y++)
for (int x = map_coord(l.p1.x); x < map_coord(l.p2.x); x++)
{
MAPTILE *psTile = mapTile(x, y);
psTile->tileInfoBits |= BITS_MARKED;
for (int y = map_coord(l.p1.y); y < map_coord(l.p2.y); y++)
{
MAPTILE *psTile = mapTile(x, y);
psTile->tileInfoBits |= BITS_MARKED;
}
}
} else if (l.type == SCRIPT_RADIUS)
{
for (int x = map_coord(l.p1.x - l.p2.x); x < map_coord(l.p1.x + l.p2.x); x++)
{
for (int y = map_coord(l.p1.y - l.p2.x); y < map_coord(l.p1.y + l.p2.x); y++)
{
if (x >= -1 && x < mapWidth + 1 && y >= -1 && y < mapWidth + 1 && iHypot(map_coord(l.p1) - Vector2i(x, y)) < map_coord(l.p2.x))
{
MAPTILE *psTile = mapTile(x, y);
psTile->tileInfoBits |= BITS_MARKED;
}
}
}
}
}
Expand Down Expand Up @@ -4516,7 +4681,8 @@ bool registerFunctions(QScriptEngine *engine, QString scriptName)
engine->globalObject().setProperty("cameraSlide", engine->newFunction(js_cameraSlide));
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("resetArea", engine->newFunction(js_resetLabel)); // deprecated
engine->globalObject().setProperty("resetLabel", engine->newFunction(js_resetLabel));
engine->globalObject().setProperty("addSpotter", engine->newFunction(js_addSpotter));
engine->globalObject().setProperty("removeSpotter", engine->newFunction(js_removeSpotter));
engine->globalObject().setProperty("syncRequest", engine->newFunction(js_syncRequest));
Expand Down Expand Up @@ -4720,6 +4886,7 @@ bool registerFunctions(QScriptEngine *engine, QString scriptName)
engine->globalObject().setProperty("ENEMIES", ENEMIES, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("POSITION", SCRIPT_POSITION, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("AREA", SCRIPT_AREA, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("RADIUS", SCRIPT_RADIUS, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("GROUP", SCRIPT_GROUP, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("PLAYER_DATA", SCRIPT_PLAYER, QScriptValue::ReadOnly | QScriptValue::Undeletable);
engine->globalObject().setProperty("RESEARCH_DATA", SCRIPT_RESEARCH, QScriptValue::ReadOnly | QScriptValue::Undeletable);
Expand Down
6 changes: 6 additions & 0 deletions src/qtscriptfuncs.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ enum SCRIPT_TYPE
SCRIPT_PLAYER,
SCRIPT_RESEARCH,
SCRIPT_GROUP,
SCRIPT_RADIUS,
SCRIPT_COUNT
};

#define SCRIPT_OBJECT_SEEN -1

#include <QtScript/QScriptEngine>

// ----------------------------------------------
Expand Down Expand Up @@ -75,6 +78,9 @@ QStandardItemModel *createLabelModel();
/// Mark and show label
void showLabel(const QString &key);

/// Check if this object marked for a seen trigger once it comes into vision
int seenLabelCheck(QScriptEngine *engine, BASE_OBJECT *seen, BASE_OBJECT *viewer);

/// Assert for scripts that give useful backtraces and other info.
#define SCRIPT_ASSERT(context, expr, ...) \
do { bool _wzeval = (expr); \
Expand Down
8 changes: 5 additions & 3 deletions src/visibility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,9 @@
#include "scriptextern.h"
#include "structure.h"
#include "projectile.h"

#include "display.h"
#include "multiplay.h"

#include "qtscript.h"
#include "wavecast.h"

// rate to change visibility level
Expand Down Expand Up @@ -660,7 +659,10 @@ static void processVisibilityVision(BASE_OBJECT *psViewer)
// Tell system that this side can see this object
setSeenBy(psObj, psViewer->player, val);

// This looks like some kind of weird hack.
// Check if scripting system wants to trigger an event for this
triggerEventSeen(psViewer, psObj);

// This looks like some kind of weird hack. Only used by wzscript.
if(psObj->type != OBJ_FEATURE && psObj->visible[psViewer->player] <= 0)
{
// features are not in the cluster system
Expand Down