Idee für eine kleine Hilfe beim Mappen

Hier können fragen Anregungen etc zur Bearbeitung der Map, sowie den dazugehörigen Tools wie Dragon oder Worldforge gestellt werden.

Moderator: Mods - Mulbearbeitung

Antworten
Nachricht
Autor
Rob Cole

Idee für eine kleine Hilfe beim Mappen

#1 Beitrag von Rob Cole » 16 Okt 2007 17:30

Da die "zufällige" Verteilung von Map Tiles eine recht unkreative und zeitraubende Angelegenheit ist würde ich das ganze gerne von einem Programm erledigen lassen.
Für vieles gibt es ja mehrere verschiedene Tiles (Gras, Berge, Sand, Wasser usw.)
Das Programm soll nun die Map einlesen und alle Tiles dieser Art zufällig verteilen.
Also für Gras z.B. würden die Tiles mit der ID 0x0003 bis 0x0006 zufällig eine ID aus diesem Bereich erhalten.
Mein Problem ist, meine Programmiererfahrung beschränkt sich bis jetzt fast nur auf RunUO. Mir fehlt also der Wissenshintergrund eines echten Programmierers.

Hat jemand vielleicht eine Idee/Tips wie man so etwas angehen könnte?
(Möchte mir das sehr gerne selbst erarbeiten, aber im Moment fehlt mir einfach der Start...)
Über einen Link mit einer guten Dokumentation über die Zugriffsmöglichkeiten auf die Map würde ich mich auch sehr freuen. Hab danach gesucht, aber anscheinend mit den falschen Stichwörtern.

(Vielleicht gibt es ja auch schon ein entsprechendes Tool und ich hab es übersehen, in dem Fall würde ich mich natürlich auch freuen ;) )

nazghul

#2 Beitrag von nazghul » 16 Okt 2007 22:30

Du möchtest eine Map mit zufälligen Tiles fluten, oder einer Gruppe von Tiles eine bestimmte (zufällige?) Höhe zuweisen, oder alle Tiles mit der Nummer X durch welche aus dem "Pool" [A..D] ersetzen, ... oder was meinst Du?

Rob Cole

#3 Beitrag von Rob Cole » 16 Okt 2007 22:51

Ich möchte Tiles aus dem "Pool" [A...D] durch ein zufälliges Tile aus demselben Pool ersetzen.
Man will z.B. eine Wiese machen und setzt flächig nur eins der Tiles. Danach sollen sie dann mit dem Programm gemischt werden.

nazghul

#4 Beitrag von nazghul » 17 Okt 2007 00:12

CentrEd benutzen. Da wird schon beim Setzen der Tiles "gewürfelt".

BTW: Wenn man seine Welt nicht per Hand baut sondern z.B. Dragon benutzt würfelt der auch schon.

Was das Programm angeht: Zuerst solltest Du ein Format festlegen, in dem Du die Pools definierst. Ich würde XML empfehlen, weil es für das Parsen bereits Funktionen in fast allen Sprachen gibt.

Dann eine Datei mit der Angabe, welche Pools, evtl. in welchem Koordinatenbereich, Du verwursten willst bei diesem Programmaufruf. Kann man interaktiv über eine GUI machen, oder eben auch per File. Ach ja, Map-Größe nicht vergessen (ML oder PreML), oder "Handeingabe" wenn man die map2.mul usw. bearbeiten will; und den Pfad zum Map-File.

Das Programm selbst öffnet dann nur das Worldfile, liest ein Stück ein, macht "Suche/Ersetzen", abspeichern, weiterlesen...

Aufbau des Map-Files ist einfach: Die Welt ist in 8*8-Felder aufgeteilt (sog. "Blocks". Jeder Block besteht also aus 64 "Cells" zu je 3 Byte: ein UWORD für die Textur, ein BYTE für die Höhe (-128..127)

Die Reihenfolge der Cells in einem Block ist

Code: Alles auswählen

|00|01|02|03|04|05|06|07|
|08|09.....
.....
|56|57|58|59|60|61|62|63|
Die Blocks sind im Worldfile leider nicht nach demselben Schema angeordnet, sondern spaltenweise: Beginn oben links, dann links darunter, dann darunter ... weiter nächste Spalte ... bis es schließlich auch unten rechts endet.

Eine preML-map0.mul besteht ergo aus 768x512 Blocks (waagerecht* senkrecht), jeder Block aus 8*8 Cells, jede Cell aus 3 Byte - macht 768*8*8*3=37.748.736 Byte

Rob Cole

#5 Beitrag von Rob Cole » 17 Okt 2007 11:43

Wow, danke für die ausführliche Antwort :))
Hab gestern noch hier etwas gefunden.
Ok, ich fang dann einfach mal mit den Item Pools an. XML klingt gut, damit hab ich schon ein paar Sachen aus RunUO heraus gemacht.

