From 699dd2ff1540c2fcda2b9f8cf9765e94c86ce6ba Mon Sep 17 00:00:00 2001
From: feber <>
Date: Sun, 24 Oct 2004 06:49:11 +0000
Subject: [PATCH] New Lua map format: map presentation.

---
 doc/ChangeLog.html               |   5 +-
 doc/scripts/index.html           |   8 +-
 doc/scripts/mappresentation.html |  50 ++++----
 src/editor/editloop.cpp          |  42 +++----
 src/game/campaign.cpp            |   3 +-
 src/game/game.cpp                |  18 ++-
 src/game/savegame.cpp            |   2 +-
 src/include/map.h                |   4 +-
 src/include/menus.h              |   1 -
 src/include/pud.h                |   2 +-
 src/map/map.cpp                  |   6 +-
 src/map/map_save.cpp             |   2 +-
 src/map/script_map.cpp           |  49 ++++++--
 src/network/commands.cpp         |   2 +-
 src/network/netconnect.cpp       |  40 +++----
 src/stratagus/pud.cpp            |  44 +++----
 src/stratagus/stratagus.cpp      |   8 +-
 src/ui/menus.cpp                 | 198 ++++++++++++++++---------------
 src/ui/script_ui.cpp             |  35 ++++++
 19 files changed, 306 insertions(+), 213 deletions(-)

diff --git a/doc/ChangeLog.html b/doc/ChangeLog.html
index a73b9740a..2193022b4 100644
--- a/doc/ChangeLog.html
+++ b/doc/ChangeLog.html
@@ -34,9 +34,10 @@
 <ul>
 <p><li>2.2 Released<p>
 	<ul>
+	<li>New Lua map format: map presentation (from Fran�ois Beerten).
 	<li>Fixed bug #1048426: [Bos] Segfault with a map. Bug with die sequence for unit without corpse (from Joris Dauphin).
-	<li>Fixed crash where cursors were not initialized to 0, introduced in recent cleaups (from Russell Smith).
-	<li>Removed Speed attribute for UnitType(its only effect was to show the value). (from Joris Dauphin)
+	<li>Fixed crash where cursors were not initialized to 0, introduced in recent cleanups (from Russell Smith).
+	<li>Removed Speed attribute for UnitType (its only effect was to show the value). (from Joris Dauphin)
 	<li>Fixed bug #1043210: [editor] several units on the same tiles (from Russell Smith).
     <li>Make InfoPanel more configurable. (from Joris Dauphin).
     <li>Added support for transparent UI graphics, allowing for non-rectangular map areas (from Jimmy Salmon).
diff --git a/doc/scripts/index.html b/doc/scripts/index.html
index 0f7813a5a..406173b26 100644
--- a/doc/scripts/index.html
+++ b/doc/scripts/index.html
@@ -134,8 +134,6 @@
 <dd></dd>
 <dt><a href="research.html#CheckDependency">CheckDependency</a></dt>
 <dd></dd>
-<dt><a href="mappresentation.html#CreateMap">CreateMap</a></dt>
-<dd></dd>
 <dt><a href="game.html#CreateUnit">CreateUnit</a></dt>
 <dd></dd>
 <dt><a href="game.html#Credits">Credits</a></dt>
@@ -212,6 +210,10 @@
 <dd></dd>
 <dt><a href="research.html#DefineModifier">DefineModifier</a></dt>
 <dd></dd>
+<dt><a href="ui.html#DefinePanel">DefinePanel</a></dt>
+<dd></dd>
+<dt><a href="mappresentation.html#DefinePlayerTypes">DefinePlayerTypes</a></dt>
+<dd></dd>
 <dt><a href="game.html#DefineRaceNames">DefineRaceNames</a></dt>
 <dd></dd>
 <dt><a href="game.html#DefineRanks">DefineRanks</a></dt>
@@ -320,6 +322,8 @@
 <dd></dd>
 <dt><a href="game.html#Player">Player</a></dt>
 <dd></dd>
+<dt><a href="mappresentation.html#PresentMap">PresentMap</a></dt>
+<dd></dd>
 <dt><a href="ui.html#ProcessMenu">ProcessMenu</a></dt>
 <dd></dd>
 <dt><a href="game.html#RemoveObjective">RemoveObjective</a></dt>
diff --git a/doc/scripts/mappresentation.html b/doc/scripts/mappresentation.html
index c1d34b0a9..55c24a678 100644
--- a/doc/scripts/mappresentation.html
+++ b/doc/scripts/mappresentation.html
@@ -36,13 +36,14 @@
 <p><b>(C) Copyright 1998-2004 by The Stratagus Project. Distributed under the
 <a href="../gpl.html">"GNU General Public License"</a></b>
 <hr>
-<a href="../index.html">Stratagus</a> 
-<a href="../faq.html">FAQ</a> 
-<a href="magic.html">PREV</a> 
+<a href="../index.html">Stratagus</a>
+<a href="../faq.html">FAQ</a>
+<a href="magic.html">PREV</a>
 <a href="mapsetup.html">NEXT</a>
 <a href="index.html">LUA Index</a>
 <hr>
 <a href="#PresentMap">PresentMap</a>
+<a href="#DefinePlayerTypes">DefinePlayerTypes</a>
 <a href="#SetMapMiniImage">SetMapMiniImage</a>
 <a href="#DefineMapSetup">DefineMapSetup</a>
 <a href="#GetMapOption">GetMapOption</a>
@@ -64,16 +65,14 @@ the needed tilesets and the whole map are loaded into the engine.</li>
 This page documents map presentation functions.
 
 <p>
-Note: This is for the new map format. Not implented yet. 
+Note: This is for the new map format. Not implented yet.
 
 <h2>Functions</h2>
 
-<a name="CreateMap"></a>
-<h3>PresentMap(name, description, numplayers, mapwidth, mapheight)</h3>
+<a name="PresentMap"></a>
+<h3>PresentMap(description, numplayers, mapwidth, mapheight)</h3>
 
 <dl>
-<dt>Name</dt>
-<dd>Name of the map as displayed to the user.</dd>
 <dt>Description</dt>
 <dd>A textual description of the map that can be displayed to the user.</dd>
 <dt>numplayers</dt>
@@ -82,16 +81,10 @@ Note: This is for the new map format. Not implented yet.
 <dd>The sizes of the map.</dd>
 </dl>
 
-<p>
-Note: This is for the new map format. Not implented yet. 
-
 <h4>Example</h4>
 
 <pre>
-    PresentMap("Doom World", 
-              "As you enter hell, you encounter the home of Doom monster. " +
-                 "Be careful for the worst of them all: John Carmack.",
-	      4, 64, 64)
+    PresentMap("Doom World", 4, 64, 64)
 </pre>
 
 <a name="SetMapMiniImage"></a>
@@ -106,7 +99,7 @@ Path to the file with the graphic.
 </dd></dl>
 
 <p>
-Note: This is for the new map format. Not implented yet. 
+Note: This is for the new map format. Not implented yet.
 
 <h4>Example</h4>
 
@@ -114,15 +107,30 @@ Note: This is for the new map format. Not implented yet.
     SetMiniImage("doomworld/doomworld.png")
 </pre>
 
+<a name="DefinePlayerTypes"></a>
+<h3>DefinePlayerType(player1, player2, ...)</h3>
+
+Define the number of players and their type on the map. Possible values for player type are:
+<ul>
+<li>"neutral"</li>
+<li>"nobody"</li>
+<li>"computer"</li>
+<li>"person"</li>
+<li>"rescue-passive"</li>
+<li>"rescue-active"</li>
+</ul>
+
+<h4>Example</h4>
+
+<pre>
+    DefinePlayerTypes("person", "person")
+</pre>
+
 <a name="DefineMapSetup"></a>
 <h3>DefineMapSetup(luafile)</h3>
 
 Define the map setup file that will be loaded if the player starts a game with this map.
 
-<p>
-Note: This is for the new map format. Not implented yet. 
-
-
 <h4>Example</h4>
 
 <pre>
@@ -139,7 +147,7 @@ A map can define configuration options. A player can modify those
 options just before starting a game. For example: the tileset to use, your race, game type,
 number of opponents or the amount of resources on the map.
 <p>
