Add entities gestion and basic animations. Unify the coordinates system
This commit is contained in:
parent
134393ec60
commit
b5dc823078
300
asci.py
300
asci.py
|
@ -1,62 +1,19 @@
|
|||
# Asci (version 1.7.0)
|
||||
|
||||
class Screen:
|
||||
def __init__(self, screen_width=21, screen_height=6):
|
||||
# Screen configuration
|
||||
self.screen_width = screen_width
|
||||
self.screen_height = screen_height
|
||||
self._data = [[" " for _ in range(screen_width)] for _ in range(screen_height)]
|
||||
|
||||
def clear(self):
|
||||
print("\n" * self.screen_height)
|
||||
|
||||
def set_world(self, world):
|
||||
self._world = [[char for char in line] for line in world.split("\n")[1:]]
|
||||
self.map_width = max([len(line) for line in self._world])
|
||||
self.map_height = len(self._world)
|
||||
|
||||
def set_data(self, x, y):
|
||||
for x_map in range(x, x + self.screen_width):
|
||||
for y_map in range(y, y + self.screen_height):
|
||||
self._data[y_map - y][x_map - x] = " "
|
||||
if 0 <= x_map < self.map_width and 0 <= y_map < self.map_height:
|
||||
try: self._data[y_map - y][x_map - x] = self._world[y_map][x_map]
|
||||
except: pass
|
||||
|
||||
def set_cell(self, x, y, value):
|
||||
self._data[y][x] = value
|
||||
|
||||
def display(self, return_input=True):
|
||||
for line in self._data:
|
||||
print("".join(line))
|
||||
|
||||
if return_input: return input(">")
|
||||
|
||||
def display_text(self, string):
|
||||
paragraphs = [i for i in text_formater(string) if i]
|
||||
nb_par = len(paragraphs)
|
||||
for index in range(nb_par):
|
||||
self.clear()
|
||||
print(paragraphs[index])
|
||||
if index + 1 == nb_par: return input(">")
|
||||
else: input()
|
||||
|
||||
def get_cell(self, x, y):
|
||||
return self._data[y][x]
|
||||
|
||||
def get_map_size(self):
|
||||
return self.map_width, self.map_height
|
||||
|
||||
# Asci (1.7.0)
|
||||
|
||||
class Asci:
|
||||
def __init__(self, maps, events_mapping, keys_mapping, screen_width=21, screen_height=6):
|
||||
def __init__(self, maps, events_mapping, keys_mapping, behaviors=None, screen_width=21, screen_height=6):
|
||||
# Load maps
|
||||
self.maps = [Map(*i) for i in maps]
|
||||
|
||||
# Custom functions
|
||||
self.legend = list(events_mapping.keys())
|
||||
self._game_events_mapping = [events_mapping[i] for i in self.legend]
|
||||
self._legend = list(events_mapping.keys())
|
||||
self._game_events_mapping = [events_mapping[i] for i in self._legend]
|
||||
self._game_keys_mapping = {key: keys_mapping[key] for key in keys_mapping if not key in (1, 2, 3, 5)}
|
||||
|
||||
# Custom entities behavior
|
||||
self._behaviors = {"stand by": stand_by, "follow": follow, "walk": walk}
|
||||
if behaviors:
|
||||
for i in behaviors: self._behaviors[i] = behaviors[i]
|
||||
|
||||
# Screen initialisation
|
||||
self.screen = Screen(screen_width, screen_height)
|
||||
|
@ -64,39 +21,36 @@ class Asci:
|
|||
self.visible_entities = []
|
||||
|
||||
def _looked_case(self, direction):
|
||||
# Left
|
||||
if direction == 1:
|
||||
return self.data[2] + 9, self.data[3] + 3
|
||||
if direction == 1: # Left
|
||||
return self.data[2] - 1, self.data[3]
|
||||
|
||||
# Right
|
||||
elif direction == 3:
|
||||
return self.data[2] + 11, self.data[3] + 3
|
||||
elif direction == 3: # Right
|
||||
return self.data[2] + 1, self.data[3]
|
||||
|
||||
# Up
|
||||
elif direction == 5:
|
||||
return self.data[2] + 10, self.data[3] + 2
|
||||
elif direction == 5: # Up
|
||||
return self.data[2], self.data[3] - 1
|
||||
|
||||
# Down
|
||||
elif direction == 2:
|
||||
return self.data[2] + 10, self.data[3] + 4
|
||||
elif direction == 2: # Down
|
||||
return self.data[2], self.data[3] + 1
|
||||
|
||||
return self.data[2] + 10, self.data[3] + 3
|
||||
return self.data[2], self.data[3]
|
||||
|
||||
def _cell_test(self, direction):
|
||||
if direction == 1:
|
||||
if self.data[2] + 9 < 0: return -1
|
||||
else: cell = self.screen.get_cell(9, 3)
|
||||
if self.data[2] - 1 < 0: return -1
|
||||
else: cell = self.screen.get_cell(self.data[2] - 10, self.data[3] - 3, self.data[2] - 1, self.data[3])
|
||||
if direction == 3:
|
||||
if self.data[2] + 11 >= self.map_width: return -1
|
||||
else: cell = self.screen.get_cell(11, 3)
|
||||
if self.data[2] + 1 >= self.map_width: return -1
|
||||
else: cell = self.screen.get_cell(self.data[2] - 10, self.data[3] - 3, self.data[2] + 1, self.data[3])
|
||||
if direction == 5:
|
||||
if self.data[3] + 2 < 0: return -1
|
||||
else: cell = self.screen.get_cell(10, 2)
|
||||
if self.data[3] - 1 < 0: return -1
|
||||
else: cell = self.screen.get_cell(self.data[2] - 10, self.data[3] - 3, self.data[2], self.data[3] - 1)
|
||||
if direction == 2:
|
||||
if self.data[3] + 4 >= self.map_height: return -1
|
||||
else: cell = self.screen.get_cell(10, 4)
|
||||
if self.data[3] + 1 >= self.map_height: return -1
|
||||
else: cell = self.screen.get_cell(self.data[2] - 10, self.data[3] - 3, self.data[2], self.data[3] + 1)
|
||||
|
||||
cell_patterns = self.legend
|
||||
print(f"'{cell}'")
|
||||
cell_patterns = self._legend
|
||||
for pattern_index in range(len(cell_patterns)):
|
||||
if cell in cell_patterns[pattern_index]: return pattern_index
|
||||
|
||||
|
@ -108,20 +62,16 @@ class Asci:
|
|||
cell_test = self._cell_test(key)
|
||||
|
||||
# Move
|
||||
if cell_test == len(self.legend) - 1:
|
||||
if cell_test == len(self._legend) - 1:
|
||||
if key == 1: self.data[2] -= 1
|
||||
if key == 3: self.data[2] += 1
|
||||
if key == 5: self.data[3] -= 1
|
||||
if key == 2: self.data[3] += 1
|
||||
|
||||
# Change map
|
||||
elif interaction and cell_test == len(self.legend) - 2: # or (self.data[1] and cell_test < 0):
|
||||
self.data[1], self.data[2], self.data[3] = self._get_map(key)
|
||||
self.current_map = self.maps[self.data[1]]
|
||||
self.screen.set_world(self.current_map.map_data)
|
||||
self._get_visible_entities()
|
||||
self.map_width, self.map_height = self.screen.get_map_size()
|
||||
|
||||
elif interaction and cell_test == len(self._legend) - 2:
|
||||
new_map, self.data[2], self.data[3] = self._get_map(key)
|
||||
self._change_map(new_map)
|
||||
|
||||
# Interaction
|
||||
elif interaction and cell_test >= 0: self._interaction(key, cell_test)
|
||||
|
@ -131,12 +81,39 @@ class Asci:
|
|||
self.screen.clear()
|
||||
self._game_keys_mapping[key](self.data, self.stat)
|
||||
|
||||
def _get_map(self, direction):
|
||||
current_coords = self._looked_case(direction)
|
||||
|
||||
for coords in self.current_map.coords:
|
||||
if coords[:2] == current_coords:
|
||||
return coords[2], coords[3], coords[4]
|
||||
|
||||
return self.data[1], self.data[2], self.data[3]
|
||||
|
||||
def _change_map(self, new_map):
|
||||
# Update entities
|
||||
if self.current_map:
|
||||
for i in range(len(self.current_map.entities)):
|
||||
entity = self.current_map.entities[i]
|
||||
if entity.behavior == "follow":
|
||||
self.maps[new_map].entities.append(entity)
|
||||
self.maps[self.data[1]].entities.pop(i)
|
||||
|
||||
# Update current map
|
||||
self.data[1] = new_map
|
||||
self.current_map = self.maps[self.data[1]]
|
||||
|
||||
# Update screen configuration
|
||||
self.screen.set_world(self.current_map.map_data)
|
||||
self.map_width, self.map_height = self.screen.get_map_size()
|
||||
self._get_visible_entities()
|
||||
|
||||
def _interaction(self, direction, cell_content):
|
||||
x, y = self._looked_case(direction)
|
||||
data_copy = [self.data[0], self.data[1], x, y, self.data[4]]
|
||||
|
||||
# Get the event
|
||||
event = self._game_events_mapping[cell_content](data_copy, self.stat, self._get_entity_id(x, y))
|
||||
event = self._game_events_mapping[cell_content](data_copy, self.stat, self.visible_entities, self._get_entity_id(x, y))
|
||||
if type(event) == tuple:
|
||||
quest, event = event
|
||||
else:
|
||||
|
@ -145,12 +122,10 @@ class Asci:
|
|||
# data modification
|
||||
self.data[0] = data_copy[0]
|
||||
if self.data[1] != data_copy[1]:
|
||||
self.data[1] = data_copy[1]
|
||||
self.screen.set_world(self.maps[self.data[1]].map_data)
|
||||
self.map_width, self.map_height = self.screen.get_map_size()
|
||||
self._change_map(data_copy[1])
|
||||
|
||||
if data_copy[2] != x: self.data[2] = data_copy[2] - 10
|
||||
if data_copy[3] != y: self.data[3] = data_copy[3] - 3
|
||||
if data_copy[2] != x: self.data[2] = data_copy[2]
|
||||
if data_copy[3] != y: self.data[3] = data_copy[3]
|
||||
|
||||
if not event: return
|
||||
event = read_event(self.data, event, quest)
|
||||
|
@ -167,28 +142,25 @@ class Asci:
|
|||
self.data[0][quest] += answer_selected
|
||||
self._interaction(direction, cell_content)
|
||||
|
||||
def _get_map(self, direction):
|
||||
current_coords = self._looked_case(direction)
|
||||
|
||||
for coords in self.current_map.coords:
|
||||
if coords[:2] == current_coords:
|
||||
return coords[2], coords[3] - 10, coords[4] - 3
|
||||
|
||||
return self.data[1], self.data[2], self.data[3]
|
||||
|
||||
# Entites gestion
|
||||
# Entities gestion
|
||||
def _get_visible_entities(self):
|
||||
self.visible_entities = []
|
||||
self.visible_entities = {}
|
||||
for entity in self.current_map.entities:
|
||||
symbol, x, y = self.current_map.entities[entity]
|
||||
if (0 <= x - self.data[2] < self.screen.screen_width) and (0 <= y - self.data[3] < self.screen.screen_height):
|
||||
self.visible_entities.append((symbol, x, y, entity))
|
||||
if (0 <= entity.pos_x - self.data[2] + 10 < self.screen.screen_width) and (0 <= entity.pos_y - self.data[3] + 3 < self.screen.screen_height):
|
||||
self.visible_entities[entity.entity_id] = entity
|
||||
|
||||
def _get_entity_id(self, x, y):
|
||||
for _, entity_x, entity_y, entity_id in self.visible_entities:
|
||||
if entity_x == x and entity_y == y:
|
||||
for entity_id in self.visible_entities:
|
||||
entity = self.visible_entities[entity_id]
|
||||
if entity.pos_x == x and entity.pos_y == y:
|
||||
return entity_id
|
||||
|
||||
def _run_entities_behaviors(self):
|
||||
for entity in self.current_map.entities:
|
||||
data_copy = get_data_copy(self.data)
|
||||
self._behaviors[entity.behavior](entity, data_copy, self.stat, self.screen, self.walkable)
|
||||
self._get_visible_entities()
|
||||
|
||||
# Mainloop
|
||||
def mainloop(self, end_game, stat=None, data=None, routine=None, player="@", door="^", walkable=" ", exit_key=9, multi_move="."):
|
||||
if exit_key in self._game_keys_mapping:
|
||||
|
@ -199,27 +171,25 @@ class Asci:
|
|||
else: self.stat = stat
|
||||
|
||||
if not data: self.data = [{"main": 0}, 0, 0, 0, 0]
|
||||
else: self.data = [data[0], data[1], data[2] - 10, data[3] - 3, 0]
|
||||
else: self.data = [data[0], data[1], data[2], data[3], 0]
|
||||
|
||||
self.legend.append(door)
|
||||
self.legend.append(walkable)
|
||||
|
||||
# Screen and map configuration
|
||||
self.current_map = self.maps[self.data[1]]
|
||||
self.screen.set_world(self.current_map.map_data)
|
||||
self.map_width, self.map_height = self.screen.get_map_size()
|
||||
# Configuration
|
||||
self.walkable = walkable
|
||||
self._legend.append(door)
|
||||
self._legend.append(walkable)
|
||||
self._change_map(data[1])
|
||||
|
||||
key = 0
|
||||
|
||||
while key != exit_key and self.stat[0] > 0 and self.data[0]["main"] < end_game:
|
||||
# Update the map
|
||||
self.screen.set_data(self.data[2], self.data[3])
|
||||
self.screen.set_screen(self.data[2], self.data[3])
|
||||
|
||||
# Display the player and entites over it
|
||||
self.screen.set_cell(10, 3, player)
|
||||
self._get_visible_entities()
|
||||
for symbol, x, y, _ in self.visible_entities:
|
||||
self.screen.set_cell(x - self.data[2], y - self.data[3], symbol)
|
||||
# Compute the player's and entities' positions
|
||||
self.screen.set_cell(self.data[2] - 10, self.data[3] - 3, self.data[2], self.data[3], player)
|
||||
self._run_entities_behaviors()
|
||||
for entity in self.visible_entities.values():
|
||||
self.screen.set_cell(self.data[2] - 10, self.data[3] - 3, entity.pos_x, entity.pos_y, entity.symbol)
|
||||
|
||||
# Display map and get the key
|
||||
key = convert(self.screen.display())
|
||||
|
@ -236,15 +206,64 @@ class Asci:
|
|||
|
||||
# Launching the game routine
|
||||
if routine:
|
||||
data_copy = [self.data[0], self.data[1], self.data[2] + 10, self.data[3] + 3, self.data[4]]
|
||||
data_copy = get_data_copy(self.data)
|
||||
routine(data_copy, self.stat)
|
||||
|
||||
if self.stat[0] <= 0: self.stat[0] = 100
|
||||
self.data[2] += 10
|
||||
self.data[3] += 3
|
||||
return self.stat, self.data[:-1]
|
||||
|
||||
|
||||
# Classes used by Asci
|
||||
class Screen:
|
||||
def __init__(self, screen_width=21, screen_height=6):
|
||||
# Screen configuration
|
||||
self.screen_width = screen_width
|
||||
self.screen_height = screen_height
|
||||
self._on_screen = [[" " for _ in range(screen_width)] for _ in range(screen_height)]
|
||||
|
||||
def get_map_size(self):
|
||||
return self.map_width, self.map_height
|
||||
|
||||
def set_world(self, world):
|
||||
self._world = [[char for char in line] for line in world.split("\n")[1:]]
|
||||
self.map_width = max([len(line) for line in self._world])
|
||||
self.map_height = len(self._world)
|
||||
|
||||
def set_screen(self, x, y):
|
||||
x -= 10 ; y -= 3
|
||||
for x_map in range(x, x + self.screen_width):
|
||||
for y_map in range(y, y + self.screen_height):
|
||||
self._on_screen[y_map - y][x_map - x] = " "
|
||||
if 0 <= x_map < self.map_width and 0 <= y_map < self.map_height:
|
||||
try: self._on_screen[y_map - y][x_map - x] = self._world[y_map][x_map]
|
||||
except: pass
|
||||
|
||||
def display(self, return_input=True):
|
||||
for line in self._on_screen:
|
||||
print("".join(line))
|
||||
|
||||
if return_input: return input(">")
|
||||
|
||||
def clear(self):
|
||||
print("\n" * self.screen_height)
|
||||
|
||||
def display_text(self, string):
|
||||
paragraphs = [i for i in text_formater(string) if i]
|
||||
nb_par = len(paragraphs)
|
||||
for index in range(nb_par):
|
||||
self.clear()
|
||||
print(paragraphs[index])
|
||||
if index + 1 == nb_par: return input(">")
|
||||
else: input()
|
||||
|
||||
def set_cell(self, x_offset, y_offset, x, y, value):
|
||||
self._on_screen[y - y_offset][x - x_offset] = value
|
||||
|
||||
def get_cell(self, x_offset, y_offset, x, y):
|
||||
if 0 <= x - x_offset < self.screen_width and 0 <= y - y_offset <= self.screen_height:
|
||||
return self._on_screen[y - y_offset][x - x_offset]
|
||||
else: return " "
|
||||
|
||||
class Event:
|
||||
def __init__(self, xp, text, answer=0, *stat):
|
||||
self.xp = xp
|
||||
|
@ -256,10 +275,24 @@ class Event:
|
|||
class Map:
|
||||
def __init__(self, map_data, entities, *coords):
|
||||
self.map_data = map_data
|
||||
self.entities = entities
|
||||
if entities: self.entities = [Entity(*i) for i in entities]
|
||||
else: self.entities = []
|
||||
self.coords = coords
|
||||
|
||||
class Entity:
|
||||
def __init__(self, entity_id, symbol, x, y, behavior, *args):
|
||||
self.entity_id = entity_id
|
||||
self.symbol = symbol
|
||||
self.pos_x = x
|
||||
self.pos_y = y
|
||||
self.behavior = behavior
|
||||
self.args = list(args)
|
||||
|
||||
def change_behavior(self, new_behavior):
|
||||
self.behavior = new_behavior
|
||||
|
||||
|
||||
# Functions used by Asci
|
||||
def convert(string, force_int=False):
|
||||
try: return int(string)
|
||||
except:
|
||||
|
@ -305,6 +338,11 @@ def read_event(data, event, quest):
|
|||
return Event(*event)
|
||||
|
||||
|
||||
def get_data_copy(data):
|
||||
return [data[0], data[1], data[2], data[3], data[4]]
|
||||
|
||||
|
||||
# Extra functions
|
||||
def print_text(text, min_value=0, max_value=0, default_value=0):
|
||||
paragraphs = [i for i in text_formater(text) if i]
|
||||
nb = len(paragraphs)
|
||||
|
@ -321,3 +359,21 @@ def print_text(text, min_value=0, max_value=0, default_value=0):
|
|||
return result
|
||||
|
||||
else: input()
|
||||
|
||||
|
||||
def stand_by(entity, data, stat, screen, walkable):
|
||||
pass
|
||||
|
||||
def follow(entity, data, stat, screen, walkable):
|
||||
if data[4] == 1 and screen.get_cell(data[2] - 10, data[3] - 3, data[2] + 1, data[3]) in walkable: entity.pos_x, entity.pos_y = data[2] + 1, data[3]
|
||||
elif data[4] == 2 and screen.get_cell(data[2] - 10, data[3] - 3, data[2], data[3] - 1) in walkable: entity.pos_x, entity.pos_y = data[2], data[3] - 1
|
||||
elif data[4] == 3 and screen.get_cell(data[2] - 10, data[3] - 3, data[2] - 1, data[3]) in walkable: entity.pos_x, entity.pos_y = data[2] - 1, data[3]
|
||||
elif data[4] == 5 and screen.get_cell(data[2] - 10, data[3] - 3, data[2], data[3] + 1) in walkable: entity.pos_x, entity.pos_y = data[2], data[3] + 1
|
||||
|
||||
def walk(entity, data, stat, screen, walkable):
|
||||
frame = (entity.args[0] + 1) % len(entity.args[1])
|
||||
new_x, new_y = entity.args[1][frame]
|
||||
print(new_x, new_y)
|
||||
if screen.get_cell(data[2] - 10, data[3] - 3, new_x, new_y) in walkable:
|
||||
entity.pos_x, entity.pos_y = new_x, new_y
|
||||
entity.args[0] = frame
|
||||
|
|
Loading…
Reference in New Issue