Skip to content

Commit

Permalink
qtscript: Add a way for a script library to register an event namespace
Browse files Browse the repository at this point in the history
New function: namespace(prefix)

The namespace function registers a new namespace for events, that the
script environment will call in addition to the non-prefixed event names.
This allows script libraries to exist that can handle events, without
overwriting or being overwritten by scripts using that library.

All existing campaign event handlers except area handlers have been
changed to use the new namespace feature.

New buttons in debug menu to show all labels, all active labels,
clear labels, and show all gateways.
  • Loading branch information
perim committed May 25, 2017
1 parent 50babf0 commit 19d3739
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 61 deletions.
62 changes: 36 additions & 26 deletions data/base/script/campaign/libcampaign.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@
HTML by HeVeA, which fails to support most of the complicated stuff.
*/

////////////////////////////////////////////////////////////////////////////////
// Library initialization.
////////////////////////////////////////////////////////////////////////////////

// Registers a private event namespace for this library, to avoid collisions with
// any event handling in code using this library. Make sure no other library uses
// the same namespace, or strange things will happen. After this, we can name our
// event handlers with the registered prefix, and they will still get called.
namespace("cam_");

////////////////////////////////////////////////////////////////////////////////
// Misc useful stuff.
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -2398,23 +2408,23 @@ var __camLastHitTime = 0;

const __CAM_EVENT_ATTACKED_INTENSITY = 5000;

__camPreHookEvent("eventPickup", function(feature, droid)
function cam_eventPickup(feature, droid)
{
if (feature.stattype === ARTIFACT)
{
__camPickupArtifact(feature);
}
});
}

__camPreHookEvent("eventGroupLoss", function(obj, group, newsize)
function cam_eventGroupLoss(obj, group, newsize)
{
if (newsize === 0)
__camCheckBaseEliminated(group);
if (camDef(__camGroupInfo[group]))
profile("__camCheckGroupMorale", group);
});
}

__camPreHookEvent("eventCheatMode", function(entered)
function cam_eventCheatMode(entered)
{
if (entered)
{
Expand All @@ -2427,9 +2437,9 @@ __camPreHookEvent("eventCheatMode", function(entered)
__camCheatMode = false;
}
__camUpdateMarkedTiles();
});
}

__camPreHookEvent("eventChat", function(from, to, message)
function cam_eventChat(from, to, message)
{
if (!__camCheatMode)
return;
Expand All @@ -2448,9 +2458,9 @@ __camPreHookEvent("eventChat", function(from, to, message)
__camNextLevel = message.substring(7).toUpperCase().replace(/-/g, "_");
__camLetMeWin();
}
});
}

__camPreHookEvent("eventStartLevel", function()
function cam_eventStartLevel()
{
isReceivingAllEvents = true;
// Variables initialized here are the ones that should not be
Expand Down Expand Up @@ -2482,9 +2492,9 @@ __camPreHookEvent("eventStartLevel", function()
setTimer("__camTick", 1000); // campaign pollers
setTimer("__camTruckTick", 150100); // some slower campaign pollers
queue("__camTacticsTick", 100); // would re-queue itself
});
}

__camPreHookEvent("eventDroidBuilt", function(droid, structure)
function cam_eventDroidBuilt(droid, structure)
{
if (!camDef(structure)) // "clone wars" cheat
return;
Expand All @@ -2496,26 +2506,26 @@ __camPreHookEvent("eventDroidBuilt", function(droid, structure)
return;
__camContinueProduction(structure);
__camAddDroidToFactoryGroup(droid, structure);
});
}

__camPreHookEvent("eventDestroyed", function(obj)
function cam_eventDestroyed(obj)
{
__camCheckPlaceArtifact(obj);
if (obj.type === DROID && obj.droidType === DROID_CONSTRUCT)
__camCheckDeadTruck(obj);
});
}

__camPreHookEvent("eventObjectSeen", function(viewer, seen)
function cam_eventObjectSeen(viewer, seen)
{
__camCheckBaseSeen(seen);
});
}

