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
groupAddArea(attackGroup, 0, 0, (mapWidth * 128), (mapHeight * 128));
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

View File

@ -4,10 +4,6 @@
// contains the rules for starting and ending a game.
// 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;
@ -122,7 +118,7 @@ function eventGameInit()
}
}
}
setGlobalTimer("checkEndConditions", 100);
setTimer("checkEndConditions", 100);
}
// /////////////////////////////////////////////////////////////////

View File

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

View File

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

View File

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

View File

@ -41,16 +41,23 @@
#include "qtscriptfuncs.h"
enum timerType
{
TIMER_REPEAT, TIMER_ONESHOT_READY, TIMER_ONESHOT_DONE
};
struct timerNode
{
QString function;
QScriptEngine *engine;
QString baseobj;
int baseobj;
int frameTime;
int ms;
int player;
timerType type;
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; }
// implement operator less TODO
};
@ -112,28 +119,41 @@ static QScriptValue js_removeTimer(QScriptContext *context, QScriptEngine *engin
return QScriptValue();
}
/// Special scripting function that registers a non-specific timer event. Note: Functions must be passed
/// quoted, otherwise they will be inlined!
static QScriptValue js_setGlobalTimer(QScriptContext *context, QScriptEngine *engine)
/// Special scripting function that registers a timer event. Note: Functions must be passed
/// quoted, otherwise they will be inlined! If a third 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_setTimer(QScriptContext *context, QScriptEngine *engine)
{
QString funcName = context->argument(0).toString();
QScriptValue ms = context->argument(1);
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);
return QScriptValue();
}
/// Special scripting function that registers a object-specific timer event. Note: Functions must be passed
/// quoted, otherwise they will be inlined!
static QScriptValue js_setObjectTimer(QScriptContext *context, QScriptEngine *engine)
/// Special scripting function that queues up a function to call once at a later time frame.
/// Note: Functions must be passed quoted, otherwise they will be inlined! If a third
/// 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();
QScriptValue ms = context->argument(1);
QScriptValue obj = context->argument(2);
int player = engine->globalObject().property("me").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);
return QScriptValue();
}
@ -195,10 +215,24 @@ bool updateScripts()
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.
// TODO - load balancing
QList<timerNode> runlist; // make a new list here, since we might trample all over the timer list during execution
QList<timerNode>::iterator iter;
// Weed out dead timers
for (iter = timers.begin(); iter != timers.end(); iter++)
{
if (iter->frameTime <= gameTime)
@ -206,10 +240,20 @@ bool updateScripts()
iter->frameTime = iter->ms + gameTime; // update for next invokation
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++)
{
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;
}
@ -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",
engine->uncaughtExceptionLineNumber(), path.toAscii().constData(), result.toString().toAscii().constData());
// Special functions
engine->globalObject().setProperty("setGlobalTimer", engine->newFunction(js_setGlobalTimer));
engine->globalObject().setProperty("setObjectTimer", engine->newFunction(js_setObjectTimer));
engine->globalObject().setProperty("setTimer", engine->newFunction(js_setTimer));
engine->globalObject().setProperty("queue", engine->newFunction(js_queue));
engine->globalObject().setProperty("removeTimer", engine->newFunction(js_removeTimer));
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.setValue("player", node.player);
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("type", (int)node.type);
ini.setValue("ms", node.ms);
ini.endGroup();
}
@ -343,7 +388,8 @@ bool loadScriptStates(const char *filename)
debug(LOG_SAVE, "Registering trigger %d for player %d", i, node.player);
node.engine = findEngineForPlayer(node.player);
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);
ini.endGroup();
}
@ -379,6 +425,9 @@ bool triggerEvent(SCRIPT_TRIGGER_TYPE trigger)
case TRIGGER_GAME_INIT:
callFunction(engine, "eventGameInit", QScriptValueList());
break;
case TRIGGER_START_LEVEL:
callFunction(engine, "eventStartLevel", QScriptValueList());
break;
}
}
return true;

View File

