Update to latest Monster, and minor simplification of the scripts.

git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@83 ea6a568a-9f4f-0410-981a-c910a81bb256
pull/7/head
nkorslund 16 years ago
parent 656007cf01
commit 6dcf6c565a

@ -31,7 +31,6 @@ import std.path;
import monster.util.aa;
import monster.util.string;
import util.random;
import bsa.bsafile;
@ -51,9 +50,6 @@ import nif.nif;
import core.filefinder;
//import core.config;
// Random number generator
DRand rnd;
// These are handles for various resources. They may refer to a file
// in the file system, an entry in a BSA archive, or point to an
// already loaded resource. Resource handles that are not implemented
@ -129,8 +125,6 @@ struct ResourceManager
void initResources()
{
rnd = new DRand;
bsa = new FileFinder(config.bsaDir, "bsa", Recurse.No);
archives.length = bsa.length;
foreach(int i, ref BSAFile f; archives)

@ -25,7 +25,7 @@ module esm.loadlevlist;
import esm.imports;
import esm.loadcrea;
import util.random;
import monster.modules.random : randInt;
/*
* Leveled lists. Since these have identical layout, I only bothered
@ -103,7 +103,7 @@ struct LeveledListT(bool creature)
// TODO: Find out if this is indeed correct.
// Test if no creature is to be selected
if(rnd.randInt(0, 255) < chanceNone) return -1;
if(randInt(0, 255) < chanceNone) return -1;
// Find the highest item below or equal to the Player level
for(i=list.length-1; i>=0; i--)
@ -126,7 +126,7 @@ struct LeveledListT(bool creature)
}
// Select a random item
return rnd.randInt(bottom, top);
return randInt(bottom, top);
}
void load()

@ -28,7 +28,7 @@ import std.stdio;
import core.memory;
import esm.esmmain;
import monster.util.string;
import mscripts.object;
import mscripts.setup;
import std.gc;
import gcstats;

@ -274,6 +274,8 @@ struct Function
auto fd = new FuncDeclaration;
// Parse and comile the function
fd.parseFile(tokens, this);
name.str = file;
name.loc.fname = file;
fd.resolve(mc.sc);
fd.resolveBody();
fd.compile();
@ -287,7 +289,7 @@ struct Function
private:
static const char[] int_class = "class _Func_Internal_;";
static const char[] int_class = "class _ScriptFile_;";
static MonsterClass int_mc;
static MonsterObject *int_mo;
}

@ -338,6 +338,13 @@ abstract class Scope
void registerImport(MonsterClass mc)
{ registerImport(new ImportHolder(mc)); }
// Even more user-friendly version. Takes a list of class names.
void registerImport(char[][] cls ...)
{
foreach(c; cls)
registerImport(MonsterClass.find(c));
}
// Used for summing up stack level. Redeclared in StackScope.
int getTotLocals() { return 0; }
int getLocals() { assert(0); }

@ -3,6 +3,7 @@ module monster.modules.all;
import monster.modules.io;
import monster.modules.timer;
import monster.modules.frames;
import monster.modules.random;
import monster.modules.threads;
void initAllModules()
@ -10,5 +11,6 @@ void initAllModules()
initIOModule();
initTimerModule();
initFramesModule();
initThreadModule;
initThreadModule();
initRandomModule();
}

@ -0,0 +1,49 @@
// This module provides simple random number generation. Since this is
// intended for game development, speed and simplicity is favored over
// flexibility and random number quality.
module monster.modules.random;
import monster.monster;
import std.random;
const char[] moduleDef =
"module random;
native uint rand(); // Return a number between 0 and uint.max, inclusive
native float frand(); // Return a number between 0 and 1, inclusive
// Return a random number between a and b, inclusive. Allows negative
// numbers, and works with a>b, a<b and a==b
native int randInt(int a, int b);
"; //"
const float _frandFactor = 1.0/uint.max;
// Return a random integer between a and b, inclusive.
int randInt(int a, int b)
out(result)
{
// Result must be in range m <= result <= M, where m=min(a,b) and M=max(a,b)
if(b >= a) assert( (a <= result) && (result <= b) );
else if(a > b) assert( (b <= result) && (result <= a) );
}
body
{
if(a>b) return cast(int)(rand() % (a-b+1)) + b;
else if(b>a) return cast(int)(rand() % (b-a+1)) + a;
else return a;
}
void initRandomModule()
{
static MonsterClass mc;
if(mc !is null) return;
mc = new MonsterClass(MC.String, moduleDef, "random");
mc.bind("rand", { stack.pushInt(rand()); });
mc.bind("frand", { stack.pushFloat(rand()*_frandFactor); });
mc.bind("randInt", { stack.pushInt(randInt(stack.popInt,
stack.popInt)); });
}

@ -0,0 +1,8 @@
module random;
native uint rand(); // Return a number between 0 and uint.max, inclusive
native float frand(); // Return a number between 0 and 1, inclusive
// Return a random number between a and b, inclusive. Allows negative
// numbers, and works with a>b, a<b and a==b
native int randInt(int a, int b);

@ -28,20 +28,12 @@ native restart();
// Call a (paused) thread directly - returns when the thread exits or
// calls an idle function.
idle resume();
idle call();
// Wait for a thread to finish. Will not return until the thread is
// dead.
idle wait();
// Call a function as a thread
thread call(char[] name)
{
var t = create(name);
t.resume();
return t;
}
// Start a function as a thread in the background
thread start(char[] name)
{
@ -51,6 +43,18 @@ thread start(char[] name)
}
"; //"
/*
The char[] name stuff above will of course be replaced with real
function pointers once those are done. We will also add:
function() wrap(function f())
{
var t = create(f);
return {{ t.call(); }
}
*/
MonsterObject *trdSing;
class Kill : IdleFunction
@ -134,49 +138,34 @@ void create()
nd = cthread.fstack.list.getNext(nd);
auto mo = nd.obj;
// Find the function
auto fn = mo.cls.findFunction(name);
if(fn.paramSize > 0)
fail("create(): function " ~ name ~ " cannot have parameters");
// Create a new thread
Thread *trd = Thread.getNew();
// Schedule the thread run the next frame
trd.pushFunc(fn, mo);
assert(trd.isPaused);
// This will mess with the stack frame though, so set it up
// correctly.
trd.fstack.cur.frame = stack.getStartInt(0);
cthread.fstack.restoreFrame();
auto trd = mo.thread(name);
stack.pushObject(createObj(trd));
}
// Resume is used to restore a thread that was previously paused. It
// will enter the thread immediately, like call. If you wish to run it
// later, use restart instead.
class Resume : IdleFunction
// Call is used to restore a thread that was previously paused. It
// will enter the thread immediately, like a normal function call, but
// it will still run in its own thread. If you only wish to schedule
// it for later, use restart instead.
class Call : IdleFunction
{
override:
IS initiate(Thread *t)
{
if(params.obj is trdSing)
fail("Cannot use resume() on our own thread.");
fail("Cannot use call() on our own thread.");
// Get the thread we're resuming
auto trd = getOwner();
if(trd is t)
fail("Cannot use resume() on our own thread.");
fail("Cannot use call() on our own thread.");
if(trd.isDead)
fail("Cannot resume a dead thread.");
fail("Cannot call a dead thread.");
if(!trd.isPaused)
fail("Can only use resume() on paused threads");
fail("Can only use call() on paused threads");
// Background the current thread. Move it to the pause list
// first, so background doesn't inadvertently delete it.
@ -234,18 +223,7 @@ class Wait : IdleFunction
}
void restart()
{
auto trd = getOwner();
if(trd.isDead)
fail("Cannot restart a dead thread");
if(!trd.isPaused)
fail("Can only use restart() on paused threads");
// Move to the runlist
trd.moveTo(scheduler.runNext);
}
{ getOwner().restart(); }
void isDead()
{ stack.pushBool(getOwner().isDead); }
@ -270,7 +248,7 @@ void initThreadModule()
trdSing = _threadClass.getSing();
_threadClass.bind("kill", new Kill);
_threadClass.bind("resume", new Resume);
_threadClass.bind("call", new Call);
_threadClass.bind("pause", new Pause);
_threadClass.bind("wait", new Wait);

@ -175,8 +175,6 @@ struct BufferList(int size)
void free(void* p) { remove(cast(ValuePtr)p); }
}
import std.stdio;
struct Buffers
{
static:
@ -204,8 +202,7 @@ struct Buffers
// Too large for our lists - just use malloc
else
{
writefln("WARNING: using malloc for %s ints (%s bytes)",
size, size*int.sizeof);
//writefln("WARNING: using malloc for %s ints (%s bytes)", size, size*int.sizeof);
return ( cast(int*)malloc(size*int.sizeof) )[0..size];
}
}

