1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 10:19:55 +00:00
openmw-tes3mp/old_d_version/monster/modules/timer.d
2009-12-19 21:31:22 +00:00

190 lines
5.1 KiB
D

/*
Monster - an advanced game scripting language
Copyright (C) 2007-2009 Nicolay Korslund
Email: <korslund@gmail.com>
WWW: http://monster.snaptoad.com/
This file (timer.d) is part of the Monster script language
package.
Monster is distributed as free software: you can redistribute it
and/or modify it under the terms of the GNU General Public License
version 3, as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
version 3 along with this program. If not, see
http://www.gnu.org/licenses/ .
*/
// This module contains (or will contain) various routines for
// timing. It is also home of the ubiquitous "sleep" idle function.
module monster.modules.timer;
import std.stdio;
// For some utterly idiotic reason, DMD's public imports will suddenly
// stop working from time to time.
import monster.vm.mclass;
import monster.vm.mobject;
import monster.vm.stack;
import monster.vm.thread;
import monster.vm.idlefunction;
import monster.monster;
import monster.options;
const char[] moduleDef =
"singleton timer;
idle sleep(float secs);
"; //"
static if(timer_useClock)
{
// Sleep a given amount of time. This implementation uses the system
// clock.
import std.date;
class IdleSleep_SystemClock : IdleFunction
{
override:
IS initiate(Thread* cn)
{
// Get the parameter
float secs = stack.popFloat;
// Get current time
cn.idleData.l = getUTCtime();
// Calculate when we should return
cn.idleData.l += secs*TicksPerSecond;
// Schedule us
return IS.Poll;
}
bool hasFinished(Thread* cn)
{
// Is it time?
return getUTCtime() >= cn.idleData.l;
}
}
} else { // If timer_useClock is NOT set:
// This implementation uses a user-driven timer instead of the system
// clock. It's more efficient, but requires the user to update the
// given timer manually each frame. The default sleep (timer.sleep) is
// bound to the default timer, but it's possible to create multiple
// independent timers.
class IdleSleep_Timer : IdleFunction
{
override:
IS initiate(Thread* cn)
{
// The timer is stored in the object's 'extra' pointer
auto t = cast(SleepManager)cn.extraData.obj;
assert(t !is null);
// Calculate the return time
cn.idleData.l = t.current + cast(long)(t.tickSize*stack.popFloat);
// Schedule us
return IS.Poll;
}
bool hasFinished(Thread* cn)
{
// Get the timer
auto t = cast(SleepManager)cn.extraData.obj;
assert(t !is null);
// Is it time?
return t.current >= cn.idleData.l;
}
}
// A manually updated timer. This can be improved quite a lot: Most
// sleep operations (depending on application of course) will skip
// many frames before they return. For example, for sleep(0.5) at 100
// fps, hasFinished will return false approximately 50 times before
// returning true. For bigger sleep values and a large number of
// objects, the impact of this is significant. A good solution would
// be to pool scheduled objects together and only perform one check on
// the entire pool. If the pool is due, all the nodes within it are
// inserted into the scheduler for detailed checking. We could have a
// series of such pools, ordered by expiration time, so that we only
// ever need to check the first pool in the list. The optimal pool
// interval, number of pools etc depends on the application and the
// fps - but it should be possible to find some reasonable defaults. A
// more generalized priority queue implementation is also possible.
class SleepManager
{
private:
// Instance of the timer class that is associated with this timer
MonsterObject *tobj;
// Current tick count
long current;
public:
// Specify a Monster object to associate with this timer. Use 'null'
// if you don't need an object.
this(MonsterObject *obj)
{
if(obj is null) return;
tobj = obj;
tobj.getExtra(_timerClass).obj = this;
}
// By default, create a new object
this()
{ this(_timerClass.createObject); }
// Number of 'ticks' per second
static const long tickSize = 1000;
// Reset the timer to zero
void reset() { current = 0; }
// Return the total number of elapsed seconds since start (or last
// reset)
double read() { return current/cast(double)tickSize; }
// Add time to the timer.
void add(double d) { current += cast(long)(tickSize*d); }
void addl(long l) { current += l; }
MonsterObject *getObj() { return tobj; }
}
SleepManager idleTime;
}
MonsterClass _timerClass;
void initTimerModule()
{
if(_timerClass !is null)
return;
_timerClass = vm.loadString(moduleDef, "timer");
static if(timer_useClock)
{
_timerClass.bind("sleep", new IdleSleep_SystemClock);
}
else
{
assert(idleTime is null);
idleTime = new SleepManager(_timerClass.getSing());
_timerClass.bind("sleep", new IdleSleep_Timer);
}
}