diff --git a/LIBSCII.py b/LIBSCII.py new file mode 100644 index 0000000..20d38eb --- /dev/null +++ b/LIBSCII.py @@ -0,0 +1,276 @@ +""" + SCII - make ASCII RPG with multiple layered maps. + Copyright (C) 2023 Mibi88 + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + You should have received a copy of the GNU General Public License + along with this program. If not, see https://www.gnu.org/licenses/. +""" + +# e-mail : mbcontact50@gmail.com + +#--- scii - make ASCII RPG with multiple layered maps --- + +# Map drawing modes + +STICKY = 0 +CENTERED = 1 +BLOCKS = 2 +STICKYBLOCKS = 3 + +class Scii: + def __init__(self, world, player, on_npc_collision, scii_keys = None, get_input_text = None, no_collision = None, collision_checker = None, + message_history_max = None, screen_width = 21, screen_height = 7): + self.world = world + self.player = player + self.map_num = world["map_num"] + self.screen_width = screen_width + self.screen_height = screen_height-1 + self.screen_x_middle = self.screen_width//2 + self.screen_y_middle = self.screen_height//2 + self.screen_size = self.screen_width*self.screen_height + self._vram = [' ' for i in range(self.screen_size)] + self.on_char_handlers = {} + if scii_keys: self.scii_keys = scii_keys + else: self.scii_keys = {"left": '4', "right": '6', "up": '8', "down": '2', "quit": 'q', "old_messages": '0'} + if get_input_text: self.get_input_text = get_input_text + else: self.get_input_text = self._default_intext + if collision_checker: self.collision_checker = collision_checker + else: self.collision_checker = self._default_collision_check + self.last_key = "" + if not message_history_max: self.message_history_max = 20 + else: self.message_history_max = message_history_max + self.old_messages = [] + self.on_npc_collision = on_npc_collision + if no_collision: self.no_collision = no_collision + else: self.no_collision = " ^" + self.collision_npc = None + def _default_collision_check(self, world, player, x, y): + collision = 0 + for i in self.world["maps"][self.map_num]["layers"]: + data = i["data"].split("\n") + while("" in data): data.remove("") + try: + c = data[y][x] + if c != i["transp_char"] and not (c in self.no_collision): + return 1 + for npc in world["npc"]: + if npc["map"] == self.map_num and npc["x"] == x and npc["y"] == y and npc["collision_check"]: + self.collision_npc = npc + return 2 + except: + if x >= 0 and x < self.get_map_width() and y >= 0 and y < self.get_map_height(): return 0 + else: return 1 + return 0 + def _default_intext(self): + return "> " + def _input(self): + i = input(self.get_input_text()) + if len(i) > 0: + self.last_key = i[0] + return self.last_key + return self.last_key + def _mainloop(self): + key = "" + while key != self.scii_keys["quit"]: + self.draw_map(self.world["dmode"], self.player["isvisible"]) + key = self._input() + nx = self.player["x"] + ny = self.player["y"] + if key == self.scii_keys["left"]: nx = self.player["x"]-1 + elif key == self.scii_keys["right"]: nx = self.player["x"]+1 + elif key == self.scii_keys["up"]: ny = self.player["y"]-1 + elif key == self.scii_keys["down"]: ny = self.player["y"]+1 + collision = self.collision_checker(self.world, self.player, nx, ny) + if self.player["collision_check"] and not collision: + self.player["x"] = nx + self.player["y"] = ny + elif collision == 2 and self.collision_npc != None: + self.on_npc_collision(self, self.collision_npc) + for jump in self.world["maps"][self.map_num]["jumps"]: + if jump["isactive"] and self.player["x"] == jump["x"] and self.player["y"] == jump["y"]: + self.player["x"] = jump["to_x"] + self.player["y"] = jump["to_y"] + self.map_num = jump["to_map"] + self.world["map_num"] = self.map_num + if key == self.scii_keys["old_messages"]: self.show_old_messages() + def mainloop(self): + try: + self._mainloop() + except Exception as e: print("Ow there was an error in SCII : {}".format(e)) + def _setc(self, x, y, c): + if len(c) == 1 and x>=0 and x=0 and ymax_len: max_len = len(line) + return max_len + def get_map_height(self, map_data): + max_len = 0 + for i in map_data["layers"]: + data = i["data"].split("\n") + while("" in data): data.remove("") + if len(data) > max_len: + max_len = len(data) + return max_len + def draw_map(self, mode, show_player): + map_data = self.world["maps"][self.map_num] + osx = self.player["x"] + osy = self.player["y"] + sx = osx + sy = osy + if mode == STICKY: + sx -= self.screen_x_middle + sy -= self.screen_y_middle + # Fix x + if sx < 0: + sx = 0 + elif sx + self.screen_width > self.get_map_width(map_data): + sx = self.get_map_width(map_data) - self.screen_width + px = osx - sx + # Fix y + if sy < 0: + sy = 0 + elif sy + self.screen_height > self.get_map_height(map_data): + sy = self.get_map_height(map_data) - self.screen_height + py = osy - sy + elif mode == CENTERED: + px = self.screen_x_middle + py = self.screen_y_middle + sx -= self.screen_x_middle + sy -= self.screen_y_middle + elif mode == BLOCKS: + sx = sx // self.screen_width * self.screen_width + sy = sy // self.screen_height * self.screen_height + px = osx-sx + py = osy-sy + elif mode == STICKYBLOCKS: + sx = sx // self.screen_width * self.screen_width + sy = sy // self.screen_height * self.screen_height + if sx > self.get_map_width(map_data) - self.screen_width: + sx = self.get_map_width(map_data) - self.screen_width + if sy > self.get_map_height(map_data) - self.screen_height: + sy = self.get_map_height(map_data) - self.screen_height + px = osx-sx + py = osy-sy + self._cvram() + layerc = 0 + for i in map_data["layers"]: + data = i["data"].split("\n") + while("" in data): data.remove("") + map_width = self.get_map_width(map_data) + map_height = self.get_map_height(map_data) + for y in range(self.screen_height): + for x in range(self.screen_width): + try: + if sx+x >= 0 and sx+x < map_width and sy+y >= 0 and sy+y < map_height: + c = data[sy+y][sx+x] + if c != i["transp_char"]: + self._setc(x, y, c) + except: pass + for npc in self.world["npc"]: + if npc["layer"] == layerc and npc["map"] == self.map_num and npc["isvisible"]: + self._setc(px + (npc["x"] - osx), py + (npc["y"] - osy), npc["char"]) + if layerc == self.player["layer"]: self._setc(px, py, self.player["playerc"]) + layerc += 1 + self._dvram() + def show_text(self, text): + dwidth = self.screen_width - 2 + self._cvram() + dtext = "" + sz = 0 + y = 0 + for i in text.split(" "): + if len(i) > dwidth: + count = sz + for c in i: + dtext += c + count += 1 + if count >= dwidth - 1: + dtext += "-\n" + count = 0 + y += 1 + dtext += ' ' + sz = count + 1 + elif sz + len(i) + 1 < dwidth: + dtext += i + ' ' + sz += len(i) + 1 + y += i.count('\n') + else: + dtext += '\n' + i + ' ' + sz = len(i) + 1 + y += i.count('\n') + 1 + x = 0 + y = 0 + msg = "" + for i in dtext: + if i == '\n': + y += 1 + x = 0 + if y >= self.screen_height: + self._setc(self.screen_width - 1, self.screen_y_middle, '>') + self._dvram() + intxt = input("Continue > ") + if len(intxt) > 0: + if intxt[0] == self.scii_keys["quit"]: return + y = 0 + x = 0 + self._add_message(msg) + msg = "" + self._cvram() + else: + self._setc(x, y, i) + x += 1 + msg += i + self._dvram() + if msg != "": self._add_message(msg) + input("End of the text > ") + def _add_message(self, message): + self.old_messages.append(message) + while(len(self.old_messages) > self.message_history_max): del self.old_messages[0] + def show_old_messages(self): + if len(self.old_messages) < 1: + self._cvram() + x = 1 + for i in "No messages.": + self._setc(x, self.screen_y_middle, i) + x += 1 + self._dvram() + input("Go back > ") + c = len(self.old_messages) + for i in [i for i in self.old_messages[::-1]]: + self.show_text(i) + c -= 1 + if c < 1: input("Go back > ") + else: intxt = input("Next message > ") + if len(intxt) > 0: + if intxt[0] == 'q': return + def ask_choice(self, text, choices): + choice = 0 + while choice < 1 or choice > len(choices): + newtext = text + "\n\n" + c = 1 + for i in choices: + newtext += "{} - {}\n".format(c, i) + c += 1 + self.show_text(newtext) + try: + choice = int(input("Choice > ")) + except: choice = -1 + return choice +#---