CentrEd werd ich mir auch mal anschauen.

Rob Cole

#6 Beitrag von Rob Cole » 17 Okt 2007 16:34

Sooo, bin um einiges weiter, aber nun steck ich ein bisschen fest.

Also, die Definition der Pools klappt. Beim starten des Programms werden diese in eine Hashtable eingelesen. (Hab das ganze erstmal nur für Tiles gemacht von denen es 4 zur Auswahl gibt.)
Zu jeder TileID kann man nun aus der Hashtable einen Satz von 4 IDs abrufen die für die Zufallsverteilung in Frage kommen.

Nun wird die Karte als FileStream eingelesen und die TileIDs abgefragt. Kommt eine Tile ID im Pool vor wird eine passende "zufällige" ID an deren Stelle gesetzt.
Für den ersten Block klappt auch alles, die Tiles werden verteilt und die Karte funktioniert auch hinterher ;)
Eigentlich wollte ich das ganze jetzt Blockweise abarbeiten lassen.

Nun hab ich aber ein Problem mit dem einlesen des korrekten Bereiches wenn der FileStream erstellt wird. Man braucht ja einen Offset von 196*i für den i-ten Block. (4 bytes am Anfang und dann 3*64 für die 8x8 Tiles pro Block)

Leider bekomm ich eben diesen Offset nicht hin :angry1:
Gehört inzwischen wohl eher ins c# Forum, aber vielleicht liest es ja auch hier jemand und hat eine Idee ;)
Ich hab die fragliche Stelle markiert.

Code: Alles auswählen


using System;
using System.IO;
using System.Collections;
using System.Xml;



class Test
{
	private static string poolpath = "pools.xml";
	private static string mappath = "map4.mul";
	public static Hashtable Pools = new Hashtable();
	private static Random r = new Random( (int) DateTime.Now.Ticks ); 
	
	
	public static void Main()
	 {
	   LoadPools();
	 }
	      
	private static void LoadPools()
	{
	   if ( !File.Exists( poolpath ) )
	   {
	   		Console.WriteLine("pools.xml nicht gefunden.");
			return;
	   }
		XmlDocument pools = new XmlDocument();
		pools.Load( poolpath );
		
		int I; int II; int III; int IV;
				
		try
		{
			XmlElement root = pools["pools"];
			
			foreach ( XmlElement pool in root.GetElementsByTagName( "pool" ) )
				{
					try
					{
						I = XmlConvert.ToInt32( pool["I"].InnerText );
						II = XmlConvert.ToInt32( pool["II"].InnerText );
						III = XmlConvert.ToInt32( pool["III"].InnerText );
						IV = XmlConvert.ToInt32( pool["IV"].InnerText );
						
						Pools.Add( Convert.ToString(I), new int[]{I,II,III,IV});
						Pools.Add( Convert.ToString(II), new int[]{I,II,III,IV});
						Pools.Add( Convert.ToString(III), new int[]{I,II,III,IV});
						Pools.Add( Convert.ToString(IV), new int[]{I,II,III,IV});
					}
					catch
					{
						Console.WriteLine( "Laden der Pools fehlgeschlagen" );
					}
				}
      	}
		catch
		{
			Console.WriteLine("pools.xml inkorrekt");
			return;
		}
		//Start();	
		MixBlock(0);
		Console.ReadLine();
	}	
	
	private static void MixBlock(int block)
	{
		try
		{
			if ( !File.Exists( mappath ) )
		  	{
		   		Console.WriteLine("Map4.mul nicht gefunden.");
				return;
		   	}			
			
				
			FileStream fsr = File.OpenRead(mappath);
			int nBytes = 196;
	        byte[] ByteArray = new byte[nBytes];
	        fsr.Read(ByteArray, 0, nBytes); <--
Das ist der Knackpunkt. Hier soll nicht vom Dateianfang sondern von einer bestimmten Position gelesen werden. Der Offset in der Mitte ist für die Startposition zum Schreiben im ByteArray.
	        fsr.Close();
	        
	        for( int i=0;i<196;i++)
		    {
	        	Console.WriteLine(Convert.ToString(ByteArray[i]));
	        }
	        
	       	
		    for( int i=0;i<62;i++)
			{          	
		       if(Pools.Contains(Convert.ToString(ByteArray[4+3*i])))
		       {        		
					int[] IDs = (int[])Pools[Convert.ToString(ByteArray[4+3*i])];
					ByteArray[4+3*i] = (byte)IDs[r.Next(4)];				
		       }
		    }
	        
	       	FileStream fsw = File.OpenWrite(mappath);
	        fsw.Write(ByteArray, 0, nBytes);	        
	        fsw.Close(); 	        
		}
		catch
		{
			Console.WriteLine("bäh");
		}
	}
}



