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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 C3%ou6 literal 0 HcmV?d00001 diff --git a/state.py b/state.py new file mode 100644 index 0000000..4bf1346 --- /dev/null +++ b/state.py @@ -0,0 +1,21 @@ +# This loads/saves and manages the gamestate and can be used to query what is on the tiles + +import pickle + +class State(): + + def __init__(self): + #print("Loading state-engine") + self.gamestate = {} + + def load(self): + self.gamestate = pickle.load( open( "savegame.empyre_save", "rb" ) ) + self.metadata = self.gamestate['metadata'] + self.terrain = self.gamestate['terrain'] + self.cities = self.gamestate['cities'] + self.coast = self.gamestate['coast'] + self.units = self.gamestate['units'] + self.fog = self.gamestate['fog'] + + def save(self): + pickle.dump(self.gamestate, open("savegame.empyre_save","wb")) diff --git a/test.py b/test.py new file mode 100644 index 0000000..91e1b24 --- /dev/null +++ b/test.py @@ -0,0 +1,109 @@ +import curses + +# Import my own libraries +from state import State +gamestate = State() +gamestate.load() + + +# Print (portion of the) map +#def fillMap(stdscr,gamestate): +# 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" ) +# 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()