Compare commits

...

17 Commits

3 changed files with 214 additions and 64 deletions

View File

@ -18,7 +18,7 @@ Tout le projet est soumis à la licence GNU General Public Licence v3.0.
### Utilisation
Tout est détaillé dans [le wiki du projet](https://gitea.planet-casio.com/Shadow15510/Asci/wiki/Cr%C3%A9ation-d%27un-jeu-de-r%C3%B4le-avec-Asci).
Tout est détaillé dans [le wiki du projet](https://gitea.planet-casio.com/Shadow15510/Asci/wiki/Home).
### Exemples

207
asci.py
View File

@ -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))]

69
samples/sample_5.py Normal file
View File

@ -0,0 +1,69 @@
from asci import *
exterieur = (r"""
_ ###
/o\__ #####
|_ <>\ ###
|^|__| /_\
|==|==|==|==|==|==|==|""",
# Points de passage
(1, 3, 1, 3, 5))
interieur = (r"""
+----------+
| |
| |
| |
| |
+-|^|------+""",
# Points de passage
(3, 5, 0, 1, 3))
carte_monde = (exterieur, interieur)
entites = (
["pnj", "*", 0, 2, 5, "stand by"],
)
def pnj(data, stat, entites, identifiant):
xp = data[0]["main"]
if identifiant == "pnj":
if xp == 1:
entites["pnj"].change_behavior("follow by player")
entites["pnj"].args = [0, ((3, 5), (16, 5), (16, 2))]
return {
"base": [0, "Moui ?"],
0: [0, "Tu me suis ?\n1.Oui\n2.Non", 2],
1: [2, "Parfait ! C'est parti !"],
2: [-2, "Rhoo alleezz..."],
3: [1, "Hehe"]
}
return [0, "Hmm ?"]
def affichage_stat(data, stat):
pv, argent = stat
print("Statistiques")
print("PV : {}".format(pv))
print("Argent : {}".format(argent))
input()
evenements = {"*": pnj}
touche = {6: affichage_stat}
def mon_jeu():
rpg_python = Asci(carte_monde, entites, evenements, touche)
rpg_python.mainloop(10, stat=[100, 5], data=[{"main": 0}, 0, 10, 3])