|
|
|
@ -1,7 +1,12 @@
|
|
|
|
|
# Asci (1.8.0)
|
|
|
|
|
# Asci (1.9.3)
|
|
|
|
|
from math import floor, ceil
|
|
|
|
|
|
|
|
|
|
SCREEN_WIDTH = 21
|
|
|
|
|
SCREEN_HEIGHT = 7
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Asci:
|
|
|
|
|
def __init__(self, maps, entities, events_mapping, keys_mapping, behaviors=None, screen_width=21, screen_height=6):
|
|
|
|
|
def __init__(self, maps, entities, events_mapping, keys_mapping, behaviors=None):
|
|
|
|
|
# Load maps and entities
|
|
|
|
|
self.maps = [Map(*i) for i in maps]
|
|
|
|
|
self.entities = {}
|
|
|
|
@ -20,12 +25,12 @@ class Asci:
|
|
|
|
|
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 = {"permanent": permanent, "stand by": stand_by, "follow": follow, "walk": walk}
|
|
|
|
|
self._behaviors = {"permanent": permanent, "stand by": stand_by, "follow": follow, "walk between": walk_between, "walk to": walk_to, "follow by player": follow_by_player}
|
|
|
|
|
if behaviors:
|
|
|
|
|
for i in behaviors: self._behaviors[i] = behaviors[i]
|
|
|
|
|
|
|
|
|
|
# Screen initialisation
|
|
|
|
|
self.screen = Screen(screen_width, screen_height)
|
|
|
|
|
self.screen = Screen()
|
|
|
|
|
self.current_map = None
|
|
|
|
|
|
|
|
|
|
def _looked_case(self, direction):
|
|
|
|
@ -155,7 +160,7 @@ class Asci:
|
|
|
|
|
return entity.entity_id
|
|
|
|
|
|
|
|
|
|
# Mainloop
|
|
|
|
|
def mainloop(self, end_game, stat=None, data=None, routine=None, player="@", door="^", walkable=" ", exit_key=9, multi_move="."):
|
|
|
|
|
def mainloop(self, end_game, stat=None, data=None, routine=None, low_bar=None, player="@", door="^", walkable=" ", exit_key=9, multi_move="."):
|
|
|
|
|
if exit_key in self._game_keys_mapping:
|
|
|
|
|
raise ValueError("'{}' is already assigned to a function.".format(exit_key))
|
|
|
|
|
|
|
|
|
@ -182,13 +187,15 @@ class Asci:
|
|
|
|
|
data_copy = self.data[:]
|
|
|
|
|
for entity in self.current_map.entities.values():
|
|
|
|
|
self._behaviors[entity.behavior](entity, data_copy, self.stat, self.screen, walkable)
|
|
|
|
|
if entity.map_id == self.data[1] and (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):
|
|
|
|
|
if entity.map_id == self.data[1] and (0 <= entity.pos_x - self.data[2] + self.screen.pos_player[0] < SCREEN_WIDTH) and (0 <= entity.pos_y - self.data[3] + self.screen.pos_player[1] < SCREEN_HEIGHT):
|
|
|
|
|
self.screen.set_cell(entity.pos_x, entity.pos_y, entity.symbol)
|
|
|
|
|
|
|
|
|
|
self.screen.set_cell(self.data[2], self.data[3], player)
|
|
|
|
|
|
|
|
|
|
# Display map, get the key and update key buffer
|
|
|
|
|
key = convert(self.screen.display())
|
|
|
|
|
# Display map, low bar, get the key and update key buffer
|
|
|
|
|
if low_bar: bar = low_bar(self.data[:], self.stat[:])
|
|
|
|
|
else: bar = None
|
|
|
|
|
key = convert(self.screen.display(low_bar=bar))
|
|
|
|
|
if not key: key = self.data[4]
|
|
|
|
|
else: self.data[4] = key
|
|
|
|
|
|
|
|
|
@ -215,11 +222,10 @@ class Asci:
|
|
|
|
|
|
|
|
|
|
# Classes used by Asci
|
|
|
|
|
class Screen:
|
|
|
|
|
def __init__(self, screen_width=21, screen_height=6):
|
|
|
|
|
def __init__(self):
|
|
|
|
|
# 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)]
|
|
|
|
|
self.pos_player = (SCREEN_WIDTH // 2, SCREEN_HEIGHT // 2)
|
|
|
|
|
self._on_screen = [[" " for _ in range(SCREEN_WIDTH)] for _ in range(SCREEN_HEIGHT)]
|
|
|
|
|
self._asci_data = []
|
|
|
|
|
|
|
|
|
|
def load_data(self, data):
|
|
|
|
@ -234,23 +240,27 @@ class Screen:
|
|
|
|
|
self.map_height = len(self._world)
|
|
|
|
|
|
|
|
|
|
def set_screen(self):
|
|
|
|
|
x = self._asci_data[2] - 10 ; y = self._asci_data[3] - 3
|
|
|
|
|
for x_map in range(x, x + self.screen_width):
|
|
|
|
|
for y_map in range(y, y + self.screen_height):
|
|
|
|
|
x = self._asci_data[2] - self.pos_player[0] ; y = self._asci_data[3] - self.pos_player[1]
|
|
|
|
|
for x_map in range(x, x + SCREEN_WIDTH):
|
|
|
|
|
for y_map in range(y, y + 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 display(self, return_input=True, low_bar=None):
|
|
|
|
|
for line_no in range(len(self._on_screen)):
|
|
|
|
|
line = "".join(self._on_screen[line_no])
|
|
|
|
|
if line_no + 1 == SCREEN_HEIGHT and return_input:
|
|
|
|
|
if not low_bar: line = line[:-6] + ">"
|
|
|
|
|
else: line = low_bar + ">"
|
|
|
|
|
print(line, end="")
|
|
|
|
|
return input()
|
|
|
|
|
else:
|
|
|
|
|
print(line)
|
|
|
|
|
|
|
|
|
|
def clear(self):
|
|
|
|
|
print("\n" * self.screen_height)
|
|
|
|
|
print("\n" * SCREEN_HEIGHT)
|
|
|
|
|
|
|
|
|
|
def display_text(self, string):
|
|
|
|
|
paragraphs = [i for i in text_formater(string) if i]
|
|
|
|
@ -262,18 +272,19 @@ class Screen:
|
|
|
|
|
else: input()
|
|
|
|
|
|
|
|
|
|
def set_cell(self, x, y, value):
|
|
|
|
|
x = x - (self._asci_data[2] - 10)
|
|
|
|
|
y = y - (self._asci_data[3] - 3)
|
|
|
|
|
if 0 <= x < self.screen_width and 0 <= y < self.screen_height:
|
|
|
|
|
x = x - (self._asci_data[2] - self.pos_player[0])
|
|
|
|
|
y = y - (self._asci_data[3] - self.pos_player[1])
|
|
|
|
|
if 0 <= x < SCREEN_WIDTH and 0 <= y < SCREEN_HEIGHT:
|
|
|
|
|
self._on_screen[y][x] = value
|
|
|
|
|
|
|
|
|
|
def get_cell(self, x, y):
|
|
|
|
|
x = x - (self._asci_data[2] - 10)
|
|
|
|
|
y = y - (self._asci_data[3] - 3)
|
|
|
|
|
if 0 <= x < self.screen_width and 0 <= y < self.screen_height:
|
|
|
|
|
x = x - (self._asci_data[2] - self.pos_player[0])
|
|
|
|
|
y = y - (self._asci_data[3] - self.pos_player[1])
|
|
|
|
|
if 0 <= x < SCREEN_WIDTH and 0 <= y < SCREEN_HEIGHT:
|
|
|
|
|
return self._on_screen[y][x]
|
|
|
|
|
else: return " "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Event:
|
|
|
|
|
def __init__(self, xp, text, answer=0, *stat):
|
|
|
|
|
self.xp = xp
|
|
|
|
@ -288,6 +299,7 @@ class Map:
|
|
|
|
|
self.coords = coords
|
|
|
|
|
self.entities = {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Entity:
|
|
|
|
|
def __init__(self, entity_id, symbol, map_id, x, y, behavior, *args):
|
|
|
|
|
self.entity_id = entity_id
|
|
|
|
@ -298,11 +310,13 @@ class Entity:
|
|
|
|
|
self.behavior = behavior
|
|
|
|
|
self.args = list(args)
|
|
|
|
|
|
|
|
|
|
def change_behavior(self, new_behavior):
|
|
|
|
|
if self.behavior != "permanent": self.behavior = new_behavior
|
|
|
|
|
def change_behavior(self, new_behavior, *args):
|
|
|
|
|
if self.behavior != "permanent":
|
|
|
|
|
self.behavior = new_behavior
|
|
|
|
|
self.args = list(args)
|
|
|
|
|
|
|
|
|
|
def teleport(self, map_id, x, y):
|
|
|
|
|
self.map_id, self.pos_x, self.pos_y = map_id, x, y
|
|
|
|
|
if self.behavior != "permanent": self.map_id, self.pos_x, self.pos_y = map_id, x, y
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Functions used by Asci
|
|
|
|
@ -313,34 +327,35 @@ def convert(string, force_int=False):
|
|
|
|
|
else: return string
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def text_formater(string, screen_width=21, screen_height=6):
|
|
|
|
|
def text_formater(string):
|
|
|
|
|
screen_displayable_height = SCREEN_HEIGHT - 1
|
|
|
|
|
|
|
|
|
|
def line_formater(string, screen_width):
|
|
|
|
|
def line_formater(string):
|
|
|
|
|
string_result = ""
|
|
|
|
|
while len(string) > screen_width:
|
|
|
|
|
stop_index = screen_width
|
|
|
|
|
while len(string) > SCREEN_WIDTH:
|
|
|
|
|
stop_index = SCREEN_WIDTH
|
|
|
|
|
while stop_index > 0 and not string[stop_index].isspace(): stop_index -= 1
|
|
|
|
|
if not stop_index: stop_index = screen_width
|
|
|
|
|
if not stop_index: stop_index = SCREEN_WIDTH
|
|
|
|
|
|
|
|
|
|
string_result += string[:stop_index].strip() + "\n"
|
|
|
|
|
string = string[stop_index:].strip()
|
|
|
|
|
|
|
|
|
|
return string_result + string
|
|
|
|
|
|
|
|
|
|
def paragraph_formater(lines, screen_height):
|
|
|
|
|
def paragraph_formater(lines):
|
|
|
|
|
paragraphs = ""
|
|
|
|
|
while len(lines) >= screen_height:
|
|
|
|
|
paragraphs += "\n".join(lines[:screen_height]) + "\n\n"
|
|
|
|
|
lines = lines[screen_height:]
|
|
|
|
|
while len(lines) >= screen_displayable_height:
|
|
|
|
|
paragraphs += "\n".join(lines[:screen_displayable_height]) + "\n\n"
|
|
|
|
|
lines = lines[screen_displayable_height:]
|
|
|
|
|
|
|
|
|
|
return paragraphs + "\n".join(lines)
|
|
|
|
|
|
|
|
|
|
lines = []
|
|
|
|
|
for line in string.split("\n"):
|
|
|
|
|
for formated_line in line_formater(line, screen_width).split("\n"):
|
|
|
|
|
for formated_line in line_formater(line).split("\n"):
|
|
|
|
|
lines.append(formated_line)
|
|
|
|
|
|
|
|
|
|
return paragraph_formater(lines, screen_height).split("\n\n")
|
|
|
|
|
return paragraph_formater(lines).split("\n\n")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def read_event(data, event, quest):
|
|
|
|
@ -377,6 +392,87 @@ def get_multi_move(key):
|
|
|
|
|
return [(convert(k), 1) for k in key]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Motions functions
|
|
|
|
|
def stand_by(entity, data, stat, screen, walkable):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def permanent(entity, data, stat, screen, walkable):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def follow(entity, data, stat, screen, walkable):
|
|
|
|
|
if entity.pos_x == entity.pos_y == -1:
|
|
|
|
|
entity.pos_x, entity.pos_y = data[2], data[3]
|
|
|
|
|
|
|
|
|
|
elif data[4] in (1, 2, 3, 5):
|
|
|
|
|
direction = (data[4] - 1) if data[4] != 5 else 3
|
|
|
|
|
|
|
|
|
|
if entity.args: walkable += entity.args[0]
|
|
|
|
|
|
|
|
|
|
cases = [(data[2] + 1, data[3]), (data[2], data[3] - 1), (data[2] - 1, data[3]), (data[2], data[3] + 1)]
|
|
|
|
|
pos = cases[direction]
|
|
|
|
|
|
|
|
|
|
if not (0 <= pos[0] < screen.map_width and 0 <= pos[1] < screen.map_height) or (not screen.get_cell(pos[0], pos[1]) in walkable):
|
|
|
|
|
find = False
|
|
|
|
|
cases.remove(cases[(direction + 2) % 4])
|
|
|
|
|
for pos in cases:
|
|
|
|
|
if (0 <= pos[0] < screen.map_width and 0 <= pos[1] < screen.map_height) and (screen.get_cell(pos[0], pos[1]) in walkable):
|
|
|
|
|
find = True
|
|
|
|
|
entity.pos_x, entity.pos_y = pos
|
|
|
|
|
break
|
|
|
|
|
if not find:
|
|
|
|
|
entity.pos_x, entity.pos_y = data[2], data[3]
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
entity.pos_x, entity.pos_y = pos
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def walk_between(entity, data, stat, screen, walkable):
|
|
|
|
|
frame = (entity.args[0] + 1) % len(entity.args[1])
|
|
|
|
|
new_x, new_y = _walk_engine(entity, frame)
|
|
|
|
|
if screen.get_cell(new_x, new_y) in walkable:
|
|
|
|
|
entity.pos_x, entity.pos_y = new_x, new_y
|
|
|
|
|
if (entity.pos_x, entity.pos_y) == entity.args[1][frame]: entity.args[0] = frame
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def walk_to(entity, data, stat, screen, walkable):
|
|
|
|
|
frame = entity.args[0]
|
|
|
|
|
print(frame, len(entity.args[1]), entity.args)
|
|
|
|
|
if len(entity.args[1]) == frame:
|
|
|
|
|
entity.behavior = "stand by"
|
|
|
|
|
entity.args = []
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
new_x, new_y = _walk_engine(entity, frame)
|
|
|
|
|
|
|
|
|
|
if screen.get_cell(new_x, new_y) in walkable:
|
|
|
|
|
entity.pos_x, entity.pos_y = new_x, new_y
|
|
|
|
|
if (entity.pos_x, entity.pos_y) == entity.args[1][frame]: entity.args[0] += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def follow_by_player(entity, data, stat, screen, walkable):
|
|
|
|
|
frame = entity.args[0]
|
|
|
|
|
if len(entity.args[1]) == frame:
|
|
|
|
|
entity.behavior = "stand by"
|
|
|
|
|
entity.args = []
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
new_x, new_y = _walk_engine(entity, frame)
|
|
|
|
|
|
|
|
|
|
if abs(data[2] - new_x) < 5 and abs(data[3] - new_y) < 3 and screen.get_cell(new_x, new_y) in walkable:
|
|
|
|
|
entity.pos_x, entity.pos_y = new_x, new_y
|
|
|
|
|
if (new_x, new_y) == entity.args[1][frame]: entity.args[0] += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _walk_engine(entity, frame):
|
|
|
|
|
delta_x, delta_y = list(map(lambda x,y: y - x, (entity.pos_x, entity.pos_y), entity.args[1][frame]))
|
|
|
|
|
new_x = entity.pos_x
|
|
|
|
|
new_y = entity.pos_y
|
|
|
|
|
if delta_x: new_x += abs(delta_x) // delta_x
|
|
|
|
|
if delta_y: new_y += abs(delta_y) // delta_y
|
|
|
|
|
return new_x, new_y
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Extra functions
|
|
|
|
|
def print_text(text, min_value=0, max_value=0, default_value=0):
|
|
|
|
@ -397,27 +493,12 @@ def print_text(text, min_value=0, max_value=0, default_value=0):
|
|
|
|
|
else: input()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def stand_by(entity, data, stat, screen, walkable):
|
|
|
|
|
pass
|
|
|
|
|
def center(string, total_length, symbol):
|
|
|
|
|
left = floor((total_length - len(string)) / 2)
|
|
|
|
|
right = ceil((total_length - len(string)) / 2)
|
|
|
|
|
|
|
|
|
|
def permanent(entity, data, stat, screen, walkable):
|
|
|
|
|
pass
|
|
|
|
|
return left * symbol + string + right * symbol
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def follow(entity, data, stat, screen, walkable):
|
|
|
|
|
if entity.pos_x == entity.pos_y == -1:
|
|
|
|
|
entity.pos_x, entity.pos_y = data[2], data[3]
|
|
|
|
|
|
|
|
|
|
elif data[4] in (1, 2, 3, 5):
|
|
|
|
|
if entity.args: walkable += entity.args[0]
|
|
|
|
|
cases = ((data[2] + 1, data[3]), (data[2], data[3] - 1), (data[2] - 1, data[3]), 0, (data[2], data[3] + 1))[data[4] - 1]
|
|
|
|
|
if not (0 <= cases[0] < screen.map_width and 0 <= cases[1] < screen.map_height): entity.pos_x, entity.pos_y = data[2], data[3]
|
|
|
|
|
elif screen.get_cell(cases[0], cases[1]) in walkable: entity.pos_x, entity.pos_y = cases
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def walk(entity, data, stat, screen, walkable):
|
|
|
|
|
frame = (entity.args[0] + 1) % len(entity.args[1])
|
|
|
|
|
new_x, new_y = entity.args[1][frame]
|
|
|
|
|
if screen.get_cell(new_x, new_y) in walkable:
|
|
|
|
|
entity.pos_x, entity.pos_y = new_x, new_y
|
|
|
|
|
entity.args[0] = frame
|
|
|
|
|
def enumerate(data):
|
|
|
|
|
return [(i, data[i]) for i in range(len(data))]
|