-Note: This is for the new map format. Not implented yet. 
+Note: This is for the new map format. Not implented yet.
 
 <h4>Example</h4>
 
diff --git a/src/editor/editloop.cpp b/src/editor/editloop.cpp
index e726a1dab..b3e684844 100644
--- a/src/editor/editloop.cpp
+++ b/src/editor/editloop.cpp
@@ -533,14 +533,14 @@ static void DrawUnitIcons(void)
 		if (i == PlayerMax / 2) {
 			y += 20;
 		}
-		if (i == CursorPlayer && TheMap.Info->PlayerType[i] != PlayerNobody) {
+		if (i == CursorPlayer && TheMap.Info.PlayerType[i] != PlayerNobody) {
 			VideoDrawRectangle(ColorWhite, x + i % 8 * 20, y, 20, 20);
 		}
 		VideoDrawRectangle(
-			i == CursorPlayer && TheMap.Info->PlayerType[i] != PlayerNobody ?
+			i == CursorPlayer && TheMap.Info.PlayerType[i] != PlayerNobody ?
 				ColorWhite : ColorGray,
 			x + i % 8 * 20, y, 19, 19);
-		if (TheMap.Info->PlayerType[i] != PlayerNobody) {
+		if (TheMap.Info.PlayerType[i] != PlayerNobody) {
 			VideoFillRectangle(Players[i].Color, x + 1 + i % 8 * 20, y + 1,
 				17, 17);
 		}
@@ -555,10 +555,10 @@ static void DrawUnitIcons(void)
 	y += 18 * 1 + 4;
 	if (SelectedPlayer != -1) {
 		i = sprintf(buf,"Plyr %d %s ", SelectedPlayer,
-				PlayerRaces.Name[TheMap. Info->PlayerSide[SelectedPlayer]]);
+				PlayerRaces.Name[TheMap. Info.PlayerSide[SelectedPlayer]]);
 		// Players[SelectedPlayer].RaceName);
 
-		switch (TheMap.Info->PlayerType[SelectedPlayer]) {
+		switch (TheMap.Info.PlayerType[SelectedPlayer]) {
 			case PlayerNeutral:
 				strcat(buf, "Neutral");
 				break;
@@ -1134,7 +1134,7 @@ static void EditorCallbackButtonDown(unsigned button __attribute__ ((unused)))
 		}
 		// Cursor on player icons
 		if (CursorPlayer != -1) {
-			if (TheMap.Info->PlayerType[CursorPlayer] != PlayerNobody) {
+			if (TheMap.Info.PlayerType[CursorPlayer] != PlayerNobody) {
 				SelectedPlayer = CursorPlayer;
 				ThisPlayer = Players + SelectedPlayer;
 			}
@@ -1588,7 +1588,7 @@ static void EditorCallbackMouse(int x, int y)
 				by += 20;
 			}
 			if (bx < x && x < bx + 20 && by < y && y < by + 20) {
-				if (TheMap.Info->PlayerType[i] != PlayerNobody) {
+				if (TheMap.Info.PlayerType[i] != PlayerNobody) {
 					sprintf(buf,"Select player #%d",i);
 					SetStatusLine(buf);
 				} else {
@@ -1819,35 +1819,35 @@ static void CreateEditor(void)
 		//
 		// Inititialize TheMap / Players.
 		//
-		TheMap.Info->MapTerrainName =
-			strdup(Tilesets[TheMap.Info->MapTerrain]->Ident);
+		TheMap.Info.MapTerrainName =
+			strdup(Tilesets[TheMap.Info.MapTerrain]->Ident);
 		InitPlayers();
 		for (i = 0; i < PlayerMax; ++i) {
 			int j;
 
 			if (i == PlayerNumNeutral) {
 				CreatePlayer(PlayerNeutral);
-				TheMap.Info->PlayerType[i] = PlayerNeutral;
-				TheMap.Info->PlayerSide[i] = Players[i].Race = PlayerRaceNeutral;
+				TheMap.Info.PlayerType[i] = PlayerNeutral;
+				TheMap.Info.PlayerSide[i] = Players[i].Race = PlayerRaceNeutral;
 			} else {
 				CreatePlayer(PlayerNobody);
-				TheMap.Info->PlayerType[i] = PlayerNobody;
+				TheMap.Info.PlayerType[i] = PlayerNobody;
 			}
 			for (j = 1; j < MaxCosts; ++j) {
-				TheMap.Info->PlayerResources[i][j] = Players[i].Resources[j];
+				TheMap.Info.PlayerResources[i][j] = Players[i].Resources[j];
 			}
 		}
 
-		strncpy(TheMap.Description, TheMap.Info->Description, 32);
-		TheMap.Width = TheMap.Info->MapWidth;
-		TheMap.Height = TheMap.Info->MapHeight;
+		strncpy(TheMap.Description, TheMap.Info.Description, 32);
+		TheMap.Width = TheMap.Info.MapWidth;
+		TheMap.Height = TheMap.Info.MapHeight;
 		TheMap.Fields = calloc(TheMap.Width * TheMap.Height, sizeof(MapField));
 		TheMap.Visible[0] = calloc(TheMap.Width * TheMap.Height / 8, 1);
 		InitUnitCache();
 
-		TheMap.Terrain = TheMap.Info->MapTerrain;
-		TheMap.TerrainName = strdup(Tilesets[TheMap.Info->MapTerrain]->Ident);
-		TheMap.Tileset = Tilesets[TheMap.Info->MapTerrain];
+		TheMap.Terrain = TheMap.Info.MapTerrain;
+		TheMap.TerrainName = strdup(Tilesets[TheMap.Info.MapTerrain]->Ident);
+		TheMap.Tileset = Tilesets[TheMap.Info.MapTerrain];
 		LoadTileset();
 
 		for (i = 0; i < TheMap.Width * TheMap.Height; ++i) {
@@ -1870,14 +1870,14 @@ static void CreateEditor(void)
 	// Place the start points, which the loader discarded.
 	//
 	for (i = 0; i < PlayerMax; ++i) {
-		if (TheMap.Info->PlayerType[i] != PlayerNobody) {
+		if (TheMap.Info.PlayerType[i] != PlayerNobody) {
 			// Set SelectedPlayer to a valid player
 			if (SelectedPlayer == 15) {
 				SelectedPlayer = i;
 			}
 #if 0
 			// FIXME: must support more races
-			switch (TheMap.Info->PlayerSide[i]) {
+			switch (TheMap.Info.PlayerSide[i]) {
 				case PlayerRaceHuman:
 					MakeUnitAndPlace(Players[i].StartX, Players[i].StartY,
 						UnitTypeByWcNum(WC_StartLocationHuman),
diff --git a/src/game/campaign.cpp b/src/game/campaign.cpp
index 686e1a0c7..81ea8aca6 100644
--- a/src/game/campaign.cpp
+++ b/src/game/campaign.cpp
@@ -168,7 +168,8 @@ void PlayCampaign(const char* name)
 	SkipCurrentChapter = 1;
 	GameResult = GameNoResult;
 
-	strcpy(CurrentMapPath, filename);
+	FreeMapInfo(&TheMap.Info);
+	sprintf(CurrentMapPath, "%s/%s", StratagusLibPath, filename);
 }
 
 /**
diff --git a/src/game/game.cpp b/src/game/game.cpp
index a2fbf23a4..e9546f03c 100644
--- a/src/game/game.cpp
+++ b/src/game/game.cpp
@@ -122,7 +122,7 @@ static void LoadStratagusMap(const char* mapname,
 		fprintf(stderr, "%s: invalid Stratagus map\n", mapname);
 		ExitFatal(-1);
 	}
-	TheMap.Info->Filename = strdup(mapname);
+	TheMap.Info.Filename = strdup(mapname);
 }
 
 /**
@@ -133,7 +133,7 @@ static void LoadStratagusMap(const char* mapname,
 */
 static void LoadMap(const char* filename, WorldMap* map)
 {
-	const char* tmp;
+	char* tmp;
 
 	tmp = strrchr(filename, '.');
 	if (tmp) {
@@ -149,6 +149,15 @@ static void LoadMap(const char* filename, WorldMap* map)
 			}
 		}
 #endif
+		if (!strcmp(tmp, ".smp")) {
+			if (!TheMap.Info.Filename) {
+				// The map info hasn't been loaded yet => do it now
+				LuaLoadFile(filename);
+			}
+			Assert(TheMap.Info.Filename);
+			LoadStratagusMap(TheMap.Info.Filename, map);
+			return;
+		}
 		if (!strcmp(tmp, ".cm")
 #ifdef USE_ZLIB
 				|| !strcmp(tmp, ".cm.gz")
@@ -164,8 +173,11 @@ static void LoadMap(const char* filename, WorldMap* map)
 	// ARI: This bombs out, if no pud, so will be safe.
 	if (strcasestr(filename, ".pud")) {
 		LoadPud(filename, map);
-		map->Info->Filename = strdup(filename);
+		map->Info.Filename = strdup(filename);
+		return;
 	}
+
+	printf("Unrecognized map format\n");
 }
 
 /*----------------------------------------------------------------------------
diff --git a/src/game/savegame.cpp b/src/game/savegame.cpp
index cab8a1749..3c0335ef8 100644
--- a/src/game/savegame.cpp
+++ b/src/game/savegame.cpp
@@ -257,7 +257,7 @@ void SaveGame(const char* filename)
 		StratagusMajorVersion, StratagusMinorVersion, StratagusPatchLevel);
 	CLprintf(file, "  SyncHash = %d, \n", SyncHash);
 	CLprintf(file, "  SyncRandSeed = %d, \n", SyncRandSeed);
-	CLprintf(file, "  SaveFile = \"%s\"\n", TheMap.Info->Filename);
+	CLprintf(file, "  SaveFile = \"%s\"\n", CurrentMapPath);
 	CLprintf(file, "\n---  \"preview\", \"%s.pam\",\n", filename);
 	CLprintf(file, "} )\n\n");
 
diff --git a/src/include/map.h b/src/include/map.h
index 61cf76e82..13508704e 100644
--- a/src/include/map.h
+++ b/src/include/map.h
@@ -291,8 +291,8 @@ typedef struct _world_map_ {
 
 	char Description[32];///< map description short
 
-	MapInfo* Info;  ///< descriptive information
-	// TODO: (MapInfo* Info DUPLICATES!)
+	MapInfo Info;  ///< descriptive information
+	// TODO: (remove MapInfo Info DUPLICATES!)
 } WorldMap;
 
 /*----------------------------------------------------------------------------
diff --git a/src/include/menus.h b/src/include/menus.h
index 02b8575c9..de93370bf 100644
--- a/src/include/menus.h
+++ b/src/include/menus.h
@@ -299,7 +299,6 @@ extern Menu* CurrentMenu;                     ///< Current menu
 extern struct _graphic_* MenuButtonGraphics[];///< Menu button graphics
 extern struct _graphic_* MenuButtonG;         ///< Current menu button graphics
 
-extern struct _map_info_* MenuMapInfo;        ///< MapInfo of map used in gui menus
 extern char MenuMapFullPath[1024];  ///< Full path to currently selected map
 
 extern int nKeyStrokeHelps;    ///< Number of loaded keystroke helps
diff --git a/src/include/pud.h b/src/include/pud.h
index 147149b2c..6ea609060 100644
--- a/src/include/pud.h
+++ b/src/include/pud.h
@@ -93,7 +93,7 @@ struct _world_map_;
 ----------------------------------------------------------------------------*/
 
 	/// Return info for pud
-extern MapInfo* GetPudInfo(const char* pud);
+extern MapInfo* GetPudInfo(const char* pud, MapInfo* info);
 
 	/// Load a pud file
 extern void LoadPud(const char* pud, struct _world_map_* map);
diff --git a/src/map/map.cpp b/src/map/map.cpp
index 784f6db3e..e333db010 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -10,7 +10,7 @@
 //
 /**@name map.c - The map. */
 //
-//      (c) Copyright 1998-2004 by Lutz Sammer and Vladi Shabanski
+//      (c) Copyright 1998-2004 by Lutz Sammer, Vladi Shabanski and Fran�ois Beerten
 //
 //      This program is free software; you can redistribute it and/or modify
 //      it under the terms of the GNU General Public License as published by
@@ -414,7 +414,7 @@ void FreeMapInfo(MapInfo* info)
 		if (info->Filename) {
 			free(info->Filename);
 		}
-		free(info);
+		memset(info, 0, sizeof(MapInfo));
 	}
 }
 
@@ -429,7 +429,7 @@ void CleanMap(void)
 
 	// Tileset freed by Tileset?
 
-	FreeMapInfo(TheMap.Info);
+	FreeMapInfo(&TheMap.Info);
 	memset(&TheMap, 0, sizeof(TheMap));
 	FlagRevealMap = 0;
 	ReplayRevealMap = 0;
diff --git a/src/map/map_save.cpp b/src/map/map_save.cpp
index 839141c63..17a0d2b6f 100644
--- a/src/map/map_save.cpp
+++ b/src/map/map_save.cpp
@@ -85,7 +85,7 @@ void SaveMap(CLFile* file)
 
 	CLprintf(file, "  \"size\", {%d, %d},\n", TheMap.Width, TheMap.Height);
 	CLprintf(file, "  \"%s\",\n", TheMap.NoFogOfWar ? "no-fog-of-war" : "fog-of-war");
-	CLprintf(file, "  \"filename\", \"%s\",\n", TheMap.Info->Filename);
+	CLprintf(file, "  \"filename\", \"%s\",\n", TheMap.Info.Filename);
 
 	CLprintf(file, "  \"map-fields\", {\n");
 	for (h = 0; h < TheMap.Height; ++h) {
diff --git a/src/map/script_map.cpp b/src/map/script_map.cpp
index dee984710..86e24d693 100644
--- a/src/map/script_map.cpp
+++ b/src/map/script_map.cpp
@@ -71,10 +71,6 @@ static int CclStratagusMap(lua_State* l)
 	//  Parse the list: (still everything could be changed!)
 	//
 
-	if (!TheMap.Info) {
-		TheMap.Info = calloc(1, sizeof(MapInfo));
-	}
-
 	args = lua_gettop(l);
 	for (j = 0; j < args; ++j) {
 		value = LuaToString(l, j + 1);
@@ -89,11 +85,11 @@ static int CclStratagusMap(lua_State* l)
 				fprintf(stderr, "Warning not saved with this version.\n");
 			}
 		} else if (!strcmp(value, "uid")) {
-			TheMap.Info->MapUID = LuaToNumber(l, j + 1);
+			TheMap.Info.MapUID = LuaToNumber(l, j + 1);
 		} else if (!strcmp(value, "description")) {
 			value = LuaToString(l, j + 1);
 			strncpy(TheMap.Description, value, sizeof(TheMap.Description));
-			TheMap.Info->Description = strdup(value);
+			TheMap.Info.Description = strdup(value);
 		} else if (!strcmp(value, "the-map")) {
 			if (!lua_istable(l, j + 1)) {
 				LuaError(l, "incorrect argument");
@@ -154,7 +150,7 @@ static int CclStratagusMap(lua_State* l)
 					--k;
 				} else if (!strcmp(value, "filename")) {
 					 lua_rawgeti(l, j + 1, k + 1);
-					TheMap.Info->Filename = strdup(LuaToString(l, -1));
+					TheMap.Info.Filename = strdup(LuaToString(l, -1));
 					lua_pop(l, 1);
 				} else if (!strcmp(value, "map-fields")) {
 					int i;
@@ -445,6 +441,43 @@ static int CclSetFogOfWarGraphics(lua_State* l)
 	return 0;
 }
 
+/**
+** Define the type of each player available for the map
+**
+**  @param l  Lua state.
+*/
+static int CclDefinePlayerTypes(lua_State* l)
+{
+	const char* type;
+	int numplayers;
+	int i;
+
+	numplayers = lua_gettop(l); /* Number of players == number of arguments */
+	if (numplayers < 2) {
+		LuaError(l, "Not enough players");
+	}
+
+	for (i = 0; i < numplayers; i++) {
+		type = LuaToString(l, i + 1);
+		if (!strcmp(type, "neutral")) {
+			TheMap.Info.PlayerType[i] = PlayerNeutral;
+		} else if (!strcmp(type, "nobody")) {
+			TheMap.Info.PlayerType[i] = PlayerNobody;
+		} else if (!strcmp(type, "computer")) {
+			TheMap.Info.PlayerType[i] = PlayerComputer;
+		} else if (!strcmp(type, "person")) {
+			TheMap.Info.PlayerType[i] = PlayerPerson;
+		} else if (!strcmp(type, "rescue-passive")) {
+			TheMap.Info.PlayerType[i] = PlayerRescuePassive;
+		} else if (!strcmp(type, "rescue-active")) {
+			TheMap.Info.PlayerType[i] = PlayerRescueActive;
+		} else {
+			LuaError(l, "Unsupported tag: %s" _C_ type);
+		}
+	}
+	return 0;
+}
+
 /**
 **  Register CCL features for map.
 */
@@ -463,6 +496,8 @@ void MapCclRegister(void)
 	lua_register(Lua, "SetFogOfWarOpacity", CclSetFogOfWarOpacity);
 
 	lua_register(Lua, "SetForestRegeneration",CclSetForestRegeneration);
+
+	lua_register(Lua, "DefinePlayerTypes", CclDefinePlayerTypes);
 }
 
 //@}
diff --git a/src/network/commands.cpp b/src/network/commands.cpp
index d5e77b1eb..204cef2bc 100644
--- a/src/network/commands.cpp
+++ b/src/network/commands.cpp
@@ -195,7 +195,7 @@ static FullReplay* StartReplay(void)
 
 	replay->Date = strdup(s);
 	replay->Map = strdup(TheMap.Description);
-	replay->MapId = (signed int)TheMap.Info->MapUID;
+	replay->MapId = (signed int)TheMap.Info.MapUID;
 	replay->MapPath = strdup(CurrentMapPath);
 	replay->Resource = GameSettings.Resources;
 	replay->NumUnits = GameSettings.NumUnits;
diff --git a/src/network/netconnect.cpp b/src/network/netconnect.cpp
index 672677371..b4e8292af 100644
--- a/src/network/netconnect.cpp
+++ b/src/network/netconnect.cpp
@@ -353,7 +353,7 @@ void NetworkServerStartGame(void)
 
 	// Make a list of the available player slots.
 	for (h = i = 0; i < PlayerMax; ++i) {
-		if (MenuMapInfo->PlayerType[i] == PlayerPerson) {
+		if (TheMap.Info.PlayerType[i] == PlayerPerson) {
 			rev[i] = h;
 			num[h++] = i;
 			DebugPrint("Slot %d is available for an interactive player (%d)\n" _C_
@@ -363,7 +363,7 @@ void NetworkServerStartGame(void)
 	// Make a list of the available computer slots.
 	n = h;
 	for (i = 0; i < PlayerMax; ++i) {
-		if (MenuMapInfo->PlayerType[i] == PlayerComputer) {
+		if (TheMap.Info.PlayerType[i] == PlayerComputer) {
 			rev[i] = n++;
 			DebugPrint("Slot %d is available for an ai computer player (%d)\n" _C_
 				i _C_ rev[i]);
@@ -371,8 +371,8 @@ void NetworkServerStartGame(void)
 	}
 	// Make a list of the remaining slots.
 	for (i = 0; i < PlayerMax; ++i) {
-		if (MenuMapInfo->PlayerType[i] != PlayerPerson &&
-				MenuMapInfo->PlayerType[i] != PlayerComputer) {
+		if (TheMap.Info.PlayerType[i] != PlayerPerson &&
+				TheMap.Info.PlayerType[i] != PlayerComputer) {
 			rev[i] = n++;
 			// PlayerNobody - not available to anything..
 		}
@@ -435,7 +435,7 @@ void NetworkServerStartGame(void)
 	j = h;
 	if (NoRandomPlacementMultiplayer == 1) {
 		for (i = 0; i < PlayerMax; ++i) {
-			if (MenuMapInfo->PlayerType[i] != PlayerComputer) {
+			if (TheMap.Info.PlayerType[i] != PlayerComputer) {
 				org[i] = Hosts[i].PlyNr;
 			}
 		}
@@ -506,7 +506,7 @@ void NetworkServerStartGame(void)
 	message.Type = MessageInitReply;
 	message.SubType = ICMConfig;
 	message.HostsCount = NetPlayers;
-	message.MapUID = htonl(MenuMapInfo->MapUID);
+	message.MapUID = htonl(TheMap.Info.MapUID);
 	for (i = 0; i < NetPlayers; ++i) {
 		message.u.Hosts[i].Host = Hosts[i].Host;
 		message.u.Hosts[i].Port = Hosts[i].Port;
@@ -519,7 +519,7 @@ void NetworkServerStartGame(void)
 	statemsg.SubType = ICMState;
 	statemsg.HostsCount = NetPlayers;
 	statemsg.u.State = ServerSetupState;
-	statemsg.MapUID = htonl(MenuMapInfo->MapUID);
+	statemsg.MapUID = htonl(TheMap.Info.MapUID);
 
 	msg = (InitMessage*)buf;
 	DebugPrint("Ready, sending InitConfig to %d host(s)\n" _C_ HostsCount);
@@ -705,7 +705,7 @@ changed:
 				message.Type = MessageInitHello;
 				message.SubType = ICMState;
 				message.u.State = LocalSetupState;
-				message.MapUID = htonl(MenuMapInfo->MapUID);
+				message.MapUID = htonl(TheMap.Info.MapUID);
 				NetworkSendRateLimitedClientMessage(&message, 450);
 			} else {
 				NetLocalState = ccs_unreachable;
@@ -725,10 +725,10 @@ changed:
 			}
 			break;
 		case ccs_mapinfo:
-			if (NetStateMsgCnt < 20 && MenuMapInfo != NULL) { // 20 retries
+			if (NetStateMsgCnt < 20) { // 20 retries
 				message.Type = MessageInitHello;
 				message.SubType = ICMMap; // ICMMapAck..
-				message.MapUID = htonl(MenuMapInfo->MapUID);
+				message.MapUID = htonl(TheMap.Info.MapUID);
 				NetworkSendRateLimitedClientMessage(&message, 650);
 			} else {
 				NetLocalState = ccs_unreachable;
@@ -739,11 +739,7 @@ changed:
 			if (NetStateMsgCnt < 20) { // 20 retries
 				message.Type = MessageInitHello;
 				message.SubType = ICMMapUidMismatch;
-				if (MenuMapInfo) {
-					message.MapUID = htonl(MenuMapInfo->MapUID); // MAP Uid doesn't match
-				} else {
-					message.MapUID = 0L; // Map not found
-				}
+				message.MapUID = htonl(TheMap.Info.MapUID); // MAP Uid doesn't match
 				NetworkSendRateLimitedClientMessage(&message, 650);
 			} else {
 				NetLocalState = ccs_unreachable;
@@ -813,7 +809,7 @@ void NetworkProcessServerRequest(void)
 	unsigned long fcd;
 	InitMessage message;
 
-	if (MenuMapInfo == NULL) {
+	if (GameRunning) {
 		return;
 		// Game already started...
 	}
@@ -965,10 +961,10 @@ static void ClientParseConnected(const InitMessage* msg)
 				NetLocalState = ccs_badmap;
 				break;
 			}
-			if (ntohl(msg->MapUID) != MenuMapInfo->MapUID) {
+			if (ntohl(msg->MapUID) != TheMap.Info.MapUID) {
 				NetLocalState = ccs_badmap;
 				fprintf(stderr, "Stratagus maps do not match (0x%08x) <-> (0x%08x)\n",
-					(unsigned int)MenuMapInfo->MapUID,
+					(unsigned int)TheMap.Info.MapUID,
 					(unsigned int)ntohl(msg->MapUID));
 				break;
 			}
@@ -1343,7 +1339,7 @@ static void ServerParseWaiting(const int h)
 			message.SubType = ICMMap; // Send Map info to the client
 			pathlen = strlen(StratagusLibPath) + 1;
 			memcpy(message.u.MapPath, MenuMapFullPath + pathlen, 256);
-			message.MapUID = htonl(MenuMapInfo->MapUID);
+			message.MapUID = htonl(TheMap.Info.MapUID);
 			n = NetworkSendICMessage(NetLastHost, NetLastPort, &message);
 			DebugPrint("Sending InitReply Message Map: (%d) to %d.%d.%d.%d:%d\n" _C_
 				n _C_ NIPQUAD(ntohl(NetLastHost)) _C_ ntohs(NetLastPort));
@@ -1377,7 +1373,7 @@ static void ServerParseWaiting(const int h)
 			message.Type = MessageInitReply;
 			message.SubType = ICMState; // Send new state info to the client
 			message.u.State = ServerSetupState;
-			message.MapUID = htonl(MenuMapInfo->MapUID);
+			message.MapUID = htonl(TheMap.Info.MapUID);
 			n = NetworkSendICMessage(NetLastHost, NetLastPort, &message);
 			DebugPrint("Sending InitReply Message State: (%d) to %d.%d.%d.%d:%d\n" _C_
 				n _C_ NIPQUAD(ntohl(NetLastHost)) _C_ ntohs(NetLastPort));
@@ -1418,7 +1414,7 @@ static void ServerParseMap(const int h)
 			message.Type = MessageInitReply;
 			message.SubType = ICMState; // Send State info to the client
 			message.u.State = ServerSetupState;
-			message.MapUID = htonl(MenuMapInfo->MapUID);
+			message.MapUID = htonl(TheMap.Info.MapUID);
 			n = NetworkSendICMessage(NetLastHost, NetLastPort, &message);
 			DebugPrint("Sending InitReply Message State: (%d) to %d.%d.%d.%d:%d\n" _C_
 				n _C_ NIPQUAD(ntohl(NetLastHost)) _C_ ntohs(NetLastPort));
@@ -1475,7 +1471,7 @@ static void ServerParseState(const int h, const InitMessage* msg)
 			message.Type = MessageInitReply;
 			message.SubType = ICMState; // Send new state info to the client
 			message.u.State = ServerSetupState;
-			message.MapUID = htonl(MenuMapInfo->MapUID);
+			message.MapUID = htonl(TheMap.Info.MapUID);
 			n = NetworkSendICMessage(NetLastHost, NetLastPort, &message);
 			DebugPrint("Sending InitReply Message State: (%d) to %d.%d.%d.%d:%d\n" _C_
 				n _C_ NIPQUAD(ntohl(NetLastHost)) _C_ ntohs(NetLastPort));
diff --git a/src/stratagus/pud.cpp b/src/stratagus/pud.cpp
index 90e694d6f..8a3688161 100644
--- a/src/stratagus/pud.cpp
+++ b/src/stratagus/pud.cpp
@@ -353,13 +353,12 @@ static int PudReadByte(CLFile* input)
 /**
 ** Get the info for a pud.
 */
-MapInfo* GetPudInfo(const char* pud)
+MapInfo* GetPudInfo(const char* pud, MapInfo* info)
 {
 	CLFile* input;
 	uint32_t length;
 	char header[5];
 	char buf[1024];
-	MapInfo* info;
 	unsigned short temp_short;
 	// FIXME: Reuse the temporary alloca buffer...
 
@@ -387,10 +386,7 @@ MapInfo* GetPudInfo(const char* pud)
 		return NULL;
 	}
 
-	info=calloc(1, sizeof(MapInfo)); // clears with 0
-	if (!info) {
-		return NULL;
-	}
+	memset(info, 0, sizeof(MapInfo)); // clears with 0
 
 	info->Filename = strdup(pud);
 
@@ -813,9 +809,7 @@ void LoadPud(const char* pud,WorldMap* map)
 	strcat(pudfull, "/");
 	strcat(pudfull, pud);
 
-	if (!map->Info) {
-		map->Info = GetPudInfo(pudfull);
-	}
+	GetPudInfo(pudfull, &map->Info);
 	if( !(input=CLopen(pudfull,CL_OPEN_READ)) ) {
 		fprintf(stderr,"Try ./path/name\n");
 		fprintf(stderr,"pud: CLopen(%s): %s\n", pud, strerror(errno));
@@ -1577,10 +1571,10 @@ int SavePud(const char* pud,const WorldMap* map)
 	buf[0x9]=0x00;
 	buf[0xA]=0x0A;
 	buf[0xB]=0xFF;
-	buf[0xC]=map->Info->MapUID >>  0;
-	buf[0xD]=map->Info->MapUID >>  8;
-	buf[0xE]=map->Info->MapUID >> 16;
-	buf[0xF]=map->Info->MapUID >> 24;
+	buf[0xC]=map->Info.MapUID >>  0;
+	buf[0xD]=map->Info.MapUID >>  8;
+	buf[0xE]=map->Info.MapUID >> 16;
+	buf[0xF]=map->Info.MapUID >> 24;
 	gzwrite(f,buf,16);
 
 	PudWriteHeader(f,"VER ",2);
@@ -1590,18 +1584,18 @@ int SavePud(const char* pud,const WorldMap* map)
 
 	PudWriteHeader(f,"DESC",32);
 	memset(buf,0,32);
-	strncpy(buf,map->Info->Description,32);
+	strncpy(buf,map->Info.Description,32);
 	gzwrite(f,buf,32);
 
 	PudWriteHeader(f,"OWNR",16);
 	for( i=0; i<16; ++i ) {
-		buf[i]=map->Info->PlayerType[i];
+		buf[i]=map->Info.PlayerType[i];
 	}
 	gzwrite(f,buf,16);
 
 	PudWriteHeader(f,"ERAX",2);
-	buf[0]=map->Info->MapTerrain >> 0;
-	buf[1]=map->Info->MapTerrain >> 8;
+	buf[0]=map->Info.MapTerrain >> 0;
+	buf[1]=map->Info.MapTerrain >> 8;
 	gzwrite(f,buf,2);
 
 	PudWriteHeader(f,"DIM ",4);
@@ -1633,34 +1627,34 @@ int SavePud(const char* pud,const WorldMap* map)
 
 	PudWriteHeader(f,"SIDE",16);
 	for( i=0; i<16; ++i ) {
-		buf[i]=map->Info->PlayerSide[i];
+		buf[i]=map->Info.PlayerSide[i];
 	}
 	gzwrite(f,buf,16);
 
 	PudWriteHeader(f,"SGLD",32);
 	for( i=0; i<16; ++i ) {
-		buf[i*2+0]=map->Info->PlayerResources[i][GoldCost] >> 0;
-		buf[i*2+1]=map->Info->PlayerResources[i][GoldCost] >> 8;
+		buf[i*2+0]=map->Info.PlayerResources[i][GoldCost] >> 0;
+		buf[i*2+1]=map->Info.PlayerResources[i][GoldCost] >> 8;
 	}
 	gzwrite(f,buf,32);
 
 	PudWriteHeader(f,"SLBR",32);
 	for( i=0; i<16; ++i ) {
-		buf[i*2+0]=map->Info->PlayerResources[i][WoodCost] >> 0;
-		buf[i*2+1]=map->Info->PlayerResources[i][WoodCost] >> 8;
+		buf[i*2+0]=map->Info.PlayerResources[i][WoodCost] >> 0;
+		buf[i*2+1]=map->Info.PlayerResources[i][WoodCost] >> 8;
 	}
 	gzwrite(f,buf,32);
 
 	PudWriteHeader(f,"SOIL",32);
 	for( i=0; i<16; ++i ) {
-		buf[i*2+0]=map->Info->PlayerResources[i][OilCost] >> 0;
-		buf[i*2+1]=map->Info->PlayerResources[i][OilCost] >> 8;
+		buf[i*2+0]=map->Info.PlayerResources[i][OilCost] >> 0;
+		buf[i*2+1]=map->Info.PlayerResources[i][OilCost] >> 8;
 	}
 	gzwrite(f,buf,32);
 
 	PudWriteHeader(f,"AIPL",16);
 	for( i=0; i<16; ++i ) {
-		buf[i]=map->Info->PlayerAi[i];
+		buf[i]=map->Info.PlayerAi[i];
 	}
 	gzwrite(f,buf,16);
 
diff --git a/src/stratagus/stratagus.cpp b/src/stratagus/stratagus.cpp
index 49bc89705..42f481164 100644
--- a/src/stratagus/stratagus.cpp
+++ b/src/stratagus/stratagus.cpp
@@ -669,8 +669,7 @@ void MenuLoop(char* filename, WorldMap* map)
 		//    - FIXME: not the ideal place for this..
 		//
 		DebugPrint("Freeing map info, wrong place\n");
-		FreeMapInfo(map->Info);
-		map->Info = NULL;
+		FreeMapInfo(&map->Info);
 
 		//
 		//  No filename given, choose with the menus
@@ -730,6 +729,7 @@ void MenuLoop(char* filename, WorldMap* map)
 			//
 			//  Create the game.
 			//
+			DebugPrint("Creating game with map: %s\n" _C_ filename);
 			CreateGame(filename, map);
 
 			SetStatusLine(NameLine);
@@ -750,7 +750,9 @@ void MenuLoop(char* filename, WorldMap* map)
 		PreMenuSetup();
 
 		filename = NextChapter();
-		DebugPrint("Next chapter %s\n" _C_ filename);
+		sprintf(CurrentMapPath, "%s/%s", StratagusLibPath, filename);
+		filename = CurrentMapPath;
+		DebugPrint("Next chapter %s\n" _C_ CurrentMapPath);
 	}
 }
 
diff --git a/src/ui/menus.cpp b/src/ui/menus.cpp
index b52484d41..b50d317b1 100644
--- a/src/ui/menus.cpp
+++ b/src/ui/menus.cpp
@@ -428,7 +428,6 @@ static char ScenSelectPath[1024];        ///< Scenario selector path
 static char ScenSelectDisplayPath[1024]; ///< Displayed selector path
 static char ScenSelectFileName[128];     ///< Scenario selector name
 
-MapInfo *MenuMapInfo;                    ///< Selected map info
 char MenuMapFullPath[1024];              ///< Selected map path+name
 
 static char *SaveDir;                    ///< Save game directory
@@ -2485,9 +2484,6 @@ static void GetInfoFromSelectPath(void)
 {
 	int i;
 
-	FreeMapInfo(MenuMapInfo);
-	MenuMapInfo = NULL;
-
 	if (ScenSelectPath[0]) {
 		i = strlen(ScenSelectPath);
 		strcat(ScenSelectPath, "/");
@@ -2496,10 +2492,10 @@ static void GetInfoFromSelectPath(void)
 	}
 	strcat(ScenSelectPath, ScenSelectFileName); // Final map name with path
 	if (strcasestr(ScenSelectFileName, ".pud")) {
-		MenuMapInfo = GetPudInfo(ScenSelectPath);
+		GetPudInfo(ScenSelectPath, &TheMap.Info);
 		strcpy(MenuMapFullPath, ScenSelectPath);
-	} else {
-		// FIXME: GetCmInfo();
+	} else if(strcasestr(ScenSelectFileName, ".smp")) {
+		LuaLoadFile(ScenSelectPath);
 	}
 	ScenSelectPath[i] = '\0'; // Remove appended part
 }
@@ -2519,21 +2515,15 @@ static void ScenSelectMenu(void)
 	GetInfoFromSelectPath();
 
 	menu = FindMenu("menu-custom-game");
-	// FIXME: This check is only needed until GetCmInfo works
-	if (!MenuMapInfo) {
-		menu->Items[12].D.Pulldown.noptions = PlayerMax-1;
+	for (n = j = 0; j < PlayerMax; ++j) {
+		t = TheMap.Info.PlayerType[j];
+		if (t == PlayerPerson || t == PlayerComputer) {
+			n++;
+		}
+	}
+	menu->Items[12].D.Pulldown.noptions = n;
+	if (menu->Items[12].D.Pulldown.curopt >= n) {
 		menu->Items[12].D.Pulldown.curopt = 0;
-	} else {
-		for (n = j = 0; j < PlayerMax; ++j) {
-			t = MenuMapInfo->PlayerType[j];
-			if (t == PlayerPerson || t == PlayerComputer) {
-				n++;
-			}
-		}
-		menu->Items[12].D.Pulldown.noptions = n;
-		if (menu->Items[12].D.Pulldown.curopt >= n) {
-			menu->Items[12].D.Pulldown.curopt = 0;
-		}
 	}
 }
 
@@ -3008,6 +2998,28 @@ static void MultiPlayerInternetGame(void)
 	}
 }
 
+/**
+** Allocate and deep copy a MapInfo structure
+*/
+static MapInfo* DuplicateMapInfo(MapInfo *orig)
+{
+	MapInfo* dest;
+
+	dest = malloc(sizeof(MapInfo));
+	memcpy(dest, orig, sizeof(MapInfo));
+	if (orig->Description) {
+		dest->Description = strdup(orig->Description);
+	}
+	if (dest->MapTerrainName) {
+		dest->MapTerrainName = strdup(orig->MapTerrainName);
+	}
+	if (dest->Filename) {
+		dest->Filename = strdup(orig->Filename);
+	}
+
+	return dest;
+}
+
 /**
 ** Free map info data
 */
@@ -3018,6 +3030,7 @@ static void FreeMapInfos(FileList *fl, int n)
 	for (i = 0; i < n; i++) {
 		if (fl[i].type && fl[i].xdata) {
 			FreeMapInfo(fl[i].xdata);
+			free(fl[i].xdata);
 			fl[i].xdata = NULL;
 		}
 		free(fl[i].name);
@@ -3092,7 +3105,7 @@ static int ScenSelectRDFilter(char *pathbuf, FileList *fl)
 
 	curopt = menu->Items[6].D.Pulldown.curopt;
 	if (curopt == 0) {
-		suf[0] = ".cm";
+		suf[0] = ".smp";
 		suf[1] = NULL;
 	} else if (curopt == 1) {
 		suf[0] = ".pud";
@@ -3141,20 +3154,22 @@ static int ScenSelectRDFilter(char *pathbuf, FileList *fl)
 #endif
 		if (*cp == 0) {
 			if (curopt == 0) {
-				// info = GetCmInfo(pathbuf);
 				info = NULL;
 				fl->type = 1;
 				fl->name = strdup(np);
-				fl->xdata = info;
+				FreeMapInfo(&TheMap.Info);
+				LuaLoadFile(pathbuf);
+				fl->xdata = DuplicateMapInfo(&TheMap.Info);
 				return 1;
 			} else if (curopt == 1) {
-				info = GetPudInfo(pathbuf);
+				FreeMapInfo(&TheMap.Info);
+				info = GetPudInfo(pathbuf, &TheMap.Info);
 				if (info) {
 					sz = szl[menu->Items[8].D.Pulldown.curopt];
 					if (sz < 0 || (info->MapWidth == sz && info->MapHeight == sz)) {
 						fl->type = 1;
 						fl->name = strdup(np);
-						fl->xdata = info;
+						fl->xdata = DuplicateMapInfo(info);
 						return 1;
 					} else {
 						FreeMapInfo(info);
@@ -3474,8 +3489,6 @@ static void ScenSelectCancel(void)
 */
 static void GameCancel(void)
 {
-	FreeMapInfo(MenuMapInfo);
-	MenuMapInfo = NULL;
 	EndMenu();
 }
 
@@ -3487,9 +3500,6 @@ static void CustomGameStart(void)
 	int i;
 	char *p;
 
-	FreeMapInfo(MenuMapInfo);
-	MenuMapInfo = NULL;
-
 	if (ScenSelectPath[0]) {
 		strcat(ScenSelectPath, "/");
 		strcat(ScenSelectPath, ScenSelectFileName); // Final map name with path
@@ -3551,21 +3561,15 @@ static void GameSetupInit(Menuitem* mi __attribute__ ((unused)))
 	GetInfoFromSelectPath();
 
 	menu = FindMenu("menu-custom-game");
-	// FIXME: This check is only needed until GetCmInfo works
-	if (!MenuMapInfo) {
-		menu->Items[12].D.Pulldown.noptions = PlayerMax-1;
+	for (n = j = 0; j < PlayerMax; ++j) {
+		t = TheMap.Info.PlayerType[j];
+		if (t == PlayerPerson || t == PlayerComputer) {
+			n++;
+		}
+	}
+	menu->Items[12].D.Pulldown.noptions = n;
+	if (menu->Items[12].D.Pulldown.curopt >= n) {
 		menu->Items[12].D.Pulldown.curopt = 0;
-	} else {
-		for (n = j = 0; j < PlayerMax; ++j) {
-			t = MenuMapInfo->PlayerType[j];
-			if (t == PlayerPerson || t == PlayerComputer) {
-				n++;
-			}
-		}
-		menu->Items[12].D.Pulldown.noptions = n;
-		if (menu->Items[12].D.Pulldown.curopt >= n) {
-			menu->Items[12].D.Pulldown.curopt = 0;
-		}
 	}
 }
 
@@ -3584,13 +3588,12 @@ static void GameDrawFunc(Menuitem* mi __attribute__((unused)))
 	l = VideoTextLength(GameFont, "Scenario:");
 	VideoDrawText(TheUI.Offset640X + 16, TheUI.Offset480Y + 360, GameFont, "Scenario:");
 	VideoDrawText(TheUI.Offset640X + 16, TheUI.Offset480Y + 360 + 24 , GameFont, ScenSelectFileName);
-	if (MenuMapInfo) {
-		if (MenuMapInfo->Description) {
-			VideoDrawText(TheUI.Offset640X + 16 + l + 8, TheUI.Offset480Y + 360, GameFont, MenuMapInfo->Description);
-		}
-		sprintf(buffer, " (%d x %d)", MenuMapInfo->MapWidth, MenuMapInfo->MapHeight);
-		VideoDrawText(TheUI.Offset640X + 16 + l + 8 + VideoTextLength(GameFont, ScenSelectFileName), TheUI.Offset480Y + 360 + 24, GameFont, buffer);
+	if (TheMap.Info.Description) {
+		VideoDrawText(TheUI.Offset640X + 16 + l + 8, TheUI.Offset480Y + 360, GameFont, TheMap.Info.Description);
 	}
+	sprintf(buffer, " (%d x %d)", TheMap.Info.MapWidth, TheMap.Info.MapHeight);
+	VideoDrawText(TheUI.Offset640X + 16 + l + 8 + VideoTextLength(GameFont, ScenSelectFileName), TheUI.Offset480Y + 360 + 24, GameFont, buffer);
+
 #if 0
 	for (n = j = 0; j < PlayerMax; j++) {
 		if (info->PlayerType[j] == PlayerPerson) {
@@ -3803,8 +3806,6 @@ static void NetworkGamePrepareGameSettings(void)
 	int x;
 	int v;
 
-	Assert(MenuMapInfo);
-
 	DebugPrint("NetPlayers = %d\n" _C_ NetPlayers);
 
 	GameSettings.NetGameType=SettingsMultiPlayerGame;
@@ -3825,10 +3826,10 @@ static void NetworkGamePrepareGameSettings(void)
 
 	// Make a list of the available player slots.
 	for (c = h = i = 0; i < PlayerMax; i++) {
-		if (MenuMapInfo->PlayerType[i] == PlayerPerson) {
+		if (TheMap.Info.PlayerType[i] == PlayerPerson) {
 			num[h++] = i;
 		}
-		if (MenuMapInfo->PlayerType[i] == PlayerComputer) {
+		if (TheMap.Info.PlayerType[i] == PlayerComputer) {
 			comp[c++] = i; // available computer player slots
 		}
 	}
@@ -3902,10 +3903,10 @@ static void MultiGamePlayerSelectorsUpdate(int initial)
 
 	// Calculate available slots from pudinfo
 	for (c = h = i = 0; i < PlayerMax; i++) {
-		if (MenuMapInfo->PlayerType[i] == PlayerPerson) {
+		if (TheMap.Info.PlayerType[i] == PlayerPerson) {
 			h++; // available interactive player slots
 		}
-		if (MenuMapInfo->PlayerType[i] == PlayerComputer) {
+		if (TheMap.Info.PlayerType[i] == PlayerComputer) {
 			c++; // available computer player slots
 		}
 	}
@@ -4039,10 +4040,10 @@ static void MultiClientUpdate(int initial)
 
 	//  Calculate available slots from pudinfo
 	for (c = h = i = 0; i < PlayerMax; i++) {
-		if (MenuMapInfo->PlayerType[i] == PlayerPerson) {
+		if (TheMap.Info.PlayerType[i] == PlayerPerson) {
 			h++; // available interactive player slots
 		}
-		if (MenuMapInfo->PlayerType[i] == PlayerComputer) {
+		if (TheMap.Info.PlayerType[i] == PlayerComputer) {
 			c++; // available computer player slots
 		}
 	}
@@ -4134,7 +4135,7 @@ static void MultiGameSetupInit(Menuitem* mi)
 	memset(&ServerSetupState, 0, sizeof(ServerSetup));
 	// Calculate available slots from pudinfo
 	for (h = i = 0; i < PlayerMax; i++) {
-		if (MenuMapInfo->PlayerType[i] == PlayerPerson) {
+		if (TheMap.Info.PlayerType[i] == PlayerPerson) {
 			h++; // available interactive player slots
 		}
 	}
@@ -4369,9 +4370,6 @@ int NetClientSelectScenario(void)
 {
 	char *cp;
 
-	FreeMapInfo(MenuMapInfo);
-	MenuMapInfo = NULL;
-
 	cp = strrchr(MenuMapFullPath, '/');
 	if (cp) {
 		strcpy(ScenSelectFileName, cp + 1);
@@ -4383,12 +4381,13 @@ int NetClientSelectScenario(void)
 		ScenSelectPath[0] = 0;
 	}
 
+	FreeMapInfo(&TheMap.Info);
 	if (strcasestr(ScenSelectFileName, ".pud")) {
-		MenuMapInfo = GetPudInfo(MenuMapFullPath);
+		GetPudInfo(MenuMapFullPath, &TheMap.Info);
 	} else {
-		// FIXME: GetCmInfo();
+		LuaLoadFile(MenuMapFullPath);
 	}
-	return MenuMapInfo == NULL;
+	return 1;
 }
 
 /**
@@ -4520,12 +4519,11 @@ static void EditorNewMap(void)
 		return;
 	}
 
-	TheMap.Info = calloc(1, sizeof(MapInfo));
 	description[strlen(description) - 3] = '\0';
-	TheMap.Info->Description = strdup(description);
-	TheMap.Info->MapTerrain = menu->Items[7].D.Pulldown.curopt;
-	TheMap.Info->MapWidth = atoi(width);
-	TheMap.Info->MapHeight = atoi(height);
+	TheMap.Info.Description = strdup(description);
+	TheMap.Info.MapTerrain = menu->Items[7].D.Pulldown.curopt;
+	TheMap.Info.MapWidth = atoi(width);
+	TheMap.Info.MapHeight = atoi(height);
 
 	VideoClearScreen();
 
@@ -4761,13 +4759,21 @@ static int EditorMainLoadRDFilter(char *pathbuf, FileList *fl)
 #endif
 		if (*cp == 0) {
 			if (strcasestr(np, ".pud")) {
-				info = GetPudInfo(pathbuf);
+				info = GetPudInfo(pathbuf, &TheMap.Info);
 				if (info) {
 					fl->type = 1;
 					fl->name = strdup(np);
-					fl->xdata = info;
+					fl->xdata = DuplicateMapInfo(info);
 					return 1;
 				}
+			} else {
+				info = NULL;
+				fl->type = 1;
+				fl->name = strdup(np);
+				FreeMapInfo(&TheMap.Info);
+				LuaLoadFile(pathbuf);
+				fl->xdata = DuplicateMapInfo(&TheMap.Info);
+				return 1;
 			}
 		}
 	}
@@ -5074,7 +5080,7 @@ static void EditorMapPropertiesMenu(void)
 	menu = FindMenu("menu-editor-map-properties");
 
 	menu->Items[2].D.Input.buffer = description;
-	strcpy(description, TheMap.Info->Description);
+	strcpy(description, TheMap.Info.Description);
 	strcat(description, "~!_");
 	menu->Items[2].D.Input.nch = strlen(description) - 3;
 	menu->Items[2].D.Input.maxch = 31;
@@ -5114,19 +5120,19 @@ static void EditorMapPropertiesOk(void)
 
 	description = menu->Items[2].D.Input.buffer;
 	description[strlen(description)-3] = '\0';
-	free(TheMap.Info->Description);
-	TheMap.Info->Description = strdup(description);
+	free(TheMap.Info.Description);
+	TheMap.Info.Description = strdup(description);
 
 	// Change the terrain
-	old = TheMap.Info->MapTerrain;
+	old = TheMap.Info.MapTerrain;
 	if (old != menu->Items[6].D.Pulldown.curopt) {
-		TheMap.Info->MapTerrain = menu->Items[6].D.Pulldown.curopt;
-		free(TheMap.Info->MapTerrainName);
-		TheMap.Info->MapTerrainName = strdup(TilesetWcNames[TheMap.Info->MapTerrain]);
-		TheMap.Terrain = TheMap.Info->MapTerrain;
+		TheMap.Info.MapTerrain = menu->Items[6].D.Pulldown.curopt;
+		free(TheMap.Info.MapTerrainName);
+		TheMap.Info.MapTerrainName = strdup(TilesetWcNames[TheMap.Info.MapTerrain]);
+		TheMap.Terrain = TheMap.Info.MapTerrain;
 		free(TheMap.TerrainName);
-		TheMap.TerrainName = strdup(TilesetWcNames[TheMap.Info->MapTerrain]);
-		TheMap.Tileset = Tilesets[TheMap.Info->MapTerrain];
+		TheMap.TerrainName = strdup(TilesetWcNames[TheMap.Info.MapTerrain]);
+		TheMap.Tileset = Tilesets[TheMap.Info.MapTerrain];
 
 		LoadTileset();
 		ChangeTilesetPud(old, &TheMap);
@@ -5244,12 +5250,12 @@ static void EditorPlayerPropertiesMenu(void)
 #define OIL_POSITION 106
 
 	for (i = 0; i < PlayerMax; ++i) {
-		menu->Items[RACE_POSITION + i].D.Pulldown.defopt = TheMap.Info->PlayerSide[i];
-		menu->Items[TYPE_POSITION + i].D.Pulldown.defopt = PlayerTypesFcToMenu[TheMap.Info->PlayerType[i]];
-		menu->Items[AI_POSITION + i].D.Pulldown.defopt = PlayerAiFcToMenu(TheMap.Info->PlayerAi[i]);
-		sprintf(gold[i], "%d~!_", TheMap.Info->PlayerResources[i][GoldCost]);
-		sprintf(lumber[i], "%d~!_", TheMap.Info->PlayerResources[i][WoodCost]);
-		sprintf(oil[i], "%d~!_", TheMap.Info->PlayerResources[i][OilCost]);
+		menu->Items[RACE_POSITION + i].D.Pulldown.defopt = TheMap.Info.PlayerSide[i];
+		menu->Items[TYPE_POSITION + i].D.Pulldown.defopt = PlayerTypesFcToMenu[TheMap.Info.PlayerType[i]];
+		menu->Items[AI_POSITION + i].D.Pulldown.defopt = PlayerAiFcToMenu(TheMap.Info.PlayerAi[i]);
+		sprintf(gold[i], "%d~!_", TheMap.Info.PlayerResources[i][GoldCost]);
+		sprintf(lumber[i], "%d~!_", TheMap.Info.PlayerResources[i][WoodCost]);
+		sprintf(oil[i], "%d~!_", TheMap.Info.PlayerResources[i][OilCost]);
 		menu->Items[GOLD_POSITION + i].D.Input.buffer = gold[i];
 		menu->Items[GOLD_POSITION + i].D.Input.nch = strlen(gold[i]) - 3;
 		menu->Items[GOLD_POSITION + i].D.Input.maxch = 7;
@@ -5264,12 +5270,12 @@ static void EditorPlayerPropertiesMenu(void)
 	ProcessMenu("menu-editor-player-properties", 1);
 
 	for (i = 0; i < PlayerMax; ++i) {
-		TheMap.Info->PlayerSide[i] = menu->Items[RACE_POSITION + i].D.Pulldown.curopt;
-		TheMap.Info->PlayerType[i] = PlayerTypesMenuToFc[menu->Items[TYPE_POSITION + i].D.Pulldown.curopt];
-		TheMap.Info->PlayerAi[i] = PlayerAiMenuToFc(menu->Items[AI_POSITION + i].D.Pulldown.curopt);
-		TheMap.Info->PlayerResources[i][GoldCost] = atoi(gold[i]);
-		TheMap.Info->PlayerResources[i][WoodCost] = atoi(lumber[i]);
-		TheMap.Info->PlayerResources[i][OilCost] = atoi(oil[i]);
+		TheMap.Info.PlayerSide[i] = menu->Items[RACE_POSITION + i].D.Pulldown.curopt;
+		TheMap.Info.PlayerType[i] = PlayerTypesMenuToFc[menu->Items[TYPE_POSITION + i].D.Pulldown.curopt];
+		TheMap.Info.PlayerAi[i] = PlayerAiMenuToFc(menu->Items[AI_POSITION + i].D.Pulldown.curopt);
+		TheMap.Info.PlayerResources[i][GoldCost] = atoi(gold[i]);
+		TheMap.Info.PlayerResources[i][WoodCost] = atoi(lumber[i]);
+		TheMap.Info.PlayerResources[i][OilCost] = atoi(oil[i]);
 	}
 }
 
@@ -6414,7 +6420,7 @@ static void ChangeGameServer(void)
 	freespots = 0;
 	players = 0;
 	for (i = 0; i < PlayerMax - 1; ++i) {
-		if (MenuMapInfo->PlayerType[i] == PlayerPerson) {
+		if (TheMap.Info.PlayerType[i] == PlayerPerson) {
 			++players;
 		}
 		if (ServerSetupState.CompOpt[i] == 0) {
diff --git a/src/ui/script_ui.cpp b/src/ui/script_ui.cpp
index 2e4b77ff8..d9e2d6cc2 100644
--- a/src/ui/script_ui.cpp
+++ b/src/ui/script_ui.cpp
@@ -4437,6 +4437,38 @@ static int CclSetGroupKeys(lua_State* l)
 	return 0;
 }
 
+/**
+** Set basic map caracteristics.
+**
+**  @param l  Lua state.
+*/
+static int CclPresentMap(lua_State* l)
+{
+	if (lua_gettop(l) != 4) {
+		LuaError(l, "incorrect argument");
+	}
+	TheMap.Info.Description = strdup(LuaToString(l, 1));
+	// Number of players in LuaToNumber(l, 3); // Not used yet.
+	TheMap.Info.MapWidth = LuaToNumber(l, 3);
+	TheMap.Info.MapHeight = LuaToNumber(l, 4);
+
+	return 0;
+}
+
+/**
+** Define the lua file that will be build the map
+**
+**  @param l  Lua state.
+*/
+static int CclDefineMapSetup(lua_State* l)
+{
+	if (lua_gettop(l) != 1) {
+		LuaError(l, "incorrect argument");
+	}
+	TheMap.Info.Filename = strdup(LuaToString(l, 1));
+	return 0;
+}
+
 /**
 **  Add a keystroke help
 **
@@ -4523,6 +4555,9 @@ void UserInterfaceCclRegister(void)
 	lua_register(Lua, "DefineMenu", CclDefineMenu);
 	lua_register(Lua, "DefineMenuGraphics", CclDefineMenuGraphics);
 
+	lua_register(Lua, "PresentMap", CclPresentMap);
+	lua_register(Lua, "DefineMapSetup", CclDefineMapSetup);
+
 	//
 	// Color cycling
 	//