Javascript improvements to prepare for AI porting. The two types of repeating timers now unified

and functionality of setObjectTimer() implemented as an extra parameter to setGlobalTimer(). Also
added a new one-shot timer called queue().
master
Per Inge Mathisen 2011-10-23 16:41:08 -04:00
parent 7426d0c2bf
commit f88f363ad6
10 changed files with 103 additions and 31 deletions

View File

@ -82,7 +82,7 @@ function eventGameInit()
attackGroup = newGroup(); // allocate a new group attackGroup = newGroup(); // allocate a new group
groupAddArea(attackGroup, 0, 0, (mapWidth * 128), (mapHeight * 128)); groupAddArea(attackGroup, 0, 0, (mapWidth * 128), (mapHeight * 128));
scavtick(); scavtick();
setGlobalTimer("scavtick", 15000); // start a constant timer function setTimer("scavtick", 15000); // start a constant timer function
} }
// deal with a droid being built by us // deal with a droid being built by us

View File

@ -4,10 +4,6 @@
// contains the rules for starting and ending a game. // contains the rules for starting and ending a game.
// as well as warning messages. // as well as warning messages.
// //
// TODO set tech level as a global, and set that up here as well
// TODO use gameTime instead of t second ticker...
// ///////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////
var lastHitTime = 0; var lastHitTime = 0;
@ -122,7 +118,7 @@ function eventGameInit()
} }
} }
} }
setGlobalTimer("checkEndConditions", 100); setTimer("checkEndConditions", 100);
} }
// ///////////////////////////////////////////////////////////////// // /////////////////////////////////////////////////////////////////

View File

@ -36,6 +36,7 @@
#include "intdisplay.h" #include "intdisplay.h"
#include "mission.h" #include "mission.h"
#include "projectile.h" #include "projectile.h"
#include "qtscript.h"
#include "random.h" #include "random.h"
#include "research.h" #include "research.h"
#include "scriptcb.h" #include "scriptcb.h"
@ -998,6 +999,7 @@ void actionUpdateDroid(DROID *psDroid)
{ {
//the script can call startMission for this callback for offworld missions //the script can call startMission for this callback for offworld missions
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL); eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);
triggerEvent(TRIGGER_START_LEVEL);
} }
} }
} }

View File

@ -68,6 +68,7 @@
#include "mission.h" #include "mission.h"
#include "modding.h" #include "modding.h"
#include "multiplay.h" #include "multiplay.h"
#include "qtscript.h"
#include "research.h" #include "research.h"
#include "scripttabs.h" #include "scripttabs.h"
#include "seqdisp.h" #include "seqdisp.h"
@ -821,6 +822,7 @@ static void startGameLoop(void)
if (game.type == SKIRMISH) if (game.type == SKIRMISH)
{ {
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL); eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);
triggerEvent(TRIGGER_START_LEVEL);
} }
screen_disableMapPreview(); screen_disableMapPreview();
} }

View File

@ -42,6 +42,7 @@
#include "intorder.h" #include "intorder.h"
#include "orderdef.h" #include "orderdef.h"
#include "transporter.h" #include "transporter.h"
#include "qtscript.h"
#include "group.h" #include "group.h"
#include "cmddroid.h" #include "cmddroid.h"
#include "lib/script/script.h" #include "lib/script/script.h"
@ -332,6 +333,7 @@ void orderUpdateDroid(DROID *psDroid)
{ {
// the script can call startMission for this callback for offworld missions // the script can call startMission for this callback for offworld missions
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL); eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);
triggerEvent(TRIGGER_START_LEVEL);
} }
} }
} }
@ -420,6 +422,7 @@ void orderUpdateDroid(DROID *psDroid)
{ {
//the script can call startMission for this callback for offworld missions //the script can call startMission for this callback for offworld missions
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL); eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);
triggerEvent(TRIGGER_START_LEVEL);
/* clear order */ /* clear order */
psDroid->order = DORDER_NONE; psDroid->order = DORDER_NONE;
@ -1156,6 +1159,7 @@ void orderUpdateDroid(DROID *psDroid)
{ {
// the script can call startMission for this callback for offworld missions // the script can call startMission for this callback for offworld missions
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL); eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);
triggerEvent(TRIGGER_START_LEVEL);
} }
} }
} }

