Repository: plaaosert/credits_public Branch: main Commit: b0f09e1116d7 Files: 22 Total size: 142.8 KB Directory structure: gitextract_b3wq7xz6/ ├── .gitattributes ├── .gitignore ├── CLIRender/ │ ├── classes.py │ └── dat.py ├── README.md ├── animation_classes.py ├── animation_functions.py ├── animation_scenes.py ├── animator.py ├── colorama/ │ ├── __init__.py │ ├── ansi.py │ ├── ansitowin32.py │ ├── initialise.py │ ├── win32.py │ └── winterm.py ├── credits.py ├── credits_pynput.py ├── media/ │ ├── README.txt │ └── credits.txt ├── ocean.py ├── requirements.txt └── string_defs.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ # Auto detect text files and perform LF normalization * text=auto ================================================ FILE: .gitignore ================================================ **/__pycache__ media/*.wav media/*.WAV media/*.mp3 media/*.MP3 venv ================================================ FILE: CLIRender/classes.py ================================================ import os from platform import system as system_type from bisect import insort_right, bisect_right from CLIRender.dat import Vector2 class RenderSection: id_inc = 0 def __init__(self, string, start, code): self.id = RenderSection.id_inc RenderSection.id_inc += 1 self.string = string self.start = start self.length = len(self.string) self.end = self.start + self.length self.code = code def __lt__(self, other): return self.start < other def __gt__(self, other): return self.start > other def add_char(self, char): self.string += char self.end += 2 self.length += 2 def mod_char(self, char, loc): if loc == self.end: self.add_char(char) else: self.string = self.string[:loc] + char + self.string[loc + 2:] # The addition operation adds the other section to this section. The other section takes precedence. # This only works with text and discards the other section's code. def add_section(self, other): diff = other.start - self.start self.string = self.string[:diff] + other.string + self.string[diff + other.length:] self.length = len(self.string) self.start = min(other.start, self.start) self.end = self.start + self.length # The addition operation adds the other section to this section. The self section takes precedence. # This only works with text and discards the other section's code. def add_section_below(self, other): diff = self.start - other.start self.string = other.string[:diff] + self.string + other.string[diff + self.length:] self.length = len(self.string) self.start = min(other.start, self.start) self.end = self.start + self.length # Same as section subtraction but only one char. def subtract_char(self, loc): first = None last = None if loc > self.start: diff = loc - self.start first = RenderSection(self.string[:diff], self.start, self.code) if loc < self.end: diff = self.end - loc last = RenderSection(self.string[diff:], loc + 2, self.code) return first, last # Subtracts another section's length from this section. This returns a 2-tuple containing the two new subsections. def subtract_section(self, other): first = None last = None if other.start > self.start: # if the other string starts later, there's still a part of the victim string remaining diff = other.start - self.start first = RenderSection(self.string[:diff], self.start, self.code) if other.end < self.end: # if the other string ends before us, the latter part needs something too diff = self.end - other.end last = RenderSection(self.string[self.length - diff:], other.end, self.code) return first, last class Canvas: # TODO write multi-layer support. shouldn't be too hard, just merge down layers as we need # but not right now. plaao tired def __init__(self, dimensions, num_layers, merge_rules): self.dimensions = Vector2(dimensions.x * 2, dimensions.y) self.num_layers = num_layers self.layers = [Layer(i, self.dimensions) for i in range(num_layers)] self.merge_rules = merge_rules self.edits_this_frame = 0 def set_char(self, layer, location, char, code): self.layers[layer].set_char(int(location.x * 2) + (location.y * self.dimensions.x), char, code) self.edits_this_frame += 1 def set_string(self, layer, location, string, code): self.layers[layer].set_string(int(location.x * 2) + (location.y * self.dimensions.x), string, code) self.edits_this_frame += 1 def clear_layer(self, layer): self.layers[layer].set_string(0, " " * self.dimensions.x * self.dimensions.y, "") def render_blank(self): print("\033[1;1H\033[1;37;40m" + ((" " * self.dimensions.x + "\n") * self.dimensions.y)) def render_all(self): # Merge layers from bottom to top then render the resulting group list. # Here is the only point we even think about line lengths. total_string = "\033[1;1H\033[1;39m" for group in self.layers[0].new_groups: lineno = group.start // self.dimensions.x add_string = "" add_string += "\033[{};{}H".format( lineno + 1, group.start % self.dimensions.x + 1 ) add_string += group.code offset = group.start % self.dimensions.x # If overrun is greater than x, then it breaks a line boundary. # So, for every x overrun, we add a \n between lines. split = 0 stop = 0 while stop <= group.length: if lineno >= self.dimensions.y: break stop = self.dimensions.x - offset + split add_string += group.string[split:stop] + ("\n" if stop <= group.length else "") offset = 0 lineno += 1 split += stop - split # We're done! How simple ... total_string += add_string self.layers[0].new_groups.clear() self.edits_this_frame = 0 print(total_string + "\033[27;0H", end="\n") class Layer: def __init__(self, layer_id, dimensions): self.dimensions = Vector2(dimensions.x * 2, dimensions.y) self.max_loc = self.dimensions.x * self.dimensions.y self.id = layer_id # new_groups contains a list of groups of text that need to be rendered next time this layer is flattened. self.new_groups = [] self.full_str = ["" for i in range(self.dimensions.x * self.dimensions.y)] def set_char(self, loc, char, code): # Set a single char in the layer. # Step 1: binary search through new_groups to see if we need to make a new group # Step 2: make a new group or mod the found group # # We want to find the rightmost value less than or equal to loc, # then check if it encompasses the given position. i = bisect_right(self.new_groups, loc) - 1 if i >= len(self.new_groups) or i < 0: i = None else: # Also check backwards for things with the SAME start location. while i >= 1 and self.new_groups[i - 1].start == loc: i -= 1 if i is not None: # If a group already exists, we might need to split it. # This happens when codes are not equal. # In that situation, split our insert group in two and add a new one as normal. insert_group = self.new_groups[i] # This is only ever relevant when our found group actually reaches loc. if insert_group.end >= loc: if insert_group.code != code: sub_groups = insert_group.subtract_char(loc) self.new_groups.pop(i) if sub_groups[0]: insort_right(self.new_groups, sub_groups[0]) if sub_groups[1]: insort_right(self.new_groups, sub_groups[1]) i = None else: insert_group.mod_char(char, loc) else: i = None if i is None: # Add this char as a new group. insort_right(self.new_groups, RenderSection(char, loc, code)) # Then replace the full string at location with our new thing. # This is only as a contingency if our console crashes / burns / explodes / vanishes without a trace. # It should be disableable if necessary. # print("\n".join("".join(char for char in canvas.layers[0].full_str[line * 40:(line + 1) * 40]) for line in range(10))) self.full_str[loc] = code + char def has_duplicate_starts(self): non_dupe_list = [] for group in self.new_groups: if group in non_dupe_list: return False non_dupe_list.append(group) return True def set_string(self, loc, string, code): # Find the two groups that are closest to the start and end points of the group. # Perform the add function on both if they exist, or the sub function if their code is different. # Remove all groups in between. # Find the rightmost section with start less than or equal to loc; so, the closest element that can intersect. # IT MAY NOT INTERSECT. CHECK. IF IT DOESN'T, THEN NOTHING INTERSECTS START. # If we are out of bounds, leave instantly if loc >= self.max_loc or loc < 0: return start_i = bisect_right(self.new_groups, loc) - 1 if start_i < 0: start_i = 0 # Also check backwards for things with the SAME start location. while start_i >= 1 and self.new_groups[start_i - 1].start == loc: start_i -= 1 # Cut the string down to the maximum possible length it could be. cut_string = string[:self.max_loc - loc] add_group = RenderSection(cut_string, loc, code) add_groups = [None, add_group, None] remove = [] for mid_i in range(start_i, len(self.new_groups)): # If the group is the same, add. If it's different, subtract. # Fill up add_groups with subtracted items. mid_group = self.new_groups[mid_i] # If it starts after our end, break. We're out. if mid_group.start >= add_group.end: break # If it ends before our start, discard. We don't need it. if mid_group.end > add_group.start: # If it is eclipsed, we don't need it at all. if mid_group.start >= add_group.start and mid_group.end <= add_group.end: remove.append(mid_i) else: # Otherwise, run either subtract or add on it. if mid_group.code == add_group.code: # Add the mid group to the code. Delete the mid group. add_group.add_section_below(mid_group) remove.append(mid_i) else: # Subtract the add group from the mid group. Remove previous mid group. sub_groups = mid_group.subtract_section(add_group) if sub_groups[0]: add_groups[0] = sub_groups[0] if sub_groups[1]: add_groups[2] = sub_groups[1] remove.append(mid_i) remove.reverse() for index in remove: self.new_groups.pop(index) # THERE'S A BETTER WAY OF DOING THIS; I JUST DONT KNOW WHAT RIGHT NOW :( add_index = bisect_right(self.new_groups, loc) for group in add_groups: if group: self.new_groups.insert(start_i + add_index, group) add_index += 1 # For full string usage. for index, char in enumerate(string): self.full_str[index] = code + char def enable_ansi(): """ Uses a hack to enable ANSI mode in the Windows console. Does nothing on Linux. """ if system_type() == "Windows": # might fuck around os.system("") """ enable_ansi() canvas = Canvas(Vector2(40, 26), 1, ()) canvas.render_blank() import time import random ind = 0 for j in range(50): canvas.render_all() canvas.layers[0].set_char( ind % (40 * 26) * 2, "##", "\033[{}m".format(31 + (ind % 12) // 2) ) ind += random.randint(0, 80 * 26) canvas.layers[0].set_char( ind % (40 * 26) * 2, "##", "\033[{}m".format(31 + (ind % 12) // 2) ) ind += random.randint(0, 80 * 26) input("") ind = 0 while True: canvas.render_all() for yval in range(26): if ind % 14 == 13: canvas.layers[0].set_string( 0 + (80 * yval), "---------|" if ind % 2 == 0 else "~~~~~~~~~/", "\033[{}m".format(32) ) canvas.layers[0].set_string( 20 + (80 * yval), "|---------" if ind % 2 == 0 else "\~~~~~~~~~", "\033[{}m".format(32) ) canvas.layers[0].set_string( 10 + (80 * yval), " ", "\033[{}m".format(32) ) else: canvas.layers[0].set_string( 0 + (80 * yval), "---------|" if ind % 2 == 0 else "~~~~~~~~~/", "\033[{}m".format(32) ) canvas.layers[0].set_string( 20 + (80 * yval), "|---------" if ind % 2 == 0 else "\~~~~~~~~~", "\033[{}m".format(32) ) canvas.layers[0].set_string( 14 - (1 * (ind % 14)) + (80 * yval), "<{}>".format("--" * (ind % 14)), "\033[{}m".format((34, 32, 33, 31)[int((ind % 14) / 14 * 4)]) ) # canvas.layers[0].set_string( # i % (40 * 26), "abcdefghijklmnopqrstuvwxyz", "\033[{}m".format(31 + (i % 12) // 2) # ) ind += 1 # TODO write nicer functions and multilayer support # multilayer entails searching upwards for groups that intersect; if so, adding them to the layer # nicer functions means an actual set_char for the canvas that takes in x, y rather than absolute loc # also add set_string which can take in a longer string, subtracting any intersecting groups """ ================================================ FILE: CLIRender/dat.py ================================================ # contains: # # x position (generic typing because python but should be an int in most cases) # y position (the same) # class Vector2: # to be honest this is me showing off def __init__(self, x, y): self.x = x self.y = y def __str__(self): # returns a formatted string for the vector2 return "({v.x}, {v.y})".format(v=self) def __add__(self, other): # allows adding of vector2s, and ints if isinstance(other, Vector2): return Vector2(self.x + other.x, self.y + other.y) elif isinstance(other, (int, float)): return Vector2(self.x + other, self.y + other) else: raise ValueError("Accepted data types are float, int and Vector2.") def __mul__(self, other): # multiplies a vector2 by an int, or by another vector2 if isinstance(other, Vector2): return Vector2(self.x * other.x, self.y * other.y) elif isinstance(other, (int, float)): return Vector2(self.x * other, self.y * other) else: raise ValueError("Accepted data types are float, int and Vector2.") def __truediv__(self, other): # returns a FLOAT value (true div) if isinstance(other, Vector2): return Vector2(self.x / other.x, self.y / other.y) elif isinstance(other, (int, float)): return Vector2(self.x / other, self.y / other) else: raise ValueError("Accepted data types are float, int and Vector2.") def __floordiv__(self, other): # returns an INT value (floor div) if isinstance(other, Vector2): return Vector2(self.x // other.x, self.y // other.y) elif isinstance(other, (int, float)): return Vector2(self.x // other, self.y // other) else: raise ValueError("Accepted data types are float, int and Vector2.") def __pos__(self): return Vector2(abs(self.x), abs(self.y)) def __neg__(self): return Vector2(-self.x, -self.y) def __eq__(self, other): return self.x == other.x and self.y == other.y def Magnitude(self): # returns the magnitude of the vector2. (length of the vector) return ((self.x ** 2) + (self.y ** 2)) ** (1/2) #sqrt class Vector3: # to be honest this is me showing off def __init__(self, x, y, z): self.x = x self.y = y self.z = z def __str__(self): # returns a formatted string for the Vector3 return "({v.x}, {v.y}, {v.z})".format(v=self) def __add__(self, other): # allows adding of Vector3s, and ints if isinstance(other, Vector3): return Vector3(self.x + other.x, self.y + other.y, self.z + other.z) elif isinstance(other, (int, float)): return Vector3(self.x + other, self.y + other, self.z + other) else: raise ValueError("Accepted data types are float, int and Vector3.") def __mul__(self, other): # multiplies a Vector3 by an int, or by another Vector3 if isinstance(other, Vector3): return Vector3(self.x * other.x, self.y * other.y, self.z * other.z) elif isinstance(other, (int, float)): return Vector3(self.x * other, self.y * other, self.z * other) else: raise ValueError("Accepted data types are float, int and Vector3.") def __truediv__(self, other): # returns a FLOAT value (true div) if isinstance(other, Vector3): return Vector3(self.x / other.x, self.y / other.y, self.z / other.z) elif isinstance(other, (int, float)): return Vector3(self.x / other, self.y / other, self.z / other) else: raise ValueError("Accepted data types are float, int and Vector3.") def __floordiv__(self, other): # returns an INT value (floor div) if isinstance(other, Vector3): return Vector3(self.x // other.x, self.y // other.y, self.z // other.z) elif isinstance(other, (int, float)): return Vector3(self.x // other, self.y // other, self.z // other) else: raise ValueError("Accepted data types are float, int and Vector3.") def __pos__(self): return Vector3(abs(self.x), abs(self.y), abs(self.z)) def __neg__(self): return Vector3(-self.x, -self.y, -self.z) def __eq__(self, other): return self.x == other.x and self.y == other.y and self.z == other.z def Magnitude(self): # returns the magnitude of the Vector3. (length of the vector) return ((self.x ** 2) + (self.y ** 2) + (self.z ** 2)) ** (1 / 2) # sqrt ================================================ FILE: README.md ================================================ # credits_public - Public version of Frums - Credits animation repository. - Should be multiplatform. Feel free to raise an issue if it isn't. Feel free to raise an issue for anything. - Tested on clean Python 3.6 (Windows 10) with minimal modules installed. # media credits - All animation work done by me (plaaosert) - Renderer used is an in-progress command line rendering library for which a separate repository will be created... eventually. - Song credit: Frums - Credits EX https://soundcloud.com/frums/credits-ex - Song is not included in this repository. Read /media/README.txt. # how to see - https://youtu.be/o3cKQzrtFgQ # how to run Run `credits.py`. Required libraries: - just-playback https://github.com/cheofusi/just_playback - keyboard https://github.com/boppreh/keyboard - **important**: if you're running **linux** or **macos**, you might have trouble with this module. if so, try using `credits_pynput.py` instead (and install pynput -- https://pypi.org/project/pynput/) - colorama https://github.com/tartley/colorama - the version of colorama used is also included inside this repository. You can install all required libraries by running: ``` pip install -r requirements.txt ``` # misc ## start-of-song skips before the animation starts, a menu will appear with options to skip to specific parts of the song. i used this a lot while debugging and left it in because i ~forgot~ felt i should include it. input nothing to let the song start normally, else hold a digit key on the keyboard to skip: - 1 | start _(equivalent to pressing no button but slightly faster)_ - 2 | title _(flashing square in center + artist and self credits)_ - 3 | funding _(start of first "Funding for this program was made possible..." section)_ - 4 | loading _(loading bar before fake error and second typography "searching for access point" segment)_ - 5 | break _(hell ocean, second weather forecast scene)_ - 6 | final _(return of the flashing square - ending sequence)_ ## generic hotkeys | hotkey | effect | | ------------------- | ------------------------ | | `p` | play/pause toggle | | `,` | small fast forward (4x) | | `.` | medium fast forward (8x) | | `/` | large fast forward (16x) | ================================================ FILE: animation_classes.py ================================================ # Contains all classes (just Weather) import random import math class Weather: def __init__(self, precip, temp, wind, gust, wind_dir, humidity, days=2): self.precip = precip self.temp = temp self.wind = wind self.gust = gust self.wind_dir = wind_dir self.humidity = humidity self.weather_name = self.get_weather_name() self.days = days def __str__(self): return "{}% precipitation\n" \ "{}F temperature\n" \ "{}mph wind\n" \ "{}mph gust\n" \ "{} wind direction\n" \ "{}% humidity\n" \ "{}\n".format( round(self.precip * 100, 2), int(self.temp), int(self.wind), int(self.gust), int(self.wind_dir), round(self.humidity * 100, 2), self.weather_name ) def get_weather_name(self): # Clear/Sunny <> Cloudy/Rainy if self.humidity > 0.5: # Cloudy or rainy if self.precip > 0.4: if self.wind > 43: return "Blizzard" if self.temp < 32 else "Hurricane" elif self.wind > 25: return "Snowstorm" if self.temp < 32 else "Storm" else: return "Snow" if self.temp < 32 else "Rain" elif self.precip > 0.25: return "Sleet" if self.temp < 32 else "Drizzle" if self.humidity > 0.8 or self.precip > 0.5: return "Overcast" elif self.humidity > 0.65 or self.precip > 0.3: return "Cloudy" else: return "Partly cloudy" else: if self.precip > 0.4: return "Snow" if self.temp < 32 else "Rain" elif self.precip > 0.2: return "Sleet" if self.temp < 32 else "Drizzle" return ("Sunny" if self.humidity < 0.1 else "Partly sunny") if self.humidity < 0.2 else "Clear" def mutate(self, steps=1): for i in range(steps): # Prefer 33% precip, move wind dir by +-3 each day, gust = speed x 2.1 - 2.6 precip_move = random.randint(-100, 100) / 400.0 precip_move += (random.randint(0, int(100 * abs(0.33 - self.precip))) / 200) * (-1 if 0.33 - self.precip < 0 else 1) self.precip = min(1, max(0, self.precip + precip_move)) self.wind_dir = (self.wind_dir + random.randint(-3, 3)) % 8 wind_move = random.randint(-100, 100) / 13 wind_move += (random.randint(0, int(abs(15 - self.wind))) / 3) * (-1 if 15 - self.wind < 0 else 1) self.wind = max(0, self.wind + wind_move) adjusted_days = self.days - 282 temp_target = 40 * (math.sin((2 * math.pi * adjusted_days) / 365 - math.pi / 3) + 1) + 20 temp_move = random.randint(-100, 100) / 20 temp_move += (random.randint(0, int(abs(temp_target - self.temp))) / 5) * (-1 if temp_target - self.temp < 0 else 1) self.temp = max(0, self.temp + temp_move) humidity_move = random.randint(-100, 100) / 500.0 humidity_move += (random.randint(0, int(abs(0.2 - self.humidity))) / 200) * (-1 if 0.5 - self.humidity < 0 else 1) self.humidity = max(0, min(1, self.humidity + humidity_move)) self.gust = self.wind * random.randint(210, 260) * 0.01 self.days += 1 self.weather_name = self.get_weather_name() # 22.10.2009: Cloudy, precip 20%, temp 43, wind dir 6, 13 (25) # 23.10.2009: Sunny, precip 4%, temp 52, wind dir 7, 12 (25) # 24.10.2009: Partly sunny, precip 7%, temp 48, wind dir 1, 8 (20) known_weathers = ( Weather(0.203, 43, 13, 25, 6, 0.66), Weather(0.04, 52, 12, 25, 7, 0.1), Weather(0.07, 48, 8, 20, 1, 0.1, 200), Weather(0.07, 48, 8, 20, 1, 0.1, 528 + 200), Weather(-1, -1, -1, -1, -1, -1) ) known_weathers[4].weather_name = "Connection lost... " ================================================ FILE: animation_functions.py ================================================ # Contains functions used inside credits.py. # Reduce clutter. Reuse clutter. Recycle clutter. import random from CLIRender.dat import Vector2 from colorama import Fore, Style def generate_random_hex(length): return ('%0' + str(length) + 'x') % random.randrange(16 ** length) def replace_text_with_spaces(string, chance): return "".join((" " if random.randint(0, 100) < chance else char) for char in string) def render_weather(c, layer, x, y, weather, mutations_after=0, spc_chance=0): # . . if weather.precip != -1: c.set_string( layer, Vector2(x + 5, y - 2), replace_text_with_spaces("{:>14}".format(weather.weather_name), spc_chance), Fore.YELLOW + Style.NORMAL ) else: c.set_string( layer, Vector2(x, y - 2), replace_text_with_spaces("{:>14}".format(weather.weather_name), spc_chance), Fore.RED + Style.BRIGHT ) temp_colour = (Fore.WHITE, Fore.CYAN, Fore.YELLOW, Fore.RED)[min(3, int(weather.temp) // 23)] c.set_string( layer, Vector2(x, y), replace_text_with_spaces("{:2}°F ".format(int(weather.temp)), spc_chance), temp_colour + Style.BRIGHT ) wind_dirs = ("N ", "NE", "E ", "SE", "S ", "SW", "W ", "NW") c.set_string( layer, Vector2(x + 5, y), replace_text_with_spaces("Wind {:2} mph {}".format(int(weather.wind), wind_dirs[weather.wind_dir]), spc_chance), Fore.CYAN + Style.BRIGHT ) c.set_string( layer, Vector2(x + 5, y + 2), replace_text_with_spaces("Precipitation ", spc_chance), Fore.BLUE + Style.BRIGHT ) c.set_string( layer, Vector2(x + 5, y + 3), replace_text_with_spaces("{:^13} ".format(str(round(weather.precip * 100, 2)) + "%"), spc_chance), Fore.BLUE + Style.BRIGHT ) weather.mutate(mutations_after) def noise(c, layer, amount, chars, colours): for n in range(amount): location = Vector2(random.randint(0, c.dimensions.x - 1), random.randint(0, c.dimensions.y - 1)) c.set_char( layer, location, random.choice(chars), random.choice(colours) ) def set_multiline_string(c, layer, x, y, string, col): for offset, line in enumerate(string.split("\n")): c.set_string( layer, Vector2(x, y + offset), line, col ), def type_text(c, generator, layer, x, y, col, render=True): # pop a char off the manager's text if there is one text_get = generator.get_data("text") offset = generator.get_data("offset") total_chars = 0 add_offset = 0 if text_get: if text_get.startswith("[##CLEAR|"): clear_bounds = text_get.split("|")[1].split(";") for yclear in range(int(clear_bounds[1])): location = Vector2(x, y + yclear) if render: c.set_string( layer, location, " " * int(clear_bounds[0]), col ) else: for linecount, text_line in enumerate(text_get.split("\n")): local_offset = offset - total_chars if 0 <= local_offset < len(text_get): place_typer = local_offset < len(text_line) - 1 string = text_line[:local_offset] + ("_" if place_typer else "") if local_offset < len(text_line) and text_line[local_offset] == "@": add_offset += 3 string = string.replace("~", "").replace("@", "") total_chars += len(text_line) location = Vector2(x, y + linecount) if render: c.set_string( layer, location, string, col ) generator.oper_data("offset", lambda t: t + 1 + add_offset) def fuck_up_text(string, chance, also_ignore=""): new_str = "" fucks = ( ".", ".", ".", " ", " ", "`", "=", "/", "?", "-", "$", "%" ) for char in string: if char == "\n": new_str += char else: if random.randint(1, 1000) < chance and char not in "@~\n" + also_ignore: new_str += random.choice(fucks) else: new_str += char return new_str def debug_info(c, g, b, frames): c.set_string( 0, Vector2(32, 1), "{:4} g | {:4} l".format(g.parent.cur_beat, g.parent.active_scene[0].internal_beat), Style.BRIGHT + Fore.YELLOW ), counted_scenes = 0 for index, scene in enumerate(filter(lambda s: s.name != "debug_counter", g.parent.active_scene)): c.set_string( 0, Vector2(32, index + 2), "{:^17}".format( scene.name + " ({})".format(len(list(filter(lambda g: g.start_beat <= b, scene.generators)))) ), Style.NORMAL + Fore.GREEN ), counted_scenes += 1 for index2 in range(counted_scenes, 6): c.set_string( 0, Vector2(32, index2 + 2), " ", Style.NORMAL + Fore.GREEN ), c.set_string( 0, Vector2(32, 8), " {:4} e/s".format(c.edits_this_frame), Style.BRIGHT + Fore.YELLOW ), avg_differences = sum(frames[i] - frames[i - 1] for i in range(len(frames) - 1, 0, -1)) if avg_differences: avg_differences /= 10 else: avg_differences = 60 c.set_string( 0, Vector2(32, 9), " {:6} fps".format(round(1 / avg_differences, 1)), Style.BRIGHT + Fore.YELLOW ), cols = ( Fore.BLACK, Fore.RED, Fore.GREEN, Fore.YELLOW, Fore.BLUE, Fore.MAGENTA, Fore.CYAN, Fore.WHITE, ) styles = ( Style.NORMAL, Style.BRIGHT ) for index in range(16): c.set_char( 0, Vector2(32 + (index % 8), 11 + (index // 8)), "##", cols[index % 8] + styles[index // 8] ), def clear(c, layer): c.clear_layer(layer) def beat_toggle(c, g, layer, x, x2, y, y2, char, col): tog = g.get_data("beat_toggle") chars = char if tog else ".." x_diff = x2 - x for yn in range(y, y2): c.set_string( layer, Vector2(x, yn), chars * x_diff, col ), g.set_data("beat_toggle", not tog) def work_out_date(b, day_offset=0): # "22.10.2009" lengths = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ) # One day passes every 64 beats. days_needed = (b // 64) + day_offset month_loc = 9 day_loc = 22 year = 2009 while days_needed > 0: leap_year = (1 if year % 4 == 0 and month_loc == 1 else 0) # leap year until_end_month = lengths[month_loc] + 1 - day_loc + leap_year day_loc += min(days_needed, until_end_month) days_needed -= min(days_needed, until_end_month) if day_loc > lengths[month_loc] + leap_year: month_loc += 1 day_loc = 1 if month_loc >= 12: month_loc = 0 year += 1 return "{:02}.{:02}.{:04}".format(day_loc, month_loc + 1, year) def split_word_template(string): return [sp.split("#") for sp in string.split("\n")] def typewrite_by_word(c, generator, layer, x, y, col, render=True, history_var="history"): # Show the text up to offset words at line lineno text_get = generator.get_data("text") offset = generator.get_data("offset") lineno = generator.get_data("lineno") if text_get: if lineno < len(text_get): line_get = text_get[lineno] line_total = "".join(line for line in line_get[:offset]) if line_get: if line_get[0].startswith("[~~CLEAR|"): clear_bounds = line_get[0].split("|")[1] location = Vector2(x, y) if render: c.set_string( layer, location, " " * int(clear_bounds), col ) else: # print line_total and increment offset by 1. # if offset is > lineno, reset offset and increment lineno # if line_total is empty (offset == 0), print a clear if render: if line_total: c.set_string( layer, Vector2(x, y), line_total.replace("~", ""), col ), else: c.set_string( layer, Vector2(x, y), " " * 60, col ), if offset >= len(line_get): history = generator.parent.get_data(history_var) is_fluff = line_total.startswith(" ") or not line_total is_important = line_total.endswith("~") colour_select = Fore.YELLOW + Style.NORMAL prefix = "- " if is_fluff: prefix = " " colour_select = Fore.BLACK + Style.BRIGHT elif is_important: prefix = "> " colour_select = Fore.GREEN + Style.NORMAL if not history: history = [( prefix + line_total.strip(" ").replace("~", ""), colour_select )] generator.parent.set_data(history_var, history) else: history.append(( prefix + line_total.strip(" ").replace("~", ""), colour_select )) generator.parent.set_data("refresh", True) generator.set_data("offset", 0) generator.set_data("lineno", lineno + 1) else: generator.set_data("offset", offset + 1) else: generator.set_data("offset", offset + 1) def write_history(c, generator, layer, x, y, col, stop, var="history"): # Write history from y position going upwards until 0 history = generator.parent.get_data(var) need_refresh = generator.parent.get_data("refresh") if need_refresh: generator.parent.set_data("refresh", False) if history: lineid = 0 for ypos in range(y, stop - 1, -1): line = history[len(history) - 1 - lineid] if lineid < len(history) else ("", Fore.BLACK + Style.BRIGHT) lineid += 1 location = Vector2(x, ypos) c.set_string( layer, location, "{:50}".format(line[0]), line[1] ), else: for ypos in range(y, stop - 1, -1): c.set_string( layer, Vector2(x, ypos), " " * 50, Fore.BLACK + Style.NORMAL ), def show_access_point_visual(c, generator, layer, x, y): # Increment counter. If counter > 8, reset it, increment the bigger counter counter = generator.get_data("counter") block_counter = generator.get_data("block") location_x = 5 * (block_counter % 6) + x location_y = 4 * (block_counter // 6) + y if counter < 8: set_multiline_string( c, layer, location_x, location_y, " ### \nPBS #{:02}\nPing {}".format(block_counter + 1, counter + 1), Fore.YELLOW + Style.NORMAL ) generator.oper_data("counter", lambda t: t + 1) else: set_multiline_string( c, layer, location_x, location_y, " ... \nPBS #{:02}\n-------".format(block_counter + 1), Fore.BLACK + Style.BRIGHT ) location_x_next = 5 * ((block_counter + 1) % 6) + x location_y_next = 4 * ((block_counter + 1) // 6) + y set_multiline_string( c, layer, location_x_next, location_y_next, " ### \nPBS #{:02}\nPing {}".format(block_counter + 2, 1), Fore.YELLOW + Style.NORMAL ) generator.set_data("counter", 1) generator.oper_data("block", lambda t: t + 1) def make_poweroff_bars(c, b, layer, col): # at b=1, full screen # then lerp height on both ends down to 0 height = int(24 / (b ** 1.3)) location = Vector2(0, 12 - (height // 2)) c.set_string( layer, location, ("##" * 40) * height, col ) ================================================ FILE: animation_scenes.py ================================================ # Contains all animation scenes except for debug_counter. # Use variable "all_scenes" which contains all scenes defined here. # This file also creates the canvas. import animator as am from animation_functions import * from animation_classes import known_weathers from ocean import begin_ocean, update_ocean_slices from CLIRender.classes import Canvas canvas = Canvas(Vector2(40, 24), 1, ()) wipe = am.Scene( "wipe", ( am.Generator( 0, am.Generator.always(), am.Generator.no_create(), lambda g, b: noise( canvas, 0, int(b ** 1.4), ("##", "@@", " "), ( *((Style.BRIGHT + Fore.WHITE,) * b), *((Style.NORMAL + Fore.WHITE,) * (70 - b)), *((Style.BRIGHT + Fore.BLACK,) * 4 * (40 - b)), ) ), am.Generator.no_request() ), ) ) clear_wipe = am.Scene( "clear_wipe", ( am.Generator( 0, am.Generator.always(), am.Generator.no_create(), lambda g, b: noise( canvas, 0, int(b ** 2.2), (" ",), (Fore.WHITE + Style.BRIGHT,) ), am.Generator.no_request() ), ) ) clear_scene = am.Scene( "clear", ( #am.Generator( # 0, am.Generator.at_beat(0), # lambda g: noise( # canvas, 0, 40 * 24, ("##",), (Style.BRIGHT + Fore.WHITE,) # ), # am.Generator.no_request(), am.Generator.no_request() #), am.Generator( 0, am.Generator.always(), lambda g: clear(canvas, 0), am.Generator.no_request(), am.Generator.no_request() ), ) ) ocean = am.Scene( "ocean_b", ( am.Generator( 0, am.Generator.every_n_beats(2), lambda g: g.set_data("ocean", begin_ocean(), "ocean_glitch", 1, "ocean_col", Style.BRIGHT + Fore.BLUE), lambda g, b: canvas.set_string( 0, Vector2(0, 14), update_ocean_slices(g.get_data("ocean"), g.get_data("ocean_glitch")), g.get_data("ocean_col") ), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", "", "offset", 0), lambda g, b: type_text(canvas, g, 0, 1, 1, Style.BRIGHT + Fore.WHITE), am.Generator.no_request() ), ) ) ocean2 = am.Scene( "ocean_c", ( am.Generator( 0, am.Generator.every_n_beats(2), lambda g: g.set_data("ocean", begin_ocean(), "ocean_glitch", 16, "ocean_col", Style.BRIGHT + Fore.BLACK), lambda g, b: canvas.set_string( 0, Vector2(0, 14), update_ocean_slices(g.get_data("ocean"), g.get_data("ocean_glitch")), g.get_data("ocean_col") ), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", "", "offset", 0), lambda g, b: type_text(canvas, g, 0, 1, 1, Style.BRIGHT + Fore.RED, int(b ** 1.143) % 20 not in (0, 8, 17, 15)), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", "", "offset", 0), lambda g, b: type_text(canvas, g, 0, 1, 1, Style.BRIGHT + Fore.RED, int(b ** 1.2) % 20 in (0, 15)), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", "", "offset", 0), lambda g, b: type_text(canvas, g, 0, 1, 1, Style.BRIGHT + Fore.RED, int(b ** 1.2) % 20 == 8), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", "", "offset", 0), lambda g, b: type_text(canvas, g, 0, 1, 1, Style.BRIGHT + Fore.RED, int(b ** 1.2) % 20 == 17), am.Generator.no_request() ), ) ) ocean3 = am.Scene( "ocean_d", ( am.Generator( 0, am.Generator.always(), lambda g: g.set_data("ocean", begin_ocean(), "ocean_glitch", 100, "ocean_col", Style.NORMAL + Fore.MAGENTA), lambda g, b: canvas.set_string( 0, Vector2(0, 14), update_ocean_slices(g.get_data("ocean"), g.get_data("ocean_glitch")), g.get_data("ocean_col") ), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), am.Generator.no_create(), lambda g, b: set_multiline_string(canvas, 0, 1, 1, "The system has encountered a fatal error. Please wait.\n\n" "[ERR: 801]\n\n" + "\n".join( " ".join(generate_random_hex(4) for _ in range(8)) for _ in range(4) ), Style.BRIGHT + Fore.RED), am.Generator.no_request() ), ) ) text = am.Scene( "typewrite", ( am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", "", "offset", 0), lambda g, b: type_text(canvas, g, 0, 1, 1, Style.BRIGHT + Fore.WHITE), am.Generator.no_request() ), ) ) title = am.Scene( "title", ( # We have 12 beat-ends to map to: # ' - plaaosert # ' - frums # ' - python 3.6 # ' - command line # drums # ' - # ' - # ' - # ' - # small moog # ' - # ' - # ' - # ' - am.Generator( 64, am.Generator.at_beat(64), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(1, 1), "animation | plaaosert", Fore.CYAN + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 128, am.Generator.at_beat(128), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(1, 2), "bgm | Frums - Credits", Fore.CYAN + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 192, am.Generator.at_beat(192), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(1, 4), "running pure Python 3.6", Fore.CYAN + Style.NORMAL ), am.Generator.no_request() ), am.Generator( 256, am.Generator.at_beat(256), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(1, 5), "in the command line", Fore.CYAN + Style.NORMAL ), am.Generator.no_request() ), am.Generator( 320, am.Generator.at_beat(320), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(0, 21), "--" * 40, Fore.WHITE + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 384, am.Generator.at_beat(384), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(1, 22), "> _ ", Fore.WHITE + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 448, am.Generator.at_beat(448), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 26, 14, "----------------------------\n" + "| |\n" * 6, Fore.WHITE + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 512, am.Generator.at_beat(512), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 27, 15, "22.10.2009", Fore.YELLOW + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 576, am.Generator.at_beat(576), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 26, 16, "----------------------------", Fore.WHITE + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 640, am.Generator.at_beat(640), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 27, 17, "43°F Wind 13 mph W \n" " ", Fore.CYAN + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 704, am.Generator.at_beat(704), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 32, 19, "Precipitation \n" " 20.3% ", Fore.BLUE + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 768, am.Generator.at_beat(768), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 36, 15, "Cloudy", Fore.YELLOW + Style.NORMAL ), am.Generator.no_request() ), ) ) beats = am.Scene( "beats", ( # beat manager am.Generator( 0, am.Generator.combine_conditions(am.Generator.every_on_off(48, 16), am.Generator.every_n_beats(4)), lambda g: g.set_data("beat_toggle", True), lambda g, b: beat_toggle(canvas, g, 0, 18, 22, 11, 15, "@@", Style.BRIGHT + Fore.YELLOW), am.Generator.no_request() ), am.Generator( 0, am.Generator.combine_conditions(am.Generator.every_off_on(48, 16), am.Generator.every_n_beats(2)), lambda g: g.set_data("beat_toggle", True), lambda g, b: beat_toggle(canvas, g, 0, 18, 22, 11, 15, "##", Style.BRIGHT + Fore.GREEN), am.Generator.no_request() ), ) ) beats_lr = am.Scene( "beats_lr", ( # beat manager am.Generator( 0, am.Generator.combine_conditions(am.Generator.every_on_off(48, 16), am.Generator.every_n_beats(4)), lambda g: g.set_data("beat_toggle", True), lambda g, b: beat_toggle(canvas, g, 0, 18, 20, 11, 15, "@@", Style.BRIGHT + Fore.YELLOW), am.Generator.no_request() ), am.Generator( 0, am.Generator.combine_conditions(am.Generator.every_off_on(48, 16), am.Generator.every_n_beats(2)), lambda g: g.set_data("beat_toggle", True), lambda g, b: beat_toggle(canvas, g, 0, 20, 22, 11, 15, "##", Style.BRIGHT + Fore.GREEN), am.Generator.no_request() ), ) ) beats_side = am.Scene( "beats_side", ( # beat manager am.Generator( 0, am.Generator.combine_conditions(am.Generator.every_on_off(48, 16), am.Generator.every_n_beats(4)), lambda g: g.set_data("beat_toggle", True), lambda g, b: beat_toggle(canvas, g, 0, 18, 20, 11, 15, "@@", Style.BRIGHT + Fore.YELLOW), am.Generator.no_request() ), am.Generator( 0, am.Generator.combine_conditions(am.Generator.every_off_on(48, 16), am.Generator.every_n_beats(2)), lambda g: g.set_data("beat_toggle", True), lambda g, b: beat_toggle(canvas, g, 0, 20, 22, 11, 15, "##", Style.BRIGHT + Fore.GREEN), am.Generator.no_request() ), ) ) date_ticker = am.Scene( "dates", ( # TODO change this shit to every n beats, use a data variable to store the current date index PLEASE # # Run at normal speed # Run at double speed # Run with slight randomness forwards # Skip forwards, go every step # At crazy part, switch off this and go to a glitchy display # am.Generator( 0, am.Generator.before_n(64 * 8), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(27, 15), work_out_date(b), Fore.YELLOW + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 64 * 8, am.Generator.before_n(64 * 12 - 4), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(27, 15), work_out_date((64 * 8) + (b - (64 * 8)) * 2), Fore.YELLOW + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 64 * 12 - 4, am.Generator.at_beat(64 * 12 - 4), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(27, 15), "??.??.????", Fore.YELLOW + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 64 * 12 + 4, am.Generator.combine_conditions(am.Generator.every_n_beats(4), am.Generator.before_n(64 * 16)), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(27, 15), work_out_date(random.randint(64 * 12 + b * 8, 64 * 12 + b * 24)), Fore.YELLOW + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 64 * 16, am.Generator.always(), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(27, 15), work_out_date(64 * 16 + b * 64 - 64 * 16 * 32), Fore.YELLOW + Style.BRIGHT ), am.Generator.no_request() ), ) ) redraw_ui = am.Scene( "redraw_ui", ( am.Generator( 0, am.Generator.at_beat(0), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(0, 21), "--" * 40, Fore.WHITE + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 0, am.Generator.at_beat(0), am.Generator.no_create(), lambda g, b: canvas.set_string( 0, Vector2(1, 22), "> _ ", Fore.WHITE + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 0, am.Generator.at_beat(2), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 26, 14, "----------------------------\n" + "| |\n" * 6, Fore.WHITE + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 0, am.Generator.at_beat(3), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 27, 15, "22.10.2009", Fore.YELLOW + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 0, am.Generator.at_beat(4), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 26, 16, "----------------------------", Fore.WHITE + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 0, am.Generator.at_beat(5), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 27, 17, "43°F Wind 13 mph W ", Fore.CYAN + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 0, am.Generator.at_beat(6), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 32, 19, "Precipitation \n" " 20.3% ", Fore.BLUE + Style.BRIGHT ), am.Generator.no_request() ), am.Generator( 0, am.Generator.at_beat(7), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 36, 15, "Cloudy", Fore.YELLOW + Style.NORMAL ), am.Generator.no_request() ), ) ) weather = am.Scene( "weather", ( am.Generator( 0, am.Generator.combine_conditions(am.Generator.every_n_beats(64), am.Generator.before_n(64 * 8)), am.Generator.no_create(), lambda g, b: render_weather(canvas, 0, 27, 17, known_weathers[min(2, b // 64)], 0 if b < 128 else 1), am.Generator.no_request() ), am.Generator( 64 * 8, am.Generator.combine_conditions(am.Generator.every_n_beats(32), am.Generator.before_n(64 * 12)), am.Generator.no_create(), lambda g, b: render_weather(canvas, 0, 27, 17, known_weathers[2], 1), am.Generator.no_request() ), am.Generator( 64 * 12 - 4, am.Generator.at_beat(64 * 12 - 4), am.Generator.no_create(), lambda g, b: render_weather(canvas, 0, 27, 17, known_weathers[4], 1), am.Generator.no_request() ), am.Generator( 64 * 12 + 4, am.Generator.combine_conditions(am.Generator.every_n_beats(4), am.Generator.before_n(64 * 16)), am.Generator.no_create(), lambda g, b: render_weather(canvas, 0, 27, 17, known_weathers[2], 14), am.Generator.no_request() ), am.Generator( 64 * 16, am.Generator.always(), am.Generator.no_create(), lambda g, b: render_weather(canvas, 0, 27, 17, known_weathers[3], 1, max(0, (b - 1080) * 2.2)), am.Generator.no_request() ), ) ) funding = am.Scene( "funding", ( am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", [], "offset", 0, "lineno", 0, "type_col", Fore.WHITE + Style.BRIGHT), lambda g, b: typewrite_by_word(canvas, g, 0, 2, 22, g.get_data("type_col")), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), am.Generator.no_create(), lambda g, b: write_history(canvas, g, 0, 1, 19, Fore.BLACK + Style.BRIGHT, 1), am.Generator.no_request() ), ) ) loading = am.Scene( "loadingbar", ( am.Generator( 0, am.Generator.at_beat(0), lambda g: g.scene.set_data("progress", 0), lambda g, b: set_multiline_string(canvas, 0, 2, 9, "----------------------------------\n" "| |\n" "----------------------------------", Fore.WHITE + Style.BRIGHT), am.Generator.no_request() ), am.Generator( 1, am.Generator.at_beat(1), lambda g: g.scene.set_data("progress", 0), lambda g, b: set_multiline_string(canvas, 0, 3, 10, " Loading... \n", Fore.BLACK + Style.BRIGHT), am.Generator.no_request() ), am.Generator( 0, am.Generator.combine_conditions(am.Generator.before_n(240), am.Generator.every_n_beats(8)), am.Generator.no_create(), lambda g, b: g.scene.oper_data("progress", lambda t: t + 1), am.Generator.no_request() ), am.Generator( 240, am.Generator.always(), am.Generator.no_create(), lambda g, b: g.scene.oper_data("progress", lambda t: t + random.randint(40, 70)), am.Generator.no_request() ), am.Generator( 0, am.Generator.before_n(240), am.Generator.no_create(), lambda g, b: canvas.set_string(0, Vector2(3, 10), "#" * g.scene.get_data("progress"), Fore.YELLOW + Style.BRIGHT), am.Generator.no_request() ), am.Generator( 240, am.Generator.always(), am.Generator.no_create(), lambda g, b: canvas.set_string(0, Vector2(3, 10), fuck_up_text("#" * g.scene.get_data("progress"), 400), Fore.RED + Style.BRIGHT), am.Generator.no_request() ), am.Generator( 0, am.Generator.before_n(240), lambda g: g.set_data("text", [], "offset", 0, "lineno", 0, "type_col", Fore.BLACK + Style.BRIGHT), lambda g, b: typewrite_by_word(canvas, g, 0, 3, 12, g.get_data("type_col")), am.Generator.no_request() ), ) ) quick_loading = am.Scene( "fastload", ( am.Generator( 0, am.Generator.at_beat(0), lambda g: g.scene.set_data("progress", 0), lambda g, b: set_multiline_string(canvas, 0, 2, 9, "----------------------------------\n" "| |\n" "----------------------------------", Fore.WHITE + Style.BRIGHT), am.Generator.no_request() ), am.Generator( 1, am.Generator.at_beat(1), lambda g: g.scene.set_data("progress", 0), lambda g, b: set_multiline_string(canvas, 0, 3, 10, " Please wait... \n", Fore.BLACK + Style.BRIGHT), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), am.Generator.no_create(), lambda g, b: g.scene.oper_data("progress", lambda t: t + random.randint(4, 6)), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), am.Generator.no_create(), lambda g, b: canvas.set_string(0, Vector2(3, 10), "#" * g.scene.get_data("progress"), Fore.GREEN + Style.BRIGHT), am.Generator.no_request() ), ) ) error_screen = am.Scene( "error", ( am.Generator( 0, am.Generator.at_beat(0), lambda g: g.scene.set_data("progress", 0), lambda g, b: set_multiline_string(canvas, 0, 0, 14, "--" * 40 + "\n" * 8 + " $ ", Fore.WHITE + Style.BRIGHT), am.Generator.no_request() ), am.Generator( 0, am.Generator.at_beat(0), lambda g: g.scene.set_data("progress", 0), lambda g, b: set_multiline_string(canvas, 0, 1, 1, "Automatic diagnosis unsuccessful. Please wait.\n> ", Fore.BLACK + Style.BRIGHT), am.Generator.no_request() ), ) ) funding_double = am.Scene( "fundingx2", ( am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", [], "offset", 0, "lineno", 0, "type_col", Fore.RED + Style.NORMAL), lambda g, b: typewrite_by_word(canvas, g, 0, 2, 2, g.get_data("type_col")), am.Generator.no_request() ), # am.Generator( # 0, am.Generator.always(), # am.Generator.no_create(), # lambda g, b: write_history(canvas, g, 0, 1, 19, Fore.BLACK + Style.BRIGHT, 1), # am.Generator.no_request() # ), am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", [], "offset", 0, "lineno", 0, "type_col", Fore.CYAN + Style.BRIGHT), lambda g, b: typewrite_by_word(canvas, g, 0, 2, 22, g.get_data("type_col"), history_var="history2"), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), am.Generator.no_create(), lambda g, b: write_history(canvas, g, 0, 1, 21, Fore.BLACK + Style.BRIGHT, 15, var="history2"), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", [], "offset", 0, "lineno", 0, "type_col", Fore.YELLOW + Style.BRIGHT), lambda g, b: typewrite_by_word(canvas, g, 0, 4, 5, g.get_data("type_col"), history_var="history3"), am.Generator.no_request() ), ) ) access_points = am.Scene( "accesspoints", ( am.Generator( 0, am.Generator.combine_conditions(am.Generator.before_n(20), am.Generator.every_n_beats(2)), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 1, 1, "\n\n".join( "\n".join( " ".join( ("PBS #{:02}".format((yblock * 6) + x + 1) if yline == 1 else (" ### ", "", "Unknown")[yline]) if random.randint(0, max(1, 32 - int(b ** 1.2))) < 4 else " " for x in range(6) ) for yline in range(3) ) for yblock in range(4) ), Fore.BLACK + Style.BRIGHT), am.Generator.no_request() ), am.Generator( 20, am.Generator.at_beat(20), am.Generator.no_create(), lambda g, b: set_multiline_string( canvas, 0, 1, 1, "\n\n".join( "\n".join( " ".join( ("PBS #{:02}".format((yblock * 6) + x + 1) if yline == 1 else (" ### ", "", "Unknown")[yline]) for x in range(6) ) for yline in range(3) ) for yblock in range(4) ), Fore.RED + Style.NORMAL), am.Generator.no_request() ), am.Generator( 0, am.Generator.at_beat(0), am.Generator.no_create(), lambda g, b: set_multiline_string(canvas, 0, 1, 17, "No access points are broadcasting.\n" "Manual search in progress.\n" "Last search 27.02.2019 (532 days ago)", Fore.BLACK + Style.BRIGHT), am.Generator.no_request() ), am.Generator( 80, am.Generator.combine_conditions(am.Generator.every_n_beats(8), am.Generator.before_n(976)), lambda g: g.set_data("counter", 0, "block", 0), lambda g, b: show_access_point_visual(canvas, g, 0, 1, 1), am.Generator.no_request() ), am.Generator( 968, am.Generator.at_beat(968), am.Generator.no_create(), lambda g, b: set_multiline_string(canvas, 0, 6, 9, " @@@ \nPBS #14\n Active", Fore.GREEN + Style.BRIGHT), am.Generator.no_request() ) ) ) funding_single = am.Scene( "fdg_single", ( am.Generator( 0, am.Generator.at_beat(0), lambda g: g.scene.set_data("progress", 0), lambda g, b: set_multiline_string(canvas, 0, 0, 20, "--" * 40 + "\n" + " Sending > ", Fore.WHITE + Style.BRIGHT), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", [], "offset", 0, "lineno", 0, "type_col", Fore.YELLOW + Style.NORMAL), lambda g, b: typewrite_by_word(canvas, g, 0, 6, 21, g.get_data("type_col")), am.Generator.no_request() ), ) ) funding_down = am.Scene( "fdg_down", ( am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", [], "offset", 0, "lineno", 0, "type_col", Fore.YELLOW + Style.NORMAL), lambda g, b: typewrite_by_word(canvas, g, 0, 1, 1 + g.get_data("lineno"), g.get_data("type_col")), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), lambda g: g.set_data("text", [], "offset", 0, "lineno", 0, "type_col", Fore.YELLOW + Style.NORMAL), lambda g, b: typewrite_by_word(canvas, g, 0, 1, 20 + g.get_data("lineno"), g.get_data("type_col")), am.Generator.no_request() ), ) ) poweroff = am.Scene( "poweroff", ( am.Generator( 3, am.Generator.always(), am.Generator.no_create(), lambda g, b: make_poweroff_bars(canvas, b - 2, 0, Fore.BLACK + Style.NORMAL), am.Generator.no_request() ), am.Generator( 2, am.Generator.always(), am.Generator.no_create(), lambda g, b: make_poweroff_bars(canvas, b - 1, 0, Fore.BLACK + Style.BRIGHT), am.Generator.no_request() ), am.Generator( 1, am.Generator.always(), am.Generator.no_create(), lambda g, b: make_poweroff_bars(canvas, b, 0, Fore.WHITE + Style.NORMAL), am.Generator.no_request() ), am.Generator( 0, am.Generator.always(), am.Generator.no_create(), lambda g, b: make_poweroff_bars(canvas, b + 1, 0, Fore.WHITE + Style.BRIGHT), am.Generator.no_request() ), ) ) all_scenes = ( wipe, clear_wipe, clear_scene, ocean, ocean2, ocean3, text, title, beats, beats_lr, funding, date_ticker, weather, redraw_ui, loading, quick_loading, error_screen, funding_double, access_points, funding_single, funding_down, poweroff ) ================================================ FILE: animator.py ================================================ class DataStoringObject: def __init__(self): self.data = {} def set_data(self, *ident): for i in range(0, len(ident), 2): self.data[ident[i]] = ident[i + 1] def get_data(self, ident): if ident in self.data: return self.data[ident] def oper_data(self, ident, oper): self.data[ident] = oper(self.data[ident]) class SceneManager(DataStoringObject): def __init__(self, scenes, events): super().__init__() self.scenes = {scene.name: scene for scene in scenes} self.events = {event.beat: [ev for ev in events if ev.beat == event.beat] for event in events} for scene in scenes: scene.set_parent(self) self.active_scene = [] self.cur_beat = -1 self.data = {} def start_scene(self, scene, at=0): if len(self.active_scene) == 0: self.active_scene.append(None) self.active_scene[0] = self.scenes[scene] self.active_scene[0].start(at) def add_scene(self, scene, at=0): self.active_scene.append(self.scenes[scene]) self.active_scene[-1].start(at) def remove_scene(self, scene): if self.scenes[scene] in self.active_scene: self.active_scene.remove(self.scenes[scene]) def request_next(self, render=True): for scene in self.active_scene: scene.request_frame(render) self.next_beat() def next_beat(self): self.cur_beat += 1 if self.cur_beat in self.events: for event in self.events[self.cur_beat]: event.do(self) def set_scene_data(self, scene, *ident): self.scenes[scene].set_data(*ident) def set_generator_data(self, scene, generator, *ident): self.scenes[scene].generators[generator].set_data(*ident) class Event: def __init__(self, beat, do): self.beat = beat self.do = do @staticmethod def swap_scene(sc, at=0): return lambda sm: sm.start_scene(sc, at) @staticmethod def layer_scene(sc, at=0): return lambda sm: sm.add_scene(sc, at) @staticmethod def remove_scene(sc): return lambda sm: sm.remove_scene(sc) class Scene(DataStoringObject): def __init__(self, name, generators): super().__init__() self.parent = None self.name = name self.generators = generators self.start_beat = 0 self.internal_beat = 0 def set_parent(self, parent): self.parent = parent # Requests a frame. This basically calls the request() and request_clear() functions in every generator. def request_frame(self, render=True): beat = self.internal_beat if render: for generator in self.generators: if beat >= generator.start_beat and generator.condition(beat): if beat != self.start_beat and beat != generator.start_beat: # Clear previous beat generator.request_clear(generator, beat - 1) # Make current generator.request(generator, beat) self.internal_beat += 1 # Starts the scene at the beat given. def start(self, at): for generator in self.generators: generator.set_parent(self.parent) generator.set_scene(self) generator.on_create(generator) self.start_beat = at self.internal_beat = at self.request_frame() class Generator(DataStoringObject): def __init__(self, start_beat, condition, on_create, request, request_clear): super().__init__() self.parent = None self.scene = None self.data = {} self.start_beat = start_beat self.condition = condition self.on_create = on_create self.request = request self.request_clear = request_clear def set_parent(self, parent): self.parent = parent def set_scene(self, scene): self.scene = scene @staticmethod def combine_conditions(*cond): return lambda b: all(c(b) for c in cond) @staticmethod def always(): return lambda b: True @staticmethod def every_n_beats(beat): return lambda b: b % beat == 0 @staticmethod def every_on_off(on, off): return lambda b: (b % (on + off)) < on @staticmethod def every_off_on(off, on): return lambda b: (b % (on + off)) >= off @staticmethod def before_n(beat): return lambda b: b < beat @staticmethod def at_beat(beat): return lambda b: b == beat @staticmethod def no_create(): return lambda g: None @staticmethod def no_request(): return lambda g, b: None ================================================ FILE: colorama/__init__.py ================================================ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. from .initialise import init, deinit, reinit, colorama_text from .ansi import Fore, Back, Style, Cursor from .ansitowin32 import AnsiToWin32 __version__ = '0.3.7' ================================================ FILE: colorama/ansi.py ================================================ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. ''' This module generates ANSI character codes to printing colors to terminals. See: http://en.wikipedia.org/wiki/ANSI_escape_code ''' CSI = '\033[' OSC = '\033]' BEL = '\007' def code_to_chars(code): return CSI + str(code) + 'm' def set_title(title): return OSC + '2;' + title + BEL def clear_screen(mode=2): return CSI + str(mode) + 'J' def clear_line(mode=2): return CSI + str(mode) + 'K' class AnsiCodes(object): def __init__(self): # the subclasses declare class attributes which are numbers. # Upon instantiation we define instance attributes, which are the same # as the class attributes but wrapped with the ANSI escape sequence for name in dir(self): if not name.startswith('_'): value = getattr(self, name) setattr(self, name, code_to_chars(value)) class AnsiCursor(object): def UP(self, n=1): return CSI + str(n) + 'A' def DOWN(self, n=1): return CSI + str(n) + 'B' def FORWARD(self, n=1): return CSI + str(n) + 'C' def BACK(self, n=1): return CSI + str(n) + 'D' def POS(self, x=1, y=1): return CSI + str(y) + ';' + str(x) + 'H' class AnsiFore(AnsiCodes): BLACK = 30 RED = 31 GREEN = 32 YELLOW = 33 BLUE = 34 MAGENTA = 35 CYAN = 36 WHITE = 37 RESET = 39 # These are fairly well supported, but not part of the standard. LIGHTBLACK_EX = 90 LIGHTRED_EX = 91 LIGHTGREEN_EX = 92 LIGHTYELLOW_EX = 93 LIGHTBLUE_EX = 94 LIGHTMAGENTA_EX = 95 LIGHTCYAN_EX = 96 LIGHTWHITE_EX = 97 class AnsiBack(AnsiCodes): BLACK = 40 RED = 41 GREEN = 42 YELLOW = 43 BLUE = 44 MAGENTA = 45 CYAN = 46 WHITE = 47 RESET = 49 # These are fairly well supported, but not part of the standard. LIGHTBLACK_EX = 100 LIGHTRED_EX = 101 LIGHTGREEN_EX = 102 LIGHTYELLOW_EX = 103 LIGHTBLUE_EX = 104 LIGHTMAGENTA_EX = 105 LIGHTCYAN_EX = 106 LIGHTWHITE_EX = 107 class AnsiStyle(AnsiCodes): BRIGHT = 1 DIM = 2 NORMAL = 22 RESET_ALL = 0 Fore = AnsiFore() Back = AnsiBack() Style = AnsiStyle() Cursor = AnsiCursor() ================================================ FILE: colorama/ansitowin32.py ================================================ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. import re import sys import os from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style from .winterm import WinTerm, WinColor, WinStyle from .win32 import windll, winapi_test winterm = None if windll is not None: winterm = WinTerm() def is_stream_closed(stream): return not hasattr(stream, 'closed') or stream.closed def is_a_tty(stream): return hasattr(stream, 'isatty') and stream.isatty() class StreamWrapper(object): ''' Wraps a stream (such as stdout), acting as a transparent proxy for all attribute access apart from method 'write()', which is delegated to our Converter instance. ''' def __init__(self, wrapped, converter): # double-underscore everything to prevent clashes with names of # attributes on the wrapped stream object. self.__wrapped = wrapped self.__convertor = converter def __getattr__(self, name): return getattr(self.__wrapped, name) def write(self, text): self.__convertor.write(text) class AnsiToWin32(object): ''' Implements a 'write()' method which, on Windows, will strip ANSI character sequences from the text, and if outputting to a tty, will convert them into win32 function calls. ''' ANSI_CSI_RE = re.compile('\001?\033\[((?:\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer ANSI_OSC_RE = re.compile('\001?\033\]((?:.|;)*?)(\x07)\002?') # Operating System Command def __init__(self, wrapped, convert=None, strip=None, autoreset=False): # The wrapped stream (normally sys.stdout or sys.stderr) self.wrapped = wrapped # should we reset colors to defaults after every .write() self.autoreset = autoreset # create the proxy wrapping our output stream self.stream = StreamWrapper(wrapped, self) on_windows = os.name == 'nt' # We test if the WinAPI works, because even if we are on Windows # we may be using a terminal that doesn't support the WinAPI # (e.g. Cygwin Terminal). In this case it's up to the terminal # to support the ANSI codes. conversion_supported = on_windows and winapi_test() # should we strip ANSI sequences from our output? if strip is None: strip = conversion_supported or (not is_stream_closed(wrapped) and not is_a_tty(wrapped)) self.strip = strip # should we should convert ANSI sequences into win32 calls? if convert is None: convert = conversion_supported and not is_stream_closed(wrapped) and is_a_tty(wrapped) self.convert = convert # dict of ansi codes to win32 functions and parameters self.win32_calls = self.get_win32_calls() # are we wrapping stderr? self.on_stderr = self.wrapped is sys.stderr def should_wrap(self): ''' True if this class is actually needed. If false, then the output stream will not be affected, nor will win32 calls be issued, so wrapping stdout is not actually required. This will generally be False on non-Windows platforms, unless optional functionality like autoreset has been requested using kwargs to init() ''' return self.convert or self.strip or self.autoreset def get_win32_calls(self): if self.convert and winterm: return { AnsiStyle.RESET_ALL: (winterm.reset_all, ), AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), AnsiFore.RED: (winterm.fore, WinColor.RED), AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), AnsiFore.WHITE: (winterm.fore, WinColor.GREY), AnsiFore.RESET: (winterm.fore, ), AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), AnsiBack.BLACK: (winterm.back, WinColor.BLACK), AnsiBack.RED: (winterm.back, WinColor.RED), AnsiBack.GREEN: (winterm.back, WinColor.GREEN), AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), AnsiBack.BLUE: (winterm.back, WinColor.BLUE), AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), AnsiBack.CYAN: (winterm.back, WinColor.CYAN), AnsiBack.WHITE: (winterm.back, WinColor.GREY), AnsiBack.RESET: (winterm.back, ), AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), } return dict() def write(self, text): if self.strip or self.convert: self.write_and_convert(text) else: self.wrapped.write(text) self.wrapped.flush() if self.autoreset: self.reset_all() def reset_all(self): if self.convert: self.call_win32('m', (0,)) elif not self.strip and not is_stream_closed(self.wrapped): self.wrapped.write(Style.RESET_ALL) def write_and_convert(self, text): ''' Write the given text to our wrapped stream, stripping any ANSI sequences from the text, and optionally converting them into win32 calls. ''' cursor = 0 text = self.convert_osc(text) for match in self.ANSI_CSI_RE.finditer(text): start, end = match.span() self.write_plain_text(text, cursor, start) self.convert_ansi(*match.groups()) cursor = end self.write_plain_text(text, cursor, len(text)) def write_plain_text(self, text, start, end): if start < end: self.wrapped.write(text[start:end]) self.wrapped.flush() def convert_ansi(self, paramstring, command): if self.convert: params = self.extract_params(command, paramstring) self.call_win32(command, params) def extract_params(self, command, paramstring): if command in 'Hf': params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) while len(params) < 2: # defaults: params = params + (1,) else: params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) if len(params) == 0: # defaults: if command in 'JKm': params = (0,) elif command in 'ABCD': params = (1,) return params def call_win32(self, command, params): if command == 'm': for param in params: if param in self.win32_calls: func_args = self.win32_calls[param] func = func_args[0] args = func_args[1:] kwargs = dict(on_stderr=self.on_stderr) func(*args, **kwargs) elif command in 'J': winterm.erase_screen(params[0], on_stderr=self.on_stderr) elif command in 'K': winterm.erase_line(params[0], on_stderr=self.on_stderr) elif command in 'Hf': # cursor position - absolute winterm.set_cursor_position(params, on_stderr=self.on_stderr) elif command in 'ABCD': # cursor position - relative n = params[0] # A - up, B - down, C - forward, D - back x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) def convert_osc(self, text): for match in self.ANSI_OSC_RE.finditer(text): start, end = match.span() text = text[:start] + text[end:] paramstring, command = match.groups() if command in '\x07': # \x07 = BEL params = paramstring.split(";") # 0 - change title and icon (we will only change title) # 1 - change icon (we don't support this) # 2 - change title if params[0] in '02': winterm.set_title(params[1]) return text ================================================ FILE: colorama/initialise.py ================================================ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. import atexit import contextlib import sys from .ansitowin32 import AnsiToWin32 orig_stdout = None orig_stderr = None wrapped_stdout = None wrapped_stderr = None atexit_done = False def reset_all(): if AnsiToWin32 is not None: # Issue #74: objects might become None at exit AnsiToWin32(orig_stdout).reset_all() def init(autoreset=False, convert=None, strip=None, wrap=True): if not wrap and any([autoreset, convert, strip]): raise ValueError('wrap=False conflicts with any other arg=True') global wrapped_stdout, wrapped_stderr global orig_stdout, orig_stderr orig_stdout = sys.stdout orig_stderr = sys.stderr if sys.stdout is None: wrapped_stdout = None else: sys.stdout = wrapped_stdout = \ wrap_stream(orig_stdout, convert, strip, autoreset, wrap) if sys.stderr is None: wrapped_stderr = None else: sys.stderr = wrapped_stderr = \ wrap_stream(orig_stderr, convert, strip, autoreset, wrap) global atexit_done if not atexit_done: atexit.register(reset_all) atexit_done = True def deinit(): if orig_stdout is not None: sys.stdout = orig_stdout if orig_stderr is not None: sys.stderr = orig_stderr @contextlib.contextmanager def colorama_text(*args, **kwargs): init(*args, **kwargs) try: yield finally: deinit() def reinit(): if wrapped_stdout is not None: sys.stdout = wrapped_stdout if wrapped_stderr is not None: sys.stderr = wrapped_stderr def wrap_stream(stream, convert, strip, autoreset, wrap): if wrap: wrapper = AnsiToWin32(stream, convert=convert, strip=strip, autoreset=autoreset) if wrapper.should_wrap(): stream = wrapper.stream return stream ================================================ FILE: colorama/win32.py ================================================ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. # from winbase.h STDOUT = -11 STDERR = -12 try: import ctypes from ctypes import LibraryLoader windll = LibraryLoader(ctypes.WinDLL) from ctypes import wintypes except (AttributeError, ImportError): windll = None SetConsoleTextAttribute = lambda *_: None winapi_test = lambda *_: None else: from ctypes import byref, Structure, c_char, POINTER COORD = wintypes._COORD class CONSOLE_SCREEN_BUFFER_INFO(Structure): """struct in wincon.h.""" _fields_ = [ ("dwSize", COORD), ("dwCursorPosition", COORD), ("wAttributes", wintypes.WORD), ("srWindow", wintypes.SMALL_RECT), ("dwMaximumWindowSize", COORD), ] def __str__(self): return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( self.dwSize.Y, self.dwSize.X , self.dwCursorPosition.Y, self.dwCursorPosition.X , self.wAttributes , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X ) _GetStdHandle = windll.kernel32.GetStdHandle _GetStdHandle.argtypes = [ wintypes.DWORD, ] _GetStdHandle.restype = wintypes.HANDLE _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo _GetConsoleScreenBufferInfo.argtypes = [ wintypes.HANDLE, POINTER(CONSOLE_SCREEN_BUFFER_INFO), ] _GetConsoleScreenBufferInfo.restype = wintypes.BOOL _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute _SetConsoleTextAttribute.argtypes = [ wintypes.HANDLE, wintypes.WORD, ] _SetConsoleTextAttribute.restype = wintypes.BOOL _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition _SetConsoleCursorPosition.argtypes = [ wintypes.HANDLE, COORD, ] _SetConsoleCursorPosition.restype = wintypes.BOOL _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA _FillConsoleOutputCharacterA.argtypes = [ wintypes.HANDLE, c_char, wintypes.DWORD, COORD, POINTER(wintypes.DWORD), ] _FillConsoleOutputCharacterA.restype = wintypes.BOOL _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute _FillConsoleOutputAttribute.argtypes = [ wintypes.HANDLE, wintypes.WORD, wintypes.DWORD, COORD, POINTER(wintypes.DWORD), ] _FillConsoleOutputAttribute.restype = wintypes.BOOL _SetConsoleTitleW = windll.kernel32.SetConsoleTitleA _SetConsoleTitleW.argtypes = [ wintypes.LPCSTR ] _SetConsoleTitleW.restype = wintypes.BOOL handles = { STDOUT: _GetStdHandle(STDOUT), STDERR: _GetStdHandle(STDERR), } def winapi_test(): handle = handles[STDOUT] csbi = CONSOLE_SCREEN_BUFFER_INFO() success = _GetConsoleScreenBufferInfo( handle, byref(csbi)) return bool(success) def GetConsoleScreenBufferInfo(stream_id=STDOUT): handle = handles[stream_id] csbi = CONSOLE_SCREEN_BUFFER_INFO() success = _GetConsoleScreenBufferInfo( handle, byref(csbi)) return csbi def SetConsoleTextAttribute(stream_id, attrs): handle = handles[stream_id] return _SetConsoleTextAttribute(handle, attrs) def SetConsoleCursorPosition(stream_id, position, adjust=True): position = COORD(*position) # If the position is out of range, do nothing. if position.Y <= 0 or position.X <= 0: return # Adjust for Windows' SetConsoleCursorPosition: # 1. being 0-based, while ANSI is 1-based. # 2. expecting (x,y), while ANSI uses (y,x). adjusted_position = COORD(position.Y - 1, position.X - 1) if adjust: # Adjust for viewport's scroll position sr = GetConsoleScreenBufferInfo(STDOUT).srWindow adjusted_position.Y += sr.Top adjusted_position.X += sr.Left # Resume normal processing handle = handles[stream_id] return _SetConsoleCursorPosition(handle, adjusted_position) def FillConsoleOutputCharacter(stream_id, char, length, start): handle = handles[stream_id] char = c_char(char.encode()) length = wintypes.DWORD(length) num_written = wintypes.DWORD(0) # Note that this is hard-coded for ANSI (vs wide) bytes. success = _FillConsoleOutputCharacterA( handle, char, length, start, byref(num_written)) return num_written.value def FillConsoleOutputAttribute(stream_id, attr, length, start): ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' handle = handles[stream_id] attribute = wintypes.WORD(attr) length = wintypes.DWORD(length) num_written = wintypes.DWORD(0) # Note that this is hard-coded for ANSI (vs wide) bytes. return _FillConsoleOutputAttribute( handle, attribute, length, start, byref(num_written)) def SetConsoleTitle(title): return _SetConsoleTitleW(title) ================================================ FILE: colorama/winterm.py ================================================ # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. from . import win32 # from wincon.h class WinColor(object): BLACK = 0 BLUE = 1 GREEN = 2 CYAN = 3 RED = 4 MAGENTA = 5 YELLOW = 6 GREY = 7 # from wincon.h class WinStyle(object): NORMAL = 0x00 # dim text, dim background BRIGHT = 0x08 # bright text, dim background BRIGHT_BACKGROUND = 0x80 # dim text, bright background class WinTerm(object): def __init__(self): self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes self.set_attrs(self._default) self._default_fore = self._fore self._default_back = self._back self._default_style = self._style # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style. # So that LIGHT_EX colors and BRIGHT style do not clobber each other, # we track them separately, since LIGHT_EX is overwritten by Fore/Back # and BRIGHT is overwritten by Style codes. self._light = 0 def get_attrs(self): return self._fore + self._back * 16 + (self._style | self._light) def set_attrs(self, value): self._fore = value & 7 self._back = (value >> 4) & 7 self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) def reset_all(self, on_stderr=None): self.set_attrs(self._default) self.set_console(attrs=self._default) def fore(self, fore=None, light=False, on_stderr=False): if fore is None: fore = self._default_fore self._fore = fore # Emulate LIGHT_EX with BRIGHT Style if light: self._light |= WinStyle.BRIGHT else: self._light &= ~WinStyle.BRIGHT self.set_console(on_stderr=on_stderr) def back(self, back=None, light=False, on_stderr=False): if back is None: back = self._default_back self._back = back # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style if light: self._light |= WinStyle.BRIGHT_BACKGROUND else: self._light &= ~WinStyle.BRIGHT_BACKGROUND self.set_console(on_stderr=on_stderr) def style(self, style=None, on_stderr=False): if style is None: style = self._default_style self._style = style self.set_console(on_stderr=on_stderr) def set_console(self, attrs=None, on_stderr=False): if attrs is None: attrs = self.get_attrs() handle = win32.STDOUT if on_stderr: handle = win32.STDERR win32.SetConsoleTextAttribute(handle, attrs) def get_position(self, handle): position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition # Because Windows coordinates are 0-based, # and win32.SetConsoleCursorPosition expects 1-based. position.X += 1 position.Y += 1 return position def set_cursor_position(self, position=None, on_stderr=False): if position is None: # I'm not currently tracking the position, so there is no default. # position = self.get_position() return handle = win32.STDOUT if on_stderr: handle = win32.STDERR win32.SetConsoleCursorPosition(handle, position) def cursor_adjust(self, x, y, on_stderr=False): handle = win32.STDOUT if on_stderr: handle = win32.STDERR position = self.get_position(handle) adjusted_position = (position.Y + y, position.X + x) win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) def erase_screen(self, mode=0, on_stderr=False): # 0 should clear from the cursor to the end of the screen. # 1 should clear from the cursor to the beginning of the screen. # 2 should clear the entire screen, and move cursor to (1,1) handle = win32.STDOUT if on_stderr: handle = win32.STDERR csbi = win32.GetConsoleScreenBufferInfo(handle) # get the number of character cells in the current buffer cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y # get number of character cells before current cursor position cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X if mode == 0: from_coord = csbi.dwCursorPosition cells_to_erase = cells_in_screen - cells_before_cursor if mode == 1: from_coord = win32.COORD(0, 0) cells_to_erase = cells_before_cursor elif mode == 2: from_coord = win32.COORD(0, 0) cells_to_erase = cells_in_screen # fill the entire screen with blanks win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) # now set the buffer's attributes accordingly win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) if mode == 2: # put the cursor where needed win32.SetConsoleCursorPosition(handle, (1, 1)) def erase_line(self, mode=0, on_stderr=False): # 0 should clear from the cursor to the end of the line. # 1 should clear from the cursor to the beginning of the line. # 2 should clear the entire line. handle = win32.STDOUT if on_stderr: handle = win32.STDERR csbi = win32.GetConsoleScreenBufferInfo(handle) if mode == 0: from_coord = csbi.dwCursorPosition cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X if mode == 1: from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) cells_to_erase = csbi.dwCursorPosition.X elif mode == 2: from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) cells_to_erase = csbi.dwSize.X # fill the entire screen with blanks win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) # now set the buffer's attributes accordingly win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) def set_title(self, title): win32.SetConsoleTitle(title) ================================================ FILE: credits.py ================================================ from animation_functions import debug_info from CLIRender.classes import enable_ansi from colorama import Fore, Style import keyboard import time import os import random from just_playback import Playback from animation_scenes import all_scenes, canvas from string_defs import data_strings import animator as am enable_ansi() # canvas.render_blank() delay = 60.0 / 179.0 / 8.0 beat = -60 offset = 5.492 # print((delay * 980) + offset) skip_by = 0 debug = False last_frames = [] def skip_beats(ctr, amount, next_debug): global beat global skip_by ctr.cur_beat += amount beat += amount if debug: controller.events[next_debug] = (am.Event(next_debug, am.Event.layer_scene("debug_counter")),) skip_by = offset + (delay * amount) counter = am.Scene( "debug_counter", ( am.Generator( 0, am.Generator.always(), am.Generator.no_create(), lambda g, b: debug_info(canvas, g, b, last_frames), am.Generator.no_request() ), ) ) ocean_events = ( am.Event(60, lambda c: c.set_generator_data( "ocean_b", 1, "text", data_strings["ocean_b_0"] )), am.Event(310, lambda c: c.set_generator_data( "ocean_b", 1, "text", "[##CLEAR|60;6" )), am.Event(310, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 3 )), am.Event(310, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.NORMAL + Fore.BLUE )), am.Event(312, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.BRIGHT + Fore.BLUE )), am.Event(314, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.NORMAL + Fore.BLUE )), am.Event(320, lambda c: c.set_generator_data( "ocean_b", 1, "text", data_strings["ocean_b_1"], "offset", 0 )), am.Event(590, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.BRIGHT + Fore.BLACK )), am.Event(646, lambda c: c.set_generator_data( "ocean_b", 1, "text", "[##CLEAR|60;8" )), am.Event(652, lambda c: c.set_generator_data( "ocean_b", 1, "text", data_strings["ocean_b_2"], "offset", 0 )), am.Event(656, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.NORMAL + Fore.YELLOW )), am.Event(666, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.BRIGHT + Fore.YELLOW )), am.Event(666, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 6 )), am.Event(850, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.NORMAL + Fore.YELLOW )), am.Event(999, lambda c: c.set_generator_data( "ocean_b", 1, "text", "[##CLEAR|60;10" )), am.Event(1000, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.BRIGHT + Fore.BLACK )), am.Event(1000, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 13 )), am.Event(1000, lambda c: c.set_generator_data( "ocean_b", 1, "text", data_strings["ocean_b_3"], "offset", 0 )), am.Event(1044, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 102 )), am.Event(1048, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 230 )), am.Event(1052, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 500 )), am.Event(1056, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 760 )), am.Event(1060, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 1600 )), am.Event(1064, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 2500 )) ) ocean2_events = ( am.Event(3896, lambda c: c.set_generator_data( "ocean_c", 1, "text", data_strings["ocean_c_0"] ) ), am.Event(3896, lambda c: c.set_generator_data( "ocean_c", 2, "text", data_strings["ocean_c_1"] ) ), am.Event(3896, lambda c: c.set_generator_data( "ocean_c", 3, "text", data_strings["ocean_c_2"] ) ), am.Event(3896, lambda c: c.set_generator_data( "ocean_c", 4, "text", data_strings["ocean_c_3"] ) ), am.Event(4400, lambda c: c.set_generator_data( "ocean_c", 1, "text", "[##CLEAR|60;10" )), *( am.Event(4401 + i * 2, lambda c: c.set_generator_data( "ocean_c", 0, "ocean_col", random.choice((Style.BRIGHT, Style.NORMAL)) + Fore.BLACK )) for i in range(5) ), am.Event(4411, lambda c: c.set_generator_data( "ocean_c", 0, "ocean_col", Style.NORMAL + Fore.BLACK )), ) controller = am.SceneManager((*all_scenes, counter), ( am.Event(0, am.Event.swap_scene("wipe")), # am.Event(1, am.Event.layer_scene("debug_counter")), am.Event(58, am.Event.swap_scene("clear")), am.Event(60, am.Event.swap_scene("ocean_b")), # am.Event(60, am.Event.layer_scene("typewrite")), *ocean_events, am.Event(1079, am.Event.swap_scene("clear")), am.Event(1080, am.Event.swap_scene("beats")), am.Event(1080, am.Event.layer_scene("title")), am.Event(1080, am.Event.swap_scene("beats")), am.Event(1336, am.Event.swap_scene("beats_lr")), am.Event(1844, am.Event.remove_scene("title")), am.Event(1848, am.Event.swap_scene("funding")), am.Event(1848, am.Event.layer_scene("dates")), am.Event(1848, am.Event.layer_scene("weather")), am.Event(1848, lambda c: c.set_data( "history", [], "refresh", True )), am.Event(1848, lambda c: c.set_generator_data( "funding", 0, "text", data_strings["funding_0"] )), am.Event(2348, lambda c: c.set_generator_data( "funding", 0, "text", (('',), ('',)) )), am.Event(2348, lambda c: c.set_data( "history", [], "refresh", True )), am.Event(2352, lambda c: c.set_generator_data( "funding", 0, "text", data_strings["funding_1"], "offset", 0, "lineno", 0 )), am.Event(2976, am.Event.remove_scene("dates")), am.Event(2976, am.Event.remove_scene("weather")), am.Event(2976, am.Event.swap_scene("clear_wipe")), am.Event(3007, am.Event.swap_scene("clear")), # idk what to do here. some sort of bootup sequence? am.Event(3132, am.Event.swap_scene("loadingbar")), am.Event(3132, lambda c: c.set_generator_data( "loadingbar", 6, "text", data_strings["funding_2"] )), am.Event(3376, am.Event.swap_scene("ocean_d")), am.Event(3380, am.Event.swap_scene("clear")), am.Event(3380, am.Event.swap_scene("fastload")), am.Event(3388, am.Event.swap_scene("clear")), # Crazy part. Go wild am.Event(3390, am.Event.swap_scene("error")), am.Event(3390, am.Event.layer_scene("fundingx2")), am.Event(3390, lambda c: c.set_data( "history", [], "refresh", True )), am.Event(3390, lambda c: c.set_generator_data( "fundingx2", 0, "text", data_strings["fundingx2_0"] )), am.Event(3390, lambda c: c.set_generator_data( "fundingx2", 1, "text", data_strings["fundingx2_1"] )), am.Event(3390, lambda c: c.set_generator_data( "fundingx2", 3, "text", data_strings["fundingx2_2"] )), am.Event(3895, am.Event.remove_scene("fundingx2")), am.Event(3895, am.Event.swap_scene("clear")), am.Event(3896, am.Event.swap_scene("ocean_c")), *ocean2_events, # remember to clean the ocean_c events up, or it will stay in the screen. At frame 4413. am.Event(4413, am.Event.swap_scene("clear")), am.Event(4460, am.Event.swap_scene("accesspoints")), am.Event(4460, am.Event.layer_scene("fdg_single")), am.Event(4534, lambda c: c.set_generator_data( "fdg_single", 1, "text", data_strings["fdg_single_0"] )), am.Event(5500, am.Event.remove_scene("fdg_single")), am.Event(5500, am.Event.swap_scene("clear")), am.Event(5500, am.Event.swap_scene("beats")), am.Event(5500, am.Event.layer_scene("fdg_down")), am.Event(5788, lambda c: c.set_generator_data( "fdg_down", 0, "text", data_strings["fdg_down_0"] )), am.Event(5916, lambda c: c.set_generator_data( "fdg_down", 0, "text", data_strings["fdg_down_1"], "lineno", 0, "offset", 0 )), am.Event(5916, lambda c: c.set_generator_data( "fdg_down", 1, "text", data_strings["fdg_down_2"] )), am.Event(6270, am.Event.swap_scene("beats_lr")), am.Event(6508, am.Event.remove_scene("fdg_down")), # am.Event(6508, am.Event.remove_scene("debug_counter")), am.Event(6508, am.Event.swap_scene("clear")) )) # class MixerWrapper: # def __init__(self): # self.is_paused = False # # def toggle_music(self): # if self.is_paused: # pygame.mixer.music.unpause() # self.is_paused = False # else: # pygame.mixer.music.pause() # self.is_paused = True # # # ffwing = MixerWrapper() paused_this_frame = False ff_this_frame = False filename = "media/credits.wav" playback = Playback() playback.load_file(filename) # Clear the console before showing the skip menu if os.name == "nt": os.system("cls") elif os.name == "posix": os.system("clear") else: print("\033[2J") print("\033[1;1Hskips\n\n1 | start\n2 | title\n3 | funding\n4 | loading\n5 | break\n6 | final") # Skips forward to the title scene time_menu = time.time() while time.time() - 2 < time_menu: if keyboard.is_pressed("1"): time_menu = 99999999999999999999 break elif keyboard.is_pressed("2"): skip_beats(controller, 1000, 1081) time_menu = 99999999999999999999 break elif keyboard.is_pressed("3"): skip_beats(controller, 1770, 1851) controller.events[1849] = am.Event(1849, am.Event.layer_scene("redraw_ui")), controller.events[1860] = am.Event(1860, am.Event.remove_scene("redraw_ui")), time_menu = 99999999999999999999 break elif keyboard.is_pressed("4"): skip_beats(controller, 3040, 3133) time_menu = 99999999999999999999 break elif keyboard.is_pressed("5"): skip_beats(controller, 3780, 3898) time_menu = 99999999999999999999 break elif keyboard.is_pressed("6"): skip_beats(controller, 5420, 5501) time_menu = 99999999999999999999 break time.sleep(0.01) if os.name == "nt": os.system("cls") elif os.name == "posix": os.system("clear") else: print("\033[2J") # wave_obj = sa.WaveObject.from_wave_file(filename) # play_obj = wave_obj.play() playback.play() playback.seek(skip_by) time_start = time.time() last_update = time.time() prev_pos = 0 while playback.active: # (17.06.21) might have broken, i used a -1 beat offset here to try and sync up everything better # since i originally used 1-indexed beats # # (24.06.21) update chat it didnt break # next_beat = (time.time() - time_start - offset) > ((beat - 1) * delay) next_beat = (playback.curr_pos - offset) > ((beat - 1) * delay) need_update = time.time() - (1/30) > last_update # print(pygame.mixer.music.get_pos()) if next_beat: controller.request_next() canvas.render_all() last_frames.append(time.time()) if len(last_frames) > 10: last_frames.pop(0) beat += 1 if need_update: if keyboard.is_pressed("p"): if not paused_this_frame: if playback.paused: playback.resume() else: playback.pause() paused_this_frame = True else: paused_this_frame = False if keyboard.is_pressed(","): if not ff_this_frame: ff_this_frame = True else: playback.seek(playback.curr_pos + delay * 3) if keyboard.is_pressed("."): if not ff_this_frame: ff_this_frame = True else: playback.seek(playback.curr_pos + delay * 7) if keyboard.is_pressed("/"): if not ff_this_frame: ff_this_frame = True else: playback.seek(playback.curr_pos + delay * 15) # else: # # print("n", ff_this_frame, pygame.mixer.music.get_pos() + prev_pos, prev_pos) # if ff_this_frame: # # print("unpausing now") # ff_this_frame = False # ffwing.toggle_music() last_update = time.time() # Add a final clear at the end to prevent the last frame from sticking around when the program ends. if os.name == "nt": os.system("cls") elif os.name == "posix": os.system("clear") else: print("\033[2J") ================================================ FILE: credits_pynput.py ================================================ from animation_functions import debug_info from CLIRender.classes import enable_ansi from colorama import Fore, Style from pynput import keyboard import time import os import random from just_playback import Playback from animation_scenes import all_scenes, canvas from string_defs import data_strings import animator as am enable_ansi() # canvas.render_blank() delay = 60.0 / 179.0 / 8.0 beat = -60 offset = 5.492 # print((delay * 980) + offset) skip_by = 0 debug = False last_frames = [] def skip_beats(ctr, amount, next_debug): global beat global skip_by ctr.cur_beat += amount beat += amount if debug: controller.events[next_debug] = (am.Event(next_debug, am.Event.layer_scene("debug_counter")),) skip_by = offset + (delay * amount) counter = am.Scene( "debug_counter", ( am.Generator( 0, am.Generator.always(), am.Generator.no_create(), lambda g, b: debug_info(canvas, g, b, last_frames), am.Generator.no_request() ), ) ) ocean_events = ( am.Event(60, lambda c: c.set_generator_data( "ocean_b", 1, "text", data_strings["ocean_b_0"] )), am.Event(310, lambda c: c.set_generator_data( "ocean_b", 1, "text", "[##CLEAR|60;6" )), am.Event(310, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 3 )), am.Event(310, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.NORMAL + Fore.BLUE )), am.Event(312, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.BRIGHT + Fore.BLUE )), am.Event(314, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.NORMAL + Fore.BLUE )), am.Event(320, lambda c: c.set_generator_data( "ocean_b", 1, "text", data_strings["ocean_b_1"], "offset", 0 )), am.Event(590, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.BRIGHT + Fore.BLACK )), am.Event(646, lambda c: c.set_generator_data( "ocean_b", 1, "text", "[##CLEAR|60;8" )), am.Event(652, lambda c: c.set_generator_data( "ocean_b", 1, "text", data_strings["ocean_b_2"], "offset", 0 )), am.Event(656, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.NORMAL + Fore.YELLOW )), am.Event(666, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.BRIGHT + Fore.YELLOW )), am.Event(666, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 6 )), am.Event(850, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.NORMAL + Fore.YELLOW )), am.Event(999, lambda c: c.set_generator_data( "ocean_b", 1, "text", "[##CLEAR|60;10" )), am.Event(1000, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_col", Style.BRIGHT + Fore.BLACK )), am.Event(1000, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 13 )), am.Event(1000, lambda c: c.set_generator_data( "ocean_b", 1, "text", data_strings["ocean_b_3"], "offset", 0 )), am.Event(1044, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 102 )), am.Event(1048, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 230 )), am.Event(1052, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 500 )), am.Event(1056, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 760 )), am.Event(1060, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 1600 )), am.Event(1064, lambda c: c.set_generator_data( "ocean_b", 0, "ocean_glitch", 2500 )) ) ocean2_events = ( am.Event(3896, lambda c: c.set_generator_data( "ocean_c", 1, "text", data_strings["ocean_c_0"] ) ), am.Event(3896, lambda c: c.set_generator_data( "ocean_c", 2, "text", data_strings["ocean_c_1"] ) ), am.Event(3896, lambda c: c.set_generator_data( "ocean_c", 3, "text", data_strings["ocean_c_2"] ) ), am.Event(3896, lambda c: c.set_generator_data( "ocean_c", 4, "text", data_strings["ocean_c_3"] ) ), am.Event(4400, lambda c: c.set_generator_data( "ocean_c", 1, "text", "[##CLEAR|60;10" )), *( am.Event(4401 + i * 2, lambda c: c.set_generator_data( "ocean_c", 0, "ocean_col", random.choice((Style.BRIGHT, Style.NORMAL)) + Fore.BLACK )) for i in range(5) ), am.Event(4411, lambda c: c.set_generator_data( "ocean_c", 0, "ocean_col", Style.NORMAL + Fore.BLACK )), ) controller = am.SceneManager((*all_scenes, counter), ( am.Event(0, am.Event.swap_scene("wipe")), # am.Event(1, am.Event.layer_scene("debug_counter")), am.Event(58, am.Event.swap_scene("clear")), am.Event(60, am.Event.swap_scene("ocean_b")), # am.Event(60, am.Event.layer_scene("typewrite")), *ocean_events, am.Event(1079, am.Event.swap_scene("clear")), am.Event(1080, am.Event.swap_scene("beats")), am.Event(1080, am.Event.layer_scene("title")), am.Event(1080, am.Event.swap_scene("beats")), am.Event(1336, am.Event.swap_scene("beats_lr")), am.Event(1844, am.Event.remove_scene("title")), am.Event(1848, am.Event.swap_scene("funding")), am.Event(1848, am.Event.layer_scene("dates")), am.Event(1848, am.Event.layer_scene("weather")), am.Event(1848, lambda c: c.set_data( "history", [], "refresh", True )), am.Event(1848, lambda c: c.set_generator_data( "funding", 0, "text", data_strings["funding_0"] )), am.Event(2348, lambda c: c.set_generator_data( "funding", 0, "text", (('',), ('',)) )), am.Event(2348, lambda c: c.set_data( "history", [], "refresh", True )), am.Event(2352, lambda c: c.set_generator_data( "funding", 0, "text", data_strings["funding_1"], "offset", 0, "lineno", 0 )), am.Event(2976, am.Event.remove_scene("dates")), am.Event(2976, am.Event.remove_scene("weather")), am.Event(2976, am.Event.swap_scene("clear_wipe")), am.Event(3007, am.Event.swap_scene("clear")), # idk what to do here. some sort of bootup sequence? am.Event(3132, am.Event.swap_scene("loadingbar")), am.Event(3132, lambda c: c.set_generator_data( "loadingbar", 6, "text", data_strings["funding_2"] )), am.Event(3376, am.Event.swap_scene("ocean_d")), am.Event(3380, am.Event.swap_scene("clear")), am.Event(3380, am.Event.swap_scene("fastload")), am.Event(3388, am.Event.swap_scene("clear")), # Crazy part. Go wild am.Event(3390, am.Event.swap_scene("error")), am.Event(3390, am.Event.layer_scene("fundingx2")), am.Event(3390, lambda c: c.set_data( "history", [], "refresh", True )), am.Event(3390, lambda c: c.set_generator_data( "fundingx2", 0, "text", data_strings["fundingx2_0"] )), am.Event(3390, lambda c: c.set_generator_data( "fundingx2", 1, "text", data_strings["fundingx2_1"] )), am.Event(3390, lambda c: c.set_generator_data( "fundingx2", 3, "text", data_strings["fundingx2_2"] )), am.Event(3895, am.Event.remove_scene("fundingx2")), am.Event(3895, am.Event.swap_scene("clear")), am.Event(3896, am.Event.swap_scene("ocean_c")), *ocean2_events, # remember to clean the ocean_c events up, or it will stay in the screen. At frame 4413. am.Event(4413, am.Event.swap_scene("clear")), am.Event(4460, am.Event.swap_scene("accesspoints")), am.Event(4460, am.Event.layer_scene("fdg_single")), am.Event(4534, lambda c: c.set_generator_data( "fdg_single", 1, "text", data_strings["fdg_single_0"] )), am.Event(5500, am.Event.remove_scene("fdg_single")), am.Event(5500, am.Event.swap_scene("clear")), am.Event(5500, am.Event.swap_scene("beats")), am.Event(5500, am.Event.layer_scene("fdg_down")), am.Event(5788, lambda c: c.set_generator_data( "fdg_down", 0, "text", data_strings["fdg_down_0"] )), am.Event(5916, lambda c: c.set_generator_data( "fdg_down", 0, "text", data_strings["fdg_down_1"], "lineno", 0, "offset", 0 )), am.Event(5916, lambda c: c.set_generator_data( "fdg_down", 1, "text", data_strings["fdg_down_2"] )), am.Event(6270, am.Event.swap_scene("beats_lr")), am.Event(6508, am.Event.remove_scene("fdg_down")), # am.Event(6508, am.Event.remove_scene("debug_counter")), am.Event(6508, am.Event.swap_scene("clear")) )) # class MixerWrapper: # def __init__(self): # self.is_paused = False # # def toggle_music(self): # if self.is_paused: # pygame.mixer.music.unpause() # self.is_paused = False # else: # pygame.mixer.music.pause() # self.is_paused = True # # # ffwing = MixerWrapper() paused_this_frame = False ff_this_frame = False filename = "media/credits.wav" playback = Playback() playback.load_file(filename) # Clear the console before showing the skip menu if os.name == "nt": os.system("cls") elif os.name == "posix": os.system("clear") else: print("\033[2J") print("\033[1;1Hskips\n\n1 | start\n2 | title\n3 | funding\n4 | loading\n5 | break\n6 | final") # Skips forward to the title scene key_states = { '1': False, '2': False, '3': False, '4': False, '5': False, '6': False, 'p': False, ',': False, '.': False, '/': False } def on_press(key): try: if key.char in key_states: key_states[key.char] = True except AttributeError: pass def on_release(key): try: if key.char in key_states: key_states[key.char] = False except AttributeError: pass listener = keyboard.Listener(on_press=on_press, on_release=on_release) listener.start() time_menu = time.time() while time.time() - 2 < time_menu: if key_states['1']: time_menu = 99999999999999999999 break elif key_states['2']: skip_beats(controller, 1000, 1081) time_menu = 99999999999999999999 break elif key_states['3']: skip_beats(controller, 1770, 1851) controller.events[1849] = am.Event(1849, am.Event.layer_scene("redraw_ui")), controller.events[1860] = am.Event(1860, am.Event.remove_scene("redraw_ui")), time_menu = 99999999999999999999 break elif key_states['4']: skip_beats(controller, 3040, 3133) time_menu = 99999999999999999999 break elif key_states['5']: skip_beats(controller, 3780, 3898) time_menu = 99999999999999999999 break elif key_states['6']: skip_beats(controller, 5420, 5501) time_menu = 99999999999999999999 break time.sleep(0.01) if os.name == "nt": os.system("cls") elif os.name == "posix": os.system("clear") else: print("\033[2J") # wave_obj = sa.WaveObject.from_wave_file(filename) # play_obj = wave_obj.play() playback.play() playback.seek(skip_by) time_start = time.time() last_update = time.time() prev_pos = 0 while playback.active: # (17.06.21) might have broken, i used a -1 beat offset here to try and sync up everything better # since i originally used 1-indexed beats # # (24.06.21) update chat it didnt break # next_beat = (time.time() - time_start - offset) > ((beat - 1) * delay) next_beat = (playback.curr_pos - offset) > ((beat - 1) * delay) need_update = time.time() - (1/30) > last_update # print(pygame.mixer.music.get_pos()) if next_beat: controller.request_next() canvas.render_all() last_frames.append(time.time()) if len(last_frames) > 10: last_frames.pop(0) beat += 1 if need_update: if key_states['p']: if not paused_this_frame: if playback.paused: playback.resume() else: playback.pause() paused_this_frame = True else: paused_this_frame = False if key_states[',']: if not ff_this_frame: ff_this_frame = True else: playback.seek(playback.curr_pos + delay * 3) if key_states['.']: if not ff_this_frame: ff_this_frame = True else: playback.seek(playback.curr_pos + delay * 7) if key_states['/']: if not ff_this_frame: ff_this_frame = True else: playback.seek(playback.curr_pos + delay * 15) # else: # # print("n", ff_this_frame, pygame.mixer.music.get_pos() + prev_pos, prev_pos) # if ff_this_frame: # # print("unpausing now") # ff_this_frame = False # ffwing.toggle_music() last_update = time.time() # Add a final clear at the end to prevent the last frame from sticking around when the program ends. if os.name == "nt": os.system("cls") elif os.name == "posix": os.system("clear") else: print("\033[2J") ================================================ FILE: media/README.txt ================================================ I can't really distribute a full .wav file of an entire song that I don't own the rights to. That would be disrespectful. If you want to run this on your own device, you'll need to find yourself a copy of Frums 「Credits LONG」 for yourself. Sorry. ----------- If you do find a copy, convert it to "credits.wav" and drop it in this /media folder. ================================================ FILE: media/credits.txt ================================================ Now for the official national weather service forecast for Eastern Massachusets inside of I-495, including Boston, issued at 7:21 PM, Thursday, October 22nd. Tonight: Mostly cloudy with isolated showers until midight, then mostly clear after midnight. Lows in the lower 40s. West winds 10-15 mph with gusts up to 25 mph. Chance of rain: 20 percent. Friday: Sunny. Lush colour with highs in the lower 50s. Northwest winds 10-15mph with gusts up to 25mph. Friday night, mostly clear. Lows in the mid-30s. North winds 10-15mph. Saturday: Partly sunny. High-igh-igh-igh-igh-igh-igh-igh-igh-igh-igh-igh-igh-igh-igh-igh-igh-igh-igh... 22.10.2009: Cloudy, precip 20%, temp 43, wind dir 6, 13 (25) 23.10.2009: Sunny, precip 4%, temp 52, wind dir 7, 12 (25) 24.10.2009: Partly sunny, precip 7%, temp 48, wind dir 1, 8 (20) 25.10.2009 and beyond: randomly mutate value. Prefer 33% precip, move wind dir by +-3 each day, gust = speed x 2.1 - 2.6 Funding for this program was made possible by by by by by Fun-by-by-by-by-ding by-by-by-by-for thiiiii Program. Program. Pro-pro-pro-pro-pro-gram. Funding for-by-by-made possible by viewers like you. like you. like you. like you. like you. like you. like you ... ... Annual financial suppor- Annual financial suppor- Annual financial suppor- Annual financial suppor- Annual financial suppor- Annual financial suppor- Annual financial suppor- Annual financial suppor- Annu- Annu- Th-th-th-thank you. By-ci-po-po-cor-cor-by-by-rrrrrrroooooaaaaaa- -wers-ble-b-b-F-f-fi-i-naaaaaaaa- Fun-funding, fu-fun-fu-fun-funding Fu-fu-fun-funding, fu-fun-fu-funding F-i-n-a-n--po-po-cor-by-por-portio-portio-portion-nnn B-b-b-by b-by b-by b-by >>>>> By-by-finan-b-b-nc-nc- Corrrrrr Viewers like you. like you. like you. Funding-Funding-Fun-fu-funding Fu-fu-fu-fu-Fu-fu-fu-fu- Corporationnnn The corportation for public broadcasting and bi-annual fiii- of-nan-for-financial su-for financial asssss- in-viewers-you- This is PBS. ... ... Here are the 7 PM observations for the Boston metropolitan area. At Logan Airport, it was cloudy. The temperature as 68 degrees. The dew point, 47 - and the relative humidity, 46 percent. The wind was southwest at 13 miles an hour. The pressure was 29.99 inches and rising. Elsewherrrrrrrrrrrrr ... ... Funding for this program was made possible by viewers... ================================================ FILE: ocean.py ================================================ # adapted from the-sea.js by plaao (me) # The ocean is stored as a list of strings which is combined into one string when rendered. # It is rendered internally in 40x8 resolution. import math import random ocean_time = math.floor(random.random() * 2000) alphabet = "abcdefghijklmnopqrstuvwxyz" def init_populate_ocean(): global ocean_time # Create 12 lists of 80 chars each and get an ocean slice for every ocean time value needed. ocean_base = get_ocean_slices(ocean_time, ocean_time + 80) ocean_time += 80 return ocean_base def get_ocean_slice(xr, glitch): x = xr / 5 c = math.cos(0.2 * x) + math.sin(0.3 * x) * math.sin(0.23 * x) y = -math.floor(2 * c * math.sin(x)) + 3 # Returns a list of chars which can then be populated into the ocean. cont_list = [] for i in range(10): if random.random() <= 0.002 * glitch: cont_list.append(alphabet[math.floor(random.random() * len(alphabet))]) else: if i == y: cont_list.append("#") elif i > y: cont_list.append(".") else: cont_list.append(" ") return cont_list def mutate_text(txt, glitch): for i in range(len(txt)): ch = txt[i] if ch in "#. " and random.random() <= (0.0002 + ((ocean_time % 1200) / 1200000)) * glitch: txt = txt[:i] + alphabet[math.floor(random.random() * len(alphabet))] + txt[i + 1:] return txt def get_ocean_slices(x1, x2): cont_list = [ "", "", "", "", "", "", "", "", "", "" ] for xr in range(x1, x2): x = xr / 5 c = math.cos(0.2 * x) + math.sin(0.3 * x) * math.sin(0.23 * x) y = -math.floor(2 * c * math.sin(x)) + 3 # Returns a list of chars which can then be populated into the ocean. for i in range(10): if random.random() <= 0.002: cont_list[i] += alphabet[math.floor(random.random() * len(alphabet))] else: if i == y: cont_list[i] += "#" elif i > y: cont_list[i] += "." else: cont_list[i] += " " return cont_list def update_ocean_slices(ocean_content, ocean_glitch): global ocean_time # edits in place # get the new slice ocean_slice = get_ocean_slice(ocean_time, ocean_glitch) ocean_time += 1 # for every line in content, remove the first char and add the slice char for i in range(10): ocean_content[i] = ocean_content[i][1:] + ocean_slice[i] raw_txt = unpack_content_to_text(ocean_content) return mutate_text(raw_txt, ocean_glitch) def unpack_content_to_text(content): # Join every line with \n return "".join(line for line in content) def begin_ocean(): return init_populate_ocean() ================================================ FILE: requirements.txt ================================================ just_playback pynput; sys_platform == "darwin" keyboard; sys_platform != "darwin" ================================================ FILE: string_defs.py ================================================ # Contains strings used inside credits.py. # Kept inside this file to reduce clutter. # Implemented as a dictionary of in-memory strings. from animation_functions import split_word_template, fuck_up_text, work_out_date data_strings = {} data_strings["ocean_b_0"] = ( "Now for the official national~ weather~ service~ forecast\n" "~~~~for~~ Eastern Massachusetts~ inside of~~ I-~~~~4~~~~9~~~~~~~5~~,\n" "~~~~~~~~ including Boston,\n\n" "~~~~~~~~issued at 7~~~~:2~~1~~~~ PM~~~~, ~~~~~~~~~~~~~~Thursday, October~~ 2~~2~~nd." ) data_strings["ocean_b_1"] = ( "Tonight:\n\n~~~~~Mostly cloudy with isolated~ showers~ until~~ mid~~~~night,\n" "~~~~~~~~~then mostly clear~~ after~~ mid~night.\n" "~~~~~~~~~Lows in the lower 4~~~~0~~~~s.\n" "~~~~~~~~~West winds 10~ to~~ 1~~5~~ miles~~ an~~ hour\n" "~~~~~with~ gusts~~ up~ to~ 2~~~~5~~~~ miles~~ an~~ hour~~.\n" "~~~~~~~~~~Chance of rain:~~~~ 2~~0~~~~ per~cent." ) data_strings["ocean_b_2"] = ( "Friday:\n\n~~Sunny.\n~~~~~~~~~~~~~~~~Lush colour with highs in the low~er 5~~~0~s.\n" "~~~~~~~~~~Northwest~~ winds~~ 10~~-~1~~5~~ miles~~ an~~ hour\n" "~~~~with gusts up to~~ 2~~~~5~~~~ miles~~ an~~ hour~~.\n" "~~~~~~~~~~~~~~~~Friday night,~~ mostly~ clear.\n" "~~~~~~~~~~~~~~Lows in the mid-3~~~~0~~s.\n" "~~~~~~~~~~~~~~~~North winds 10-1~~~5~~ miles~~ an~~ hour~~." ) data_strings["ocean_b_3"] = ( "Saturday:\n\n~~~~~~~~Partly sunny.\n" "~~~~~~~~High-@igh-@igh-@igh-@igh-@igh-@igh-@igh-@igh-@igh-@igh-@igh-@igh-@igh-@igh-@igh-@igh-@igh-@igh-i" ) data_strings["ocean_c_0"] = ( "Here~ are~ the 7~~~~ P~~~~M~~~~ ob~ser~va~tions for~~ the\n" "~~~Bos~ton~~ metro~po~li~tan~~~ ar~ea.\n\n" "~~~~~~~~~~~~~~~At Logan~~~~ Airport,~~~~~~ it was clou~~~~dy.\n" "~~~~~~~~~~~~~~~The tem~per~a~ture was 6~~~~8~~~~ de~grees,\n" "~~~~~~~~the dew point,~~ 4~~~~7~~ -\n" "~~~~~~and~ the~ re~la~tive~~ hu~mi~di~ty,~~~~ 4~~~~6~~~~ per~~cent.\n" "~~~~~~~~~~~~~~~~The~ wind~ was~ south~~west~ at 1~~~~3~ miles~~~~ an~~~~ ho~ur.\n" "~~~~~~~~~~~~The~ pres~~sure~~ was~~ 2~~~~9~~~~.~~~~~~~~9~~~~~~9~~~~~~ in~ches and ri~sing.\n" "~~~~~~~~Elsewher" + fuck_up_text( "rrrrrrrrr\n", 800 ) ) data_strings["ocean_c_1"] = ( fuck_up_text( "Here~ are~ the 7~~~~ P~~~~M~~~~ ob~ser~va~tions for~~ the\n" "~~~Bos~ton~~ metro~po~li~tan~~~ ar~ea.\n\n" "~~~~~~~~~~~~~~~At Logan~~~~ Airport,~~~~~~ it was clou~~~~dy.\n" "~~~~~~~~~~~~~~~The tem~per~a~ture was 6~~~~8~~~~ de~grees,\n" "~~~~~~~~the dew point,~~ 4~~~~7~~ -\n" "~~~~~~and~ the~ re~la~tive~~ hu~mi~di~ty,~~~~ 4~~~~6~~~~ per~~cent.\n" "~~~~~~~~~~~~~~~~The~ wind~ was~ south~~west~ at 1~~~~3~ miles~~~~ an~~~~ ho~ur.\n" "~~~~~~~~~~~~The~ pres~~sure~~ was~~ 2~~~~9~~~~.~~~~~~~~9~~~~~~9~~~~~~ in~ches and ri~sing.\n" "~~~~~~~~Elsewher", 100 ) + fuck_up_text( "rrrrrrrrr\n", 900 ) ) data_strings["ocean_c_2"] = ( fuck_up_text( "Here~ are~ the 7~~~~ P~~~~M~~~~ ob~ser~va~tions for~~ the\n" "~~~Bos~ton~~ metro~po~li~tan~~~ ar~ea.\n\n" "~~~~~~~~~~~~~~~At Logan~~~~ Airport,~~~~~~ it was clou~~~~dy.\n" "~~~~~~~~~~~~~~~The tem~per~a~ture was 6~~~~8~~~~ de~grees,\n" "~~~~~~~~the dew point,~~ 4~~~~7~~ -\n" "~~~~~~and~ the~ re~la~tive~~ hu~mi~di~ty,~~~~ 4~~~~6~~~~ per~~cent.\n" "~~~~~~~~~~~~~~~~The~ wind~ was~ south~~west~ at 1~~~~3~ miles~~~~ an~~~~ ho~ur.\n" "~~~~~~~~~~~~The~ pres~~sure~~ was~~ 2~~~~9~~~~.~~~~~~~~9~~~~~~9~~~~~~ in~ches and ri~sing.\n" "~~~~~~~~Elsewher", 200 ) + fuck_up_text( "rrrrrrrrr\n", 1000 ) ) data_strings["ocean_c_3"] = ( fuck_up_text( "Here~ are~ the 7~~~~ P~~~~M~~~~ ob~ser~va~tions for~~ the\n" "~~~Bos~ton~~ metro~po~li~tan~~~ ar~ea.\n\n" "~~~~~~~~~~~~~~~At Logan~~~~ Airport,~~~~~~ it was clou~~~~dy.\n" "~~~~~~~~~~~~~~~The tem~per~a~ture was 6~~~~8~~~~ de~grees,\n" "~~~~~~~~the dew point,~~ 4~~~~7~~ -\n" "~~~~~~and~ the~ re~la~tive~~ hu~mi~di~ty,~~~~ 4~~~~6~~~~ per~~cent.\n" "~~~~~~~~~~~~~~~~The~ wind~ was~ south~~west~ at 1~~~~3~ miles~~~~ an~~~~ ho~ur.\n" "~~~~~~~~~~~~The~ pres~~sure~~ was~~ 2~~~~9~~~~.~~~~~~~~9~~~~~~9~~~~~~ in~ches and ri~sing.\n" "~~~~~~~~Elsewher", 250 ) + fuck_up_text( "rrrrrrrrr\n", 1000 ) ) data_strings["funding_0"] = ( split_word_template( "Fun####ding#### for#### this#### pro####gram#### was#### made#### pos####si####ble###~\n" " by###\n" " by###\n" " by###\n" " by###\n" " by#\n" "Fun#\n" " by\n" " by\n" " by\n" " by\n" "Funding#\n" " by\n" " by\n" " by\n" " by\n" "Funding for\n" "Funding for thi#i#i#i#i#\n" "Funding for this pro####gram###\n" "Funding for this pro####gram###\n" " pro\n" " pro\n" " pro\n" "Funding for this pro#gram.#~\n" "Fun####ding#### for#\n" " by#\n" " by#\n" "Funding made#### pos####si####ble#### by#### view####ers#### like#### you.###~\n" "####like#### you.##\n" "####like#### you.##\n" "####like#### you.##\n" "####like#### you.##\n" "####like#### you.##\n" "####like#### you.\n" " Fu\n" " Fu\n" ) * 2 ) data_strings["funding_1"] = ( split_word_template( "Broad####cast####\n" "Broadcast Cor####por##a####tion.~#####\n" "Cor####po##ra####tion.#####\n" "Cor####po##ra####tion.#####\n" " Cor###po\n" " Cor###po\n" " Co\n" " Co\n" "Cor####po##ra####tion.#####\n" "Cor####po##ra####tion.#####\n" "Cor####po##ra####tion.#####\n" " Cor###po\n" " Cor###po\n" " Co\n" " Co\n" "Cor####po##ra###tion.#\n" " Co\n" " Co\n" "Cor####po##ra###tion.#\n" " Co\n" " Co\n" "Cor####po##ra###tion.#\n" " Co\n" " Co\n" "Cor####po##ra###tion.#\n" " Co\n" " Co\n" "Cor####po##ra###tion.#\n" " Co\n" " Co\n" "Cor####po##ra###tion.#\n" " Co\n" " Co\n" " Cor###po\n" " Cor###po\n" " Cor###po\n" " Cor###po\n" "...#######\n" " Cor###po#\n" " cor##\n" " cor###po#\n" " cor###po#\n" " cor##\n" " cor###po#\n" " cor###po#\n" " cor##\n" " cor###po#\n" " cor##\n" " cor##\n" " cor##\n" " co#\n" " co#\n" " cor###po#\n" " cor##\n" " cor###po#\n" " cor###po#\n" " cor##\n" " cor##\n" " cor###po#\n" + fuck_up_text( " cor###po#\n" " cor#\n" " cor###po#\n" " cor#\n" " cor#\n", 100, "# " ) + " ...#\n" + fuck_up_text( " co#\n" " co#\n" " cor###po#\n" " cor##\n" " cor###po#\n" " cor###po#\n" " cor##\n" " cor###po#\n", 280, "# " ) + fuck_up_text( " cor###po#\n" " cor##\n" " cor###po#\n" " cor#\n" " cor#\n" " cor#\n", 500, "# " ) + fuck_up_text( " co#\n" " co#\n" " cor###po#\n" " cor#\n" " cor###po#\n" " cor###po#\n" " cor#\n" " cor###po#\n", 650, "# " ) + " ???###p?#\n" " ??r#\n" " ???###??#\n" " co?#\n" " ???#\n" "....#....#....#....#....#....#....#....#...#...#...#...#..#..\n" " <##C##O##N##N##E##C##T##I##O##N## ##L##O##S##T##>" "##########################################################################################################" "##########################################################################################################" "##########################################################################################################" "##########################################################################################################" ) ) data_strings["funding_2"] = ( split_word_template( "--####--####-- ####-i####-a-####---- ####-up####-o-#\n" + "-n####nu####-l ####fi####nan####cial ####sup####por#\n" + "An####nu####al ####fi####nan####cial ####sup####por#\n" * 5 + "An####nu###\nAn####nu###\n" ) ) data_strings["fundingx2_0"] = ( split_word_template( " By\n" " ci\n" " po\n" " po\n" " cor#\n" " cor#\n" " by#\n" " by#\n" "rr#rr#rr#ro#oo#oo#aa#aa#aa#\n##" " wers#\n" " ble#\n" " b\n" " b\n" " F#\n" " f\n" " fi\n" " i\n" "na#aa#aa#aa#aa#aa#aa#aa#\n" " Fun\n" "Fun####ding#\n" " Fu#\n" " Fun#\n" " fu#\n" "Fun####ding#\n" " Fu#\n" " Fun#\n" " fu#\n" "Fun####ding#\n" " Fu#\n" " Fun#\n" " fu#\n" "Fun####ding#\n" "F\n" " Fi\n" " Fin\n" " Fina\n" " Finan\n" "po\n" "po\n" "cor#\n" "by#\n" " por\n" "Por#tio##\n" "Por#tio##\n" "Por#tion# nn#nn#nn\n" "b\n" " b\n" "by###\n" "---- by###\n" "-------- by###\n" "------------ by###\n" "---------------- by#######\n" ">>#>>###\n" "By#\n" " by#\n" "fi#nan#\n" " b#\n" " b#\n" " nc#\n" " nc#\n" "Corr#rr#rr#\n" "View####ers#### like#### you.#\n" " like#### you.#\n" " like## you.#\n" "Fun####ding###\n" "Fun####ding###\n" " Fun###\n" " Fu#\n" "Fun####ding###\n" " fu#\n" " fu#\n" " fu#\n" " fu#\n" " fu#\n" "Cor####por####a####tion\n" "The#### cor####por####a####tion#### for#### pub####lic####" " broad####cast####ing#### and#### bi####-an####nual#### fii#ii#i\n" "of#\n" "nan#\n" "for#\n" "fin####an####cial#### su#\n" "for#### fin####an####cial#### ass#ss#ss\n" "in#\n" "view####ers###\n" "you#####\n" "| ########This######## is######## P####B####S!############~\n" ) ) data_strings["fundingx2_1"] = ( split_word_template("\n".join( "{}#### Unknown###".format( work_out_date((64 * 3390) + i * 64)).replace(".", ".####") for i in range(26) ) + "\n| ########This######## is######## P####B####S!############~\n") ) data_strings["fundingx2_2"] = ( split_word_template( "S#e#a#r#c#h#i#n#g# #f#o#r# #a#c#c#e#s##s## ##p##o##i##n##t" "########.########.########.########\n" + "Searching for access point########.########.########.########\n" * 11 + "Found:################" " P#B#S# #O#f#f#i#c#i#a#l# #1#1#.#### #R#e#s#t#a#r#t#i#n#g#.####.####.####" ) ) data_strings["fdg_single_0"] = ( split_word_template( "########.########.########.########.########.########.########.#######\n" "########.########.########.########.########.########.########.#######\n" "########.########.########.########.########.########.########.#######\n" "########.########.########.########.########.########.########.#######\n" "########.########.########.########.########.########.########.#######\n" "########.########.########.########.########.########.########.#######\n" "Fun####ding#### for#### this#### pro####gram#### was#### made#### pos####sible#\n" " 7 > > > by###\n" " 7 > > > > by###\n" " 8 > > > by###\n" " 8 > > > > by###\n" " 9 > > > by###\n" "Fun##\n" " by\n" " by\n" " by\n" " by\n" "Funding#\n" " by\n" " by\n" " by\n" " by\n" "for\n" "thi#i#i#i\n" "Pro####gram.#\n" "Pro####gram.#\n" "Pro\n" " pro\n" " pro\n" " pro\n" "Pro####gram.###\n" "Fun####ding#### for####\n" " by#\n" " by#\n" "Funding for made#### pos####sible#### by#### view####ers#### like#### you.###\n" "like#### you.#####\n" "##like#### you.#####\n" "##like#### you.#####\n" "##like#### you.#####\n" "##like#### you.#####\n" "##like#### you.###\n" "Fu\n" " Fu\n" "Fun####ding#### for#### this#### pro####gram#### was#### made#### pos####sible#### by#\n" " by###\n" " by###\n" " by###\n" " by#\n" "Fun###\n" " by\n" " by\n" " by\n" " by\n" "Funding#\n" " by\n" " by\n" " by\n" " by\n" "for\n" "thi#i#i#i\n" "Pro####gram.###\n" "Pro####gram.###\n" "Pro\n" " pro\n" " pro\n" " pro\n" "Pro####gram.#\n" "Fun####ding#### for####\n" " by#\n" " by#\n" "Funding for made#### pos####sible#### by#### view####ers#### like#### you.###\n" "like#### you.#####\n" "##like#### you.#####\n" "##like#### you.#####\n" "##like#### you.#####\n" "##like#### you.#####\n" "####< RET 200################################################################\n" ) ) data_strings["fdg_down_0"] = ( split_word_template( "Fun####ding#### for#### this#### pro####gram#### was#\n" "made#### made#### made#### made#### made#### made#### made#### made#### made###\n" "pos####sible#### by#### view####ers#### like#### you.#######" ) ) data_strings["fdg_down_1"] = ( split_word_template( "---####----#### ---#### ----#### ---####----#### ---#\n" "----#### ----#### ----#### ----#### ----#### ----#### ----#### ----#### ----###\n" "---####-----#### --#### ----####---###### ----######## -##-##-##.#######" ) ) data_strings["fdg_down_2"] = ( split_word_template( "Fun####ding#### for#### this#### pro####gram#### was#\n" "made#### made#### made#### made#### made#### made#### made#### made#### made###\n" "pos####sible#### by#### view####ers###### like######## y##o##u##.#######" ) )