@ -131,7 +131,9 @@ struct StackPoint
name = func.name.str;
if(isIdle) type = "idle";
else type = "function";
else if(isNormal) type = "script";
else if(isNative) type = "native";
else assert(0);
}
// Function location and name
@ -322,8 +324,17 @@ struct FunctionStack
{
char[] res;
int i;
foreach(ref c; list)
res = c.toString ~ '\n' ~ res;
{
char[] msg;
if(i == 0)
msg = " (<---- current function)";
else if(i == list.length-1)
msg = " (<---- start of function stack)";
res = c.toString ~ msg ~ '\n' ~ res;
i++;
}
return "Trace:\n" ~ res;
}

@ -27,6 +27,7 @@ import monster.vm.thread;
import monster.vm.error;
import monster.vm.mclass;
import monster.vm.arrays;
import monster.vm.stack;
import monster.util.freelist;
import monster.util.list;
@ -34,6 +35,7 @@ import monster.util.list;
import monster.compiler.states;
import monster.compiler.variables;
import monster.compiler.scopes;
import monster.compiler.functions;
import std.string;
import std.stdio;
@ -262,6 +264,44 @@ struct MonsterObject
cls.findFunction(name).call(this);
}
// Create a paused thread that's set up to call the given
// function. It must be started with Thread.call() or
// Thread.restart().
Thread *thread(char[] name)
{ return thread(cls.findFunction(name)); }
Thread *thread(Function *fn)
{
assert(fn !is null);
if(fn.paramSize > 0)
fail("thread(): function " ~ fn.name.str ~ " cannot have parameters");
Thread *trd = Thread.getNew();
// Schedule the function to run the next frame
trd.pushFunc(fn, this);
assert(trd.isPaused);
assert(trd.fstack.cur !is null);
// pushFunc will mess with the stack frame though, so fix it.
trd.fstack.cur.frame = stack.getStart();
if(cthread !is null)
cthread.fstack.restoreFrame();
return trd;
}
// Create a thread containing the function and schedule it to start
// the next frame
Thread *start(char[] name)
{ return start(cls.findFunction(name)); }
Thread *start(Function *fn)
{
assert(fn !is null);
auto trd = thread(fn);
trd.restart();
return trd;
}
// Call a function non-virtually. In other words, ignore
// derived objects.
void nvcall(char[] name)

