1# Copyright 2004-2021 Tom Rothamel <pytom@bishoujo.us> 2# 3# Permission is hereby granted, free of charge, to any person 4# obtaining a copy of this software and associated documentation files 5# (the "Software"), to deal in the Software without restriction, 6# including without limitation the rights to use, copy, modify, merge, 7# publish, distribute, sublicense, and/or sell copies of the Software, 8# and to permit persons to whom the Software is furnished to do so, 9# subject to the following conditions: 10# 11# The above copyright notice and this permission notice shall be 12# included in all copies or substantial portions of the Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22# This file contains code that creates a few new statements. 23 24init -1200 python in audio: 25 pass 26 27init -1200 python: 28 29 config.default_sound_loop = None 30 31 def _audio_eval(expr): 32 return eval(expr, locals=store.audio.__dict__) 33 34 def _try_eval(e, what): 35 try: 36 return _audio_eval(e) 37 except: 38 renpy.error('unable to evaluate %s %r' % (what, e)) 39 40python early hide: 41 42 def warp_audio(p): 43 """ 44 Determines if we should play this statement while warping. 45 """ 46 47 if p.get("channel", None) is not None: 48 channel = eval(p["channel"]) 49 else: 50 channel = "music" 51 52 return renpy.music.is_music(channel) 53 54 def parse_play_music(l): 55 56 file = l.simple_expression() 57 if not file: 58 renpy.error("play requires a file") 59 60 fadeout = "None" 61 fadein = "0" 62 channel = None 63 loop = None 64 if_changed = False 65 volume = "1.0" 66 67 while not l.eol(): 68 69 if l.keyword('fadeout'): 70 fadeout = l.simple_expression() 71 if fadeout is None: 72 renpy.error('expected simple expression') 73 74 continue 75 76 if l.keyword('fadein'): 77 fadein = l.simple_expression() 78 if fadein is None: 79 renpy.error('expected simple expression') 80 81 continue 82 83 if l.keyword('channel'): 84 channel = l.simple_expression() 85 if channel is None: 86 renpy.error('expected simple expression') 87 88 continue 89 90 if l.keyword('loop'): 91 loop = True 92 continue 93 94 if l.keyword('noloop'): 95 loop = False 96 continue 97 98 if l.keyword('if_changed'): 99 if_changed = True 100 continue 101 102 if l.keyword('volume'): 103 volume = l.simple_expression() 104 continue 105 106 renpy.error('expected end of line') 107 108 return dict(file=file, 109 fadeout=fadeout, 110 fadein=fadein, 111 channel=channel, 112 loop=loop, 113 if_changed=if_changed, 114 volume=volume) 115 116 def execute_play_music(p): 117 118 if p["channel"] is not None: 119 channel = eval(p["channel"]) 120 else: 121 channel = "music" 122 123 renpy.music.play(_audio_eval(p["file"]), 124 fadeout=eval(p["fadeout"]), 125 fadein=eval(p["fadein"]), 126 channel=channel, 127 loop=p.get("loop", None), 128 if_changed=p.get("if_changed", False), 129 relative_volume=eval(p.get("volume", "1.0"))) 130 131 def predict_play_music(p): 132 if renpy.emscripten or os.environ.get('RENPY_SIMULATE_DOWNLOAD', False): 133 fn = _audio_eval(p["file"]) 134 try: 135 with renpy.loader.load(fn) as f: 136 pass 137 except renpy.webloader.DownloadNeeded as exception: 138 renpy.webloader.enqueue(exception.relpath, 'music', None) 139 return [ ] 140 141 def lint_play_music(p, channel="music"): 142 143 file = _try_eval(p["file"], 'filename') 144 145 if p["channel"] is not None: 146 channel = _try_eval(p["channel"], 'channel') 147 148 if not isinstance(file, list): 149 file = [ file ] 150 151 for fn in file: 152 if isinstance(fn, basestring): 153 try: 154 if not renpy.music.playable(fn, channel): 155 renpy.error("%r is not loadable" % fn) 156 except: 157 pass 158 159 renpy.register_statement('play music', 160 parse=parse_play_music, 161 execute=execute_play_music, 162 predict=predict_play_music, 163 lint=lint_play_music, 164 warp=warp_audio) 165 166 # From here on, we'll steal bits of other statements when defining other 167 # statements. 168 169 def parse_queue_music(l): 170 171 file = l.simple_expression() 172 if not file: 173 renpy.error("queue requires a file") 174 175 channel = None 176 loop = None 177 volume = "1.0" 178 fadein = "0" 179 180 while not l.eol(): 181 182 if l.keyword('channel'): 183 channel = l.simple_expression() 184 if channel is None: 185 renpy.error('expected simple expression') 186 187 continue 188 189 if l.keyword('loop'): 190 loop = True 191 continue 192 193 if l.keyword('noloop'): 194 loop = False 195 continue 196 197 if l.keyword('volume'): 198 volume = l.simple_expression() 199 continue 200 201 if l.keyword('fadein'): 202 fadein = l.simple_expression() 203 if fadein is None: 204 renpy.error('expected simple expression') 205 206 continue 207 208 renpy.error('expected end of line') 209 210 return dict(file=file, channel=channel, loop=loop, volume=volume, fadein=fadein) 211 212 def execute_queue_music(p): 213 if p["channel"] is not None: 214 channel = eval(p["channel"]) 215 else: 216 channel = "music" 217 218 renpy.music.queue( 219 _audio_eval(p["file"]), 220 channel=channel, 221 loop=p.get("loop", None), 222 relative_volume=eval(p.get("volume", "1.0")), 223 fadein=eval(p.get("fadein", "0")), 224 ) 225 226 227 renpy.register_statement('queue music', 228 parse=parse_queue_music, 229 execute=execute_queue_music, 230 lint=lint_play_music, 231 warp=warp_audio) 232 233 def parse_stop_music(l): 234 channel = None 235 fadeout = "None" 236 237 while not l.eol(): 238 239 if l.keyword("fadeout"): 240 fadeout = l.simple_expression() 241 if fadeout is None: 242 renpy.error('expected simple expression') 243 244 continue 245 246 if l.keyword('channel'): 247 channel = l.simple_expression() 248 if channel is None: 249 renpy.error('expected simple expression') 250 251 continue 252 253 renpy.error('expected end of line') 254 255 return dict(fadeout=fadeout, channel=channel) 256 257 def execute_stop_music(p): 258 if p["channel"] is not None: 259 channel = eval(p["channel"]) 260 else: 261 channel = "music" 262 263 renpy.music.stop(fadeout=eval(p["fadeout"]), channel=channel) 264 265 renpy.register_statement('stop music', 266 parse=parse_stop_music, 267 execute=execute_stop_music, 268 warp=warp_audio) 269 270 271 # Sound statements. They share alot with the equivalent music 272 # statements. 273 274 def warp_sound(p): 275 """ 276 Determines if we should play this statement while warping. 277 """ 278 279 if p.get("channel", None) is not None: 280 channel = eval(p["channel"]) 281 else: 282 channel = "sound" 283 284 return renpy.music.is_music(channel) 285 286 def execute_play_sound(p): 287 288 if p["channel"] is not None: 289 channel = eval(p["channel"]) 290 else: 291 channel = "sound" 292 293 fadeout = eval(p["fadeout"]) or 0 294 295 loop = p.get("loop", False) 296 297 if loop is None: 298 loop = config.default_sound_loop 299 300 renpy.sound.play(_audio_eval(p["file"]), 301 fadeout=fadeout, 302 fadein=eval(p["fadein"]), 303 loop=loop, 304 channel=channel, 305 relative_volume=eval(p.get("volume", "1.0"))) 306 307 def lint_play_sound(p, lint_play_music=lint_play_music): 308 return lint_play_music(p, channel="sound") 309 310 renpy.register_statement('play sound', 311 parse=parse_play_music, 312 execute=execute_play_sound, 313 lint=lint_play_sound, 314 warp=warp_sound) 315 316 def execute_queue_sound(p): 317 if p["channel"] is not None: 318 channel = eval(p["channel"]) 319 else: 320 channel = "sound" 321 322 loop = p.get("loop", False) 323 324 if loop is None: 325 loop = config.default_sound_loop 326 327 renpy.sound.queue(_audio_eval(p["file"]), channel=channel, loop=loop, relative_volume=eval(p.get("volume", "1.0"))) 328 329 330 renpy.register_statement('queue sound', 331 parse=parse_queue_music, 332 execute=execute_queue_sound, 333 lint=lint_play_sound, 334 warp=warp_sound) 335 336 def execute_stop_sound(p): 337 if p["channel"] is not None: 338 channel = eval(p["channel"]) 339 else: 340 channel = "sound" 341 342 fadeout = eval(p["fadeout"]) or 0 343 344 renpy.sound.stop(fadeout=fadeout, channel=channel) 345 346 renpy.register_statement('stop sound', 347 parse=parse_stop_music, 348 execute=execute_stop_sound, 349 warp=warp_sound) 350 351 352 # Generic play/queue/stop statements. These take a channel name as 353 # the second thing. 354 355 def parse_play_generic(l, parse_play_music=parse_play_music): 356 channel = l.name() 357 358 if channel is None: 359 renpy.error('play requires a channel') 360 361 rv = parse_play_music(l) 362 if rv["channel"] is None: 363 rv["channel"] = repr(channel) 364 365 return rv 366 367 def parse_queue_generic(l, parse_queue_music=parse_queue_music): 368 channel = l.name() 369 370 if channel is None: 371 renpy.error('queue requires a channel') 372 373 rv = parse_queue_music(l) 374 if rv["channel"] is None: 375 rv["channel"] = repr(channel) 376 377 return rv 378 379 def parse_stop_generic(l, parse_stop_music=parse_stop_music): 380 channel = l.name() 381 382 if channel is None: 383 renpy.error('stop requires a channel') 384 385 rv = parse_stop_music(l) 386 if rv["channel"] is None: 387 rv["channel"] = repr(channel) 388 389 return rv 390 391 def lint_play_generic(p, lint_play_music=lint_play_music): 392 channel = eval(p["channel"]) 393 394 if not renpy.music.channel_defined(channel): 395 renpy.error("channel %r is not defined" % channel) 396 397 lint_play_music(p, channel) 398 399 def lint_stop_generic(p): 400 channel = eval(p["channel"]) 401 402 if not renpy.music.channel_defined(channel): 403 renpy.error("channel %r is not defined" % channel) 404 405 renpy.register_statement('play', 406 parse=parse_play_generic, 407 execute=execute_play_music, 408 predict=predict_play_music, 409 lint=lint_play_generic, 410 warp=warp_audio) 411 412 renpy.register_statement('queue', 413 parse=parse_queue_generic, 414 execute=execute_queue_music, 415 lint=lint_play_generic, 416 warp=warp_audio) 417 418 renpy.register_statement('stop', 419 parse=parse_stop_generic, 420 execute=execute_stop_music, 421 lint=lint_stop_generic, 422 warp=warp_audio) 423 424 425 426 ########################################################################## 427 # Pause statement. 428 429 # Should the pause statement use the pause Transition? 430 config.pause_with_transition = False 431 432 def parse_pause(l): 433 434 delay = l.simple_expression() 435 436 if not l.eol(): 437 renpy.error("expected end of line.") 438 439 return { "delay" : delay } 440 441 def lint_pause(p): 442 443 if p["delay"]: 444 _try_eval(p["delay"], 'pause statement') 445 446 def execute_pause(p): 447 448 if p["delay"]: 449 delay = eval(p["delay"]) 450 if config.pause_with_transition: 451 renpy.with_statement(Pause(delay)) 452 else: 453 renpy.pause(delay) 454 else: 455 renpy.pause() 456 457 renpy.register_statement('pause', 458 parse=parse_pause, 459 lint=lint_pause, 460 execute=execute_pause) 461 462 463############################################################################## 464# Screen-related statements. 465 466python early hide: 467 468 # Should we predict screens? 469 config.predict_screen_statements = True 470 471 def warp_true(p): 472 return True 473 474 def parse_show_call_screen(l): 475 476 # Parse a name. 477 name = l.require(l.name) 478 479 # Parse the list of arguments. 480 arguments = renpy.parser.parse_arguments(l) 481 482 predict = True 483 transition_expr = None 484 485 while True: 486 487 if l.keyword('nopredict'): 488 predict = False 489 490 elif l.keyword('with'): 491 transition_expr = l.require(l.simple_expression) 492 493 else: 494 break 495 496 l.expect_eol() 497 498 return dict(name=name, arguments=arguments, predict=predict, transition_expr=transition_expr) 499 500 def parse_hide_screen(l): 501 name = l.require(l.name) 502 503 transition_expr = None 504 505 if l.keyword('with'): 506 transition_expr = l.require(l.simple_expression) 507 508 l.expect_eol() 509 510 return dict(name=name, transition_expr=transition_expr) 511 512 def predict_screen(p): 513 514 if not config.predict_screen_statements: 515 return 516 517 predict = p.get("predict", False) 518 519 if not predict: 520 return 521 522 name = p["name"] 523 a = p["arguments"] 524 525 if a is not None: 526 args, kwargs = a.evaluate() 527 else: 528 args = [ ] 529 kwargs = { } 530 531 renpy.predict_screen(name, *args, **kwargs) 532 533 def execute_show_screen(p): 534 535 name = p["name"] 536 a = p["arguments"] 537 538 if a is not None: 539 args, kwargs = a.evaluate() 540 else: 541 args = [ ] 542 kwargs = { } 543 544 transition_expr = p.get("transition_expr", None) 545 if transition_expr is not None: 546 renpy.with_statement(None) 547 548 renpy.show_screen(name, *args, **kwargs) 549 550 if transition_expr is not None: 551 renpy.with_statement(eval(transition_expr)) 552 553 def execute_call_screen(p): 554 555 name = p["name"] 556 a = p["arguments"] 557 558 transition_expr = p.get("transition_expr", None) 559 560 if transition_expr is not None: 561 renpy.transition(eval(transition_expr)) 562 563 if a is not None: 564 args, kwargs = a.evaluate() 565 else: 566 args = [ ] 567 kwargs = { } 568 569 store._return = renpy.call_screen(name, *args, **kwargs) 570 571 def execute_hide_screen(p): 572 name = p["name"] 573 574 transition_expr = p.get("transition_expr", None) 575 if transition_expr is not None: 576 renpy.with_statement(None) 577 578 renpy.hide_screen(name) 579 580 if transition_expr is not None: 581 renpy.with_statement(eval(transition_expr)) 582 583 584 def lint_screen(p): 585 name = p["name"] 586 if not renpy.has_screen(name): 587 renpy.error("Screen %s does not exist." % name) 588 589 590 renpy.register_statement("show screen", 591 parse=parse_show_call_screen, 592 execute=execute_show_screen, 593 predict=predict_screen, 594 lint=lint_screen, 595 warp=warp_true) 596 597 renpy.register_statement("call screen", 598 parse=parse_show_call_screen, 599 execute=execute_call_screen, 600 predict=predict_screen, 601 lint=lint_screen, 602 force_begin_rollback=True) 603 604 renpy.register_statement("hide screen", 605 parse=parse_hide_screen, 606 execute=execute_hide_screen, 607 warp=warp_true) 608