mirror of
				https://github.com/OpenMW/openmw.git
				synced 2025-11-04 00:56:39 +00:00 
			
		
		
		
	git-svn-id: https://openmw.svn.sourceforge.net/svnroot/openmw/trunk@138 ea6a568a-9f4f-0410-981a-c910a81bb256
		
			
				
	
	
		
			471 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			D
		
	
	
	
	
	
			
		
		
	
	
			471 lines
		
	
	
	
		
			13 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 openmw;
 | 
						|
 | 
						|
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 scene.player;
 | 
						|
 | 
						|
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 debugOut = false;
 | 
						|
  bool extTest = false;
 | 
						|
  bool doGen = false;
 | 
						|
  bool nextSave = false;
 | 
						|
  bool loadSave = 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;
 | 
						|
 | 
						|
  // Savegame to load
 | 
						|
  char[] savefile;
 | 
						|
 | 
						|
  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") config.noSound = true;
 | 
						|
    else if(a == "-save") nextSave = true;
 | 
						|
    else if(a == "-debug")
 | 
						|
      {
 | 
						|
        // Enable Monster debug output
 | 
						|
        dbg.dbgOut = dout;
 | 
						|
 | 
						|
        // Tell OGRE to do the same later on
 | 
						|
        debugOut = true;
 | 
						|
      }
 | 
						|
    else if(nextSave)
 | 
						|
      {
 | 
						|
        savefile = a;
 | 
						|
        nextSave = false;
 | 
						|
      }
 | 
						|
    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("    -save <file>  Load cell/pos from savegame");
 | 
						|
      writefln("    -h            Show this help");
 | 
						|
      writefln("");
 | 
						|
      writefln("Specifying more than one cell implies -n");
 | 
						|
    }
 | 
						|
 | 
						|
  if(help)
 | 
						|
    {
 | 
						|
      showHelp();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  initializeMemoryRegions();
 | 
						|
  initMonsterScripts();
 | 
						|
 | 
						|
  // This is getting increasingly hackish, but this entire engine
 | 
						|
  // design is now quickly outgrowing its usefulness, and a rewrite is
 | 
						|
  // coming soon anyway.
 | 
						|
  PlayerSaveInfo pi;
 | 
						|
  if(savefile != "")
 | 
						|
    {
 | 
						|
      if(cells.length)
 | 
						|
        {
 | 
						|
          writefln("Please don't specify both a savegame file (%s) and cell names (%s)", savefile, cells);
 | 
						|
          return;
 | 
						|
        }
 | 
						|
 | 
						|
      loadSave = true;
 | 
						|
      writefln("Loading savegame %s", savefile);
 | 
						|
      pi = importSavegame(savefile);
 | 
						|
      writefln("    Player name: %s", pi.playerName);
 | 
						|
      writefln("    Cell name:   %s", pi.cellName);
 | 
						|
      writefln("    Pos:         %s", pi.pos.position);
 | 
						|
      writefln("    Rot:         %s", pi.pos.rotation);
 | 
						|
 | 
						|
      cells = [pi.cellName];
 | 
						|
    }
 | 
						|
 | 
						|
  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 && !loadSave)
 | 
						|
    config.defaultCell = cells[0];
 | 
						|
 | 
						|
  if(cells.length == 0)
 | 
						|
    {
 | 
						|
      showHelp();
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
  if(!config.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;
 | 
						|
	}
 | 
						|
 | 
						|
      // If we're loading from save, override the player position
 | 
						|
      if(loadSave)
 | 
						|
        *playerData.position = pi.pos;
 | 
						|
    }
 | 
						|
 | 
						|
  // 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(!config.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(!config.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(!config.noSound)
 | 
						|
        Music.play();
 | 
						|
 | 
						|
      // Run it until the user tells us to quit
 | 
						|
      startRendering();
 | 
						|
    }
 | 
						|
  else if(debugOut) writefln("Skipping rendering");
 | 
						|
 | 
						|
  if(!config.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);
 | 
						|
    }
 | 
						|
}
 |