You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/openmw.d

439 lines
12 KiB
D

/*
OpenMW - The completely unofficial reimplementation of Morrowind
Copyright (C) 2008-2009 Nicolay Korslund
Email: < korslund@gmail.com >
WWW: http://openmw.snaptoad.com/
This file (openmw.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 morro;
import std.stdio;
import std.string;
import std.cstream;
import std.file;
import ogre.ogre;
import ogre.bindings;
import gui.bindings;
import gui.gui;
import bullet.bullet;
import scene.celldata;
import scene.soundlist;
import scene.gamesettings;
import core.resource;
import core.memory;
import core.config;
import monster.util.string;
import monster.vm.mclass;
import monster.vm.dbg;
import mscripts.setup;
import sound.audio;
import input.events;
import terrain.terrain;
// Set up exit handler
alias void function() c_func;
extern(C) int atexit(c_func);
bool cleanExit = false;
void exitHandler()
{
// If we exit uncleanly, print the function stack.
if(!cleanExit)
writefln(dbg.getTrace());
}
//*
import std.gc;
import gcstats;
void poolSize()
{
GCStats gc;
getStats(gc);
writefln("Pool size: ", comma(gc.poolsize));
writefln("Used size: ", comma(gc.usedsize));
}
//*/
void main(char[][] args)
{
bool render = true;
bool help = false;
bool resetKeys = false;
bool showOgreFlag = false;
bool noSound = false;
bool debugOut = false;
bool extTest = false;
bool doGen = false;
// Some examples to try:
//
// "Abandoned Shipwreck, Upper Level";
// "Gro-Bagrat Plantation";
// "Abinabi";
// "Abebaal Egg Mine";
// "Ald-ruhn, Ald Skar Inn";
// "Koal Cave";
// "Ald-ruhn, Arobar Manor Bedrooms"
// "Sud";
// "Vivec, The Lizard's Head";
// "ToddTest";
// Cells to load
char[][] cells;
foreach(char[] a; args[1..$])
if(a == "-n") render = false;
else if(a == "-ex") extTest = true;
else if(a == "-gen") doGen = true;
else if(a == "-h") help=true;
else if(a == "-rk") resetKeys = true;
else if(a == "-oc") showOgreFlag = true;
else if(a == "-ns") noSound = true;
else if(a == "-debug")
{
// Enable Monster debug output
dbg.dbgOut = dout;
// Tell OGRE to do the same later on
debugOut = true;
}
else cells ~= a;
if(cells.length > 1)
{
writefln("More than one cell specified, rendering disabled");
render=false;
}
void showHelp()
{
writefln("Syntax: %s [options] cell-name [cell-name]", args[0]);
writefln(" Options:");
writefln(" -n Only load, do not render");
writefln(" -ex Test the terrain system");
writefln(" -gen Generate landscape cache");
writefln(" -rk Reset key bindings to default");
writefln(" -oc Show the Ogre config dialogue");
writefln(" -ns Completely disable sound");
writefln(" -debug Print debug information");
writefln(" -h Show this help");
writefln("");
writefln("Specifying more than one cell implies -n");
}
if(help)
{
showHelp();
return;
}
initializeMemoryRegions();
initMonsterScripts();
/*
importSavegame("data/quiksave.ess");
importSavegame("data/Perm1hal0000.ess");
return;
*/
config.initialize(resetKeys);
scope(exit) config.writeConfig();
// Check if the data directory exists
if(!exists(config.dataDir) || !isdir(config.dataDir))
{
writefln("Cannot find data directory '", config.dataDir,
"' - please edit openmw.ini.");
return;
}
// If the -oc parameter is specified, we override any config
// setting.
if(showOgreFlag) config.finalOgreConfig = true;
if(cells.length == 0)
if(config.defaultCell.length)
cells ~= config.defaultCell;
if(cells.length == 1)
config.defaultCell = cells[0];
if(cells.length == 0)
{
showHelp();
return;
}
if(!noSound) initializeSound();
resources.initResources();
// Load all ESM and ESP files
loadTESFiles(config.gameFiles);
scene.gamesettings.loadGameSettings();
CellData cd = cellList.get();
foreach(char[] cName; cells)
{
// Release the last cell data
cellList.release(cd);
// Get a cell data holder and load an interior cell
cd = cellList.get();
try cd.loadIntCell(cName);
catch(Exception e)
{
writefln("\nUnable to load cell '%s'.", cName);
writefln("\nDetails: %s", e);
writefln("
Perhaps this cell does not exist in your Morrowind language version?
Try specifying another cell name on the command line, or edit openmw.ini.");
return;
}
}
// Simple safety hack
NodePtr putObject(MeshIndex m, Placement *pos, float scale,
bool collide=false)
{
if(m is null)
writefln("WARNING: CANNOT PUT NULL OBJECT");
else if(!m.isEmpty)
return placeObject(m, pos, scale, collide);
//writefln("WARNING: CANNOT INSERT EMPTY MESH '%s'", m.getName);
return null;
}
if(cd.inCell !is null)
// Set the name for the GUI (temporary hack)
gui_setCellName(cd.inCell.id.ptr);
// Set up the exit handler
atexit(&exitHandler);
scope(exit) cleanExit = true;
if(render)
{
// Warm up OGRE
setupOgre(debugOut);
scope(exit) cleanupOgre();
// Create the GUI system
initGUI(debugOut);
// Set up Bullet
initBullet();
scope(exit) cleanupBullet();
// Initialize the internal input and event manager. The
// lower-level input system (OIS) is initialized by the
// setupOgre() call further up.
initializeInput();
// Play some old tunes
if(extTest)
{
// Exterior cell
/*
Color c;
c.red = 180;
c.green = 180;
c.blue = 180;
setAmbient(c, c, c, 0);
// Put in the water
ogre_createWater(cd.water);
// Create an ugly sky
ogre_makeSky();
*/
initTerrain(doGen);
}
else
{
// Interior cell
assert(cd.inCell !is null);
setAmbient(cd.ambi.ambient, cd.ambi.sunlight,
cd.ambi.fog, cd.ambi.fogDensity);
// Not all interior cells have water
if(cd.inCell.flags & CellFlags.HasWater)
ogre_createWater(cd.water);
// Insert the meshes of statics into the scene
foreach(ref LiveStatic ls; cd.statics)
putObject(ls.m.model, ls.getPos(), ls.getScale(), true);
// Inventory lights
foreach(ref LiveLight ls; cd.lights)
{
NodePtr n = putObject(ls.m.model, ls.getPos(), ls.getScale());
ls.lightNode = attachLight(n, ls.m.data.color, ls.m.data.radius);
if(!noSound)
{
Sound *s = ls.m.sound;
if(s)
{
ls.loopSound = soundScene.insert(s, true);
if(ls.loopSound)
{
auto p = ls.getPos();
ls.loopSound.setPos(p.position[0],
p.position[1],
p.position[2]);
}
}
}
}
// Static lights
foreach(ref LiveLight ls; cd.statLights)
{
NodePtr n = putObject(ls.m.model, ls.getPos(), ls.getScale(), true);
ls.lightNode = attachLight(n, ls.m.data.color, ls.m.data.radius);
if(!noSound)
{
Sound *s = ls.m.sound;
if(s)
{
ls.loopSound = soundScene.insert(s, true);
if(ls.loopSound)
{
auto p = ls.getPos();
ls.loopSound.setPos(p.position[0],
p.position[1],
p.position[2]);
}
}
}
}
// Misc items
foreach(ref LiveMisc ls; cd.miscItems)
putObject(ls.m.model, ls.getPos(), ls.getScale());
/*
// NPCs (these are complicated, usually do not have normal meshes)
foreach(ref LiveNPC ls; cd.npcs)
putObject(ls.m.model, ls.getPos(), ls.getScale());
*/
// Containers
foreach(ref LiveContainer ls; cd.containers)
putObject(ls.m.model, ls.getPos(), ls.getScale(), true);
// Doors
foreach(ref LiveDoor ls; cd.doors)
putObject(ls.m.model, ls.getPos(), ls.getScale());
// Activators (including beds etc)
foreach(ref LiveActivator ls; cd.activators)
putObject(ls.m.model, ls.getPos(), ls.getScale(), true);
// Potions
foreach(ref LivePotion ls; cd.potions)
putObject(ls.m.model, ls.getPos(), ls.getScale());
// Apparatus
foreach(ref LiveApparatus ls; cd.appas)
putObject(ls.m.model, ls.getPos(), ls.getScale());
// Ingredients
foreach(ref LiveIngredient ls; cd.ingredients)
putObject(ls.m.model, ls.getPos(), ls.getScale());
// Armors
foreach(ref LiveArmor ls; cd.armors)
putObject(ls.m.model, ls.getPos(), ls.getScale());
// Weapons
foreach(ref LiveWeapon ls; cd.weapons)
putObject(ls.m.model, ls.getPos(), ls.getScale());
// Books
foreach(ref LiveBook ls; cd.books)
putObject(ls.m.model, ls.getPos(), ls.getScale());
// Clothes
foreach(ref LiveClothing ls; cd.clothes)
putObject(ls.m.model, ls.getPos(), ls.getScale());
// Tools
foreach(ref LiveTool ls; cd.tools)
putObject(ls.m.model, ls.getPos(), ls.getScale());
// Creatures (not displayed very well yet)
foreach(ref LiveCreature ls; cd.creatures)
putObject(ls.m.model, ls.getPos(), ls.getScale());
// End of interior cell
}
// Run GUI system
startGUI();
// Play some old tunes
if(!noSound)
Music.play();
// Run it until the user tells us to quit
startRendering();
}
else if(debugOut) writefln("Skipping rendering");
if(!noSound)
{
soundScene.kill();
shutdownSound();
}
if(debugOut)
{
writefln();
writefln("%d statics", cd.statics.length);
writefln("%d misc items", cd.miscItems.length);
writefln("%d inventory lights", cd.lights.length);
writefln("%d static lights", cd.statLights.length);
writefln("%d NPCs", cd.npcs.length);
writefln("%d containers", cd.containers.length);
writefln("%d doors", cd.doors.length);
writefln("%d activators", cd.activators.length);
writefln("%d potions", cd.potions.length);
writefln("%d apparatuses", cd.appas.length);
writefln("%d ingredients", cd.ingredients.length);
writefln("%d armors", cd.armors.length);
writefln("%d weapons", cd.weapons.length);
writefln("%d books", cd.books.length);
writefln("%d tools", cd.tools.length);
writefln("%d clothes", cd.clothes.length);
writefln("%d creatures", cd.creatures.length);
writefln();
}
// This isn't necessary but it's here for testing purposes.
cellList.release(cd);
// Write some statistics
if(debugOut)
{
poolSize();
writefln(esmRegion);
writefln("Total objects: ", MonsterClass.getTotalObjects);
}
}