#!/usr/bin/env python3

import random, math

# Please note that the script is not fully
# tested (it might create malformed output
# or otherwise cause the game to crash).
# Use with caution.

# It was written for GC2 Ultimate Edition.
# In order to use it with earlier versions,
# you might need to change the first two 
# parametres to 18 and 5, respectively.

width = 21  # number of sectors
size = 6    # GC2 map size: "Immense"

# The following two can be modified, but
# it can cause failures.

nstar = 1000    # number of stars
nplan = 1000    # numberof planets

# The following two describe how the stars
# are placed.
# If secsize > secspan, then there are gaps
# between the planetary systems.
# If secsize < secspan, then the planetary
# systems can overlap.

secsize = 5 # size of gaps between stars
secspan = 5 # size of spans the stars take

# The following describes the parametres
# of the galaxy generated by the script.

galcore = 0.15  # size of the galaxy core
galarms = 4     # number of galaxy arms
galawidth = 1.  # galactic arm width
galaskew = 1.   # galactic arm curvedness
galrota = 0.1   # galaxy rotation

# The following are the parametres of the 
# stars and planet. Most of them are best
# left untouched.

nstartype = 7
astarsize = 50.
bstarsize = 70.
astarspeed = 0.02
bstarspeed = 0.08
astarpatt = 37.5
bstarpatt = 37.5
astardens = 20.
bstardens = 20.

nplanqual = 18  # maximum planet quality
tplanqual = 0   # typical planet quality
aplansize = 10.
bplansize = 20.
aplanspeed = 0.02
bplanspeed = 0.08
nplaninfl = 30  # maximum planet influence
tplaninfl = 0   # typical planet influence

def getplanqual():
    if bool(random.getrandbits(1)):
        return 0
    else:
        return int(random.triangular(0,nplanqual,tplanqual))

def getplaninfl():
    if bool(random.getrandbits(1)):
        return 0
    else:
        return int(random.triangular(0,nplaninfl,tplaninfl))

### START

print( """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<GC2Map>
    <Info>
        <Width>"""+str(width)+"""</Width>
        <Height>"""+str(width)+"""</Height>
        <Size>"""+str(size)+"""</Size>
        <Stars>"""+str(nstar)+"""</Stars>
        <Planets>"""+str(nplan)+"""</Planets>
        <Resources>0</Resources>
        <Anomalies>0</Anomalies>
        <Ships>0</Ships>
        <HomelessPlayers>0</HomelessPlayers>
    </Info>
""" );

### GENERATE THE GALAXY

secs = width*16//secsize-2

lsec = []

def gengalaxy(size,n):
    global lsec
    for t in [random.random() for _ in range(n)]:
        angle = t*galaskew*2*math.pi 
        angle += random.randrange(galarms)*2*math.pi/galarms 
        angle += galawidth*(1.6-t)*random.betavariate(2,2) 
        angle += galrota*2*math.pi

        dist = t*1.5 
        dist += galcore 
        dist += galcore*galawidth*random.betavariate(2,2)
        
        x = math.sin(angle)*dist
        y = math.cos(angle)*dist
        nx = int( (x+1)*size//2 )
        ny = int( (y+1)*size//2 )
        if nx >= 0 and nx < size and ny >= 0 and ny < size:
            lsec += [(nx,ny)]

lstar = []

sectaken = [[False for _ in range(secs)] for _ in range(secs)]

taken = [[False for _ in range(width*16)] for _ in range(width*16)]

gengalaxy(secs,nstar*100)

### GENERATE STARS

def genstars(nstar):
    global lstar, sectaken, taken
    for istar in range(nstar):
        xsec,ysec = random.choice(lsec)
        while sectaken[xsec][ysec]:
            xsec,ysec = random.choice(lsec)
        sectaken[xsec][ysec] = True

        x = xsec*secsize + random.randrange(secspan) + secsize//2
        y = ysec*secsize + random.randrange(secspan) + secsize//2
        while taken[x][y]:
            x = xsec*secsize + random.randrange(secspan) + secsize//2
            y = ysec*secsize + random.randrange(secspan) + secsize//2
        taken[x][y] = True
    
        lstar += [(istar,xsec,ysec)]
    
        startype = random.randrange(nstartype)
        starsize = random.uniform(astarsize, bstarsize)
        starspeed = random.uniform(astarspeed, bstarspeed)
        starpatt = random.uniform(astarpatt, bstarpatt)
        stardens = random.uniform(astardens, bstardens)
        print( """
        <Star Name="Star"""+str(istar)+"""">
            <X>"""+str(x)+"""</X>
            <Y>"""+str(y)+"""</Y>
            <Type>"""+str(startype)+"""</Type>
            <CustomName>0</CustomName>
            <Size>"""+str("%.4f"%starsize)+"""</Size>
            <RotationSpeed>"""+str("%.4f"%starspeed)+"""</RotationSpeed>
            <Pattern>"""+str("%.4f"%starpatt)+"""</Pattern>
            <Density>"""+str("%.4f"%stardens)+"""</Density>
        </Star>
        """ )
    
genstars(nstar)

### GENERATE PLANETS

def genplanets(nplan):
    for iplan in range(nplan):
        global taken
        istar,xsec,ysec = random.choice(lstar)
    
        x = xsec*secsize+random.randrange(secspan) + secsize//2
        y = ysec*secsize+random.randrange(secspan) + secsize//2
        while taken[x][y]:
            x = xsec*secsize+random.randrange(secspan) + secsize//2
            y = ysec*secsize+random.randrange(secspan) + secsize//2
        taken[x][y] = True
    
        planqual = getplanqual()
        plansize = random.uniform(aplansize, bplansize)
        planspeed = random.uniform(aplanspeed, bplanspeed)
        planinfl = getplaninfl()
        print( """
        <Planet Name="Planet"""+str(iplan)+"""">
            <CustomName>0</CustomName>
            <X>"""+str(x)+"""</X>
            <Y>"""+str(y)+"""</Y>
            <Quality>"""+str(planqual)+"""</Quality>
            <Size>"""+str("%.4f"%plansize)+"""</Size>
            <RotationSpeed>"""+str("%.4f"%planspeed)+"""</RotationSpeed>
            <Influence>"""+str(planinfl)+"""</Influence>
            <Star>Star"""+str(istar)+"""</Star>
        </Planet>
        """ )

genplanets(nplan)
    
### END

print( """
</GC2Map>
""" )