@ -139,12 +139,9 @@ struct CodeStack
}
// Get the pointer from the start of the stack
int *getStartInt(int pos)
int *getStart()
{
if(pos < 0 || pos >= getPos)
fail("CodeStack.getStartInt() pointer out of range");
return &data[pos];
return cast(int*)data.ptr;
}
// Get the pointer to an int at the given position backwards from

@ -124,6 +124,20 @@ struct Thread
return cn;
}
// Schedule the function to run the next frame. Can only be used on
// paused threads.
void restart()
{
if(isDead)
fail("Cannot restart a dead thread");
if(!isPaused)
fail("Can only use restart() on paused threads");
// Move to the runlist
moveTo(scheduler.runNext);
}
// Stop the thread and return it to the freelist
void kill()
{
@ -238,6 +252,7 @@ struct Thread
bool isTransient() { return list is &scheduler.transient; }
bool isRunning() { return cthread is this; }
bool isDead() { return list is null; }
bool isAlive() { return !isDead; }
bool isPaused() { return list is &scheduler.paused; }
// Get the next node in the freelist
@ -293,6 +308,7 @@ struct Thread
// Background the thread
background();
assert(cthread is null);
}
// Put this thread in the background. Acquires the stack and
@ -401,6 +417,8 @@ struct Thread
if(fstack.cur !is null)
fl = fstack.cur.getFloc();
msg ~= '\n' ~ fstack.toString();
.fail(msg, fl);
}

@ -21,7 +21,7 @@
*/
singleton Config : Object;
singleton Config;
// Only some config options have been moved into Monster. Key bindings
// and other low-level settings are still handled in D.

@ -1,12 +1,14 @@
// Small script that prints the FPS to screen with regular intervals.
import io, timer, frames;
import frames;
// Sleep one frame. This makes sure that we won't start running until
// the rendering begins. Not critically important, but it prevents the
// first printed value from being 'nan'.
fsleep(1);
// counter and totalTime (in the 'frames' module) are updated
// automatically by the system.
ulong lastFrame = counter;
float lastTime = totalTime;

@ -23,7 +23,7 @@
// An object that exists inside a cell. All cell objects must have a
// position in space.
class GameObject : Object;
class GameObject;
// Is this object placed in a cell? isPlaced is true if the object is
// displayed inside a cell with a mesh and given 3D coordinates, and

@ -24,7 +24,7 @@
// Contains all the game settings (GMST) variables of Morrowind,
// Tribunal and Bloodmoon. Based on "Morrowind Scripting for Dummies"
// (v9).
singleton GMST : Object;
singleton GMST;
// Most of the comments are copied from MSfD. A bit of cleanup is
// still needed.

@ -1,30 +0,0 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (object.mn) is part of the OpenMW package.
OpenMW 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 is the base class of all OpenMW Monster classes.
class Object;
import io, timer;
// Get a random number between a and b (inclusive)
native int randInt(int a, int b);

