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
22init -1600 python:
23
24    config.keymap = dict(
25
26        # Bindings present almost everywhere, unless explicitly
27        # disabled.
28        rollback = [ 'K_PAGEUP', 'repeat_K_PAGEUP', 'K_AC_BACK', 'mousedown_4' ],
29        screenshot = [ 's', 'alt_K_s', 'alt_shift_K_s', 'noshift_K_s' ],
30        toggle_afm = [ ],
31        toggle_fullscreen = [ 'f', 'alt_K_RETURN', 'alt_K_KP_ENTER', 'K_F11', 'noshift_K_f' ],
32        game_menu = [ 'K_ESCAPE', 'K_MENU', 'K_PAUSE', 'mouseup_3' ],
33        hide_windows = [ 'mouseup_2', 'h', 'noshift_K_h' ],
34        launch_editor = [ 'E', 'shift_K_e' ],
35        dump_styles = [ ],
36        reload_game = [ 'R', 'alt_shift_K_r', 'shift_K_r' ],
37        inspector = [ 'I', 'shift_K_i' ],
38        full_inspector = [ 'alt_shift_K_i' ],
39        developer = [ 'shift_K_d', 'alt_shift_K_d' ],
40        quit = [ ],
41        iconify = [ ],
42        help = [ 'K_F1', 'meta_shift_/' ],
43        choose_renderer = [ 'G', 'alt_shift_K_g', 'shift_K_g' ],
44        progress_screen = [ 'alt_shift_K_p', 'meta_shift_K_p', 'K_F2' ],
45        accessibility = [ "K_a" ],
46
47        # Accessibility.
48        self_voicing = [ 'v', 'V', 'alt_K_v', 'K_v' ],
49        clipboard_voicing = [ 'C', 'alt_shift_K_c', 'shift_K_c' ],
50        debug_voicing = [ 'alt_shift_K_v', 'meta_shift_K_v' ],
51
52        # Say.
53        rollforward = [ 'mousedown_5', 'K_PAGEDOWN', 'repeat_K_PAGEDOWN' ],
54        dismiss = [ 'mouseup_1', 'K_RETURN', 'K_SPACE', 'K_KP_ENTER', 'K_SELECT' ],
55        dismiss_unfocused = [ ],
56
57        # Pause.
58        dismiss_hard_pause = [ ],
59
60        # Focus.
61        focus_left = [ 'K_LEFT', 'repeat_K_LEFT' ],
62        focus_right = [ 'K_RIGHT', 'repeat_K_RIGHT' ],
63        focus_up = [ 'K_UP', 'repeat_K_UP' ],
64        focus_down = [ 'K_DOWN', 'repeat_K_DOWN' ],
65
66        # Button.
67        button_ignore = [ 'mousedown_1' ],
68        button_select = [ 'mouseup_1', 'K_RETURN', 'K_KP_ENTER', 'K_SELECT' ],
69        button_alternate = [ 'mouseup_3' ],
70        button_alternate_ignore = [ 'mousedown_3' ],
71
72        # Input.
73        input_backspace = [ 'K_BACKSPACE', 'repeat_K_BACKSPACE' ],
74        input_enter = [ 'K_RETURN', 'K_KP_ENTER' ],
75        input_left = [ 'K_LEFT', 'repeat_K_LEFT' ],
76        input_right = [ 'K_RIGHT', 'repeat_K_RIGHT' ],
77        input_up = [ 'K_UP', 'repeat_K_UP' ],
78        input_down = [ 'K_DOWN', 'repeat_K_DOWN' ],
79        input_delete = [ 'K_DELETE', 'repeat_K_DELETE' ],
80        input_home = [ 'K_HOME', 'meta_K_LEFT' ],
81        input_end = [ 'K_END', 'meta_K_RIGHT' ],
82        input_copy = [ 'ctrl_noshift_K_INSERT', 'ctrl_noshift_K_c' ],
83        input_paste = [ 'shift_K_INSERT', 'ctrl_noshift_K_v' ],
84        input_jump_word_left = [ 'osctrl_K_LEFT' ],
85        input_jump_word_right = [ 'osctrl_K_RIGHT' ],
86        input_delete_word = [ 'osctrl_K_BACKSPACE' ],
87        input_delete_full = [ 'meta_K_BACKSPACE' ],
88
89        # Viewport.
90        viewport_leftarrow = [ 'K_LEFT', 'repeat_K_LEFT' ],
91        viewport_rightarrow = [ 'K_RIGHT', 'repeat_K_RIGHT' ],
92        viewport_uparrow = [ 'K_UP', 'repeat_K_UP' ],
93        viewport_downarrow = [ 'K_DOWN', 'repeat_K_DOWN' ],
94        viewport_wheelup = [ 'mousedown_4' ],
95        viewport_wheeldown = [ 'mousedown_5' ],
96        viewport_drag_start = [ 'mousedown_1' ],
97        viewport_drag_end = [ 'mouseup_1' ],
98        viewport_pageup = [ 'K_PAGEUP', 'repeat_K_PAGEUP' ],
99        viewport_pagedown = [ 'K_PAGEDOWN', 'repeat_K_PAGEDOWN' ],
100
101        # These keys control skipping.
102        skip = [ 'K_LCTRL', 'K_RCTRL' ],
103        stop_skipping = [ ],
104        toggle_skip = [ 'K_TAB' ],
105        fast_skip = [ '>', 'shift_K_PERIOD' ],
106
107        # Bar.
108        bar_activate = [ 'mousedown_1', 'K_RETURN', 'K_KP_ENTER', 'K_SELECT' ],
109        bar_deactivate = [ 'mouseup_1', 'K_RETURN', 'K_KP_ENTER', 'K_SELECT' ],
110        bar_left = [ 'K_LEFT', 'repeat_K_LEFT' ],
111        bar_right = [ 'K_RIGHT', 'repeat_K_RIGHT' ],
112        bar_up = [ 'K_UP', 'repeat_K_UP' ],
113        bar_down = [ 'K_DOWN', 'repeat_K_DOWN' ],
114
115        # Delete a save.
116        save_delete = [ 'K_DELETE' ],
117
118        # Draggable.
119        drag_activate = [ 'mousedown_1' ],
120        drag_deactivate = [ 'mouseup_1' ],
121
122        # Debug console.
123        console = [ 'shift_K_o', 'alt_shift_K_o' ],
124        console_older = [ 'K_UP', 'repeat_K_UP' ],
125        console_newer = [ 'K_DOWN', 'repeat_K_DOWN'],
126
127        # Director
128        director = [ 'noshift_K_d' ],
129
130        # Ignored (kept for backwards compatibility).
131        toggle_music = [ 'm' ],
132        viewport_up = [ 'mousedown_4' ],
133        viewport_down = [ 'mousedown_5' ],
134
135        # Profile commands.
136        performance = [ 'K_F3' ],
137        image_load_log = [ 'K_F4' ],
138        profile_once = [ 'K_F8' ],
139        memory_profile = [ 'K_F7' ],
140
141    )
142
143    config.default_keymap = { k : list(v) for k, v in config.keymap.items() }
144
145    config.pad_bindings = {
146        "pad_leftshoulder_press" : [ "rollback", ],
147        "pad_lefttrigger_pos" : [ "rollback", ],
148        "pad_back_press" : [ "rollback", ],
149
150        "repeat_pad_leftshoulder_press" : [ "rollback", ],
151        "repeat_pad_lefttrigger_pos" : [ "rollback", ],
152        "repeat_pad_back_press" : [ "rollback", ],
153
154        "pad_guide_press" : [ "game_menu", ],
155        "pad_start_press" : [ "game_menu", ],
156
157        "pad_y_press" : [ "hide_windows", ],
158
159        "pad_rightshoulder_press" : [ "rollforward", ],
160        "repeat_pad_rightshoulder_press" : [ "rollforward", ],
161
162        "pad_righttrigger_pos" : [ "dismiss", "button_select", "bar_activate", "bar_deactivate" ],
163        "pad_a_press" : [ "dismiss", "button_select", "bar_activate", "bar_deactivate"],
164        "pad_b_press" : [ "button_alternate" ],
165
166        "pad_dpleft_press" : [ "focus_left", "bar_left", "viewport_leftarrow" ],
167        "pad_leftx_neg" : [ "focus_left", "bar_left", "viewport_leftarrow" ],
168        "pad_rightx_neg" : [ "focus_left", "bar_left", "viewport_leftarrow" ],
169
170        "pad_dpright_press" : [ "focus_right", "bar_right", "viewport_rightarrow" ],
171        "pad_leftx_pos" : [ "focus_right", "bar_right", "viewport_rightarrow" ],
172        "pad_rightx_pos" : [ "focus_right", "bar_right", "viewport_rightarrow" ],
173
174        "pad_dpup_press" : [ "focus_up", "bar_up", "viewport_uparrow" ],
175        "pad_lefty_neg" : [ "focus_up", "bar_up", "viewport_uparrow" ],
176        "pad_righty_neg" : [ "focus_up", "bar_up", "viewport_uparrow" ],
177
178        "pad_dpdown_press" : [ "focus_down", "bar_down", "viewport_downarrow" ],
179        "pad_lefty_pos" : [ "focus_down", "bar_down", "viewport_downarrow" ],
180        "pad_righty_pos" : [ "focus_down", "bar_down", "viewport_downarrow" ],
181
182        "repeat_pad_dpleft_press" : [ "focus_left", "bar_left", "viewport_leftarrow" ],
183        "repeat_pad_leftx_neg" : [ "focus_left", "bar_left", "viewport_leftarrow" ],
184        "repeat_pad_rightx_neg" : [ "focus_left", "bar_left", "viewport_leftarrow" ],
185
186        "repeat_pad_dpright_press" : [ "focus_right", "bar_right", "viewport_rightarrow" ],
187        "repeat_pad_leftx_pos" : [ "focus_right", "bar_right", "viewport_rightarrow" ],
188        "repeat_pad_rightx_pos" : [ "focus_right", "bar_right", "viewport_rightarrow" ],
189
190        "repeat_pad_dpup_press" : [ "focus_up", "bar_up", "viewport_uparrow" ],
191        "repeat_pad_lefty_neg" : [ "focus_up", "bar_up", "viewport_uparrow" ],
192        "repeat_pad_righty_neg" : [ "focus_up", "bar_up", "viewport_uparrow" ],
193
194        "repeat_pad_dpdown_press" : [ "focus_down", "bar_down", "viewport_downarrow" ],
195        "repeat_pad_lefty_pos" : [ "focus_down", "bar_down", "viewport_downarrow" ],
196        "repeat_pad_righty_pos" : [ "focus_down", "bar_down", "viewport_downarrow" ],
197    }
198
199    # Should we use the autoreload system?
200    config.autoreload = True
201
202init -1600 python:
203
204    # Are the windows currently hidden?
205    _windows_hidden = False
206
207    def _keymap_toggle_afm():
208        if renpy.context()._menu:
209            return
210
211        renpy.run(Preference("auto-forward", "toggle"))
212
213    def _toggle_skipping():
214
215        if not renpy.config.allow_skipping:
216            return
217
218        if not renpy.store._skipping:
219            return
220
221        if not config.skipping:
222            config.skipping = "slow"
223        else:
224            config.skipping = None
225
226        if renpy.context()._menu:
227            renpy.jump("_noisy_return")
228        else:
229            renpy.restart_interaction()
230
231    toggle_skipping = _toggle_skipping
232
233    def _keymap_toggle_skipping():
234        if renpy.context()._menu:
235            return
236
237        _toggle_skipping()
238
239    config.help = None
240
241    config.help_screen = "help"
242
243    def _help(help=None):
244
245        if help is None:
246            help = config.help
247
248        if help is None:
249            if config.help_screen and renpy.has_screen(config.help_screen):
250                renpy.run(ShowMenu(config.help_screen))
251
252            return
253
254        if renpy.has_label(help):
255            renpy.call_in_new_context(help)
256            return
257
258        _preferences.fullscreen = False
259
260        try:
261            import webbrowser
262            import os
263
264            file_path = os.path.join(config.basedir, help)
265            if not os.path.isfile(file_path):
266                return
267
268            webbrowser.open_new("file:///" + file_path)
269        except:
270            pass
271
272    import os
273    config.screenshot_pattern = os.environ.get("RENPY_SCREENSHOT_PATTERN", "screenshot%04d.png")
274    del os
275
276    # Called to make a screenshot happen.
277    def _screenshot():
278        import os.path
279        import os
280        import __main__
281
282        dest = config.renpy_base
283
284        if renpy.macapp:
285            dest = os.path.expanduser(b"~/Desktop")
286
287        pattern = renpy.store._screenshot_pattern or config.screenshot_pattern
288
289        # Try to pick a filename.
290        i = 1
291        while True:
292            fn = os.path.join(dest, pattern % i)
293            if not os.path.exists(fn):
294                break
295            i += 1
296
297        try:
298            dn = os.path.dirname(fn)
299            if not os.path.exists(dn):
300                os.makedirs(dn)
301        except:
302            pass
303
304        try:
305            if not renpy.screenshot(fn):
306                renpy.notify(__("Failed to save screenshot as %s.") % fn)
307                return
308        except:
309            import traceback
310            traceback.print_exc()
311            renpy.notify(__("Failed to save screenshot as %s.") % fn)
312            return
313
314        if config.screenshot_callback is not None:
315            config.screenshot_callback(fn)
316
317    def _screenshot_callback(fn):
318        renpy.notify(__("Saved screenshot as %s.") % fn)
319
320    config.screenshot_callback = _screenshot_callback
321
322    def _fast_skip():
323        if not config.fast_skipping and not config.developer:
324            return
325
326        Skip(fast=True, confirm=not config.developer)()
327
328    def _reload_game():
329        if not config.developer:
330            return
331
332        if not config.autoreload:
333            renpy.exports.reload_script()
334            return
335
336        if renpy.get_autoreload():
337            renpy.set_autoreload(False)
338            renpy.restart_interaction()
339        else:
340            renpy.set_autoreload(True)
341            renpy.exports.reload_script()
342
343    def _launch_editor():
344        if not config.developer:
345            return
346
347        filename, line = renpy.get_filename_line()
348        renpy.launch_editor([ filename ], line)
349
350    def _developer():
351
352        if not config.developer:
353            return
354
355        renpy.show_screen("_developer")
356        renpy.restart_interaction()
357
358    def _profile_once():
359
360        if not config.profile:
361            config.profile_time = 10.0
362            config.profile = True
363
364        renpy.display.interface.profile_once = True
365
366    def _memory_profile():
367        import os
368
369        if not renpy.experimental:
370            return
371
372        renpy.memory.diff_memory()
373
374    def _progress_screen():
375        if renpy.context_nesting_level():
376            return
377
378        if renpy.get_screen("_progress"):
379            renpy.hide_screen("_progress")
380        else:
381            renpy.show_screen("_progress")
382
383        renpy.restart_interaction()
384
385screen _progress:
386    $ new = renpy.count_newly_seen_dialogue_blocks()
387    $ seen = renpy.count_seen_dialogue_blocks()
388    $ total = renpy.count_dialogue_blocks()
389
390    drag:
391        draggable True
392        focus_mask None
393        xpos 0
394        ypos 0
395
396        text "[new] [seen]/[total]":
397            size 14
398            color "#fff"
399            outlines [ (1, "#000", 0, 0) ]
400            alt ""
401
402init -1100 python:
403
404    # The default keymap. We might also want to put some of this into
405    # the launcher.
406    _default_keymap = renpy.Keymap(
407        rollback = renpy.rollback,
408        screenshot = _screenshot,
409        toggle_fullscreen = renpy.toggle_fullscreen,
410        toggle_afm = _keymap_toggle_afm,
411        toggle_skip = _keymap_toggle_skipping,
412        fast_skip = _fast_skip,
413        game_menu = _invoke_game_menu,
414        hide_windows = renpy.curried_call_in_new_context("_hide_windows"),
415        launch_editor = _launch_editor,
416        reload_game = _reload_game,
417        developer = _developer,
418        quit = renpy.quit_event,
419        iconify = renpy.iconify,
420        help = _help,
421        choose_renderer = renpy.curried_call_in_new_context("_choose_renderer"),
422        console = _console.enter,
423        profile_once = _profile_once,
424        memory_profile = _memory_profile,
425        self_voicing = Preference("self voicing", "toggle"),
426        clipboard_voicing = Preference("clipboard voicing", "toggle"),
427        debug_voicing = Preference("debug voicing", "toggle"),
428        progress_screen = _progress_screen,
429        director = director.Start(),
430        performance = ToggleScreen("_performance"),
431        accessibility = ToggleScreen("_accessibility"),
432        )
433
434    config.underlay = [ _default_keymap ]
435
436init 1100 python hide:
437
438    import os
439
440    if "RENPY_DEFAULT_KEYMAP" in os.environ:
441        renpy.config.keymap = renpy.config.default_keymap
442        config.underlay.insert(0, _default_keymap)
443
444
445label _hide_windows:
446
447    if renpy.context()._menu:
448        return
449
450    if _windows_hidden:
451        return
452
453    if renpy.has_label("hide_windows"):
454        call hide_windows
455        if _return:
456            return
457
458    python:
459        _windows_hidden = True
460        voice_sustain()
461        ui.saybehavior(dismiss=['dismiss', 'hide_windows'])
462        ui.interact(suppress_overlay=True, suppress_window=True)
463        _windows_hidden = False
464
465    return
466
467
468label _save_reload_game:
469    python hide:
470
471        renpy.music.stop()
472
473        if renpy.can_load("_reload-1"):
474            renpy.utter_restart()
475
476        import time
477        renpy.session["_reload_time"] = time.time()
478
479        renpy.take_screenshot((config.thumbnail_width, config.thumbnail_height))
480
481        ui.add(Solid((0, 0, 0, 255)))
482        ui.text("Saving game...",
483                size=32, xalign=0.5, yalign=0.5, color="#fff", style="_text")
484
485        ui.pausebehavior(0)
486        ui.interact(suppress_overlay=True, suppress_underlay=True)
487
488        renpy.save("_reload-1", "reload save game")
489
490        ui.add(Solid((0, 0, 0, 255)))
491        ui.text("Reloading script...",
492                size=32, xalign=0.5, yalign=0.5, color="#fff", style="_text")
493
494        ui.pausebehavior(0)
495        ui.interact(suppress_overlay=True, suppress_underlay=True)
496
497        renpy.utter_restart()
498
499label _load_reload_game:
500
501    if not renpy.can_load("_reload-1"):
502        return
503
504    python hide:
505        renpy.rename_save("_reload-1", "_reload-2")
506
507        ui.add(Solid((0, 0, 0, 255)))
508        ui.text("Reloading game...",
509                size=32, xalign=0.5, yalign=0.5, color="#fff", style="_text")
510
511        ui.pausebehavior(0)
512        ui.interact(suppress_underlay=True, suppress_overlay=True)
513
514        renpy.load("_reload-2")
515
516    return
517