From 14c182a39c10fa8427e5b5ddfe243d62463f614d Mon Sep 17 00:00:00 2001 From: Sacha Ligthert Date: Mon, 17 Oct 2022 19:17:22 +0200 Subject: [PATCH] Initial Commit --- README.md | 72 +++++++++ empyre.py | 21 +++ experiments/animateLacunarity.py | 28 ++++ experiments/camera.py | 68 +++++++++ experiments/make_map_animation.py | 16 ++ experiments/make_map_animation_lacunarity.py | 18 +++ experiments/make_map_animation_shift.py | 19 +++ experiments/test.py | 31 ++++ gen_map.py | 28 ++++ loadstate.py | 20 +++ location_test.py | 59 +++++++ map.py | 152 +++++++++++++++++++ notes.txt | 86 +++++++++++ render.py | 114 ++++++++++++++ requirements.txt | 1 + savegame.empyre_save | Bin 0 -> 42467 bytes state.py | 21 +++ test.py | 109 +++++++++++++ 18 files changed, 863 insertions(+) create mode 100644 README.md create mode 100644 empyre.py create mode 100644 experiments/animateLacunarity.py create mode 100644 experiments/camera.py create mode 100644 experiments/make_map_animation.py create mode 100644 experiments/make_map_animation_lacunarity.py create mode 100644 experiments/make_map_animation_shift.py create mode 100644 experiments/test.py create mode 100644 gen_map.py create mode 100644 loadstate.py create mode 100644 location_test.py create mode 100644 map.py create mode 100644 notes.txt create mode 100644 render.py create mode 100644 requirements.txt create mode 100644 savegame.empyre_save create mode 100644 state.py create mode 100644 test.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..c5628ee --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# emPYre +(should change as soon as the game is more complete) + +## About +EmPYre is an (experimental) python 3 implementation of the old Empire game. + +### Why? +The current port on Ubuntu is slow AF and also the control-scheme like many tools from that era it didn't era very well. (ignoring features we see in modern open-source RTS-games these days) + +# Requirements +* Python 3.4+ + +# Developer notes +TODO: +* Look into how the original version did the battle system ( units vs units vs cities ) +* Look into ASCII, Colours, blinking and other flashy things + * + land + * . sea + * ^ (mountains?) +* Look into nCurses to deal with the interface issues + * Different parts of the screen + * Pop-up a window if something is required? +* Look into how to do this + * Separate the logic from the interface? + * Multiple opponents? + * Multi-player? + * Multi-session multi-player? +* Modern systems + * Waypoints? + * Build-queues? + * Patrols? + +## Combat + +``` while (att_obj->hits > 0 && def_obj->hits > 0) { + if (irand (2) == 0) /* defender hits? */ + att_obj->hits -= piece_attr[def_obj->type].strength; + else def_obj->hits -= piece_attr[att_obj->type].strength; + } +``` + +|Piece|You|Enemy|Moves|Hits|Str|Cost| +| --- | ---| ---| ---| ---| ---| ---| +|Army|A|a|1|1|1|5(6)| +|Fighter|F|f|8|1|1|10(12)| +|Patrol Boat|P|p|4|1|1|15(18)| +|Destroyer|D|d|2|3|1|20(24)| +|Submarine|S|s|2|2|3|20(24)| +|Troop Transport|T|t|2|1|1|30(36)| +|Aircraft Carrier|C|c|2|8|1|30(36)| +|Battleship|B|b|2|10|2|40(48)| +|Satellite|Z|z|10|--|--|50(60)| + +Other details: http://www.catb.org/~esr/vms-empire/vms-empire.html + + +``` +typedef struct piece_attr { + char sname; /* eg 'C' */ + char name[20]; /* eg "aircraft carrier" */ + char nickname[20]; /* eg "carrier" */ + char article[20]; /* eg "an aircraft carrier" */ + char plural[20]; /* eg "aircraft carriers" */ + char terrain[4]; /* terrain piece can pass over eg "." */ + uchar build_time; /* time needed to build unit */ + uchar strength; /* attack strength */ + uchar max_hits; /* number of hits when completely repaired */ + uchar speed; /* number of squares moved per turn */ + uchar capacity; /* max objects that can be held */ + long range; /* range of piece */ +} piece_attr_t; +``` diff --git a/empyre.py b/empyre.py new file mode 100644 index 0000000..02504fb --- /dev/null +++ b/empyre.py @@ -0,0 +1,21 @@ +# Import mapgen +# Import state-engine + + + +# Main loop + # Check if there is a save-file + # Load Gamestate + # Else + # Generate Gamestate + # Generate map+assesories + # Create a fog of war + # Create extra metadata entries: round# movesPlayer movesAI Stats turn(c or p) + + # Look who's turn it is + # If its Computer + # Magic happens here + # else + # Player's turn + # Iterate through cities + # Iterate through units diff --git a/experiments/animateLacunarity.py b/experiments/animateLacunarity.py new file mode 100644 index 0000000..01bb8da --- /dev/null +++ b/experiments/animateLacunarity.py @@ -0,0 +1,28 @@ +from PIL import Image, ImageDraw +import noise + +slides = [] +width=500 +height=500 +#cycles=5948 +cycles=595 +divider=1000 +divider=100 + +for lacu in range(0,cycles): + + print("Lacunarity: "+str(round(lacu/divider,3)) ) + + img = Image.new('RGB', (width,height)) + + for x in range(1,width): + for y in range(1,height): + area = noise.pnoise2( x/128, y/128, octaves=50, persistence=0.5, lacunarity=round(lacu/divider,3), repeatx=width, repeaty=height, base=25 ) + img.putpixel( (int(x),int(y)), round(area*255) ) + + d = ImageDraw.Draw(img) + d.text( (1,1), "Lacunarity: "+str(round(lacu/divider,3)), fill=(255,0,0)) + + slides.append(img) + +slides[0].save("lacunarity.gif", save_all=True, append_images=slides[1:], optimize=True, duration=50, loop=0) diff --git a/experiments/camera.py b/experiments/camera.py new file mode 100644 index 0000000..886bb37 --- /dev/null +++ b/experiments/camera.py @@ -0,0 +1,68 @@ + +# Import 1st party modules +import curses + +# Import my own libraries +from state import State + +gamestate = State() +gamestate.load() + +#print(gamestate.terrain[1][2]) + +def draw_map(stdscr): + # key press + k = 0 + + # Map center + center = {} + center['x'] = round(gamestate.metadata['width']/2) + center['y'] = round(gamestate.metadata['height']/2) + + # Clear and refresh the screen for a blank canvas + stdscr.clear() + stdscr.refresh() + + # Start colors in curses + curses.start_color() + curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_GREEN) # Neutral Land + curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLUE) # Neutral Sea + curses.init_pair(3, curses.COLOR_WHITE, curses.COLOR_GREEN) # Friendly Land + curses.init_pair(4, curses.COLOR_WHITE, curses.COLOR_BLUE) # Friendly Sea + curses.init_pair(5, curses.COLOR_RED, curses.COLOR_GREEN) # Enemy Land + curses.init_pair(6, curses.COLOR_RED, curses.COLOR_BLUE) # Enemy Sea + curses.init_pair(7, curses.COLOR_BLACK, curses.COLOR_BLACK) # Outside of the map + + while (k != ord('q')): + # Initialization + stdscr.clear() + height, width = stdscr.getmaxyx() + + if k == curses.KEY_DOWN: + center['y'] = center['y'] + 1 + elif k == curses.KEY_UP: + center['y'] = center['y'] - 1 + elif k == curses.KEY_RIGHT: + center['x'] = center['x'] + 1 + elif k == curses.KEY_LEFT: + center['x'] = center['x'] - 1 + + for y in range(1,height): + for x in range(1,width): + if gamestate.terrain[x][y] == "l": + stdscr.addstr( y, x, " ", curses.color_pair(1) ) + elif gamestate.terrain[x][y] == "w": + stdscr.addstr( y, x, " ", curses.color_pair(2) ) + + + # Refresh the screen + stdscr.refresh() + + # Wait for next input + k = stdscr.getch() + +def main(): + curses.wrapper(draw_map) + +if __name__ == "__main__": + main() diff --git a/experiments/make_map_animation.py b/experiments/make_map_animation.py new file mode 100644 index 0000000..b85624b --- /dev/null +++ b/experiments/make_map_animation.py @@ -0,0 +1,16 @@ +from map import Map +from PIL import Image, ImageDraw + +slides = [] + +if __name__ == "__main__": + for x in range(1,100): + map = Map(sealevel=x, xoffset=0, yoffset=0) + map.generateMap() +# map.findCoast() +# map.generateCities() +# map.addCities() + #map.printWorld() + slides.append(map.saveWorld()) + + slides[0].save("zlevel_7.gif", save_all=True, append_images=slides[1:], optimize=True, duration=500, loop=0) diff --git a/experiments/make_map_animation_lacunarity.py b/experiments/make_map_animation_lacunarity.py new file mode 100644 index 0000000..c12c348 --- /dev/null +++ b/experiments/make_map_animation_lacunarity.py @@ -0,0 +1,18 @@ +from map import Map +# from PIL import Image, ImageDraw + +slides = [] + +if __name__ == "__main__": + max_scale = 32 + max_lacunarity = 100 + width=150 + height=50 + for lacunarity in range(1,max_lacunarity): + lacunarity = lacunarity/100 + print("Lacunarity: "+str(lacunarity)) + map = Map(scale=32, base=1, width=width, height=height, lacunarity=lacunarity) + map.generateMap() + slides.append(map.saveWorld()) + + slides[0].save("zoom_base1_zoom"+str(max_scale)+"_lacunarity"+str(max_lacunarity)+"_width"+str(width)+"_height"+str(height)+".gif", save_all=True, append_images=slides[1:], optimize=True, duration=40, loop=0) diff --git a/experiments/make_map_animation_shift.py b/experiments/make_map_animation_shift.py new file mode 100644 index 0000000..59c64f1 --- /dev/null +++ b/experiments/make_map_animation_shift.py @@ -0,0 +1,19 @@ +from map import Map +from PIL import Image, ImageDraw + +slides = [] + +if __name__ == "__main__": + max_scale = 64 + lacunarity = 1.5 + for scale in range(32,max_scale): + print("Scale: "+str(scale)) + map = Map(scale=scale, base=1, width=640, height=480, lacunarity=lacunarity) + map.generateMap() +# map.findCoast() +# map.generateCities() +# map.addCities() + #map.printWorld() + slides.append(map.saveWorld()) + + slides[0].save("zoom_base1_zoom"+str(max_scale)+"_lacunarity"+str(lacunarity)+".gif", save_all=True, append_images=slides[1:], optimize=True, duration=150, loop=0) diff --git a/experiments/test.py b/experiments/test.py new file mode 100644 index 0000000..4fc7095 --- /dev/null +++ b/experiments/test.py @@ -0,0 +1,31 @@ +import curses + +from state import State + +k = 0 + +gamestate = State() +gamestate.load() +terrain = gamestate.terrain + +def main(stdscr): + mypad = curses.newpad(62,62) + for y in range(0,62): + mypad.addstr( y, 1, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqerstuvwxyz1234567890") + + mypad_pos = 0 + mypad.refresh(mypad_pos, 0, 5, 5, 10, 30) + + while (k != ord('q')): + mypad.refresh(mypad_pos, 0, 5, 5, 10, 30) + + if k == curses.KEY_DOWN: + mypad_pos += 1 + mypad.refresh(mypad_pos, 0, 5, 5, 10, 60) + elif k == curses.KEY_UP: + mypad_pos -= 1 + mypad.refresh(mypad_pos, 0, 5, 5, 10, 60) + + k = stdscr.getch() + +curses.wrapper(main) diff --git a/gen_map.py b/gen_map.py new file mode 100644 index 0000000..5eb35d3 --- /dev/null +++ b/gen_map.py @@ -0,0 +1,28 @@ +from map import Map +import pickle + +slides = [] + +if __name__ == "__main__": + # Generate the basic map. + map = Map() + map.generateMap() + + # Add the cities + map.findCoast() + map.generateCities() + map.addCities() + + # Print stuff or dump to image + #map.printWorld() + terrain, cities, coast, metadata = map.dumpMap() + gamestate = {} + gamestate['metadata'] = metadata + gamestate['terrain'] = terrain + gamestate['cities'] = cities + gamestate['coast'] = coast + gamestate['units'] = [] + gamestate['fog'] = [] + + pickle.dump(gamestate, open("savegame.empyre_save","wb")) + #gamestate = pickle.load( open( "savegame.empyre", "rb" ) ) diff --git a/loadstate.py b/loadstate.py new file mode 100644 index 0000000..a821be0 --- /dev/null +++ b/loadstate.py @@ -0,0 +1,20 @@ +#from map import Map +from state import State + +if __name__ == "__main__": + gamestate = State() + gamestate.load() + + print(gamestate.metadata) + #print(gamestate.terrain) + #Map.printWorldExt(gamestate.terrain, gamestate.metadata) + + for y in range(1,gamestate.metadata['height']+1): + for x in range(1,gamestate.metadata['width']+1): + if gamestate.terrain[x][y] == "c": + print('\x1b[1;37;42m' + '*' + '\x1b[0m', end='') + elif gamestate.terrain[x][y] == "l": + print('\x1b[1;37;42m' + ' ' + '\x1b[0m', end='') + else: + print('\x1b[1;37;44m' + ' ' + '\x1b[0m', end='') + print(" ") diff --git a/location_test.py b/location_test.py new file mode 100644 index 0000000..6545243 --- /dev/null +++ b/location_test.py @@ -0,0 +1,59 @@ +import curses + +# Import my own libraries +from state import State +gamestate = State() +gamestate.load() + +from render import Render + +# -- Initialize -- +stdscr = curses.initscr() +curses.noecho() +curses.cbreak() +stdscr.keypad(1) +stdscr.notimeout(1) + +# Clear and refresh the screen for a blank canvas +stdscr.clear() +stdscr.refresh() + +height, width = stdscr.getmaxyx() + +curses.start_color() +curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) # Neutral Land +curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK) # Neutral Sea +curses.init_pair(3 , curses.COLOR_RED, curses.COLOR_BLACK) # Hostile +curses.init_pair(7, curses.COLOR_BLACK, curses.COLOR_BLACK) # Outside of the map + +#stdscr = Render.renderScreen(stdscr, gamestate, 40, 12) +stdscr = Render.renderScreen(stdscr, gamestate, 75, 25) + +k = 0 +map_focus_x = round(gamestate.metadata['width']/2) +map_focus_y = round(gamestate.metadata['height']/2) +while (k != ord('q')): + + if k == curses.KEY_DOWN: + map_focus_y = map_focus_y + 1 + elif k == curses.KEY_UP: + map_focus_y = map_focus_y - 1 + elif k == curses.KEY_RIGHT: + map_focus_x = map_focus_x + 1 + elif k == curses.KEY_LEFT: + map_focus_x = map_focus_x - 1 + + map_focus_x = max(0, map_focus_x) + map_focus_x = min(gamestate.metadata['width'], map_focus_x) + + map_focus_y = max(0, map_focus_y) + map_focus_y = min(gamestate.metadata['height'], map_focus_y) + + stdscr = Render.renderScreen(stdscr, gamestate, map_focus_x, map_focus_y) + + k = stdscr.getch() + +stdscr.keypad(0) +curses.echo() +curses.nocbreak() +curses.endwin() diff --git a/map.py b/map.py new file mode 100644 index 0000000..772ca77 --- /dev/null +++ b/map.py @@ -0,0 +1,152 @@ +import noise +import random +from collections import defaultdict +import math + +class Map(): + + def __init__( self, width=150, height=50, sealevel=50, scale=32, base=random.randint(1,250), lacunarity=0.5+random.random(), octaves=50, persistence=0.5, xoffset=random.randint(1,10000), yoffset=random.randint(1,10000) ): + + print("Initializing map") + + # Dealing with basic params + self.width = width + self.height = height + self.sealevel = -0.37+((0.75/100)*sealevel) + self.scale = scale + self.base = base + self.octaves = octaves + self.persistence = persistence + self.lacunarity = lacunarity + self.xoffset = xoffset + self.yoffset = yoffset + + # The master map we are going to need + self.map = defaultdict(dict) + self.coast = [] + + # All the required stuff to determine cities + self.cities = [] + # self.num_cities = 50+(100-int(self.sealevel)) + self.num_cities = 75 + self.coast_percentage = 25 + self.distance = 3 + + + def generateMap(self): + print("Generating Map") + # Fill in the blanks with random stuff + for x in range(1,self.width+1): + for y in range(1,self.height+1): + area = noise.pnoise2( self.xoffset+x/self.scale, self.yoffset+y/(self.scale/3), octaves=self.octaves, persistence=self.persistence, lacunarity=self.lacunarity, repeatx=self.height, repeaty=self.width, base=self.base ) + if area > self.sealevel: + self.map[x][y] = "l" + else: + self.map[x][y] = "w" + + + # Find coastal positions + def findCoast(self): + print("Finding coastal tiles") + # Should replace with a range()-loop + y = 1 + + while y <= self.height: + x = 1 + while x <= self.width: + water = 0 + if x !=1 and x!=self.width and y!=1 and y!=self.height and self.map[x][y]=="l": + if self.map[x-1][y-1]=="w": + water += 1 + if self.map[x][y-1]=="w": + water += 1 + if self.map[x+1][y-1]=="w": + water += 1 + if self.map[x+1][y]=="w": + water += 1 + if self.map[x+1][y+1]=="w": + water += 1 + if self.map[x][y+1]=="w": + water += 1 + if self.map[x-1][y+1]=="w": + water += 1 + if self.map[x-1][y]=="w": + water += 1 + if water >= 1: + coords = {} + coords['x'] = x + coords['y'] = y + self.coast.append(coords) + x+=1 + y+=1 + + + def generateCities(self): + print("Generate cities") + for round in range(0,self.num_cities): + if random.randint(1,100) < self.coast_percentage: + self.cities.append(random.choice(self.coast)) + else: + found = 0 + while found == 0: + randX = random.randint(1,self.width) + randY = random.randint(1,self.height) + if self.map[randX][randY]=="l" and self.noCityNear(self.distance,randX,randY): + found = 1 + coords = {} + coords['x'] = randX + coords['y'] = randY + self.cities.append(coords) + + + def noCityNear(self,distance,x,y): + noCityNear = True + counter = 0 + for city in self.cities: + #Lay-Z D-Bug: print( str(counter) +" x:"+ str( abs(city['x']-x) ) +"; y:"+ str( abs(city['y']-y) ) +";") + # This should be an OR, but this takes forever! So this is good enough. + if abs(city['x']-x) gamestate.metadata['width']: + xoffset = round( (width - gamestate.metadata['width'])/2 ) + if height > gamestate.metadata['height']: + yoffset = round( (height-gamestate.metadata['height'])/2 ) + + + # Draw the visible segment of the map + for y in range(1,height): + top_left_x = centerx - round(width/2) + for x in range(1,width-3): + if top_left_y <= 0 or top_left_y > gamestate.metadata['height'] or top_left_x <= 0 or top_left_x > gamestate.metadata['width']: + stdscr.addstr( y-1, x-1, " ", curses.color_pair(7)) + elif gamestate.terrain[top_left_x][top_left_y] == "l": + stdscr.addstr( y-1, x-1, "+", curses.color_pair(1) ) + elif gamestate.terrain[top_left_x][top_left_y] == "w": + stdscr.addstr( y-1, x-1, ".", curses.color_pair(2) ) + elif gamestate.terrain[top_left_x][top_left_y] == "c": + stdscr.addstr( y-1, x-1, "*" ) + top_left_x += 1 + top_left_y += 1 + + +##### Print the horizontal ruler at the bottom of the screen + xruler = "" + xrulery = 0 + xrulerx = 0 + xrulerx_offset = 0 + + + if width > gamestate.metadata['width']: + xrulerx_offset = xoffset + 1 + xrulerx = gamestate.metadata['width'] + elif width < gamestate.metadata['width']: + xrulerx = width + else: + xrulerx = gamestate.metadata['width'] + + + if gamestate.metadata['height']-1 < height-1: + xrulery = yoffset+gamestate.metadata['height'] + else: + xrulery = height-1 + + + skip = 0 + if width > gamestate.metadata['width']: + xruler_start = 0 + xruler_end = gamestate.metadata['width'] + else: + xruler_start = centerx - round(width/2) + xruler_end = (xruler_start + width) - 4 + + for scale in range(xruler_start,xruler_end): + if skip != 0: + skip-=1 + continue + if scale%10 == 0: + skip=len(str(scale))-1 + xruler+=str(scale) + elif str(scale)[-1:]=="5": + xruler+="|" + else: + xruler+="." + stdscr.addstr( xrulery, xrulerx_offset, xruler ) + + +##### Print the verticle ruler + yruler = centery - round(height/2) + yruler_offset = 0 + yrulery = 1 + #yruler = 0 + yrulerx = 0 + + if width > gamestate.metadata['width']: + yrulerx = int((width - gamestate.metadata['width'])/2)+1+gamestate.metadata['width'] + yruler_offset = (width - gamestate.metadata['width'])/2 + elif gamestate.metadata['width'] < width-4: + yrulerx = gamestate.metadata['width'] + else: + yrulerx = width-4 + + while yrulery < height-1 and yrulery-yruler_offset < gamestate.metadata['height'] and yruler <= gamestate.metadata['height']-1: + if yruler >= 0: + if yruler%5==0: + yruler_text = "{: >3}".format( yruler ) + stdscr.addstr( yrulery, yrulerx, yruler_text ) + else: + stdscr.addstr( yrulery, yrulerx, "{: >3}".format(".") ) + yruler+=1 + yrulery+=1 + + # Return stdscr asif nothing happened + return stdscr diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..81d45d7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +noise diff --git a/savegame.empyre_save b/savegame.empyre_save new file mode 100644 index 0000000000000000000000000000000000000000..8f5e254db479dab49fad8cc66e52705e6a2eccbc GIT binary patch literal 42467 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 gamestate.metadata['height'] or x < 0 or x > gamestate.metadata['width']: +# stdscr.addstr( y-1, x-1, " ", curses.color_pair(7)) +# elif gamestate.terrain[x][y] == "l": +# stdscr.addstr( y-1, x-1, "+", curses.color_pair(1) ) +# elif gamestate.terrain[x][y] == "w": +# stdscr.addstr( y-1, x-1, ".", curses.color_pair(2) ) +# elif gamestate.terrain[x][y] == "c": +# stdscr.addstr( y-1, x-1, "O" ) +# return stdscr + +# -- Initialize -- +stdscr = curses.initscr() +curses.noecho() +curses.cbreak() +stdscr.keypad(1) +stdscr.notimeout(1) + +height, width = stdscr.getmaxyx() + +curses.start_color() +curses.init_pair(1, curses.COLOR_GREEN, curses.COLOR_BLACK) # Neutral Land +curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK) # Neutral Sea +curses.init_pair(3 , curses.COLOR_RED, curses.COLOR_BLACK) # Hostile +curses.init_pair(7, curses.COLOR_BLACK, curses.COLOR_BLACK) # Outside of the map + + +# Print (portion of the) map +for y in range(1,height): + for x in range(1,width-3): + if y < 0 or y > gamestate.metadata['height'] or x < 0 or x > gamestate.metadata['width']: + stdscr.addstr( y-1, x-1, " ", curses.color_pair(7)) + elif gamestate.terrain[x][y] == "l": + stdscr.addstr( y-1, x-1, "+", curses.color_pair(1) ) + elif gamestate.terrain[x][y] == "w": + stdscr.addstr( y-1, x-1, ".", curses.color_pair(2) ) + elif gamestate.terrain[x][y] == "c": + stdscr.addstr( y-1, x-1, "O" ) + +#stdscr = fillMap(stdscr, gamestate) + +# Print horizontal ruler +xruler = "" +xrulery = 0 +xrulerx = 0 + +if width < gamestate.metadata['width']: + xrulerx = width +else: + xrulerx = gamestate.metadata['width'] + +if gamestate.metadata['height']-1 < height-1: + xrulery = gamestate.metadata['height']-1 +else: + xrulery = height-1 + +for scale in range(0,100): + xruler += "{:.<10}".format(scale*10) + if (xrulerx - 10) - len(xruler) < 6 : + xruler += str((scale+1)*10) + break +while len(xruler) < width-4 and len(xruler) < gamestate.metadata['width']: + xruler += "." +stdscr.addstr( xrulery, 0, xruler ) + + +# Print the verticle ruler +yruler = 0 +yrulerx = 0 + +if gamestate.metadata['width'] < width-4: + yrulerx = gamestate.metadata['width'] +else: + yrulerx = width-4 + +while yruler < height-1 and yruler < gamestate.metadata['height']: + if yruler%5==0: + yruler_text = "{: >3}".format( yruler ) + stdscr.addstr( yruler, yrulerx, yruler_text ) + else: + stdscr.addstr( yruler, yrulerx, "{: >3}".format(".") ) + yruler+=1 + + +#stdscr.addstr( 1, 22, "a", curses.color_pair(3)) +#stdscr.addstr( 5, 5, "p", curses.color_pair(3)) + + +while True: + # stay in this loop till the user presses 'q' + ch = stdscr.getch() + if ch == ord('q'): + break + +stdscr.keypad(0) +curses.echo() +curses.nocbreak() +curses.endwin()