Initial commit for the Python version
This commit is contained in:
commit
7415f825ef
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*.pyc
|
||||||
|
__pycache__/
|
||||||
|
/.venv/
|
||||||
|
/.coverage
|
32
data/plane.erodar.txt
Normal file
32
data/plane.erodar.txt
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
~~~~~~~===~===~~~~~~~~=~~~~~~~~~
|
||||||
|
~~~~~~====~~==~~~~~~~=~~~~~~~~~~
|
||||||
|
~~~~~=====#=======~~~=~~~~~~~~~~
|
||||||
|
~~~~=====#======~==~===~~~~~~~~~
|
||||||
|
~~~=======#==============~~~~~~~
|
||||||
|
~~=========#==============~~~~~~
|
||||||
|
~~~=========#==============~~~~~
|
||||||
|
~~===========#==============~~~~
|
||||||
|
~~~===========#======~=======~~~
|
||||||
|
~~~=======####==============~~~~
|
||||||
|
~~~~======#================~~~~~
|
||||||
|
~~~~~=====#=====~========~~~~~~~
|
||||||
|
~~~~~~~====#===~~====~==~~~~~~~~
|
||||||
|
~~~~~~~~==#===~~~~===~~==~~~~~~~
|
||||||
|
~~~~~~~===#=~~~~~~~~===~=~~~~~~~
|
||||||
|
~~~~~~===#===~~~~~~====~~~~~~~~~
|
||||||
|
~~~~~~~~=#====~~~~~~~~~~~~~~~~~~
|
||||||
|
~~~~~~~~==#==~~~~~~~=~~~~~~~~~~~
|
||||||
|
~~~~~~~~~=#=~~~~~~~==~~~~~~~~~~~
|
||||||
|
~~~~~~~~~===~~~~~~~~~~~~~~~~~~~~
|
||||||
|
~~~~~~~~====~~~~~~~~~~~~~~~~~~~~
|
||||||
|
~~~~~~~~~====~~~~~~~~~~~~~~~~~~~
|
||||||
|
~~~~~~~~~~===~~~~~~~~~~~~~~~~~~~
|
||||||
|
~~~~~~~~~=====~~~~~~~~~~~~~~~~~~
|
||||||
|
~~~~~~~~~~===~~~~~~~=~~~~~~~~~~~
|
||||||
|
~~~~~~~~~~~==~~~~~~==~~~~~~~~~~~
|
||||||
|
~~~~~~~~~~==~~~~~~=~==~~~~~~~~~~
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
23
data/terrain.json
Normal file
23
data/terrain.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"water": {
|
||||||
|
"icon": "~",
|
||||||
|
"map_char": "~",
|
||||||
|
"color16": 12,
|
||||||
|
"color256": 57,
|
||||||
|
"color": "#0000bb"
|
||||||
|
},
|
||||||
|
"field": {
|
||||||
|
"icon": "=",
|
||||||
|
"map_char": "=",
|
||||||
|
"color16": 11,
|
||||||
|
"color256": 228,
|
||||||
|
"color": "#bbbb00"
|
||||||
|
},
|
||||||
|
"road": {
|
||||||
|
"icon": "#",
|
||||||
|
"map_char": "#",
|
||||||
|
"color16": 8,
|
||||||
|
"color256": 244,
|
||||||
|
"color": "#808080"
|
||||||
|
}
|
||||||
|
}
|
0
wmud/__init__.py
Normal file
0
wmud/__init__.py
Normal file
8
wmud/__main__.py
Normal file
8
wmud/__main__.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from wmud.world.map import Plane, load_terrain
|
||||||
|
|
||||||
|
load_terrain('data/terrain.json')
|
||||||
|
map = Plane()
|
||||||
|
map.read('data/plane.erodar.txt')
|
||||||
|
print(map)
|
||||||
|
print('')
|
||||||
|
map.print((0, 0), 11)
|
30
wmud/world/__init__.py
Normal file
30
wmud/world/__init__.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
"""World definitions for wMUD
|
||||||
|
"""
|
||||||
|
|
||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
|
||||||
|
class Directions(IntEnum):
|
||||||
|
"""All the available directions
|
||||||
|
"""
|
||||||
|
|
||||||
|
NORTH = 0
|
||||||
|
SOUTH = 1
|
||||||
|
EAST = 2
|
||||||
|
WEST = 3
|
||||||
|
NORTH_EAST = 4
|
||||||
|
SOUTH_WEST = 5
|
||||||
|
NORTH_WEST = 6
|
||||||
|
SOUTH_EAST = 7
|
||||||
|
UP = 8
|
||||||
|
DOWN = 9
|
||||||
|
|
||||||
|
@property
|
||||||
|
def opposite(self):
|
||||||
|
"""Get the opposite direction
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self % 2 == 0:
|
||||||
|
return self.__class__(self + 1)
|
||||||
|
|
||||||
|
return self.__class__(self - 1)
|
175
wmud/world/map.py
Normal file
175
wmud/world/map.py
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
import json
|
||||||
|
from math import sqrt
|
||||||
|
|
||||||
|
|
||||||
|
TYPES = {}
|
||||||
|
|
||||||
|
|
||||||
|
class TileType(object):
|
||||||
|
def __init__(self, data):
|
||||||
|
self.map_char = data.pop('map_char')
|
||||||
|
self.extra_data = data
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.map_char
|
||||||
|
|
||||||
|
|
||||||
|
class Coordinate(object):
|
||||||
|
def __init__(self, x, y):
|
||||||
|
if x < 0 or y < 0:
|
||||||
|
raise ValueError(f'Invalid coordinates [{x}; {y}]')
|
||||||
|
|
||||||
|
self.__x = x
|
||||||
|
self.__y = y
|
||||||
|
|
||||||
|
@property
|
||||||
|
def x(self):
|
||||||
|
return self.__x
|
||||||
|
|
||||||
|
@property
|
||||||
|
def y(self):
|
||||||
|
return self.__y
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'[{self.x}; {self.y}]'
|
||||||
|
|
||||||
|
|
||||||
|
class Tile(object):
|
||||||
|
def __init__(self, type):
|
||||||
|
self.__type = type
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.__type.char
|
||||||
|
|
||||||
|
|
||||||
|
class Plane(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.__tiles = {}
|
||||||
|
self.width = None
|
||||||
|
self.height = 0
|
||||||
|
|
||||||
|
def read(self, filename):
|
||||||
|
with open(filename) as f:
|
||||||
|
fields = f.read().split()
|
||||||
|
|
||||||
|
self.width = None
|
||||||
|
|
||||||
|
for row_num, row in enumerate(fields):
|
||||||
|
# We simply skip completely empty rows
|
||||||
|
if not row:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self.width is None:
|
||||||
|
self.width = len(row)
|
||||||
|
elif self.width != len(row):
|
||||||
|
raise ValueError(f'Row {row_num} differs in length!')
|
||||||
|
|
||||||
|
for col_num, field in enumerate(row):
|
||||||
|
self.__tiles[Coordinate(col_num, row_num)] = get_type(field)
|
||||||
|
|
||||||
|
self.height = row_num + 1
|
||||||
|
|
||||||
|
def print(self, coords, radius):
|
||||||
|
"""Print a round part of the map, with `coords` in the midpoint, and the
|
||||||
|
horizontal radius (ie. width in characters) `radius`
|
||||||
|
|
||||||
|
This will result in a shape that resembles a circle, but, as text
|
||||||
|
characters have about 1:2 ratio, is actually an ellipse.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(coords, Coordinate):
|
||||||
|
coords = Coordinate(*coords)
|
||||||
|
|
||||||
|
if radius > self.width // 2:
|
||||||
|
raise ValueError('Radius is too big')
|
||||||
|
|
||||||
|
if radius % 2 != 1:
|
||||||
|
raise ValueError('Radius must be odd')
|
||||||
|
|
||||||
|
width = (radius - 1) * 2 + 1
|
||||||
|
|
||||||
|
mid_x, mid_y = width // 2, radius // 2
|
||||||
|
|
||||||
|
for row in range(0, radius):
|
||||||
|
y_offset = row - mid_y
|
||||||
|
y = y_offset * 2
|
||||||
|
map_y = coords.y + y_offset
|
||||||
|
|
||||||
|
if map_y < 0:
|
||||||
|
map_y += self.height
|
||||||
|
elif map_y >= self.height:
|
||||||
|
map_y -= self.height
|
||||||
|
|
||||||
|
for column in range(0, width):
|
||||||
|
x_offset = column - mid_x
|
||||||
|
x = x_offset
|
||||||
|
map_x = coords.x + x_offset
|
||||||
|
|
||||||
|
if map_x < 0:
|
||||||
|
map_x += self.width
|
||||||
|
elif map_x >= self.width:
|
||||||
|
map_x -= self.width
|
||||||
|
|
||||||
|
if x < 0:
|
||||||
|
x -= 1
|
||||||
|
elif x > 0:
|
||||||
|
x += 1
|
||||||
|
|
||||||
|
distance = sqrt(y ** 2 + x ** 2)
|
||||||
|
|
||||||
|
if row == mid_y and column == mid_x:
|
||||||
|
print('@', end='')
|
||||||
|
elif distance > radius + 0.25:
|
||||||
|
print(' ', end='')
|
||||||
|
else:
|
||||||
|
tile = self[(map_x, map_y)]
|
||||||
|
print(tile, end='')
|
||||||
|
|
||||||
|
print('')
|
||||||
|
|
||||||
|
def __getitem__(self, coord):
|
||||||
|
if not isinstance(coord, Coordinate):
|
||||||
|
coord = Coordinate(*coord)
|
||||||
|
|
||||||
|
for key, tile in self.__tiles.items():
|
||||||
|
if key.x == coord.x and key.y == coord.y:
|
||||||
|
return tile
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
map_str = ''
|
||||||
|
|
||||||
|
prev_y = -1
|
||||||
|
|
||||||
|
for coord in sorted(self.__tiles.keys(), key=lambda coord: (coord.y, coord.x)):
|
||||||
|
if coord.y != prev_y:
|
||||||
|
prev_y = coord.y
|
||||||
|
map_str += '\n'
|
||||||
|
|
||||||
|
map_str += self.__tiles[coord].map_char
|
||||||
|
|
||||||
|
return map_str
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<Map>'
|
||||||
|
|
||||||
|
|
||||||
|
def get_type(type_char):
|
||||||
|
candidates = {identifier: data for identifier, data in TYPES.items() if data.map_char == type_char}
|
||||||
|
|
||||||
|
if not candidates:
|
||||||
|
raise KeyError(f'Map character "{type_char}" is unknown')
|
||||||
|
|
||||||
|
if len(candidates) > 1:
|
||||||
|
raise KeyError(f'Map character "{type_char}" has multiple definitions!')
|
||||||
|
|
||||||
|
return candidates.popitem()[1]
|
||||||
|
|
||||||
|
|
||||||
|
def load_terrain(filename):
|
||||||
|
with open(filename, 'r') as f:
|
||||||
|
terrain_data = json.load(f)
|
||||||
|
|
||||||
|
for identifier, data in terrain_data.items():
|
||||||
|
# TODO: Validate terrain data
|
||||||
|
tile_type = TileType(data)
|
||||||
|
TYPES[identifier] = tile_type
|
Loading…
Reference in New Issue
Block a user