Rob Cole

#7 Beitrag von Rob Cole » 17 Okt 2007 19:35

Es klappt 8)
Hab irgendwie zu kompliziert gedacht.

Code: Alles auswählen

/*
 * Created by SharpDevelop.
 * User: Jan
 * Date: 17.10.2007
 * Time: 11:41
 * 
 */

using System;
using System.IO;
using System.Collections;
using System.Xml;



class Test
{
	private static string poolpath = "pools.xml";
	private static string mappath = "map4.mul";
	public static Hashtable Pools = new Hashtable();
	private static Random r = new Random( (int) DateTime.Now.Ticks ); 
	
	
	public static void Main()
	 {
	   LoadPools();
	 }
	      
	private static void LoadPools()
	{
	   if ( !File.Exists( poolpath ) )
	   {
	   		Console.WriteLine("pools.xml nicht gefunden.");
			return;
	   }
		XmlDocument pools = new XmlDocument();
		pools.Load( poolpath );
		
		int I; int II; int III; int IV;
				
		try
		{
			XmlElement root = pools["pools"];
			
			foreach ( XmlElement pool in root.GetElementsByTagName( "pool" ) )
				{
					try
					{
						I = XmlConvert.ToInt32( pool["I"].InnerText );
						II = XmlConvert.ToInt32( pool["II"].InnerText );
						III = XmlConvert.ToInt32( pool["III"].InnerText );
						IV = XmlConvert.ToInt32( pool["IV"].InnerText );
						
						Pools.Add( Convert.ToString(I), new int[]{I,II,III,IV});
						Pools.Add( Convert.ToString(II), new int[]{I,II,III,IV});
						Pools.Add( Convert.ToString(III), new int[]{I,II,III,IV});
						Pools.Add( Convert.ToString(IV), new int[]{I,II,III,IV});
					}
					catch
					{
						Console.WriteLine( "Laden der Pools fehlgeschlagen" );
					}
				}
      	}
		catch
		{
			Console.WriteLine("pools.xml inkorrekt");
			return;
		}		
		Start();
		Console.ReadLine();
	}	
	private static void Start()
	{
		try
		{
			if ( !File.Exists( mappath ) )
		  	{
		   		Console.WriteLine("Map4.mul nicht gefunden.");
				return;
		   	}			
				
			FileStream fsr = File.OpenRead(mappath);
			int nBytes = 6421156; //32761 Blöcke * 196 byte = 6421156 byte
	        byte[] ByteArray = new byte[nBytes];
	        fsr.Read(ByteArray, 0, nBytes);
    		fsr.Close();
	        
	        
	       	for( int j=0;j<32759;j++) //Blöcke: 181x181 = 32761 
			{ 
			    for( int i=0;i<62;i++) //8x8 = 64 Tiles pro Block
				{          	
			       if(Pools.Contains(Convert.ToString(ByteArray[4+3*i+196*j])))
			       {        		
						int[] IDs = (int[])Pools[Convert.ToString(ByteArray[4+3*i+196*j])];
						ByteArray[4+3*i+196*j] = (byte)IDs[r.Next(4)];				
			       }
			    }
			    Console.Write(".");
	       	}
	       	FileStream fsw = File.OpenWrite(mappath);
	        fsw.Write(ByteArray, 0, nBytes);	        
	        fsw.Close(); 
	        Console.WriteLine("Fertig!");
		}
		catch
		{			
		}
	}
}


Beispiel für XML Datei:

Code: Alles auswählen

<xml>
<pools>
	<pool>
		<I>3</I>
		<II>4</II>
		<III>5</III>
		<IV>6</IV>		
	</pool>
	<pool>
		<I>168</I>
		<II>169</II>
		<III>170</III>
		<IV>171</IV>		
	</pool>
	
</pools>
Funktioniert so nur für Tokuno, für andere Karten muss die Anzahl der Blöcke (und der Pfad) angepasst werden.
Danke für die Hilfe :)
Immer offen für Verbesserungsvorschläge, will ja was lernen!

Garantiere für nichts, sollte jemand testen... Backups machen :P

Antworten