View File

@ -41,16 +41,23 @@
#include "qtscriptfuncs.h" #include "qtscriptfuncs.h"
enum timerType
{
TIMER_REPEAT, TIMER_ONESHOT_READY, TIMER_ONESHOT_DONE
};
struct timerNode struct timerNode
{ {
QString function; QString function;
QScriptEngine *engine; QScriptEngine *engine;
QString baseobj; int baseobj;
int frameTime; int frameTime;
int ms; int ms;
int player; int player;
timerType type;
timerNode() {} timerNode() {}
timerNode(QScriptEngine *caller, QString val, int plr, int frame) : function(val) { player = plr; ms = frame; frameTime = frame + gameTime; engine = caller; } timerNode(QScriptEngine *caller, QString val, int plr, int frame)
: function(val), engine(caller), baseobj(-1), frameTime(frame + gameTime), ms(frame), player(plr), type(TIMER_REPEAT) {}
bool operator== (const timerNode &t) { return function == t.function && player == t.player; } bool operator== (const timerNode &t) { return function == t.function && player == t.player; }
// implement operator less TODO // implement operator less TODO
}; };
@ -112,28 +119,41 @@ static QScriptValue js_removeTimer(QScriptContext *context, QScriptEngine *engin
return QScriptValue(); return QScriptValue();
} }
/// Special scripting function that registers a non-specific timer event. Note: Functions must be passed /// Special scripting function that registers a timer event. Note: Functions must be passed
/// quoted, otherwise they will be inlined! /// quoted, otherwise they will be inlined! If a third parameter is passed, this must be an
static QScriptValue js_setGlobalTimer(QScriptContext *context, QScriptEngine *engine) /// object, which is then passed to the timer. If the object is dead, the timer stops.
static QScriptValue js_setTimer(QScriptContext *context, QScriptEngine *engine)
{ {
QString funcName = context->argument(0).toString(); QString funcName = context->argument(0).toString();
QScriptValue ms = context->argument(1); QScriptValue ms = context->argument(1);
int player = engine->globalObject().property("me").toInt32(); int player = engine->globalObject().property("me").toInt32();
timerNode node(engine, funcName, player, ms.toInt32() + gameTime); timerNode node(engine, funcName, player, ms.toInt32());
if (context->argumentCount() == 3)
{
QScriptValue obj = context->argument(2);
node.baseobj = obj.property("id").toInt32();
}
node.type = TIMER_REPEAT;
timers.push_back(node); timers.push_back(node);
return QScriptValue(); return QScriptValue();
} }
/// Special scripting function that registers a object-specific timer event. Note: Functions must be passed /// Special scripting function that queues up a function to call once at a later time frame.
/// quoted, otherwise they will be inlined! /// Note: Functions must be passed quoted, otherwise they will be inlined! If a third
static QScriptValue js_setObjectTimer(QScriptContext *context, QScriptEngine *engine) /// parameter is passed, this must be an object, which is then passed to the timer. If
/// the object is dead, the timer stops.
static QScriptValue js_queue(QScriptContext *context, QScriptEngine *engine)
{ {
QString funcName = context->argument(0).toString(); QString funcName = context->argument(0).toString();
QScriptValue ms = context->argument(1); QScriptValue ms = context->argument(1);
QScriptValue obj = context->argument(2);
int player = engine->globalObject().property("me").toInt32(); int player = engine->globalObject().property("me").toInt32();
timerNode node(engine, funcName, player, ms.toInt32()); timerNode node(engine, funcName, player, ms.toInt32());
node.baseobj = obj.toString(); if (context->argumentCount() == 3)
{
QScriptValue obj = context->argument(2);
node.baseobj = obj.property("id").toInt32();
}
node.type = TIMER_ONESHOT_READY;
timers.push_back(node); timers.push_back(node);
return QScriptValue(); return QScriptValue();
} }
@ -195,10 +215,24 @@ bool updateScripts()
engine->globalObject().setProperty("gameTime", gameTime, QScriptValue::ReadOnly | QScriptValue::Undeletable); engine->globalObject().setProperty("gameTime", gameTime, QScriptValue::ReadOnly | QScriptValue::Undeletable);
} }
// Weed out dead timers
for (int i = 0; i < timers.count(); )
{
const timerNode node = timers.at(i);
if (node.type == TIMER_ONESHOT_DONE || (node.baseobj > NOT_CURRENT_LIST && !IdToPointer(node.baseobj, node.player)))
{
timers.removeAt(i);
}
else
{
i++;
}
}
// Check for timers, and run them if applicable. // Check for timers, and run them if applicable.
// TODO - load balancing // TODO - load balancing
QList<timerNode> runlist; // make a new list here, since we might trample all over the timer list during execution QList<timerNode> runlist; // make a new list here, since we might trample all over the timer list during execution
QList<timerNode>::iterator iter; QList<timerNode>::iterator iter;
// Weed out dead timers
for (iter = timers.begin(); iter != timers.end(); iter++) for (iter = timers.begin(); iter != timers.end(); iter++)
{ {
if (iter->frameTime <= gameTime) if (iter->frameTime <= gameTime)
@ -206,10 +240,20 @@ bool updateScripts()
iter->frameTime = iter->ms + gameTime; // update for next invokation iter->frameTime = iter->ms + gameTime; // update for next invokation
runlist.append(*iter); runlist.append(*iter);
} }
else if (iter->type == TIMER_ONESHOT_READY)
{
iter->type = TIMER_ONESHOT_DONE;
runlist.append(*iter);
}
} }
for (iter = runlist.begin(); iter != runlist.end(); iter++) for (iter = runlist.begin(); iter != runlist.end(); iter++)
{ {
callFunction(iter->engine, iter->function, QScriptValueList()); QScriptValueList args;
if (iter->baseobj > NOT_CURRENT_LIST)
{
args += convObj(IdToPointer(iter->baseobj, iter->player), iter->engine);
}
callFunction(iter->engine, iter->function, args);
} }
return true; return true;
} }
@ -241,8 +285,8 @@ bool loadPlayerScript(QString path, int player, int difficulty)
ASSERT_OR_RETURN(false, !engine->hasUncaughtException(), "Uncaught exception at line %d, file %s: %s", ASSERT_OR_RETURN(false, !engine->hasUncaughtException(), "Uncaught exception at line %d, file %s: %s",
engine->uncaughtExceptionLineNumber(), path.toAscii().constData(), result.toString().toAscii().constData()); engine->uncaughtExceptionLineNumber(), path.toAscii().constData(), result.toString().toAscii().constData());
// Special functions // Special functions
engine->globalObject().setProperty("setGlobalTimer", engine->newFunction(js_setGlobalTimer)); engine->globalObject().setProperty("setTimer", engine->newFunction(js_setTimer));
engine->globalObject().setProperty("setObjectTimer", engine->newFunction(js_setObjectTimer)); engine->globalObject().setProperty("queue", engine->newFunction(js_queue));
engine->globalObject().setProperty("removeTimer", engine->newFunction(js_removeTimer)); engine->globalObject().setProperty("removeTimer", engine->newFunction(js_removeTimer));
engine->globalObject().setProperty("include", engine->newFunction(js_include)); engine->globalObject().setProperty("include", engine->newFunction(js_include));
@ -300,11 +344,12 @@ bool saveScriptStates(const char *filename)
ini.beginGroup(QString("triggers_") + QString::number(i)); ini.beginGroup(QString("triggers_") + QString::number(i));
ini.setValue("player", node.player); ini.setValue("player", node.player);
ini.setValue("function", node.function.toUtf8().constData()); ini.setValue("function", node.function.toUtf8().constData());
if (!node.baseobj.isEmpty()) if (!node.baseobj >= NOT_CURRENT_LIST)
{ {
ini.setValue("object", node.baseobj.toUtf8().constData()); ini.setValue("object", QVariant(node.baseobj));
} }
ini.setValue("frame", node.frameTime); ini.setValue("frame", node.frameTime);
ini.setValue("type", (int)node.type);
ini.setValue("ms", node.ms); ini.setValue("ms", node.ms);
ini.endGroup(); ini.endGroup();
} }
@ -343,7 +388,8 @@ bool loadScriptStates(const char *filename)
debug(LOG_SAVE, "Registering trigger %d for player %d", i, node.player); debug(LOG_SAVE, "Registering trigger %d for player %d", i, node.player);
node.engine = findEngineForPlayer(node.player); node.engine = findEngineForPlayer(node.player);
node.function = ini.value("function").toString(); node.function = ini.value("function").toString();
node.baseobj = ini.value("baseobj", QString()).toString(); node.baseobj = ini.value("baseobj", -1).toInt();
node.type = (timerType)ini.value("type", TIMER_REPEAT).toInt();
timers.push_back(node); timers.push_back(node);
ini.endGroup(); ini.endGroup();
} }
@ -379,6 +425,9 @@ bool triggerEvent(SCRIPT_TRIGGER_TYPE trigger)
case TRIGGER_GAME_INIT: case TRIGGER_GAME_INIT:
callFunction(engine, "eventGameInit", QScriptValueList()); callFunction(engine, "eventGameInit", QScriptValueList());
break; break;
case TRIGGER_START_LEVEL:
callFunction(engine, "eventStartLevel", QScriptValueList());
break;
} }
} }
return true; return true;