@ -27,7 +27,8 @@
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("z", psStruct->pos.z, QScriptValue::ReadOnly);
value.setProperty("player", psStruct->player, QScriptValue::ReadOnly);
value.setProperty("selected", psStruct->selected, QScriptValue::ReadOnly);
return value;
}
@ -71,6 +72,7 @@ QScriptValue convDroid(DROID *psDroid, QScriptEngine *engine)
value.setProperty("y", psDroid->pos.y, QScriptValue::ReadOnly);
value.setProperty("z", psDroid->pos.z, QScriptValue::ReadOnly);
value.setProperty("player", psDroid->player, QScriptValue::ReadOnly);
value.setProperty("selected", psDroid->selected, QScriptValue::ReadOnly);
return value;
}
@ -83,6 +85,7 @@ QScriptValue convObj(BASE_OBJECT *psObj, QScriptEngine *engine)
value.setProperty("y", psObj->pos.y, QScriptValue::ReadOnly);
value.setProperty("z", psObj->pos.z, QScriptValue::ReadOnly);
value.setProperty("player", psObj->player, QScriptValue::ReadOnly);
value.setProperty("selected", psObj->selected, QScriptValue::ReadOnly);
return value;
}
@ -654,6 +657,12 @@ static QScriptValue js_translate(QScriptContext *context, QScriptEngine *engine)
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
@ -677,6 +686,7 @@ bool registerFunctions(QScriptEngine *engine)
engine->globalObject().setProperty("groupAddDroid", engine->newFunction(js_groupAddDroid));
engine->globalObject().setProperty("groupSize", engine->newFunction(js_groupSize));
engine->globalObject().setProperty("orderDroidLoc", engine->newFunction(js_orderDroidLoc));
engine->globalObject().setProperty("playerPower", engine->newFunction(js_playerPower));
// Functions that operate on the current player only
engine->globalObject().setProperty("centreView", engine->newFunction(js_centreView));

View File

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

View File

@ -111,6 +111,7 @@ QScriptValue convStructure(QScriptEngine *engine)
value.setProperty("y", 11, QScriptValue::ReadOnly);
value.setProperty("z", 0, QScriptValue::ReadOnly);
value.setProperty("player", 1, QScriptValue::ReadOnly);
value.setProperty("selected", 0, QScriptValue::ReadOnly);
return value;
}
@ -122,6 +123,7 @@ QScriptValue convDroid(QScriptEngine *engine)
value.setProperty("y", 11, QScriptValue::ReadOnly);
value.setProperty("z", 0, QScriptValue::ReadOnly);
value.setProperty("player", 1, QScriptValue::ReadOnly);
value.setProperty("selected", 0, QScriptValue::ReadOnly);
return value;
}
@ -133,6 +135,7 @@ QScriptValue convObj(QScriptEngine *engine)
value.setProperty("y", 11, QScriptValue::ReadOnly);
value.setProperty("z", 0, QScriptValue::ReadOnly);
value.setProperty("player", 1, QScriptValue::ReadOnly);
value.setProperty("selected", 0, QScriptValue::ReadOnly);
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
/// 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_NUMBER(1);
if (context->argumentCount() == 3) ARG_OBJ(2);
QString funcName = context->argument(0).toString();
// TODO - check that a function by that name exists
// TODO - object argument
int player = engine->globalObject().property("me").toInt32();
timerNode node(engine, funcName, player);
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
/// 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_NUMBER(1);
ARG_OBJ(2);
if (context->argumentCount() == 3) ARG_OBJ(2);
QString funcName = context->argument(0).toString();
// TODO - check that a function by that name exists
// TODO - object argument
int player = engine->globalObject().property("me").toInt32();
timerNode node(engine, funcName, player);
timers.push_back(node);
@ -536,8 +542,8 @@ bool testPlayerScript(QString path, int player, int difficulty)
return false;
}
// Special functions
engine->globalObject().setProperty("setGlobalTimer", engine->newFunction(js_setGlobalTimer));
engine->globalObject().setProperty("setObjectTimer", engine->newFunction(js_setObjectTimer));
engine->globalObject().setProperty("setTimer", engine->newFunction(js_setTimer));
engine->globalObject().setProperty("queue", engine->newFunction(js_queue));
engine->globalObject().setProperty("removeTimer", engine->newFunction(js_removeTimer));
engine->globalObject().setProperty("include", engine->newFunction(js_include));