Started implementing ALL in-game objects as Monster scripts.

git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@72 ea6a568a-9f4f-0410-981a-c910a81bb256
pull/7/head
nkorslund 16 years ago
parent c2d992c8f0
commit 76295fbd45

@ -25,6 +25,7 @@ module esm.defs;
public import std.string;
public import monster.util.string;
import monster.monster;
/*
* Types and definitions related to parsing esm and esp files
@ -135,3 +136,50 @@ align(1) struct ENAMstruct
static assert(ENAMstruct.sizeof==24);
}
// Common stuff for all the load* structs
template LoadTT(T)
{
LoadState state;
char[] name, id;
MonsterObject *proto;
static MonsterClass mc;
void makeProto(char[] clsName = null)
{
// Use the template type name as the Monster class name if none
// is specified.
if(clsName == "")
{
clsName = typeid(T).toString;
// Remove the module name
int i = clsName.rfind('.');
if(i != -1)
clsName = clsName[i+1..$];
}
// Set up a prototype object
if(mc is null)
mc = MonsterClass.find(clsName);
proto = mc.createObject();
proto.setString8("id", id);
proto.setString8("name", name);
static if(is(typeof(data.weight) == float))
{
proto.setFloat("weight", data.weight);
proto.setInt("value", data.value);
}
static if(is(typeof(data.enchant)==int))
proto.setInt("enchant", data.enchant);
static if(is(typeof(data.health)==int))
proto.setInt("health", data.health);
}
}
template LoadT() { mixin LoadTT!(typeof(*this)); }

@ -1,8 +1,8 @@
module esm.imports;
/* This is a file that imports common modules used by the load*.d
record loaders. It is really a cut down version of the start of
records.d.
record loaders. It is really a cut down version of what used to be
the start of records.d.
This file MUST NOT import records.d - directly or indirectly -
because that will trigger a nice three page long list of template
@ -41,4 +41,7 @@ import esm.loadscpt;
import esm.loadsoun;
import esm.loadspel;
import esm.loadench;
import monster.monster;
}

@ -36,10 +36,8 @@ import esm.imports;
struct Activator
{
char[] id;
LoadState state;
mixin LoadT!();
char[] name;
Script *script;
MeshIndex model;
@ -48,6 +46,8 @@ struct Activator
model = getMesh();
name = getHNString("FNAM");
script = getHNOPtr!(Script)("SCRI", scripts);
makeProto();
}}
}
ListID!(Activator) activators;

@ -30,9 +30,6 @@ import esm.imports;
struct Potion
{
char[] id;
LoadState state;
align(1) struct ALDTstruct
{
float weight;
@ -43,7 +40,7 @@ struct Potion
ALDTstruct data;
char[] name;
mixin LoadT!();
MeshIndex model;
IconIndex icon;
@ -69,6 +66,8 @@ struct Potion
readHExact(&effects.array[$-1], effects.array[$-1].sizeof);
}
makeProto();
proto.setInt("autoCalc", data.autoCalc);
}}
}
ListID!(Potion) potions;

@ -30,9 +30,6 @@ import esm.imports;
struct Apparatus
{
char[] id, name;
LoadState state;
enum AppaType : int
{
MortarPestle = 0,
@ -50,6 +47,8 @@ struct Apparatus
static assert(AADTstruct.sizeof == 16);
}
mixin LoadT!();
AADTstruct data;
MeshIndex model;
IconIndex icon;
@ -62,6 +61,11 @@ struct Apparatus
readHNExact(&data, data.sizeof, "AADT");
script = getHNOPtr!(Script)("SCRI", scripts);
icon = getIcon();
makeProto();
proto.setFloat("quality", data.quality);
proto.setInt("type", data.type);
}}
}
ListID!(Apparatus) appas;

@ -116,8 +116,7 @@ struct Armor
AODTstruct data;
char[] name, id;
LoadState state;
mixin LoadT!();
PartReferenceList parts;
@ -138,6 +137,11 @@ struct Armor
parts.load();
enchant = getHNOPtr!(Enchantment)("ENAM", enchants);
makeProto();
proto.setInt("type", data.type);
proto.setInt("armor", data.armor);
}}
}
ListID!(Armor) armors;

@ -74,8 +74,8 @@ struct BodyPart
BYDTstruct data;
char[] name, id;
LoadState state;
mixin LoadT;
MeshIndex model;
void load()
@ -83,6 +83,9 @@ struct BodyPart
model = getMesh();
name = getHNString("FNAM");
readHNExact(&data, data.sizeof, "BYDT");
// don't need to run makeProto here yet, no BodyPart monster
// class.
}}
}

@ -44,7 +44,9 @@ struct Book
IconIndex icon;
Script *script;
Enchantment *enchant;
char[] name, text, id;
mixin LoadT;
char[] text;
LoadState state;
@ -57,6 +59,11 @@ struct Book
icon = getIcon();
text = getHNOString("TEXT");
enchant = getHNOPtr!(Enchantment)("ENAM", enchants);
makeProto();
proto.setInt("skillID", data.skillID);
proto.setBool("isScroll", data.isScroll != 0);
}}
}
ListID!(Book) books;

@ -30,9 +30,9 @@ import esm.imports;
struct BirthSign
{
LoadState state;
char[] description;
char[] id, name, description;
mixin LoadT;
TextureIndex texture;
@ -48,6 +48,8 @@ struct BirthSign
description = getHNOString("DESC");
powers.load();
// No monster class equivalent yet
}}
}
ListID!(BirthSign) birthSigns;

@ -72,8 +72,9 @@ struct Class
static assert(CLDTstruct.sizeof == 60);
}
LoadState state;
char[] id, name, description;
mixin LoadT;
char[] description;
CLDTstruct data;
void load()
@ -85,6 +86,8 @@ struct Class
esFile.fail("Unknown bool value");
description = esFile.getHNOString("DESC");
// no makeProto yet
}
}
ListID!(Class) classes;

@ -50,15 +50,15 @@ struct Clothing
Type type;
float weight;
short value;
short enchantPoints;
short enchant;
static assert(CTDTstruct.sizeof == 12);
}
CTDTstruct data;
LoadState state;
char[] id, name;
mixin LoadT;
PartReferenceList parts;
MeshIndex model;
@ -78,6 +78,9 @@ struct Clothing
parts.load();
enchant = getHNOPtr!(Enchantment)("ENAM", enchants);
makeProto();
proto.setInt("type", data.type);
}}
}
ListID!(Clothing) clothes;

@ -69,9 +69,6 @@ struct Container
Unknown = 8
}
char[] id, name;
LoadState state;
MeshIndex model;
Script *script;
@ -79,6 +76,8 @@ struct Container
Flags flags;
InventoryList inventory;
mixin LoadT!();
void load()
{with(esFile){
model = getMesh();
@ -100,6 +99,9 @@ struct Container
script = getHNOPtr!(Script)("SCRI", scripts);
inventory.load();
makeProto();
proto.setFloat("weight", weight);
}}
}

@ -30,13 +30,12 @@ import esm.imports;
struct Door
{
LoadState state;
char[] id, name;
MeshIndex model;
Script *script;
Sound* openSound, closeSound;
mixin LoadT!();
void load()
{with(esFile){
model = getMesh();
@ -44,6 +43,8 @@ struct Door
script = getHNOPtr!(Script)("SCRI", scripts);
openSound = getHNOPtr!(Sound)("SNAM", sounds);
closeSound = getHNOPtr!(Sound)("ANAM", sounds);
makeProto();
}}
}
ListID!(Door) doors; // Break on through

@ -29,9 +29,6 @@ import esm.imports;
struct Light
{
char[] id;
LoadState state;
enum Flags : uint
{
Dynamic = 0x001,
@ -59,14 +56,14 @@ struct Light
LHDTstruct data;
char[] name;
MeshIndex model;
IconIndex icon;
mixin LoadT!();
Sound* sound;
Script* script;
MeshIndex model;
IconIndex icon;
void load()
{with(esFile){
model = getMesh();
@ -77,6 +74,14 @@ struct Light
script = getHNOPtr!(Script)("SCRI", scripts);
sound = getHNOPtr!(Sound)("SNAM", sounds);
// Stash the data in the Monster object prototype
makeProto();
proto.setUint("flags", data.flags);
proto.setFloat("lifetime", data.time);
proto.setInt("radius", data.radius);
}}
}
ListID!(Light) lights;

@ -71,8 +71,7 @@ struct Weapon
WPDTstruct data;
LoadState state;
char[] name, id;
mixin LoadT!();
MeshIndex model;
IconIndex icon;
@ -87,6 +86,11 @@ struct Weapon
script = getHNOPtr!(Script)("SCRI", scripts);
icon = getOIcon();
enchant = getHNOPtr!(Enchantment)("ENAM", enchants);
makeProto();
proto.setFloat("speed", data.speed);
proto.setFloat("reach", data.reach);
}}
}
ListID!(Weapon) weapons;

@ -393,6 +393,8 @@ struct Assembler
addi(i);
}
void cloneObj() { cmd(BC.Clone); }
// Copy the topmost int on the stack
void dup() { cmd(BC.Dup); }

@ -69,6 +69,12 @@ enum BC
// giving the class index (in the file lookup
// table)
Clone, // Clones an object - create a new object of
// the same class, then copy variable values
// and state from the old object to the
// new. Replaces the object index on the stack
// with the new index.
Jump, // Jump to given position (int)
JumpZ, // Pop a value, if it is zero then jump to

@ -1004,6 +1004,7 @@ class VariableExpr : MemberExpression
isNext(toks, TT.Identifier) ||
isNext(toks, TT.Singleton) ||
isNext(toks, TT.State) ||
isNext(toks, TT.Clone) ||
isNext(toks, TT.Const);
}
@ -1146,6 +1147,9 @@ class VariableExpr : MemberExpression
if(name.type == TT.Const)
fail("Cannot use const as a variable", name.loc);
if(name.type == TT.Clone)
fail("Cannot use clone as a variable", name.loc);
// These are special cases that work both as properties
// (object.state) and as non-member variables (state=...) inside
// class functions / state code. Since we already handle them

@ -159,6 +159,8 @@ class ClassProperties : SimplePropertyScope
{
super("ClassProperties");
insert("clone", "owner", { tasm.cloneObj(); });
// For testing purposes. Makes 'singleton' an alias for the
// first variable in the data segment. This might actually not
// be far from how the end result would work - the singleton

@ -537,6 +537,41 @@ final class MonsterClass
return top;
}
// Create a new object based on an existing object
MonsterObject* createClone(MonsterObject *source)
{
requireCompile();
assert(source.tree.length == tree.length);
assert(source.thread.topObj == source,
"createClone can only clone the topmost object");
// Create a new thread
CodeThread *trd = threads.getNew();
// Loop through the objects in the source tree, and clone each
// of them
MonsterObject* otree[] = source.tree.dup;
foreach(i, ref obj; otree)
{
obj = obj.cls.getClone(obj);
obj.tree = otree[0..i+1];
obj.thread = trd;
}
// Pick out the top object
MonsterObject* top = otree[$-1];
assert(top !is null);
// Initialize the thread
trd.initialize(top);
// Set the same state
trd.setState(source.thread.getState(), null);
return top;
}
// Free an object and its thread
void deleteObject(MonsterObject *obj)
{
@ -757,6 +792,46 @@ final class MonsterClass
// Point to the static data segment
obj.sdata = sdata;
obj.extra = null;
// Call the custom native constructor
if(constType != FuncType.Native)
{
fstack.pushNConst(obj);
if(constType == FuncType.NativeDDel)
dg_const();
else if(constType == FuncType.NativeDFunc)
fn_const();
else if(constType == FuncType.NativeCFunc)
c_const();
fstack.pop();
}
return obj;
}
// Clone an existing object
MonsterObject *getClone(MonsterObject *source)
{
assert(source !is null);
assert(source.cls is this);
assert(source.data.length == data.length);
assert(source.sdata.ptr is sdata.ptr);
requireCompile();
MonsterObject *obj = objects.getNew();
// Set the class
obj.cls = this;
// TODO: Fix memory management here too.
// Copy the data segment from the source
obj.data = source.data.dup;
// Point to the static data segment
obj.sdata = sdata;
obj.extra = null;
// Call the custom native constructor
if(constType != FuncType.Native)

@ -69,6 +69,7 @@ struct MonsterObject
// the MonsterClass.
MonsterObject* tree[];
/*******************************************************
* *
* Functions for object handling *
@ -98,6 +99,16 @@ struct MonsterObject
cls.deleteObject(this);
}
// Create a clone of this object. Note that this will always clone
// and return the top object (thread.topObj), regardless of which
// object in the list it is called on. In other words, the class
// mo.cls is not always the same as mo.clone().cls.
MonsterObject *clone()
{
auto t = thread.topObj;
return t.cls.createClone(t);
}
/*******************************************************
* *
* Casting / polymorphism functions *

@ -480,6 +480,10 @@ struct CodeThread
.createObject());
break;
case BC.Clone:
stack.pushObject(stack.popObject().clone());
break;
case BC.Jump:
code.jump(code.getInt);
break;

@ -0,0 +1,25 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (activator.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/ .
*/
class Activator : GameObject;