__camPreHookEvent("eventGroupSeen", function(viewer, group)
function cam_eventGroupSeen(viewer, group)
{
__camCheckBaseSeen(group);
});
}

__camPreHookEvent("eventTransporterExit", function(transport)
function cam_eventTransporterExit(transport)
{
if (transport.player !== CAM_HUMAN_PLAYER ||
(__camWinLossCallback === "__camVictoryStandard" &&
Expand All @@ -2531,28 +2541,28 @@ __camPreHookEvent("eventTransporterExit", function(transport)
__camGameWon();
return;
}
});
}

__camPreHookEvent("eventTransporterLanded", function(transport)
function cam_eventTransporterLanded(transport)
{
if (transport.player !== CAM_HUMAN_PLAYER)
__camLandTransporter(transport.player, camMakePos(transport));
});
}

__camPreHookEvent("eventMissionTimeout", function()
function cam_eventMissionTimeout()
{
if (__camDefeatOnTimeout)
{
camTrace("0 minutes remaining.");
__camGameLost();
}
});
}

__camPreHookEvent("eventAttacked", function(victim, attacker)
function cam_eventAttacked(victim, attacker)
{
if (camDef(victim) && victim &&
victim.type === DROID && camDef(__camGroupInfo[victim.group]))
{
__camGroupInfo[victim.group].lastHit = gameTime;
}
});
}
30 changes: 28 additions & 2 deletions src/qtscript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ typedef struct monitor_bin
} MONITOR_BIN;
typedef QHash<QString, MONITOR_BIN> MONITOR;
static QHash<QScriptEngine *, MONITOR *> monitors;
static QHash<QScriptEngine *, QStringList> eventNamespaces; // separate event namespaces for libraries

static MODELMAP models;
static QStandardItemModel *triggerModel;
Expand All @@ -149,9 +150,21 @@ void doNotSaveGlobal(const QString &global)
}

// Call a function by name
static QScriptValue callFunction(QScriptEngine *engine, const QString &function, const QScriptValueList &args, bool required = false)
static QScriptValue callFunction(QScriptEngine *engine, const QString &function, const QScriptValueList &args, bool event = true)
{
code_part level = required ? LOG_ERROR : LOG_SCRIPT;
if (event)
{
// recurse into variants, if any
for (const QString &s : eventNamespaces[engine])
{
const QScriptValue &value = engine->globalObject().property(s + function);
if (value.isValid() && value.isFunction())
{
callFunction(engine, s + function, args, event);
}
}
}
code_part level = event ? LOG_SCRIPT : LOG_ERROR;
QScriptValue value = engine->globalObject().property(function);
if (!value.isValid() || !value.isFunction())
{
Expand Down Expand Up @@ -351,6 +364,18 @@ void scriptRemoveObject(BASE_OBJECT *psObj)
groupRemoveObject(psObj);
}

//-- \subsection{namespace(prefix)}
//-- Registers a new event namespace. All events can now have this prefix. This is useful for
//-- code libraries, to implement event that do not conflict with events in main code. This
//-- function should be called from global; do not (for hopefully obvious reasons) put it
//-- inside an event.
static QScriptValue js_namespace(QScriptContext *context, QScriptEngine *engine)
{
QString prefix(context->argument(0).toString());
eventNamespaces[engine].append(prefix);
return QScriptValue(true);
}

//-- \subsection{include(file)}
//-- Includes another source code file at this point. You should generally only specify the filename,
//-- not try to specify its path, here.
Expand Down Expand Up @@ -554,6 +579,7 @@ QScriptEngine *loadPlayerScript(const QString& path, int player, int difficulty)
engine->globalObject().setProperty("removeTimer", engine->newFunction(js_removeTimer));
engine->globalObject().setProperty("profile", engine->newFunction(js_profile));
engine->globalObject().setProperty("include", engine->newFunction(js_include));
engine->globalObject().setProperty("namespace", engine->newFunction(js_namespace));

// Special global variables
//== \item[version] Current version of the game, set in \emph{major.minor} format.
Expand Down
40 changes: 36 additions & 4 deletions src/qtscriptdebug.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ void ScriptDebugger::deityButtonClicked()
kf_ToggleGodMode();
}