View File

@ -27,7 +27,8 @@
enum SCRIPT_TRIGGER_TYPE enum SCRIPT_TRIGGER_TYPE
{ {
TRIGGER_GAME_INIT TRIGGER_GAME_INIT,
TRIGGER_START_LEVEL
}; };
// ---------------------------------------------- // ----------------------------------------------

View File

@ -59,6 +59,7 @@ QScriptValue convStructure(STRUCTURE *psStruct, QScriptEngine *engine)
value.setProperty("y", psStruct->pos.y, QScriptValue::ReadOnly); value.setProperty("y", psStruct->pos.y, QScriptValue::ReadOnly);
value.setProperty("z", psStruct->pos.z, QScriptValue::ReadOnly); value.setProperty("z", psStruct->pos.z, QScriptValue::ReadOnly);
value.setProperty("player", psStruct->player, QScriptValue::ReadOnly); value.setProperty("player", psStruct->player, QScriptValue::ReadOnly);
value.setProperty("selected", psStruct->selected, QScriptValue::ReadOnly);
return value; return value;
} }
@ -71,6 +72,7 @@ QScriptValue convDroid(DROID *psDroid, QScriptEngine *engine)
value.setProperty("y", psDroid->pos.y, QScriptValue::ReadOnly); value.setProperty("y", psDroid->pos.y, QScriptValue::ReadOnly);
value.setProperty("z", psDroid->pos.z, QScriptValue::ReadOnly); value.setProperty("z", psDroid->pos.z, QScriptValue::ReadOnly);
value.setProperty("player", psDroid->player, QScriptValue::ReadOnly); value.setProperty("player", psDroid->player, QScriptValue::ReadOnly);
value.setProperty("selected", psDroid->selected, QScriptValue::ReadOnly);
return value; return value;
} }
@ -83,6 +85,7 @@ QScriptValue convObj(BASE_OBJECT *psObj, QScriptEngine *engine)
value.setProperty("y", psObj->pos.y, QScriptValue::ReadOnly); value.setProperty("y", psObj->pos.y, QScriptValue::ReadOnly);
value.setProperty("z", psObj->pos.z, QScriptValue::ReadOnly); value.setProperty("z", psObj->pos.z, QScriptValue::ReadOnly);
value.setProperty("player", psObj->player, QScriptValue::ReadOnly); value.setProperty("player", psObj->player, QScriptValue::ReadOnly);
value.setProperty("selected", psObj->selected, QScriptValue::ReadOnly);
return value; return value;
} }
@ -654,6 +657,12 @@ static QScriptValue js_translate(QScriptContext *context, QScriptEngine *engine)
return QScriptValue(gettext(context->argument(0).toString().toUtf8().constData())); return QScriptValue(gettext(context->argument(0).toString().toUtf8().constData()));
} }
static QScriptValue js_playerPower(QScriptContext *context, QScriptEngine *engine)
{
int player = context->argument(0).toInt32();
return QScriptValue(getPower(player));
}
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
// Register functions with scripting system // Register functions with scripting system
@ -677,6 +686,7 @@ bool registerFunctions(QScriptEngine *engine)
engine->globalObject().setProperty("groupAddDroid", engine->newFunction(js_groupAddDroid)); engine->globalObject().setProperty("groupAddDroid", engine->newFunction(js_groupAddDroid));
engine->globalObject().setProperty("groupSize", engine->newFunction(js_groupSize)); engine->globalObject().setProperty("groupSize", engine->newFunction(js_groupSize));
engine->globalObject().setProperty("orderDroidLoc", engine->newFunction(js_orderDroidLoc)); engine->globalObject().setProperty("orderDroidLoc", engine->newFunction(js_orderDroidLoc));
engine->globalObject().setProperty("playerPower", engine->newFunction(js_playerPower));
// Functions that operate on the current player only // Functions that operate on the current player only
engine->globalObject().setProperty("centreView", engine->newFunction(js_centreView)); engine->globalObject().setProperty("centreView", engine->newFunction(js_centreView));