@ -21,16 +21,13 @@
*/
module mscripts.object;
module mscripts.setup;
import monster.monster;
import monster.compiler.scopes : global;
import monster.modules.timer;
import std.stdio;
import std.date;
import core.resource : rnd;
import core.config;
import sound.music;
// Set up the base Monster classes we need in OpenMW
void initMonsterScripts()
@ -43,17 +40,13 @@ void initMonsterScripts()
vm.addPath("mscripts/gameobjects/");
vm.addPath("mscripts/sound/");
// Make sure the Object class is loaded
auto mc = new MonsterClass("Object", "object.mn");
// Import some modules into the global scope, so we won't have to
// import them manually in each script.
global.registerImport("io", "random", "timer");
// Get the Config singleton object
config.mo = (new MonsterClass("Config")).getSing();
// Bind various functions
mc.bind("randInt",
{ stack.pushInt(rnd.randInt
(stack.popInt,stack.popInt));});
// Run the fps ticker
vm.run("fpsticker.mn");

@ -26,7 +26,7 @@
fade in and fade out, and adjust volume.
*/
class Jukebox : Object;
class Jukebox;
// Between 0 (off) and 1 (full volume)
float fadeLevel = 0.0;

@ -22,7 +22,7 @@
*/
// This class controls all the music.
singleton Music : Object;
singleton Music;
// Create one jukebox for normal music, and one for battle music. This
// way we can pause / fade out one while the other resumes / fades in.

@ -1,7 +1,5 @@
// A short example script
import io, timer;
sleep(6);
while(true)

@ -43,7 +43,7 @@ import core.config;
import monster.util.string;
import monster.vm.mclass;
import mscripts.object;
import mscripts.setup;
import sound.audio;

@ -1,155 +0,0 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (random.d) is part of the OpenMW package.
OpenMW 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/ .
*/
module util.random;
private import std.date;
private import std.random;
abstract class Random
{
static const double scale = 1.0/uint.max;
// Initialize from current time
this() { initialize(); }
// Initialize from parameter
this(long seed) { initialize(seed); }
// Reinitialize with seed
abstract void initialize(long newSeed);
// Produce random numbers between 0 and uint.max
abstract uint rand();
// Default is to initialize using current time as seed
void initialize() { initialize(getUTCtime()); }
// Produce a uniform random number between 0 and 1
double random()
{
return rand() * scale;
}
// Return a uniform random number between a and b. Works for the
// both the cases a < b and a > b.
double random(double a, double b)
in
{
// We only disallow nan parameters
assert(a <>= b);
}
out(result)
{
// Result must be in range m < result < M, where m=min(a,b) and M=max(a,b)
if(b > a) assert( (a < result) && (result < b) );
else if(a > b) assert( (b < result) && (result < a) );
}
body
{ return random()*(b - a) + a; }
// Return a random integer between a and b, inclusive.
int randInt(int a, int b)
out(result)
{
// Result must be in range m <= result <= M, where m=min(a,b) and M=max(a,b)
if(b >= a) assert( (a <= result) && (result <= b) );
else if(a > b) assert( (b <= result) && (result <= a) );
}
body
{
if(a>b) return cast(int)(rand() % (a-b+1)) + b;
else if(b>a) return cast(int)(rand() % (b-a+1)) + a;
else return a;
}
// Allow using "function call" syntax:
//
// Random ran = new Random1;
// double d = ran(); // Calls ran.random()
// d = ran(a,b); // Calls ran.random(a,b);
double opCall() { return random(); }
double opCall(double a, double b) { return random(a, b); }
// Return the seed originally given the object
long getSeed() {return origSeed;}
protected:
long origSeed; // Value used to seed the generator
}
// Uses the standard library generator
class DRand : Random
{
// Initialize from current time
this() { super(); }
// Initialize from parameter
this(long seed) { super(seed); }
uint rand() { return std.random.rand(); }
void initialize(long newSeed)
{
origSeed = newSeed;
rand_seed(cast(uint)newSeed, 0);
}
alias Random.initialize initialize;
unittest
{
struct tmp { import std.stdio; }
alias tmp.writef writef;
alias tmp.writefln writefln;
writefln("Unittest for class DRand");
DRand ran = new DRand;
writefln("Seed (from time) = ", ran.getSeed());
// Take a look at some numbers on screen
writefln("Some random numbers in [0,1]:");
for(int i=0; i<10; i++)
writef(" ", ran());
ran = new DRand(0);
writefln("\nNew seed (preset) = ", ran.getSeed());
// Take a look at some numbers on screen
writefln("Some random numbers in [0,1]:");
for(int i=0; i<10; i++)
writef(" ", ran());
// Check that all interfaces work (compile time)
ran();
ran(1,2);
ran.random();
ran.random(3,4);
ran.initialize();
ran.initialize(10);
ran.randInt(-3,5);
writefln("\nEnd of unittest for class DRand\n");
}
}
Loading…
Cancel
Save