void ScriptDebugger::gatewayButtonClicked()
{
kf_ToggleShowGateways();
}

void ScriptDebugger::weatherButtonClicked()
{
kf_ToggleWeather();
Expand Down Expand Up @@ -134,7 +139,8 @@ ScriptDebugger::ScriptDebugger(const MODELMAP &models, QStandardItemModel *trigg
miscLayout->addWidget(createButton("Fog", SLOT(fogButtonClicked()), this));
mainLayout->addLayout(miscLayout);
QHBoxLayout *worldLayout = new QHBoxLayout();
worldLayout->addWidget(createButton("Show all", SLOT(deityButtonClicked()), this));
worldLayout->addWidget(createButton("Show gateways", SLOT(gatewayButtonClicked()), this));
worldLayout->addWidget(createButton("Reveal all", SLOT(deityButtonClicked()), this));
worldLayout->addWidget(createButton("Weather", SLOT(weatherButtonClicked()), this));
worldLayout->addWidget(createButton("Reveal mode", SLOT(revealButtonClicked()), this));
mainLayout->addLayout(worldLayout);
Expand Down Expand Up @@ -214,11 +220,20 @@ ScriptDebugger::ScriptDebugger(const MODELMAP &models, QStandardItemModel *trigg
labelView.setSelectionMode(QAbstractItemView::SingleSelection);
labelView.setSelectionBehavior(QAbstractItemView::SelectRows);
connect(&labelView, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(labelClickedIdx(const QModelIndex &)));
QPushButton *button = new QPushButton("Show", this);
connect(button, SIGNAL(pressed()), this, SLOT(labelClicked()));
QPushButton *buttonShow = new QPushButton("Show selected", this);
QPushButton *buttonShowAll = new QPushButton("Show all", this);
QPushButton *buttonShowActive = new QPushButton("Show active", this);
QPushButton *buttonClear = new QPushButton("Clear", this);
connect(buttonShow, SIGNAL(pressed()), this, SLOT(labelClicked()));
connect(buttonShowAll, SIGNAL(pressed()), this, SLOT(labelClickedAll()));
connect(buttonShowActive, SIGNAL(pressed()), this, SLOT(labelClickedActive()));
connect(buttonClear, SIGNAL(pressed()), this, SLOT(labelClear()));
QVBoxLayout *labelLayout = new QVBoxLayout(this);
labelLayout->addWidget(&labelView);
labelLayout->addWidget(button);
labelLayout->addWidget(buttonShow);
labelLayout->addWidget(buttonShowAll);
labelLayout->addWidget(buttonShowActive);
labelLayout->addWidget(buttonClear);
QWidget *dummyWidget = new QWidget(this);
dummyWidget->setLayout(labelLayout);
tab.addTab(dummyWidget, "Labels");
Expand Down Expand Up @@ -251,6 +266,23 @@ void ScriptDebugger::updateModels()
doUpdateModels = true;
}

void ScriptDebugger::labelClear()
{
clearMarks();
}

void ScriptDebugger::labelClickedAll()
{
clearMarks();
markAllLabels(false);
}

void ScriptDebugger::labelClickedActive()
{
clearMarks();
markAllLabels(true);
}

void ScriptDebugger::labelClicked()
{
QItemSelectionModel *selected = labelView.selectionModel();
Expand Down
4 changes: 4 additions & 0 deletions src/qtscriptdebug.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ class ScriptDebugger : public QDialog
protected slots:
void labelClickedIdx(const QModelIndex &idx);
void labelClicked();
void labelClickedAll();
void labelClickedActive();
void labelClear();
void runClicked(QObject *obj);
void updateModels();
void powerEditing(const QString &value);
Expand All @@ -77,6 +80,7 @@ protected slots:
void researchButtonClicked();
void sensorsButtonClicked();
void deityButtonClicked();
void gatewayButtonClicked();
void weatherButtonClicked();
void revealButtonClicked();
void shadowButtonClicked();
Expand Down
Loading

0 comments on commit 19d3739

Please sign in to comment.