@ -0,0 +1,28 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (apparatus.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/ .
*/
// Covers all alchemy apparatus - mortars, retorts, etc
class Apparatus : InventoryItem;
float quality;
int type;

@ -0,0 +1,28 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (armor.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/ .
*/
// Covers all weapons and projectile weapons you can carry (like
// arrows and throwable items.)
class Armor : Repairable;
int type, armor;

@ -0,0 +1,28 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (book.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/ .
*/
class Book : EnchantItem;
bool isScroll;
int skillID; // Skill that is enhanced by reading this book, if any

@ -0,0 +1,27 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (clothing.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/ .
*/
// All items that can be repaired (weapons and armor)
class Clothing : EnchantItem;
int type;

@ -0,0 +1,26 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (container.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/ .
*/
class Container : LockedObject;
float weight; // Not sure, might be max total weight allowed?

@ -4,7 +4,7 @@
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (equipitem.mn) is part of the OpenMW package.
This file (enchantitem.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
@ -21,7 +21,7 @@
*/
// Anything that can be equiped, like clothes, armor and weapons.
class EquipItem : InventoryItem;
// Items that can be enchanted
class EnchantItem : InventoryItem;
int enchant;

@ -40,6 +40,8 @@ float r1, r2, r3;
float scale;
char[] name, id;
// Various variables that are currently unused. Most of the strings
// will be replaced by object references at some point.

@ -26,5 +26,6 @@ class Light : InventoryItem;
// Time left in seconds (for carried lights)
float lifetime;
// Is this a carryable light?
bool carry;
int radius;
uint flags;

@ -0,0 +1,26 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (potion.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/ .
*/
class Potion : InventoryItem;
int autoCalc;

@ -0,0 +1,27 @@
/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (repairable.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/ .
*/
// All items that can be repaired (weapons and armor)
class Repairable : EnchantItem;
int health;

@ -21,11 +21,11 @@
*/
// Weapons (This is just an example, it's not used for anything yet.)
class Weapon : EquipItem;
// Covers all weapons and carry-able projectiles.
class Weapon : Repairable;
float speed, reach;
// Not set yet.
bool magical, silver;
bool isTwohanded;
bool twoHanded;

@ -39,7 +39,8 @@ import sound.audio;
import scene.player;
// Generic version of a "live" object
// Generic version of a "live" object. Do we even need this at all?
// No, I don't think so.
struct GenLive(T)
{
// Instance of class GameObject or a derived class (depending on
@ -259,16 +260,13 @@ class CellData
private:
static
MonsterClass gameObjC, doorC, lightC, lockedC;
MonsterClass gameObjC;
void setup()
{
if(gameObjC !is null) return;
gameObjC = new MonsterClass("GameObject");
doorC = new MonsterClass("Door");
lightC = new MonsterClass("Light");
lockedC = new MonsterClass("LockedObject");
gameObjC = MonsterClass.find("GameObject");
}
void loadReferences()
@ -329,24 +327,20 @@ class CellData
{
LiveLight ls;
ls.m = m;
ls.obj = lightC.createObject;
ls.obj = m.proto.clone();
mo = ls.obj;
mo.setFloat("lifetime", m.data.time);
bool carry = (m.data.flags&Light.Flags.Carry) != 0;
mo.setBool("carry", carry);
if(carry)
lights.insert(ls);
else
statLights.insert(ls);
}
else if(Container *c = it.getContainer())
{
LiveContainer ls;
ls.m = c;
ls.obj = lockedC.createObject;
ls.obj = c.proto.clone();
mo = ls.obj;
containers.insert(ls);
container = true;
@ -356,7 +350,7 @@ class CellData
{
LiveDoor ls;
ls.m = d;
ls.obj = doorC.createObject;
ls.obj = d.proto.clone();
mo = ls.obj;
doors.insert(ls);
door = true;
@ -416,7 +410,7 @@ class CellData
{
LiveWeapon ls;
ls.m = m;
ls.obj = gameObjC.createObject;
ls.obj = m.proto.clone();
mo = ls.obj;
weapons.insert(ls);
}

Loading…
Cancel
Save