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 python: 23 24 # Styles 25 style.prefs_frame = Style(style.default, help="the frame containing all of the preferences") 26 style.prefs_frame_box = Style(style.default, help="the box inside the frame containing all of the preferences") 27 28 style.prefs_pref_frame = Style(style.menu_frame, help="a frame containing a single preference") 29 style.prefs_pref_box = Style(style.vbox, help="the box separating the preference label from the preference choices") 30 style.prefs_pref_choicebox = Style(style.vbox, help="the box containing the preference choices") 31 32 style.prefs_label = Style(style.label, help="a preference label (window)") 33 style.prefs_label_text = Style(style.label_text, help="a preference label (text)") 34 35 style.prefs_button = Style(style.radio_button, help="preference value button") 36 style.prefs_button_text = Style(style.radio_button_text, help="preference value button (text)") 37 38 style.prefs_slider = Style(style.slider, help="preference value slider bar") 39 40 style.prefs_volume_box = Style(style.vbox, help="box containing a volume slider and soundtest button") 41 style.prefs_volume_slider = Style(style.prefs_slider, help="volume slider bar") 42 43 style.soundtest_button = Style(style.small_button, help="soundtest button") 44 style.soundtest_button_text = Style(style.small_button_text, help="soundtest button (text)") 45 46 style.prefs_jump = Style(style.prefs_pref_frame, help="jump preference frame") 47 style.prefs_jump_button = Style(style.button, help="jump preference button") 48 style.prefs_jump_button_text = Style(style.button_text, help="jump preference button (text)") 49 50 # This is a map from the name of the style that is applied to 51 # a list of preferences that should be placed into a vbox 52 # with that style. 53 config.preferences = { } 54 55 # This is a map from preference name to that preference 56 # object, that can be used in rearranging preferences. 57 config.all_preferences = { } 58 59 # Should the soundtest button be before the volume slider? 60 config.soundtest_before_volume = False 61 62 def _prefs_screen_run(prefs_map): 63 64 layout.navigation("preferences") 65 66 ui.window(style='prefs_frame') 67 ui.fixed(style='prefs_frame_box') 68 69 for style, prefs in prefs_map.iteritems(): 70 71 ui.vbox(style=style) 72 for i in prefs: 73 i.render() 74 ui.close() 75 76 ui.close() 77 78 ui.interact(mouse="gamemenu") 79 80 class _Preference(object): 81 """ 82 This is a class that's used to represent a multiple-choice 83 preference. 84 """ 85 86 def __init__(self, name, field, values, base=_preferences): 87 """ 88 @param name: The name of this preference. It will be 89 displayed to the user. 90 91 @param variable: The field on the base object 92 that will be assigned the selected value. This field 93 must exist. 94 95 @param values: A list of value name, value, condition 96 triples. The value name is the name of this value that 97 will be shown to the user. The value is the literal 98 python value that will be assigned if this value is 99 selected. The condition is a condition that will be 100 evaluated to determine if this is a legal value. If no 101 conditions are true, this preference will not be 102 displayed to the user. A condition of None is always 103 considered to be True. 104 105 @param base: The base object on which the variable is 106 read from and set. This defaults to _preferences, 107 the user preferences object. 108 """ 109 110 self.name = name 111 self.field = field 112 self.values = values 113 self.base = base 114 115 config.all_preferences[name] = self 116 117 118 def render(self): 119 values = [ (name, val) for name, val, cond in self.values 120 if cond is None or renpy.eval(cond) ] 121 122 if not values: 123 return 124 125 ui.window(style=style.prefs_pref_frame[self.name]) 126 ui.vbox(style=style.prefs_pref_box[self.name]) 127 128 layout.label(self.name, "prefs") 129 130 cur = getattr(self.base, self.field) 131 132 ui.hbox(style=style.prefs_pref_choicebox[self.name]) 133 134 for name, value in values: 135 def clicked(value=value): 136 setattr(self.base, self.field, value) 137 return True 138 139 layout.button(name, "prefs", 140 selected=(cur==value), 141 clicked=clicked) 142 143 ui.close() # choicebox 144 ui.close() # vbox 145 146 class _VolumePreference(object): 147 """ 148 This represents a preference that controls one of the 149 volumes in the system. It is represented as a slider bar, 150 and a button that can be pushed to play a sample sound on 151 a channel. 152 """ 153 154 def __init__(self, name, mixer, enable='True', sound='None', channel="sound"): 155 """ 156 @param name: The name of this preference, as shown to the user. 157 158 @param mixer: The mixer this preference controls. 159 160 @param enable: A string giving a python expression. If 161 the expression is evaluates to false, this preference 162 is not shown. 163 164 @param sound: A string that is evaluated to yield 165 another string. The yielded string is expected to give 166 a sound file, which is played as the sample sound. (We 167 apologize for the convolution of this.) 168 169 @param channel: The number of the channel the sample 170 sound is played on. 171 """ 172 173 self.name = name 174 self.mixer = mixer 175 self.enable = enable 176 self.sound = sound 177 self.channel = channel 178 179 config.all_preferences[name] = self 180 181 def render(self): 182 183 if not eval(self.enable): 184 return 185 186 sound = eval(self.sound) 187 188 ui.window(style=style.prefs_pref_frame[self.name]) 189 ui.vbox(style=style.prefs_pref_box[self.name]) 190 191 layout.label(self.name, "prefs") 192 193 ui.vbox(style=style.prefs_volume_box[self.name]) 194 195 if sound: 196 def clicked(): 197 renpy.sound.play(sound, channel=self.channel) 198 199 if sound and config.soundtest_before_volume: 200 layout.button(u"Test", "soundtest", clicked=clicked, index=self.name) 201 202 def changed(v): 203 _preferences.set_volume(self.mixer, v / 128.0) 204 205 ui.bar(128, 206 int(_preferences.get_volume(self.mixer) * 128), 207 changed=changed, 208 style=style.prefs_volume_slider[self.name]) 209 210 if sound and not config.soundtest_before_volume: 211 layout.button(u"Test", "soundtest", clicked=clicked, index=self.name) 212 213 ui.close() 214 ui.close() 215 216 class _SliderPreference(object): 217 """ 218 A class that represents a preference that is controlled by a 219 slider. 220 """ 221 222 def __init__(self, name, field, range, enable='True', base=_preferences): 223 """ 224 @param name: The name of this preference, that is shown to the user. 225 226 @param range: An integer giving the maximum value of 227 this slider. The slider goes from 0 to range. 228 229 @param get: A function that's called to get the 230 initial value of the slider. It's called with no 231 arguments, and should return an integet between 0 and 232 range, inclusive. 233 234 @param set: A function that's called when the value of 235 the slider is set by the user. It is called with a 236 single integer, in the range 0 to range, inclusive. 237 238 @param enable: A string giving a python expression. If 239 the expression is evaluates to false, this preference 240 is not shown. 241 """ 242 243 self.name = name 244 self.field = field 245 self.range = range 246 self.enable = enable 247 self.base = base 248 249 config.all_preferences[name] = self 250 251 def get(self): 252 value = getattr(self.base, self.field) 253 if value == 0: 254 value = self.range 255 else: 256 value -= 1 257 258 return value 259 260 def set(self, value): 261 value += 1 262 if value == self.range + 1: 263 value = 0 264 265 setattr(self.base, self.field, value) 266 267 def render(self): 268 269 if not eval(self.enable): 270 return 271 272 ui.window(style=style.prefs_pref_frame[self.name]) 273 ui.vbox(style=style.prefs_pref_box[self.name]) 274 275 layout.label(self.name, "prefs") 276 277 def changed(v): 278 self.set(v) 279 280 ui.bar(self.range, 281 self.get(), 282 changed=changed, 283 style=style.prefs_slider[self.name]) 284 285 ui.close() 286 287 288 class _JumpPreference(object): 289 290 def __init__(self, name, target, condition="True", show="True"): 291 self.name = name 292 self.target = target 293 self.condition = condition 294 self.show = show 295 296 config.all_preferences[name] = self 297 298 def render(self): 299 300 if not eval(self.show): 301 return 302 303 ui.window(style=style.prefs_jump[self.name]) 304 305 if eval(self.condition): 306 clicked=ui.jumps(self.target) 307 else: 308 clicked=None 309 310 layout.button(self.name, 'prefs_jump', clicked=clicked) 311 312 def _remove_preference(name): 313 """ 314 Removes the preference with the given name from the 315 preferences menu. 316 """ 317 318 pref = config.all_preferences.get(name, None) 319 if not pref: 320 return 321 322 for k, v in config.preferences.iteritems(): 323 if pref in v: 324 v.remove(pref) 325 326init python hide: 327 328 # Enablers for some preferences. 329 config.sample_sound = None 330 config.sample_voice = None 331 config.has_transitions = True 332 config.has_cps = True 333 config.has_afm = True 334 config.has_skipping = True 335 config.has_skip_after_choice = True 336 config.always_has_joystick = False 337 config.has_joystick = True 338 339 340 # Left 341 342 _Preference(u'Display', 'fullscreen', [ 343 (u'Window', False, None), 344 (u'Fullscreen', True, None), 345 ]) 346 347 _Preference(u'Transitions', 'transitions', [ 348 (u'All', 2, 'config.has_transitions'), 349 (u'Some', 1, 'config.has_transitions and default_transition'), 350 (u'None', 0, 'config.has_transitions'), 351 ]) 352 353 # Center 354 355 _Preference(u'Skip', 'skip_unseen', [ 356 (u'Seen Messages', False, 'config.allow_skipping and config.has_skipping'), 357 (u'All Messages', True, 'config.allow_skipping and config.has_skipping'), 358 ]) 359 360 _JumpPreference(u'Begin Skipping', 361 '_return_skipping', 362 'not main_menu', 363 'config.allow_skipping and config.has_skipping') 364 365 366 _Preference(u'After Choices', 'skip_after_choices', [ 367 (u'Stop Skipping', False, 'config.allow_skipping and config.has_skip_after_choice'), 368 (u'Keep Skipping', True, 'config.allow_skipping and config.has_skip_after_choice'), 369 ]) 370 371 config.old_names['Keep Skipping'] = 'Continue Skipping' 372 373 _SliderPreference(u'Text Speed', "text_cps", 150, 'config.has_cps') 374 _SliderPreference(u'Auto-Forward Time', "afm_time", 40, 'config.has_afm') 375 376 # Right 377 378 _VolumePreference(u"Music Volume", 379 'music', 380 'config.has_music') 381 382 _VolumePreference(u"Sound Volume", 383 'sfx', 384 'config.has_sound', 385 'config.sample_sound', 386 'sound') 387 388 _VolumePreference(u'Voice Volume', 389 'voice', 390 'config.has_voice', 391 'config.sample_voice', 392 'voice') 393 394 _JumpPreference(u'Joystick...', 395 'joystick_preferences_screen', 396 'renpy.display.joystick.enabled or config.always_has_joystick', 397 'config.has_joystick') 398 399