├── README.md ├── sa1.mp3 ├── still_alive_credit.py ├── still_alive_informer213.jpg └── still_alive_linux.jpg /README.md: -------------------------------------------------------------------------------- 1 | 在终端上演示《传送门》片尾曲效果的 Python 脚本程序。 2 | 3 | ## 使用条件 4 | 5 | `still_alive_credit.py` 脚本使用 Python 3,以下提到的 `pip` 多数情况下对应 `pip3` 命令 6 | 以调用 Python 3 的 `pip` 组件。 7 | 8 | Windows 下需要使用 Windows terminal,MinTTY 等支持 ANSI 终端转义序列的终端模拟器。 9 | 10 | 为了播放音乐,需要用 `pip` 安装 `playsound`。`playground` 在 Linux 下依赖 11 | `python-gobject` 软件包(Ubuntu 已默认安装)。在 MacOS 下还需要用 `pip` 安装 `PyObjC`。 12 | 13 | ## 使用方法 14 | 15 | 在当前目录下执行: 16 | 17 | ``` 18 | python3 still_alive_credit.py 19 | ``` 20 | 21 | 脚本会读取 `TERM`,`COLUMNS` 和 `LINES` 环境变量来调整输出区域大小并决定是否启用终端颜色等 22 | 特性。如果希望在一台标准 VT100 终端上演示,应该运行: 23 | 24 | ``` 25 | TERM=vt100 python3 still_alive_credit.py 26 | ``` 27 | 28 | 可以使用`--no-stay`参数使得播放完音乐后自动退出,默认是停留在播放完的界面。你也可以按`Ctrl+C`退出 29 | 30 | 可以使用 `--no-sound` 参数不带音乐进行演示,此时脚本只依赖 Python 标准库: 31 | 32 | ``` 33 | python3 still_alive_credit.py --no-sound 34 | ``` 35 | 36 | --- 37 | 38 | A demo of the credit song 'Still Alive' of Portal 1 written in Python, running 39 | in text terminal. 40 | 41 | ## Dependency 42 | 43 | `still_alive_credit.py` is written with Python 3. In most cases the following 44 | `pip` should be `pip3` command. 45 | 46 | In Windows system, you need a teminal emulator supporting ANSI escape sequences 47 | like Windows Terminal, MinTTY, Cmder or ConEmu。 48 | 49 | For playing music, you need install `playsound` with `pip`. In Linux `playsound` 50 | depends on `python-gobject` (default installed in Ubuntu). In MacOS you also need 51 | to use `pip` to install `PyObjC`. 52 | 53 | ## Usage 54 | 55 | In current directory, execute: 56 | 57 | ``` 58 | python3 still_alive_credit.py 59 | ``` 60 | 61 | The script will read environment variable `TERM`, `COLUMNS` and `LINES` to determine 62 | the output area size and whether to enable features such as terminal color. If you 63 | want run it on a standard VT100 terminal, you should execute: 64 | 65 | ``` 66 | TERM=vt100 python3 still_alive_credit.py 67 | ``` 68 | 69 | You can use the `--no-stay` option to automatically exit after the music finishes playing. By default, it remains on the playback screen. You can also press `Ctrl+C` to exit. 70 | 71 | It's able to use `--no-sound` option to run the script without playing sound. In this 72 | case, the script only depends on Python standard library: 73 | 74 | ``` 75 | python3 still_alive_credit.py --no-sound 76 | ``` 77 | 78 | ## Linux 运行效果 / Snapshot on Linux 79 | 80 | ![](still_alive_linux.jpg) 81 | 82 | ## 演示视频 / demonstration video 83 | 84 | ![](still_alive_informer213.jpg) 85 | 86 | 87 | -------------------------------------------------------------------------------- /sa1.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/errorer/Portal_StillAlive_Python/40dafe8631bef3ad900a0c771d71754267d81958/sa1.mp3 -------------------------------------------------------------------------------- /still_alive_credit.py: -------------------------------------------------------------------------------- 1 | # This program is free software: you can redistribute it and/or modify 2 | # it under the terms of the GNU General Public License as published by 3 | # the Free Software Foundation, either version 3 of the License, or 4 | # (at your option) any later version. 5 | # 6 | # This program is distributed in the hope that it will be useful, 7 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 8 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 9 | # GNU General Public License for more details. 10 | # 11 | # You should have received a copy of the GNU General Public License 12 | # along with this program. If not, see . 13 | # 14 | # The copyright for song belongs to Jonathan Coulton and Valve Software 15 | # Contact me if there's copyright violation and if deletion of the content is needed. 16 | # 17 | # 18 | # 19 | # PORTAL--STILL ALIVE demo by LHF (BD4SUP) 20 | # September 2021 21 | # Runs with Python 3 on most terminals with an 80x24 characters area. 22 | # This program was adjusted specifically for serial terminal under Linux 23 | # (in this case, an Informer 213 @ 19200bps and Ubuntu 14.04) 24 | # Feel free to do whatever you like with this code. 25 | # Personnel credits come from this project: https://github.com/xBytez/aperturescience 26 | # ASCII arts come from this project: https://sites.google.com/site/gaddc11/Stillalive.rar?attredirects=0&d=1 27 | # My blog: EE Archeology @ http://7400.me 28 | 29 | 30 | import time 31 | import sys 32 | import threading 33 | import os 34 | import shutil 35 | import re 36 | import signal 37 | from pathlib import Path 38 | 39 | cursor_x = 1 40 | cursor_y = 1 41 | print_lock = threading.Lock() 42 | 43 | 44 | term = os.getenv("TERM", "vt100") 45 | is_vt = re.search(r"vt(\d+)", term) 46 | 47 | # xterm, rxvt, konsole ... 48 | # but fbcon in linux kernel does not support screen buffer 49 | enable_screen_buffer = not (is_vt or term == "linux") 50 | 51 | # color support is after VT241 52 | enable_color = not is_vt or int(re.search(r"\d+", is_vt.group()).group()) >= 241 53 | 54 | enable_sound = '--no-sound' not in sys.argv 55 | 56 | enable_stay = '--no-stay' not in sys.argv 57 | if enable_sound: 58 | import playsound 59 | 60 | term_columns, term_lines = 0, 0 61 | if is_vt: 62 | term_columns, term_lines = 80, 24 63 | else: 64 | term_columns, term_lines = shutil.get_terminal_size() 65 | 66 | term_columns = int(os.getenv("COLUMNS", term_columns)) 67 | term_lines = int(os.getenv("LINES", term_lines)) 68 | 69 | if term_columns < 80 or term_lines < 24: 70 | print("the terminal size should be at least 80x24") 71 | sys.exit(1) 72 | 73 | is_draw_end = False 74 | 75 | def sigint_handler(sig, frame): 76 | end_draw() 77 | print('Interrupt by user') 78 | sys.exit(0) 79 | 80 | signal.signal(signal.SIGINT, sigint_handler) 81 | 82 | def begin_draw(): 83 | if enable_screen_buffer: 84 | print_lock.acquire() 85 | print('\033[?1049h', end='') 86 | print_lock.release() 87 | if enable_color: 88 | print_lock.acquire() 89 | print('\033[33;40;1m', end='') 90 | print_lock.release() 91 | 92 | 93 | def end_draw(): 94 | global is_draw_end 95 | print_lock.acquire() 96 | is_draw_end = True 97 | if enable_color: 98 | print('\033[0m', end='') 99 | if enable_screen_buffer: 100 | print('\033[?1049l', end='') 101 | else: 102 | clear(False) 103 | move(1, 1, False, False) 104 | print_lock.release() 105 | 106 | def move(x, y, update_cursor=True, mutex=True): 107 | global cursor_x, cursor_y 108 | global print_lock 109 | if(mutex): 110 | print_lock.acquire() 111 | print("\033[%d;%dH" % (y, x), end='') 112 | sys.stdout.flush() 113 | if(update_cursor): 114 | cursor_x = x 115 | cursor_y = y 116 | if(mutex): 117 | print_lock.release() 118 | 119 | 120 | def clear(mutex=True): 121 | global cursor_x, cursor_y 122 | global print_lock 123 | cursor_x = 1 124 | cursor_y = 1 125 | if mutex: 126 | print_lock.acquire() 127 | print('\033[2J', end='') 128 | if mutex: 129 | print_lock.release() 130 | 131 | # print with mutex lock and cursor update. Use this for convenience 132 | 133 | 134 | def _print(str, newline=True): 135 | global cursor_x, cursor_y 136 | global print_lock 137 | print_lock.acquire() 138 | if(newline): 139 | print(str) 140 | cursor_x = 1 141 | cursor_y = cursor_y + 1 142 | else: 143 | print(str, end='') 144 | cursor_x = cursor_x + len(str) 145 | print_lock.release() 146 | 147 | 148 | class lyric: 149 | def __init__(self, _words, _time, _interval, _mode): 150 | ''' 151 | Interval: -1 means to calculate based on last 152 | Mode: 0: Lyric with new line 153 | 1: Lyric without new line 154 | 2: ASCII art 155 | 3: Clear lyrics 156 | 4: Start music 157 | 5: Start credits 158 | 9: END 159 | ''' 160 | self.words = _words 161 | self.time = _time 162 | self.interval = _interval 163 | self.mode = _mode 164 | 165 | 166 | ascii_art_width = 40 167 | ascii_art_height = 20 168 | 169 | credits_width = min((term_columns - 4) // 2, 56) 170 | credits_height = term_lines - ascii_art_height - 2 171 | 172 | lyric_width = term_columns - 4 - credits_width 173 | lyric_height = term_lines - 2 174 | 175 | credits_pos_x = lyric_width + 4 176 | 177 | ascii_art_x = lyric_width + 4 + (credits_width - ascii_art_width) // 2 178 | ascii_art_y = credits_height + 3 179 | 180 | a1 = [" .,-:;//;:=, ", 181 | " . :H@@@MM@M#H/.,+%;, ", 182 | " ,/X+ +M@@M@MM%=,-%HMMM@X/, ", 183 | " -+@MM; #M@@MH+-,;XMMMM@MMMM@+- ", 184 | " ;@M@@M- XM@X;. -+XXXXXHHH@M@M#@/. ", 185 | " ,%MM@@MH ,@%= .---=-=:=,. ", 186 | " =@#@@@MX ., -%HX##%%%+; ", 187 | " =-./@M@M$ .;@MMMM@MM: ", 188 | " X@/ -#MM/ .+MM@@@M$ ", 189 | ",@M@H: :@: . =X#@@@@-", 190 | ",@@@MMX, . /H- ;@M@M=", 191 | ".H@@@@M@+, %MM+..%#$.", 192 | " /MMMM@MMH/. XM@MH; =; ", 193 | " /%+%#XHH@$= , .H@@@@MX, ", 194 | " .=--------. -%H.,@@@@@MX, ", 195 | " .%MM@@@HHHXX###%+= .:#MMX =M@@MM%. ", 196 | " =XMMM@MM@MM#H;,-+HMM@M+ /MMMX= ", 197 | " =%@M@M#@$-.=#@MM@@@M; %M%= ", 198 | " ,:+$+-,/H#MMMMMMM@= =, ", 199 | " =++%%%%+/:-. "] 200 | 201 | a2 = [" =+$HM####@H%;, ", 202 | " /H###############M$, ", 203 | " ,@################+ ", 204 | " .H##############+ ", 205 | " X############/ ", 206 | " $##########/ ", 207 | " %########/ ", 208 | " /X/;;+X/ ", 209 | " -XHHX- ", 210 | " ,######, ", 211 | "#############X .M####M. X#############", 212 | "##############- -//- -##############", 213 | "X##############%, ,+##############X", 214 | "-##############X X##############-", 215 | " %############% %############% ", 216 | " %##########; ;##########% ", 217 | " ;#######M= =M#######; ", 218 | " .+M###@, ,@###M+. ", 219 | " :XH. .HX: ", 220 | " "] 221 | 222 | a3 = [" =/;;/- ", 223 | " +: // ", 224 | " /; /; ", 225 | " -X H. ", 226 | ".//;;;:;;-, X= :+ .-;:=;:;%;.", 227 | "M- ,=;;;#:, ,:#;;:=, ,@", 228 | ":% :%.=/++++/=.$= %=", 229 | " ,%; %/:+/;,,/++:+/ ;+. ", 230 | " ,+/. ,;@+, ,%H;, ,/+, ", 231 | " ;+;;/= @. .H##X -X :///+; ", 232 | " ;+=;;;.@, .XM@$. =X.//;=%/. ", 233 | " ,;: :@%= =$H: .+%- ", 234 | " ,%= %;-///==///-// =%, ", 235 | ";+ :%-;;;:;;;;-X- +:", 236 | "@- .-;;;;M- =M/;;;-. -X", 237 | " :;;::;;-. %- :+ ,-;;-;:== ", 238 | " ,X H. ", 239 | " ;/ %= ", 240 | " // +; ", 241 | " ,////, "] 242 | 243 | a4 = [" .,---. ", 244 | " ,/XM#MMMX;, ", 245 | " -%##########M%, ", 246 | " -@######% $###@= ", 247 | " .,--, -H#######$ $###M: ", 248 | " ,;$M###MMX; .;##########$;HM###X=", 249 | " ,/@##########H= ;################+", 250 | "-+#############M/, %##############+", 251 | "%M###############= /##############:", 252 | "H################ .M#############;.", 253 | "@###############M ,@###########M:. ", 254 | "X################, -$=X#######@: ", 255 | "/@##################%- +######$- ", 256 | ".;##################X .X#####+, ", 257 | " .;H################/ -X####+. ", 258 | " ,;X##############, .MM/ ", 259 | " ,:+$H@M#######M#$- .$$= ", 260 | " .,-=;+$@###X: ;/=. ", 261 | " .,/X$; .::, ", 262 | " ., .. "] 263 | 264 | a5 = [" .+ ", 265 | " /M; ", 266 | " H#@: ;, ", 267 | " -###H- -@/ ", 268 | " %####$. -; .%#X ", 269 | " M#####+;#H :M#M. ", 270 | ".. .+/;%#########X###- ", 271 | " -/%H%+;-, +##############/ ", 272 | " .:$M###MH$%+############X ,--=;- ", 273 | " -/H#####################H+=. ", 274 | " .+#################X. ", 275 | " =%M####################H;. ", 276 | " /@###############+;;/%%;, ", 277 | " -%###################$. ", 278 | " ;H######################M= ", 279 | " ,%#####MH$%;+#####M###-/@####% ", 280 | " :$H%+;=- -####X.,H# -+M##@- ", 281 | " . ,###; ; =$##+ ", 282 | " .#H, :XH, ", 283 | " + .;-"] 284 | 285 | a6 = [" -$- ", 286 | " .H##H, ", 287 | " +######+ ", 288 | " .+#########H. ", 289 | " -$############@. ", 290 | " =H###############@ -X: ", 291 | " .$##################: @#@- ", 292 | " ,; .M###################; H###; ", 293 | " ;@#: @###################@ ,#####: ", 294 | " -M###. M#################@. ;######H ", 295 | " M####- +###############$ =@#######X ", 296 | " H####$ -M###########+ :#########M, ", 297 | " /####X- =########% :M########@/. ", 298 | " ,;%H@X; .$###X :##MM@%+;:- ", 299 | " .. ", 300 | " -/;:-,. ,,-==+M########H ", 301 | " -##################@HX%%+%%$%%%+:,, ", 302 | " .-/H%%%+%%$H@###############M@+=:/+: ", 303 | "/XHX%:#####MH%= ,---:;;;;/%%XHM,:###$", 304 | "$@#MX %+;- . "] 305 | 306 | a7 = [" :X-", 307 | " :X### ", 308 | " ;@####@ ", 309 | " ;M######X ", 310 | " -@########$ ", 311 | " .$##########@ ", 312 | " =M############-", 313 | " +##############$", 314 | " .H############$=. ", 315 | " ,/: ,M##########M;. ", 316 | " -+@###; =##########M; ", 317 | " =%M#######; :#########M/ ", 318 | "-$M###########; :#########/ ", 319 | " ,;X###########; =########$. ", 320 | " ;H#########+#######M= ", 321 | " ,+##############+ ", 322 | " /M#########@- ", 323 | " ;M######% ", 324 | " +####: ", 325 | " ,$M- "] 326 | 327 | a8 = [" .-;+$XHHHHHHX$+;-. ", 328 | " ,;X@@X%/;=----=:/%X@@X/, ", 329 | " =$@@%=. .=+H@X: ", 330 | " -XMX: =XMX= ", 331 | " /@@: =H@+ ", 332 | " %@X, .$@$ ", 333 | " +@X. $@% ", 334 | "-@@, .@@=", 335 | "%@% +@$", 336 | "H@: :@H", 337 | "H@: :HHHHHHHHHHHHHHHHHHX, =@H", 338 | "%@% ;@M@@@@@@@@@@@@@@@@@H- +@$", 339 | "=@@, :@@@@@@@@@@@@@@@@@@@@@= .@@:", 340 | " +@X :@@@@@@@@@@@@@@@M@@@@@@:%@% ", 341 | " $@$, ;@@@@@@@@@@@@@@@@@M@@@@@@$. ", 342 | " +@@HHHHHHH@@@@@@@@@@@@@@@@@@@@@@@+ ", 343 | " =X@@@@@@@@@@@@@@@@@@@@@@@@@@@@X= ", 344 | " :$@@@@@@@@@@@@@@@@@@@M@@@@$: ", 345 | " ,;$@@@@@@@@@@@@@@@@@@X/- ", 346 | " .-;+$XXHHHHHX$+;-. "] 347 | 348 | a9 = [" ,:/+/- ", 349 | " /M/ .,-=;//;- ", 350 | " .:/= ;MH/, ,=/+%$XH@MM#@: ", 351 | " -$##@+$###@H@MMM#######H:. -/H#", 352 | " .,H@H@ X######@ -H#####@+- -+H###@ ", 353 | " .,@##H; +XM##M/, =%@###@X;- ", 354 | "X%- :M##########$. .:%M###@%: ", 355 | "M##H, +H@@@$/-. ,;$M###@%, -", 356 | "M####M=,,---,.-%%H####M$: ,+@##", 357 | "@##################@/. :%H##@$- ", 358 | "M###############H, ;HM##M$= ", 359 | "#################. .=$M##M$= ", 360 | "################H..;XM##M$= .:+", 361 | "M###################@%= =+@MH%", 362 | "@################M/. =+H#X%= ", 363 | "=+M##############M, -/X#X+;. ", 364 | " .;XM##########H= ,/X#H+:, ", 365 | " .=+HM######M+/+HM@+=. ", 366 | " ,:/%XM####H/. ", 367 | " ,.:=-. "] 368 | 369 | a10 = [" #+ @ # # M#@ ", 370 | " . .X X.%##@;# # +@#######X. @#% ", 371 | " ,==. ,######M+ -#####%M####M- #", 372 | " :H##M%:=##+ .M##M,;#####/+#######% ,M#", 373 | " .M########= =@#@.=#####M=M#######= X#", 374 | " :@@MMM##M. -##M.,#######M#######. = M", 375 | " @##..###:. .H####. @@ X,", 376 | " ############: ###,/####; /##= @#. M ", 377 | " ,M## ;##,@#M;/M#M @# X#% X# ", 378 | ".%= ######M## ##.M#: ./#M ,M #M ,#$ ", 379 | "##/ $## #+;#: #### ;#/ M M- @# :", 380 | "#+ #M@MM###M-;M #:$#-##$H# .#X @ + $#. #", 381 | " ######/.: #%=# M#:MM./#.-# @#: H#", 382 | "+,.= @###: /@ %#,@ ##@X #,-#@.##% .@#", 383 | "#####+;/##/ @## @#,+ /#M . X, ", 384 | " ;###M#@ M###H .#M- ,##M ;@@; ###", 385 | " .M#M##H ;####X ,@#######M/ -M###$ -H", 386 | " .M###% X####H .@@MM@; ;@#M@ ", 387 | " H#M /@####/ ,++. / ==-, ", 388 | " ,=/:, .+X@MMH@#H #####$="] 389 | 390 | ascii_art = [a1, a2, a3, a4, a5, a6, a7, a8, a9, a10] 391 | 392 | # Timestamps are adjusted according to actual situations... 393 | # For Informer213 running at 19200bps, refreshing a ASCII art pattern 394 | # takes ~600ms, so we add 700ms between every pattern and the next line 395 | 396 | lyrics = [ 397 | ########## Page 1 ########## 398 | lyric("Forms FORM-29827281-12:", 0, -1, 0), 399 | lyric("Test Assessment Report", 200, -1, 0), 400 | lyric("\00\00\00\00\00\00\00", 400, - \ 401 | 1, 0), # Keep flushing the buffer 402 | lyric("", 710, 0, 4), # Music start 403 | lyric("This was a triumph.", 730, 2, 0), 404 | lyric("", 930, 0, 5), # Credits start 405 | lyric("I'm making a note here:", 1123, 2, 0), 406 | lyric("HUGE SUCCESS.", 1347, 1.7, 0), 407 | lyric("It's hard to overstate", 1627, -1, 0), 408 | lyric("my satisfaction.", 1873, 2.6, 0), 409 | lyric("Aperture Science", 2350, 1.8, 0), 410 | lyric(0, 2350, 0, 2), # ASCII 1 411 | lyric("We do what we must", 2733, 1.6, 0), 412 | lyric("because we can.", 2910, 1.5, 0), 413 | lyric("For the good of all of us.", 3237, -1, 0), 414 | lyric(1, 3500, 0, 2), # ASCII 2 415 | lyric("Except the ones who are dead.", 3567, -1, 0), 416 | lyric("", 3717, 0.05, 0), 417 | lyric(0, 3717, 0, 2), # ASCII 1 418 | lyric("But there's no sense crying", 3787, -1, 0), 419 | lyric("over every mistake.", 3973, 1.77, 0), 420 | lyric("You just keep on trying", 4170, -1, 0), 421 | lyric("till you run out of cake.", 4370, -1, 0), 422 | lyric(2, 4500, 0, 2), # ASCII 3 423 | lyric("And the Science gets done.", 4570, -1, 0), 424 | lyric("And you make a neat gun.", 4767, -1, 0), 425 | lyric(0, 4903, 0, 2), # ASCII 1 426 | lyric("For the people who are", 4973, -1, 0), 427 | lyric("still alive.", 5110, 1.6, 1), 428 | 429 | ########## Page 2 ########## 430 | lyric(0, 5353, 431 | 0, 3), # Clear lyrics 432 | lyric("Forms FORM-55551-5:", 5413, -1, 0), 433 | lyric("Personnel File Addendum:", 5477, 1.13, 0), 434 | lyric("", 5650, 0.05, 0), 435 | lyric("Dear <>,", 5650, -1, 0), 436 | lyric("", 5900, -1, 0), 437 | lyric("I'm not even angry.", 5900, 1.86, 0), 438 | lyric("I'm being ", 6320, -1, 1), 439 | lyric("so ", 6413, -1, 1), 440 | lyric("sincere right now.", 6470, 1.9, 0), 441 | lyric("Even though you broke ", 6827, -1, 1), 442 | lyric(3, 7020, 0, 2), # ASCII 4 443 | lyric("my heart.", 7090, -1, 0), 444 | lyric("And killed me.", 7170, 1.43, 0), 445 | lyric(4, 7300, 0, 2), # ASCII 5 446 | lyric("And tore me to pieces.", 7500, 1.83, 0), 447 | lyric("And threw every piece ", 7900, -1, 1), 448 | lyric("into a fire.", 8080, 1.8, 0), 449 | lyric(5, 8080, 0, 2), # ASCII 6 450 | lyric("As they burned it hurt because", 8430, -1, 0), 451 | lyric(6, 8690, 0, 2), # ASCII 7 452 | lyric("I was so happy for you!", 8760, 1.67, 0), 453 | lyric("Now, these points of data", 8960, -1, 0), 454 | lyric("make a beautiful line.", 9167, -1, 0), 455 | lyric("And we're out of beta.", 9357, -1, 0), 456 | lyric("We're releasing on time.", 9560, -1, 0), 457 | lyric(4, 9700, 0, 2), # ASCII 5 458 | lyric("So I'm GLaD I got burned.", 9770, -1, 0), 459 | lyric(2, 9913, 0, 2), # ASCII 3 460 | lyric("Think of all the things we learned", 9983, -1, 0), 461 | lyric(0, 10120, 0, 2), # ASCII 1 462 | lyric("For the people who are", 10190, -1, 0), 463 | lyric("Still alive.", 10327, 1.8, 0), 464 | 465 | ########## Page 3 ########## 466 | lyric(0, 10603, 467 | 0, 3), # Clear lyrics 468 | lyric("Forms FORM-55551-6:", 10663, -1, 0), 469 | lyric("Personnel File Addendum Addendum:", 10710, 1.36, 0), 470 | lyric("", 10710, 0.05, 0), 471 | lyric("One last thing:", 10910, -1, 0), 472 | lyric("", 11130, 0.05, 0), 473 | lyric("Go ahead and leave ", 11130, -1, 1), 474 | lyric("me.", 11280, 0.5, 0), 475 | lyric("I think I'd prefer to stay ", 11507, -1, 1), 476 | lyric("inside.", 11787, 1.13, 0), 477 | lyric("Maybe you'll find someone else", 12037, -1, 0), 478 | lyric("To help you.", 12390, 1.23, 0), 479 | lyric("Maybe Black ", 12737, -1, 1), 480 | lyric(7, 12787, 0, 2), # ASCII 8 481 | lyric("Mesa...", 12857, 2.7, 0), 482 | lyric("THAT WAS A JOKE.", 13137, 1.46, 1), 483 | lyric(" FAT CHANCE.", 13387, 1.1, 0), 484 | lyric("Anyway, ", 13620, -1, 1), 485 | lyric(8, 13670, 0, 2), # ASCII 9 486 | lyric("this cake is great.", 13740, -1, 0), 487 | lyric("It's so delicious and moist.", 13963, -1, 0), 488 | lyric(9, 14123, 0, 2), # ASCII 10 489 | lyric("Look at me still talking", 14193, -1, 0), 490 | lyric(1, 14320, 0, 2), # ASCII 2 491 | lyric("when there's science to do.", 14390, -1, 0), 492 | lyric(0, 14527, 0, 2), # ASCII 1 493 | lyric("When I look out there,", 14597, -1, 0), 494 | lyric("It makes me GLaD I'm not you.", 14767, -1, 0), 495 | lyric(2, 14913, 0, 2), # ASCII 3 496 | lyric("I've experiments to run.", 14983, -1, 0), 497 | lyric(4, 15120, 0, 2), # ASCII 5 498 | lyric("There is research to be done.", 15190, -1, 0), 499 | lyric(0, 15320, 0, 2), # ASCII 1 500 | lyric("On the people who are", 15390, -1, 0), 501 | lyric("still alive", 15553, 2.0, 1), 502 | 503 | ########## Page 4 ########## 504 | lyric(0, 15697, 505 | 0, 3), # Clear lyrics 506 | lyric("", 15757, 0.05, 0), 507 | lyric("", 15757, 0.05, 0), 508 | lyric("", 15757, 0.05, 0), 509 | lyric("PS: And believe me I am", 15757, -1, 0), 510 | lyric("still alive.", 15960, 1.13, 0), 511 | lyric("PPS: I'm doing Science and I'm", 16150, -1, 0), 512 | lyric("still alive.", 16363, 1.13, 0), 513 | lyric("PPPS: I feel FANTASTIC and I'm", 16550, -1, 0), 514 | lyric("still alive.", 16760, -1, 0), 515 | lyric("", 16860, -1, 0), 516 | lyric("FINAL THOUGH:", 16860, -1, 0), 517 | lyric("While you're dying I'll be", 16993, -1, 0), 518 | lyric("still alive.", 17157, -1, 0), 519 | lyric("", 17277, -1, 0), 520 | lyric("FINAL THOUGH PS:", 17277, -1, 0), 521 | lyric("And when you're dead I will be", 17367, -1, 0), 522 | lyric("still alive.", 17550, 1.13, 0), 523 | lyric("", 17550, -1, 0), 524 | lyric("", 17550, 0.05, 0), 525 | lyric("STILL ALIVE", 17760, 1.13, 0), 526 | lyric(0, 17900, 527 | 0, 3), # Clear lyrics 528 | lyric(0, 18500, 529 | 0, 3), # Clear lyrics 530 | lyric("ENDENDENDENDENDENDENDEND", 18500, 0.05, 9)] 531 | 532 | credits = r""">LIST PERSONNEL 533 | 534 | Gautam Babbar 535 | Ted Backman 536 | Kelly Bailey 537 | Jeff Ballinger 538 | Aaron Barber 539 | Jeep Barnett 540 | Jeremy Bennett 541 | Dan Berger 542 | Yahn Bernier 543 | Ken Birdwell 544 | Derrick BirumMike Blaszczak 545 | Iestyn Bleasdale-Shepherd 546 | Chris Bokitch 547 | Steve Bond 548 | Matt Boone 549 | Antoine Bourdon 550 | Jamaal Bradley 551 | Jason Brashill 552 | Charlie Brown 553 | Charlie Burgin 554 | Andrew Burke 555 | Augusta Butlin 556 | Julie Caldwell 557 | Dario Casali 558 | Chris Chin 559 | Jess Cliffe 560 | Phil Co 561 | John Cook 562 | Christen Coomer 563 | Greg Coomer 564 | Scott Dalton 565 | Kerry Davis 566 | Jason Deakins 567 | Joe Demers 568 | Ariel Diaz 569 | Quintin Doroquez 570 | Jim Dose 571 | Chris Douglass 572 | Laura Dubuk 573 | Mike Dunkle 574 | Mike Durand 575 | Mike Dussault 576 | Dhabih Eng 577 | Katie Engel 578 | Chet Faliszek 579 | Adrian Finol 580 | Bill Fletcher 581 | Moby Francke 582 | Stephane Gaudette 583 | Kathy Gehrig 584 | Vitaliy Genkin 585 | Paul Graham 586 | Chris Green 587 | Chris Grinstead 588 | John Guthrie 589 | Aaron Halifax 590 | Reagan Halifax 591 | Leslie Hall 592 | Jeff Hameluck 593 | Joe Han 594 | Don Holden 595 | Jason Holtman 596 | Gray Horsfield 597 | Keith Huggins 598 | Jim Hughes 599 | Jon Huisingh 600 | Brian Jacobson 601 | Lars Jensvold 602 | Erik Johnson 603 | Jakob Jungels 604 | Rich Kaethler 605 | Steve Kalning 606 | Aaron Kearly 607 | Iikka Keranen 608 | David Kircher 609 | Eric Kirchmer 610 | Scott Klintworth 611 | Alden Kroll 612 | Marc Laidlaw 613 | Jeff Lane 614 | Tim Larkin 615 | Dan LeFree 616 | Isabelle LeMay 617 | Tom Leonard 618 | Jeff Lind 619 | Doug Lombardi 620 | Bianca Loomis 621 | Richard Lord 622 | Realm Lovejoy 623 | Randy Lundeen 624 | Scott Lynch 625 | Ido Magal 626 | Nick Maggiore 627 | John McCaskey 628 | Patrick McClard 629 | Steve McClure 630 | Hamish McKenzie 631 | Gary McTaggart 632 | Jason Mitchell 633 | Mike Morasky 634 | John Morello II 635 | Bryn Moslow 636 | Arsenio Navarro 637 | Gabe Newell 638 | Milton Ngan 639 | Jake Nicholson 640 | Martin Otten 641 | Nick Papineau 642 | Karen Prell 643 | Bay Raitt 644 | Tristan Reidford 645 | Alfred Reynolds 646 | Matt Rhoten 647 | Garret Rickey 648 | Dave Riller 649 | Elan Ruskin 650 | Matthew Russell 651 | Jason Ruymen 652 | David Sawyer 653 | Marc Scaparro 654 | Wade Schin 655 | Matthew Scott 656 | Aaron Seeler 657 | Jennifer Seeley 658 | Taylor Sherman 659 | Eric Smith 660 | Jeff Sorensen 661 | David Speyrer 662 | Jay Stelly 663 | Jeremy Stone 664 | Eric Strand 665 | Kim Swift 666 | Kelly Thornton 667 | Eric Twelker 668 | Carl Uhlman 669 | Doug Valente 670 | Bill Van Buren 671 | Gabe Van Engel 672 | Alex Vlachos 673 | Robin Walker 674 | Joshua Weier 675 | Andrea Wicklund 676 | Greg Winkler 677 | Erik Wolpaw 678 | Doug Wood 679 | Matt T. Wood 680 | Danika Wright 681 | Matt Wright 682 | Shawn Zabecki 683 | Torsten Zabka 684 | 685 | 686 | 'Still Alive' by: 687 | Jonathan Coulton 688 | 689 | Voices: 690 | Ellen McLain - GlaDOS, Turrets 691 | Mike Patton - THE ANGER SPHERE 692 | 693 | Voice Casting: 694 | Shana Landsburg\Teri Fiddleman 695 | 696 | Voice Recording: 697 | Pure Audio, Seattle, WA 698 | 699 | Voice recording 700 | scheduling and logistics: 701 | Pat Cockburn, Pure Audio 702 | 703 | Translations: 704 | SDL 705 | 706 | Crack Legal Team: 707 | Liam Lavery 708 | Karl Quackenbush 709 | Kristen Boraas 710 | Kevin Rosenfield 711 | Alan Bruggeman 712 | Dennis Tessier 713 | 714 | Thanks for the use of their face: 715 | Alesia Glidewell - Chell 716 | 717 | Special thanks to everyone at: 718 | Alienware 719 | ATI 720 | Dell 721 | Falcon Northwest 722 | Havok 723 | SOFTIMAGE 724 | and Don Kemmis, SLK Technologies 725 | 726 | 727 | THANK YOU FOR PARTICIPATING 728 | IN THIS 729 | ENRICHMENT CENTER ACTIVITY!!""" 730 | 731 | 732 | def drawAA(x, y, ch): 733 | for dy in range(ascii_art_height): 734 | move(x, y + dy) 735 | print(ascii_art[ch][dy], end='') 736 | sys.stdout.flush() 737 | time.sleep(0.01) 738 | 739 | 740 | def drawFrame(): 741 | move(1, 1) 742 | _print(' ' + '-' * lyric_width + ' ' + '-' * credits_width + ' ', not is_vt) 743 | for _ in range(credits_height): 744 | _print('|' + ' ' * lyric_width + '||' + ' ' * credits_width + '|', not is_vt) 745 | _print('|' + ' ' * lyric_width + '| ' + '-' * credits_width + ' ', not is_vt) 746 | for _ in range(lyric_height - 1 - credits_height): 747 | _print('|' + ' ' * lyric_width + '|') 748 | _print(' ' + '-' * lyric_width + ' ', False) 749 | move(2, 2) 750 | sys.stdout.flush() 751 | time.sleep(1) 752 | 753 | 754 | def clearLyrics(): 755 | move(1, 2) 756 | for _ in range(lyric_height): 757 | _print('|' + ' ' * lyric_width) 758 | move(2, 2) 759 | 760 | 761 | def drawLyrics(str, x, y, interval, newline): 762 | move(x + 2, y + 2) 763 | for ch in str: 764 | _print(ch, False) 765 | sys.stdout.flush() 766 | time.sleep(interval) 767 | x = x + 1 768 | if(newline): 769 | x = 0 770 | y = y + 1 771 | move(x + 2, y + 2) 772 | return x 773 | 774 | 775 | class thread_credits (threading.Thread): 776 | def run(self): 777 | global print_lock 778 | global cursor_x, cursor_y 779 | credit_x = 0 780 | i = 0 781 | length = len(credits) 782 | last_credits = [""] 783 | startTime = time.time() 784 | for ch in credits: 785 | currentTime = startTime + 174.0 / length * i 786 | i += 1 787 | if ch == '\n': 788 | credit_x = 0 789 | last_credits.append("") 790 | if len(last_credits) > credits_height: 791 | last_credits = last_credits[-credits_height:] 792 | print_lock.acquire() 793 | if is_draw_end: 794 | print_lock.release() 795 | break 796 | for y in range(2, 2 + credits_height - len(last_credits)): 797 | move(credits_pos_x, y, False, False) 798 | print(' ' * credits_width, end='') 799 | for k in range(len(last_credits)): 800 | y = 2 + credits_height - len(last_credits) + k 801 | move(credits_pos_x, y, False, False) 802 | print(last_credits[k], end='') 803 | print(' ' * (credits_width - len(last_credits[k])), end='') 804 | move(cursor_x, cursor_y, False, False) 805 | print_lock.release() 806 | else: 807 | last_credits[-1] += ch 808 | print_lock.acquire() 809 | if is_draw_end: 810 | print_lock.release() 811 | break 812 | move(credits_pos_x + credit_x, credits_height + 1, False, False) 813 | print(ch, end='') 814 | move(cursor_x, cursor_y, False, False) 815 | print_lock.release() 816 | credit_x += 1 817 | while time.time() < currentTime: 818 | time.sleep(0.01) 819 | 820 | 821 | ################# Main ################ 822 | begin_draw() 823 | clear() 824 | drawFrame() 825 | move(2, 2) 826 | time.sleep(1) 827 | 828 | startTime = time.time() * 100 829 | currentTime = 0 830 | currentLyric = 0 831 | currentCredit = 0 832 | x = 0 833 | y = 0 834 | 835 | while(lyrics[currentLyric].mode != 9): 836 | currentTime = time.time() * 100 - startTime 837 | 838 | if(currentTime > lyrics[currentLyric].time): 839 | 840 | if(lyrics[currentLyric].mode <= 1 or lyrics[currentLyric].mode >= 5): 841 | wordCount = len(lyrics[currentLyric].words) 842 | if(wordCount == 0): 843 | wordCount = 1 844 | 845 | if(lyrics[currentLyric].interval < 0): 846 | interval = (lyrics[currentLyric + 1].time - 847 | lyrics[currentLyric].time) / 100.0 / wordCount 848 | else: 849 | interval = lyrics[currentLyric].interval / wordCount 850 | 851 | if(lyrics[currentLyric].mode == 0): 852 | x = drawLyrics(lyrics[currentLyric].words, 853 | x, y, 854 | interval, 855 | True) 856 | y = y + 1 857 | elif(lyrics[currentLyric].mode == 1): 858 | x = drawLyrics(lyrics[currentLyric].words, 859 | x, y, 860 | interval, 861 | False) 862 | elif(lyrics[currentLyric].mode == 2): 863 | drawAA(ascii_art_x, ascii_art_y, lyrics[currentLyric].words) 864 | move(x + 2, y + 2) 865 | elif(lyrics[currentLyric].mode == 3): 866 | clearLyrics() 867 | x = 0 868 | y = 0 869 | elif(lyrics[currentLyric].mode == 4): 870 | if enable_sound: 871 | playsound.playsound(str(Path.cwd() / 'sa1.mp3'), False) 872 | elif(lyrics[currentLyric].mode == 5): 873 | th_credit = thread_credits() 874 | th_credit.daemon = True 875 | th_credit.start() 876 | currentLyric = currentLyric + 1 877 | 878 | time.sleep(0.01) 879 | 880 | end_draw() 881 | 882 | if enable_sound: 883 | while True: 884 | time.sleep(600) 885 | 886 | -------------------------------------------------------------------------------- /still_alive_informer213.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/errorer/Portal_StillAlive_Python/40dafe8631bef3ad900a0c771d71754267d81958/still_alive_informer213.jpg -------------------------------------------------------------------------------- /still_alive_linux.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/errorer/Portal_StillAlive_Python/40dafe8631bef3ad900a0c771d71754267d81958/still_alive_linux.jpg --------------------------------------------------------------------------------