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();
}