View File

@ -41,6 +41,7 @@
#include "mission.h" #include "mission.h"
#include "objects.h" #include "objects.h"
#include "display.h" #include "display.h"
#include "qtscript.h"
#include "lib/script/script.h" #include "lib/script/script.h"
#include "scripttabs.h" #include "scripttabs.h"
#include "order.h" #include "order.h"
@ -1638,6 +1639,7 @@ bool updateTransporter(DROID *psTransporter)
//the script can call startMission for this callback for offworld missions //the script can call startMission for this callback for offworld missions
eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL); eventFireCallbackTrigger((TRIGGER_TYPE)CALL_START_NEXT_LEVEL);
triggerEvent(TRIGGER_START_LEVEL);
// clear order // clear order
psTransporter->order = DORDER_NONE; psTransporter->order = DORDER_NONE;

View File

@ -111,6 +111,7 @@ QScriptValue convStructure(QScriptEngine *engine)
value.setProperty("y", 11, QScriptValue::ReadOnly); value.setProperty("y", 11, QScriptValue::ReadOnly);
value.setProperty("z", 0, QScriptValue::ReadOnly); value.setProperty("z", 0, QScriptValue::ReadOnly);
value.setProperty("player", 1, QScriptValue::ReadOnly); value.setProperty("player", 1, QScriptValue::ReadOnly);
value.setProperty("selected", 0, QScriptValue::ReadOnly);
return value; return value;
} }
@ -122,6 +123,7 @@ QScriptValue convDroid(QScriptEngine *engine)
value.setProperty("y", 11, QScriptValue::ReadOnly); value.setProperty("y", 11, QScriptValue::ReadOnly);
value.setProperty("z", 0, QScriptValue::ReadOnly); value.setProperty("z", 0, QScriptValue::ReadOnly);
value.setProperty("player", 1, QScriptValue::ReadOnly); value.setProperty("player", 1, QScriptValue::ReadOnly);
value.setProperty("selected", 0, QScriptValue::ReadOnly);
return value; return value;
} }
@ -133,6 +135,7 @@ QScriptValue convObj(QScriptEngine *engine)
value.setProperty("y", 11, QScriptValue::ReadOnly); value.setProperty("y", 11, QScriptValue::ReadOnly);
value.setProperty("z", 0, QScriptValue::ReadOnly); value.setProperty("z", 0, QScriptValue::ReadOnly);
value.setProperty("player", 1, QScriptValue::ReadOnly); value.setProperty("player", 1, QScriptValue::ReadOnly);
value.setProperty("selected", 0, QScriptValue::ReadOnly);
return value; return value;
} }
@ -442,13 +445,15 @@ static QScriptValue js_removeTimer(QScriptContext *context, QScriptEngine *engin
/// Special scripting function that registers a non-specific timer event. Note: Functions must be passed /// Special scripting function that registers a non-specific timer event. Note: Functions must be passed
/// quoted, otherwise they will be inlined! /// quoted, otherwise they will be inlined!
static QScriptValue js_setGlobalTimer(QScriptContext *context, QScriptEngine *engine) static QScriptValue js_setTimer(QScriptContext *context, QScriptEngine *engine)
{ {
ARG_COUNT_EXACT(2); ARG_COUNT_VAR(2, 3);
ARG_STRING(0); ARG_STRING(0);
ARG_NUMBER(1); ARG_NUMBER(1);
if (context->argumentCount() == 3) ARG_OBJ(2);
QString funcName = context->argument(0).toString(); QString funcName = context->argument(0).toString();
// TODO - check that a function by that name exists // TODO - check that a function by that name exists
// TODO - object argument
int player = engine->globalObject().property("me").toInt32(); int player = engine->globalObject().property("me").toInt32();
timerNode node(engine, funcName, player); timerNode node(engine, funcName, player);
timers.push_back(node); timers.push_back(node);
@ -457,14 +462,15 @@ static QScriptValue js_setGlobalTimer(QScriptContext *context, QScriptEngine *en
/// Special scripting function that registers a object-specific timer event. Note: Functions must be passed /// Special scripting function that registers a object-specific timer event. Note: Functions must be passed
/// quoted, otherwise they will be inlined! /// quoted, otherwise they will be inlined!
static QScriptValue js_setObjectTimer(QScriptContext *context, QScriptEngine *engine) static QScriptValue js_queue(QScriptContext *context, QScriptEngine *engine)
{ {
ARG_COUNT_EXACT(2); ARG_COUNT_VAR(2, 3);
ARG_STRING(0); ARG_STRING(0);
ARG_NUMBER(1); ARG_NUMBER(1);
ARG_OBJ(2); if (context->argumentCount() == 3) ARG_OBJ(2);
QString funcName = context->argument(0).toString(); QString funcName = context->argument(0).toString();
// TODO - check that a function by that name exists // TODO - check that a function by that name exists
// TODO - object argument
int player = engine->globalObject().property("me").toInt32(); int player = engine->globalObject().property("me").toInt32();
timerNode node(engine, funcName, player); timerNode node(engine, funcName, player);
timers.push_back(node); timers.push_back(node);
@ -536,8 +542,8 @@ bool testPlayerScript(QString path, int player, int difficulty)
return false; return false;
} }
// Special functions // Special functions
engine->globalObject().setProperty("setGlobalTimer", engine->newFunction(js_setGlobalTimer)); engine->globalObject().setProperty("setTimer", engine->newFunction(js_setTimer));
engine->globalObject().setProperty("setObjectTimer", engine->newFunction(js_setObjectTimer)); engine->globalObject().setProperty("queue", engine->newFunction(js_queue));
engine->globalObject().setProperty("removeTimer", engine->newFunction(js_removeTimer)); engine->globalObject().setProperty("removeTimer", engine->newFunction(js_removeTimer));
engine->globalObject().setProperty("include", engine->newFunction(js_include)); engine->globalObject().setProperty("include", engine->newFunction(js_include));