how it works is that all the map parts are either connected with 2 small doors or 1 small and 1 large door the algorithm simply pick map segments that fit until the map is long enough then add a central piece which is the control point.
if you want to do your own map pieces, just use the same layout of doors and file name prefix i am using in RMG_resource, i'll try to add some documentation later but basically the script locate where it needs to add entities or connect parts together using the color coded pixels.
i don't know if i need to do any legal disclaimer on anything since i don't have any programming background but i used python because i needed something that could handle image editing (the PILLOW module) and i've compiled the script using py2exe which use visual basic so you might need additional dlls if the script doesn't work.
import random
from PIL import Image, PngImagePlugin
import glob
import os
#get maps, improve the prefix system later
os.chdir(os.getcwd() + "/RMG_resource")
spawnlist = []
lpointlist = []
spointlist = []
lllist = []
sslist = []
lslist = []
for file in glob.glob('*.png'):
#for file in os.listdir():
if file.startswith("spawn"):
spawnlist.append(file)
elif file.startswith("pl"):
lpointlist.append(file)
elif file.startswith("ps"):
spointlist.append(file)
elif file.startswith("ll"):
lllist.append(file)
elif file.startswith("ss"):
sslist.append(file)
elif file.startswith("ls"):
lslist.append(file)
def rightposition(image, colour):
width, height = image.size
band = image.crop((width-1, 0, width, height))
for i, pixel in list(enumerate(band.getdata())):
if pixel == colour:
return i
def leftposition(image, colour):
height = image.height
band = image.crop((0, 0, 1, height))
for i, pixel in list(enumerate(band.getdata())):
if pixel == colour:
return i
def findpixel(image, colour):
width, height = image.size
for i, pixel in list(enumerate(image.getdata())):
if pixel == colour:
yp, xp = divmod(i, width)
return xp, yp
#colours used to identify connecting nodes and locators
purple = (163, 73, 164, 255) # l side
blue = (0, 162, 232, 255) # s side
gold = (255, 201, 14, 255) # player spawn
red = (136, 0, 21, 255) # medcab
tgrey = (195, 195, 195, 255) # top of cap zone
bgrey = (127, 127, 127, 255) # bot of cap zone
white = (255, 255, 255, 255)
black = (0, 0, 0, 255)
spawn = Image.open(random.choice(spawnlist))
koth = Image.new('RGBA', spawn.size, black)
koth.paste(spawn, (0, 0))
globalwidth = spawn.width
shorttop = True # all spawns are ss connected
heightanchor = rightposition(spawn, blue)
while globalwidth < 240 and sslist != []:
if shorttop:
segmentname = random.choice(sslist+lslist)
segment = Image.open(segmentname)
if segmentname.startswith("ls") or {segmentname.startswith("ss") and random.randint(0, 1) == 1}:
segment = segment.transpose(Image.FLIP_LEFT_RIGHT)
delta = leftposition(segment, blue)
else:
segmentname = random.choice(lllist+lslist)
segment = Image.open(segmentname)
if segmentname.startswith("ll") and random.randint(0, 1) == 1:
segment = segment.transpose(Image.FLIP_LEFT_RIGHT)
delta = leftposition(segment, purple)
# add some stuff later to check if the segment picked is valid and if there exist at least one ss segment
width = segment.width
globalwidth += width
draft = Image.new('RGBA', (globalwidth, 300), black)
draft.paste(koth, (0, 0))
draft.paste(segment, (globalwidth-width, heightanchor-delta))
koth = Image.new('RGBA', (globalwidth, 300))
koth.paste(draft, (0, 0))
if rightposition(koth, blue) is None:
shorttop = False
heightanchor = rightposition(koth, purple)
else:
shorttop = True
heightanchor = rightposition(koth, blue)
if shorttop:
segment = Image.open(random.choice(spointlist))
delta = leftposition(segment, blue)
else:
segment = Image.open(random.choice(lpointlist))
delta = leftposition(segment, purple)
width = segment.width
globalwidth += width
draft = Image.new('RGBA', (globalwidth, 300), black)
draft.paste(koth, (0, 0))
draft.paste(segment, (globalwidth-width, heightanchor-delta))
#cut black border
maxx, maxy = findpixel(draft, white)
reverseddraft = draft.transpose(Image.FLIP_TOP_BOTTOM)
minx, miny = findpixel(reverseddraft, white)
cropddraft = draft.crop((0, maxy - 5, globalwidth, 300 - miny + 5))
globalheight = cropddraft.height
#get capture point here and turn locators white
toppx, toppy = findpixel(cropddraft, tgrey)
cropddraft.putpixel((toppx, toppy), white)
botpx, botpy = findpixel(cropddraft, bgrey)
cropddraft.putpixel((botpx, botpy), white)
#final layout
koth = Image.new('RGBA', (2*globalwidth, globalheight))
koth.paste(cropddraft, (0, 0))
koth.paste(cropddraft.transpose(Image.FLIP_LEFT_RIGHT), (globalwidth, 0))
#red entities
entities = '{ENTITIES}' + chr(10) + '[' + '{background:ffffff,type:meta,void:000000}'
spawnx, spawny = findpixel(koth, gold)
entities += ',{' + 'type:redspawn,x:{},y:{}'.format(spawnx*6, spawny*6-32) + '}'
entities += ',{' + 'type:redspawn,x:{},y:{}'.format(spawnx*6 + 36, spawny*6-32) + '}'
entities += ',{' + 'type:redspawn,x:{},y:{}'.format(spawnx*6 + 72, spawny*6-32) + '}'
cabx, caby = findpixel(koth, red)
entities += ',{' + 'type:medCabinet,x:{},y:{}'.format(cabx*6 + 6, caby*6-48) + '}'
entities += ',{' + 'type:spawnroom,x:0,xscale:{:.2f},y:0,yscale:{:2f}'.format((spawn.width-2)/7, (globalheight - 1)/7) + '}'
entities += ',{' + 'type:redteamgate,x:{},xscale:3,y:0,yscale:{:2f}'.format((spawn.width-2)*6, globalheight/10) + '}'
#koth entities
entities += ',{' + 'type:KothControlPoint,x:{},y:{}'.format(globalwidth*6, (toppy + botpy)*3) + '}'
entities += ',{' + 'type:CapturePoint,x:{},xscale:{:.2f},y:{},yscale:{:.2f}'.format(toppx*6,(globalwidth - toppx)*2/7, toppy*6, (botpy - toppy)/7) + '}'
globalwidth *= 2
entities += ',{' + 'type:bluespawn,x:{},y:{}'.format((globalwidth-spawnx)*6, spawny*6-32) + '}'
entities += ',{' + 'type:bluespawn,x:{},y:{}'.format((globalwidth-spawnx)*6 - 36, spawny*6-32) + '}'
entities += ',{' + 'type:bluespawn,x:{},y:{}'.format((globalwidth-spawnx)*6 - 72, spawny*6-32) + '}'
entities += ',{' + 'type:medCabinet,x:{},y:{}'.format((globalwidth-cabx-6)*6, caby*6-48) + '}'
entities += ',{' + 'type:spawnroom,x:{},xscale:{:.2f},y:0,yscale:{:2f}'.format((globalwidth - spawn.width + 2)*6, (spawn.width - 2)/7, (globalheight - 1)/7) + '}'
entities += ',{' + 'type:blueteamgate,x:{},xscale:3,y:0,yscale:{:2f}'.format((globalwidth - spawn.width - 1)*6, globalheight/10) + '}]' + chr(10)
entities += '{END ENTITIES}'
#walkmask compression algorithm from gg2 source adapted to simply convert white to non solid
#bottom left pixel must be white for the decoder
koth.putpixel((0, globalheight-1), white)
walkmask = '{WALKMASK}' + chr(10) + str(globalwidth) + chr(10) + "{}".format(globalheight) + chr(10)
fill = 0
numv = 0
for y in range(0, globalheight):
for x in range(0, globalwidth):
numv <<= 1
if koth.getpixel((x, y)) != white:
numv += 1
fill += 1
if fill == 6:
walkmask += chr(numv + 32)
numv = 0
fill = 0
if fill > 0:
for fill in range(fill, 6, 1):
numv <<= 1
walkmask += chr(numv + 32)
walkmask += chr(10) + '{END WALKMASK}'
#recolour n stuff
koth.putpixel((0, globalheight-1), black)
medcab = Image.open("medcab.png")
koth.paste(medcab, (cabx, caby - 9))
koth.paste(medcab, (globalwidth - cabx - 7, caby - 9))
door = Image.open("door.png")
koth.paste(door, (spawn.width - 1, rightposition(spawn, blue) - maxy + 5))
door = door.transpose(Image.FLIP_LEFT_RIGHT)
koth.paste(door, (globalwidth - spawn.width - 1, rightposition(spawn, blue)- maxy + 5))
# imageops.colorize needs type L (black/white) images
# randomgreys
rsgrey = (random.randrange(146, 154), random.randrange(140, 148), random.randrange(135, 145), 255)
rgrey = (random.randrange(157, 163), random.randrange(157, 163), random.randrange(157, 163), 255)
(rgr, rgg, rgb, rgs) = rgrey
bsgrey = (random.randrange(134, 142), random.randrange(142, 147), random.randrange(146, 150), 255)
for y in range(1, globalheight-1):
for x in range(1, spawn.width - 1):
if koth.getpixel((x, y)) == white:
koth.putpixel((x, y), rsgrey)
if spawny - 7 < y < spawny - 3: # 139 < y < 143
koth.putpixel((x, y), (167, 99, 97, 255))
for x in range(spawn.width + 1, globalwidth - spawn.width - 1):
if koth.getpixel((x, y)) == white:
koth.putpixel((x, y), rgrey)
if 1 < y < 4:
koth.putpixel((x, y), ((rgr - 10), (rgg - 10), (rgb - 10), 255))
if 3 < y < 21:
koth.putpixel((x, y), ((rgr - 6), (rgg - 6), (rgb - 6), 255))
if 20 < y < 39:
koth.putpixel((x, y), ((rgr - 3), (rgg - 3), (rgb - 3), 255))
for x in range(globalwidth - spawn.width + 1, globalwidth - 1):
if koth.getpixel((x, y)) == white:
koth.putpixel((x, y), bsgrey)
if spawny - 7 < y < spawny - 3:
koth.putpixel((x, y), (97, 129, 167, 255))
metadata = PngImagePlugin.PngInfo()
metadata.add_text('Gang Garrison 2 Level Data', entities+walkmask)
#koth.show()
#complete = Image.new("RGB", koth.size)
#complete.paste(koth, mask=koth.split()[3])
os.chdir(os.getcwd()[:-13]+"/Maps")
mapname = "koth_random_{}.png".format(random.randrange(10000, 99999))
koth.save(mapname, "png", optimize=True, pnginfo=metadata)
#koth.save("koth_random3.png", "png", optimize=True)
#newmap = Image.open("koth_random.png")
#print(newmap.text)
#ggon = Image.open("a5.png")
#print(ggon.text)
os.chdir(os.getcwd()[:-5])
fichier = open("randomname.gg2", "w")
fichier.write(mapname[:-4])
fichier.close()
there's probably a lot of place where the code is inefficient but it works so i don't think it's a problem
the only thing i couldn't figure out is why walkmask compressed using garrison builder are much more smaller than the one made by my script.
-open gooogle and type "the programming language you're using" + the thing you want to accomplish, repeat until you're done