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