The virtual land creation tool generates a random set of rooms based on a template xml file and an image. It reads each pixel and uses the xml template to generate a room based on the pixel color. It can pull from multiple room descriptions to make your area more unique by haveing a set of room descriptions.
This is a python script that uses PIL to read the image.
Example Data:
<xml> <colors> <color rgb="0,0,0" type="pointofinterest" /> <color rgb="1,50,100" type="water" /> <color rgb="60,100,30" type="grass" /> <color rgb="150,130,90" type="dirt" /> <color rgb="255,255,255" type="bridge" /> <color rgb="250,250,250" type="bridge2" /> <color rgb="100,80,50" type="road" /> </colors> <pointofinterest> <roomdetails> <room> <short>Some Magical Happy Place</short> <daylong>A fun place with a crack in the ground.</daylong> <nightlong>A fun place with a crack in the ground.</nightlong> <ambientlight>0</ambientlight> <nightlight>10</nightlight> <daylight>20</daylight> <terraintype>T_CUSTOM</terraintype> <medium>MEDIUM_LAND</medium> <viewitems>({"crack","ground", "earth", "baked earth"}) : "The crack in the ground is about a foot long and is quite dark. There is a strange hot breeze coming from the crack. Perhaps you could search around in it to find out more.", </viewitems> <inventoryitems> </inventoryitems> <exits> <!-- Any explicit exits that are not the default. --> </exits> </room> </roomdetails> <header><![CDATA[ #include <lib.h> #include <daemons.h> #include <medium.h> #include <terrain_types.h> #include <damage_types.h> #include ROOMS_H inherit LIB_ROOM; int searched; static void heart_beat(){ searched = 0; } string SearchCrack(){ string result; string item; int losthand = 0; if(!searched){ if(!random(100)){ result="Poking your hand into the crack you reach in and "+ "discover a kevlar helmet."; say(this_player()->GetName()+" searches near the crack "+ "and seems to have found something of value."); if(!new("/domains/savsoul/armor/helmet2")->eventMove(this_player())) new("/domains/savsoul/armor/helmet2")->eventMove(environment(this_object())); searched=1; return result; } else if (!random(2)){ say(this_player()->GetName()+" searches the crack "+ "and seems to have found something."); switch(random(1)) { case 1: result="You stick your hand into the crack in the ground and discover a %^CYAN%^black wool cap%^RESET%^."; item = "/domains/savsoul/armor/wool_cap"; break; default: result="As you stick your hand into the crack you feel an intense rush of %^RED%^flames%^RESET%^."; losthand = 1; } if (losthand == 1) { this_player()->eventReceiveDamage(environment(this_object()),(HEAT), 100, 0, "right hand"); } else { if(!new(item)->eventMove(this_player())) new(item)->eventMove(environment(this_object())); } searched=1; return result; } } if (searched) { result="You feel a bit tired, perhaps you can search again later"; } else { result="You search the crack in the ground but find nothing."; } say(this_player()->GetName()+" searches the crack in the ground and finds nothing."); searched=1; return result; } void create() { room::create(); ]]></header> <footer><![CDATA[ SetSearch( "crack", (:SearchCrack:) ); } void init(){ ::init(); set_heart_beat(30); } ]]></footer> </pointofinterest> <road> <landmarkdescriptions> <description> <distance>2</distance> <description>A distant road can be seen to the $DIR.</description> </description> <description> <distance>1</distance> <description>There is a road that can be seen to the $DIR.</description> </description> </landmarkdescriptions> <roomdetails> <room> <short>Road</short> <daylong>A road 1.</daylong> <nightlong>A road 1 at night.</nightlong> <ambientlight>0</ambientlight> <nightlight>10</nightlight> <daylight>20</daylight> <terraintype>T_ROAD</terraintype> <medium>MEDIUM_LAND</medium> <inventoryitems> </inventoryitems> <exits> <!-- Any explicit exits that are not the default. --> </exits> </room> <room> <short>Road</short> <daylong>A road 2.</daylong> <nightlong>A road 2 at night.</nightlong> <ambientlight>0</ambientlight> <nightlight>10</nightlight> <daylight>20</daylight> <terraintype>T_ROAD</terraintype> <medium>MEDIUM_LAND</medium> <inventoryitems> </inventoryitems> <exits> <!-- Any explicit exits that are not the default. --> </exits> </room> <room> <short>Road</short> <daylong>A road 3.</daylong> <nightlong>A road 3 at night.</nightlong> <ambientlight>0</ambientlight> <nightlight>10</nightlight> <daylight>20</daylight> <terraintype>T_ROAD</terraintype> <medium>MEDIUM_LAND</medium> <inventoryitems> </inventoryitems> <exits> <!-- Any explicit exits that are not the default. --> </exits> </room> </roomdetails> <header><![CDATA[ #include <lib.h> #include <daemons.h> #include <medium.h> #include <terrain_types.h> inherit LIB_ROOM; static void create() { room::create(); ]]></header> <footer><![CDATA[ } void init(){ ::init(); } ]]></footer> </road> <bridge> <landmarkdescriptions> <description> <distance>2</distance> <description>A bridge can be seen to the $DIR.</description> </description> <description> <distance>1</distance> <description>A wooden bridge can be seen to the $DIR.</description> </description> </landmarkdescriptions> <roomdetails> <room> <short>Wooden Bridge</short> <daylong>A wooden bridge 1.</daylong> <nightlong>A wooden bridge 1 at night.</nightlong> <ambientlight>0</ambientlight> <nightlight>10</nightlight> <daylight>20</daylight> <terraintype>T_ROAD</terraintype> <medium>MEDIUM_LAND</medium> <inventoryitems> </inventoryitems> <exits> <!-- Any explicit exits that are not the default. --> <exit direction="down">"down" :"/domains/somedomain/room/start",</exit> </exits> </room> <room> <short>Wooden Bridge</short> <daylong>A wooden bridge 2.</daylong> <nightlong>A wooden bridge 2 at night.</nightlong> <ambientlight>0</ambientlight> <nightlight>10</nightlight> <daylight>20</daylight> <terraintype>T_ROAD</terraintype> <medium>MEDIUM_LAND</medium> <inventoryitems> </inventoryitems> <exits> <!-- Any explicit exits that are not the default. --> <exit direction="down">"down" :"/domains/somedomain/room/start",</exit> </exits> </room> </roomdetails> <header><![CDATA[ #include <lib.h> #include <daemons.h> #include <medium.h> #include <terrain_types.h> inherit LIB_ROOM; static void create() { room::create(); ]]></header> <footer><![CDATA[ } void init(){ ::init(); } ]]></footer> </bridge> <bridge2> <landmarkdescriptions> <description> <distance>5</distance> <description>Far off in the distance a bridge can be seen to the $DIR.</description> </description> <description> <distance>2</distance> <description>A bridge can be seen to the $DIR.</description> </description> <description> <distance>1</distance> <description>A bridge can be seen to the $DIR.</description> </description> </landmarkdescriptions> <roomdetails> <room> <short>Bridge</short> <daylong>A bridge 1.</daylong> <nightlong>A bridge 1 at night.</nightlong> <ambientlight>0</ambientlight> <nightlight>10</nightlight> <daylight>20</daylight> <terraintype>T_ROAD</terraintype> <medium>MEDIUM_LAND</medium> <inventoryitems> </inventoryitems> <exits> <!-- Any explicit exits that are not the default. --> <exit direction="down">"down" :"/domains/somedomain/room/start",</exit> </exits> </room> <room> <short>Bridge</short> <daylong>A bridge 2.</daylong> <nightlong>A bridge 2 at night.</nightlong> <ambientlight>0</ambientlight> <nightlight>10</nightlight> <daylight>20</daylight> <terraintype>T_ROAD</terraintype> <medium>MEDIUM_LAND</medium> <inventoryitems> </inventoryitems> <exits> <!-- Any explicit exits that are not the default. --> <exit direction="down">"down" :"/domains/somedomain/room/start",</exit> </exits> </room> </roomdetails> <header><![CDATA[ #include <lib.h> #include <daemons.h> #include <medium.h> #include <terrain_types.h> inherit LIB_ROOM; static void create() { room::create(); ]]></header> <footer><![CDATA[ } void init(){ ::init(); } ]]></footer> </bridge2> <water> <landmarkdescriptions> <description> <distance>2</distance> <description>A distant river can be seen to the $DIR.</description> </description> <description> <distance>1</distance> <description>A slow moving river can be seen to the $DIR.</description> </description> </landmarkdescriptions> <roomdetails> <room> <short>Daylay River</short> <daylong>Some Water.</daylong> <nightlong>Some Water at night.</nightlong> <ambientlight>0</ambientlight> <nightlight>10</nightlight> <daylight>20</daylight> <terraintype>T_SURFACE</terraintype> <medium>MEDIUM_WATER</medium> <viewitems> <item>({"water","bubbles"}) : "The water has some bubbles!",</item> </viewitems> <inventoryitems> </inventoryitems> <exits> <!-- Any explicit exits that are not the default. --> <exit direction="down">"down" :"/domains/somedomain/room/start",</exit> </exits> </room> </roomdetails> <header><![CDATA[ #include <lib.h> #include <daemons.h> #include <medium.h> #include <terrain_types.h> inherit LIB_ROOM; static void create() { room::create(); ]]></header> <footer><![CDATA[ } int CanReceive(object ob) { if(living(ob) && !interactive(ob)){ message("info","NPC's not allowed, sorry.", ob); return 0; } return room::CanReceive(ob); } void init(){ ::init(); } ]]></footer> </water> <dirt> <roomdetails> <room> <short>Open Field</short> <daylong>Dirt desc 1.</daylong> <nightlong>Dirt desc 1 night.</nightlong> <ambientlight>0</ambientlight> <nightlight>10</nightlight> <daylight>20</daylight> <terraintype>T_SAND</terraintype> <medium>MEDIUM_LAND</medium> <inventoryitems> </inventoryitems> <exits> <!-- Any explicit exits that are not the default. --> </exits> </room> <room> <short>Open Field</short> <daylong>Dirt desc 2.</daylong> <nightlong>Dirt desc 2 night.</nightlong> <ambientlight>0</ambientlight> <nightlight>10</nightlight> <daylight>20</daylight> <terraintype>T_SAND</terraintype> <medium>MEDIUM_LAND</medium> <inventoryitems> </inventoryitems> <exits> <!-- Any explicit exits that are not the default. --> </exits> </room> </roomdetails> <header><![CDATA[ #include <lib.h> #include <daemons.h> #include <medium.h> #include <terrain_types.h> inherit LIB_ROOM; static void create() { room::create(); ]]></header> <footer><![CDATA[ } void init(){ ::init(); } ]]></footer> </dirt> <grass> <roomdetails> <room> <short>Grass</short> <daylong>grass description 1.</daylong> <nightlong>grass description 1 night.</nightlong> <ambientlight>0</ambientlight> <nightlight>10</nightlight> <daylight>20</daylight> <terraintype>T_OUTDOORS</terraintype> <medium>MEDIUM_LAND</medium> <inventoryitems> <invitem chance="100">"/domains/somedomain/npc/monster":({150, 1}),</invitem> </inventoryitems> <exits> <!-- Any explicit exits that are not the default. --> </exits> </room> </roomdetails> <header><![CDATA[ #include <lib.h> #include <daemons.h> #include <medium.h> #include <terrain_types.h> #include <damage_types.h> #include ROOMS_H inherit LIB_ROOM; void create() { room::create(); ]]></header> <footer><![CDATA[ } void init(){ ::init(); } ]]></footer> </grass> </xml>
try: from PIL import Image except ImportError: print('PIL module is not installed. Please visit: ' + 'http://www.pythonware.com/products/pil/\n\n') print('If you are on Ubuntu you may want to look at this guide: ' + 'http://obroll.com/install-python-pil-python-image-library-on-ubuntu-11-10-oneiric/') import random import argparse import os import sys import re from amara import bindery from math import degrees, atan2, sqrt import shutil def getdirection(dirx, diry, playerx, playery): angle = degrees(atan2(diry - playery, dirx - playerx)) bearing = (angle + 360) % 360 #print "%d, %d, %d, %d, %d, %d" % (dirx, diry, playerx, playery, angle, bearing) if (bearing >= 0 and bearing <= 20) or (bearing > 340): return "west" if bearing > 30 and bearing <= 60: return "northwest" if bearing > 60 and bearing <= 120: return "north" if bearing > 120 and bearing <= 160: return "northeast" if bearing > 160 and bearing <= 200: return "east" if bearing > 200 and bearing <= 240: return "southeast" if bearing > 240 and bearing <= 300: return "south" if bearing > 300 and bearing <= 340: return "southwest" return "distance" def GetNearbyLandmarks(xmldata, roomtype, x, y, pixels, colors): #roomtype = None foundlandmarks = {} cpixel = None for xexp in range(x-5, x+5): for yexp in range(y-5, y+5): if xexp == x and yexp == y: #skip the zone were in. continue try: cpixel = pixels[xexp, yexp] except: #skip bad areas. continue for colortup in colors: if int(colortup[0]) == cpixel[0] and int(colortup[1]) == cpixel[1] and int(colortup[2]) == cpixel[2]: newroomtype = colortup[3] if roomtype == newroomtype: # same as the current room, skip. continue if xmldata[newroomtype] == None: continue try: if xmldata[newroomtype].landmarkdescriptions == None: continue except AttributeError: continue distance = sqrt( (xexp - x)**2 + (yexp - y)**2 ) direction = getdirection( x, y, xexp, yexp) for landmark in xmldata[newroomtype].landmarkdescriptions.description: if distance <= int(str(landmark.distance)): try: if foundlandmarks[newroomtype]["distance"] < int(str(landmark.distance)): continue except KeyError: pass foundlandmarks[newroomtype] = {"distance":int(str(landmark.distance)), "description":str(landmark.description).replace("$DIR", direction)} return foundlandmarks def buildRoom(xmldata, roomtype, width, height, x, y, domainname, virtualname, imgxpos, imgypos, pixels, colors): outputroom = str(xmldata[roomtype].header) randroom = random.randint(0, len(xmldata[roomtype].roomdetails.room)-1) outputroom += '\n SetAmbientLight(' + str(xmldata[roomtype].roomdetails.room[randroom].ambientlight) + ');\n'; outputroom += ' SetDayLight(' + str(xmldata[roomtype].roomdetails.room[randroom].daylight) + ');\n'; outputroom += ' SetNightLight(' + str(xmldata[roomtype].roomdetails.room[randroom].nightlight) + ');\n'; outputroom += ' SetTerrainType(' + str(xmldata[roomtype].roomdetails.room[randroom].terraintype) + ');\n'; outputroom += ' SetMedium(' + str(xmldata[roomtype].roomdetails.room[randroom].medium) + ');\n'; outputroom += ' SetShort("' + str(xmldata[roomtype].roomdetails.room[randroom].short) + '");\n'; foundlandmarks = GetNearbyLandmarks(xmldata, roomtype, imgxpos, imgypos, pixels, colors); daylong = str(xmldata[roomtype].roomdetails.room[randroom].daylong) + " " for landmark in foundlandmarks.values(): daylong += str(landmark["description"]) + " " outputroom += ' SetDayLong("' + daylong + '");\n'; # Don't display the landmarks at night. outputroom += ' SetNightLong("' + str(xmldata[roomtype].roomdetails.room[randroom].nightlong) + '");\n'; if xmldata[roomtype].roomdetails.room[randroom].viewitems != None: outputroom += ' SetItems( ([\n' + str(xmldata[roomtype].roomdetails.room[randroom].viewitems) + '\n'; outputroom += ']));' if xmldata[roomtype].roomdetails.room[randroom].inventoryitems.invitem != None: outputroom += ' SetInventory( ([\n' for item in xmldata[roomtype].roomdetails.room[randroom].inventoryitems.invitem: if item["chance"] == None: continue rand = random.randint(1, 100) if rand <= int(str(item["chance"])): outputroom += str(item) + '\n'; outputroom += '\n' outputroom += ']));' outputroom += '\n SetExits(([\n' exits = {} if imgxpos-1 >= 0: exits["west"] = ('"west" : "/domains/' + domainname + '/virtual/' + virtualname + '/' + str(x-1) + "," + str(y) + '",\n') if imgxpos+2 <= width -2: if (x == 50) and (y == -99): print imgxpos+2 print width-2 exits["east"] = ('"east" : "/domains/' + domainname + '/virtual/' + virtualname + '/' + str(x+1) + "," + str(y) + '",\n') if imgypos-1 >= 0: exits["north"] = ('"north" : "/domains/' + domainname + '/virtual/' + virtualname + '/' + str(x) + "," + str(y+1) + '",\n') if imgypos+1 <= height-1: exits["south"] = ('"south" : "/domains/' + domainname + '/virtual/' + virtualname + '/' + str(x) + "," + str(y-1) + '",\n') if imgxpos-1 >= 0 and imgypos-1 >= 0: exits["northwest"] = ('"northwest" : "/domains/' + domainname + '/virtual/' + virtualname + '/' + str(x-1) + "," + str(y+1) + '",\n') if imgxpos+2 <= width-2 and imgypos-2 >= 0: exits["northeast"] = ('"northeast" : "/domains/' + domainname + '/virtual/' + virtualname + '/' + str(x+1) + "," + str(y+1) + '",\n') if imgxpos-1 >= 0 and imgypos+1 <= height-1: exits["southwest"] = ('"southwest" : "/domains/' + domainname + '/virtual/' + virtualname + '/' + str(x-1) + "," + str(y-1) + '",\n') if imgxpos+2 <= width-2 and imgypos+1 <= height-1: exits["southeast"] = ('"southeast" : "/domains/' + domainname + '/virtual/' + virtualname + '/' + str(x+1) + "," + str(y-1) + '",\n') if xmldata[roomtype].roomdetails.room[randroom].exits.exit != None: for exit in xmldata[roomtype].roomdetails.room[randroom].exits: if exit['removedir'] == 'true': try: del exits[exit['direction']] except KeyError: continue else: exits[exit['direction']] = exit for exit in exits.values(): if exit != None: outputroom += str(exit) outputroom += '\n]));\n\n' outputroom += str(xmldata[roomtype].footer) return outputroom def processImage(args, xmldata, colors): i = Image.open(args.image) pixels = i.load() width, height = i.size startxpos = 2 startypos = 0 #directions 1 = southeast 2 = southwest 3 = northeast 4 = northwest direction = 1 cnt = 0 somestr = "" prevpercent = 0 for x in range(width): currpercent = int((float(x) / float(width)) * 100) if currpercent > prevpercent: sys.stdout.write("\rPercent Complete: " + str(currpercent)) sys.stdout.flush() prevpercent = currpercent for y in range(height): mudx = x mudy = y cnt+=1 cpixel = pixels[x, y] cfile = None if direction == 1: mudx = startxpos + x mudy = startypos - y if args.outdir.endswith("/"): filename = args.outdir + str(mudx) + "," + str(mudy) + ".c" else: filename = args.outdir + "/" + str(mudx) + "," + str(mudy) + ".c" #print filename roomtype = None for colortup in colors: if int(colortup[0]) == cpixel[0] and int(colortup[1]) == cpixel[1] and int(colortup[2]) == cpixel[2]: roomtype = colortup[3] if roomtype == None: print("Missing Room Definition for Pixel Color: " + str(cpixel[0]) + ", " + str(cpixel[1]) + ", " + str(cpixel[2]) + " at position: " + str(x) + ", " + str(y)) quit() #print roomtype if xmldata[roomtype] == None: #ignore any colors that do not have room definitions. This will let the generic virtual area be used instead. continue cfile = open(filename, 'w') cfile.write(buildRoom(xmldata, roomtype, width, height, mudx, mudy, args.domainname, args.virtualname, x, y, pixels, colors)) sys.stdout.write("\rRoom Generation Complete!\n\n") def GetArgs(): parser = argparse.ArgumentParser(description='Converts an image by pixel into ' + 'individual rooms for virtual creation.') parser.add_argument('--image', '-i', dest='image', required=True, help='The image can be of any type supported by PIL. Jpeg, PNG, GIF, etc...') parser.add_argument('--domain', '-d', dest='domainname', required=True, help='The name of the domain where this will be created. This is used for building the exit path') parser.add_argument('--virtual', '-v', dest='virtualname', required=True, help='The name of the virtual area where this will be created. This is used for building the exit path') parser.add_argument('--outputdir', '-o', dest='outdir', default='roomfiles', help='The directory where the X,X.c files will be written. By default this is "roomfiles"') parser.add_argument('--template', '-t', dest='template', required=True, help='The source template that will be used to generate the rooms.') parser.add_argument('--replace', '-r', dest='replace', type=bool, default=False, help='If the output directory / files already exist they will be replaced ' + 'when this is set to true. By default this is false.') return parser.parse_args() def parseXml(template): xmldata = None try: xmldata = bindery.parse(open(template)) print("Room Types Parsed:\n") for room in xmldata.xml.colors.color: print(" " + room['type']) print('\n') sys.stdout.flush() return xmldata.xml except Exception as error: print('Template Error: ') print(error) print('Could not parse the template file. Please see sample xml file ' + 'for details.') quit() def createFolder(outdir, replace): if not os.path.exists(outdir): os.makedirs(outdir) else: if replace == True: shutil.rmtree(outdir) os.makedirs(outdir) else: print('The output folder already exists. You can remove it yourself ' + 'or pass --replace True as an argument.') quit() def main(): args = GetArgs() print("Parsing XML template...\n") xmldata = parseXml(args.template) colors = [] # only perform this split once so it can avoid calling split. for color in xmldata.colors.color: colortup = color['rgb'].split(',') colortup.append(color['type']) colors.append(colortup) createFolder(args.outdir, args.replace) processImage(args, xmldata, colors) if __name__ == "__main__": main()
#include <lib.h> #include <daemons.h> #include <medium.h> #include <terrain_types.h> inherit LIB_ROOM; static void create() { room::create(); SetAmbientLight(0); SetDayLight(20); SetNightLight(10); SetTerrainType(T_OUTDOORS); SetMedium(MEDIUM_LAND); SetShort("its room 1 grass"); SetDayLong("It's Daytime in room 1 type grass"); SetNightLong("It's Nighttime in room 1 grass"); SetExits(([ "northeast" : "/domains/savsoul/virtual/ROWasteland/11,-13", "north" : "/domains/savsoul/virtual/ROWasteland/10,-15", "west" : "/domains/savsoul/virtual/ROWasteland/9,-14", "southeast" : "/domains/savsoul/virtual/ROWasteland/11,-15", "east" : "/domains/savsoul/virtual/ROWasteland/11,-14", "southwest" : "/domains/savsoul/virtual/ROWasteland/9,-15", ])); } void init(){ ::init(); }