Browse Source

in-game options for audio/graphics

Inderjit Gill 1 year ago
parent
commit
c8a55b2eae
5 changed files with 125 additions and 41 deletions
  1. 84
    34
      src/game.rs
  2. 18
    2
      src/lib.rs
  3. 4
    0
      web/js/Audio.js
  4. 6
    3
      web/js/GLRenderer.js
  5. 13
    2
      web/js/index.js

+ 84
- 34
src/game.rs View File

@@ -3,6 +3,7 @@ use error;
3 3
 use geometry::Geometry;
4 4
 use log;
5 5
 use audio_play;
6
+use audio_volume;
6 7
 use colour::{Colour, Format};
7 8
 use sprite::Sprite;
8 9
 use std::collections::HashMap;
@@ -71,7 +72,7 @@ impl Board {
71 72
         );
72 73
         let width = config.board_width;
73 74
         let height = config.board_height;
74
-        let crt_update_speed = config.crt_update_speed;
75
+        let crt_update_speed = 1.0; // this will be updated by Game::new() calling set_crt_update_speed
75 76
 
76 77
         Board {
77 78
             board: vec![vec![Block::new(inactive_colour); width]; height],
@@ -90,7 +91,10 @@ impl Board {
90 91
             config.block_inactive_col_b,
91 92
             config.block_inactive_col_a,
92 93
         );
93
-        self.crt_update_speed = config.crt_update_speed;
94
+    }
95
+
96
+    pub fn set_crt_update_speed(&mut self, new_crt_update_speed: f32) {
97
+        self.crt_update_speed = new_crt_update_speed;
94 98
     }
95 99
 
96 100
     pub fn is_line_filled(&self, line_number: usize) -> bool {
@@ -216,13 +220,14 @@ pub struct Game {
216 220
     t_count: i32,
217 221
     z_count: i32,
218 222
 
223
+    debug_mode: bool,
219 224
     debug_flash: bool,
220 225
     debug_delta: f32,
221 226
 }
222 227
 
223 228
 impl Game {
224 229
     pub fn new(config: &Config) -> Game {
225
-        Game {
230
+        let mut game = Game {
226 231
             mode: GameMode::Playing,
227 232
             resume_from_paused: false,
228 233
 
@@ -247,15 +252,12 @@ impl Game {
247 252
 
248 253
             menu_num_items: 5,
249 254
             menu_active_item: 0,
250
-
251
-            menu_volume: 4,
252
-            menu_max_volume: 5,
253
-
254
-            menu_crt_update_speed: 4,
255
-            menu_max_crt_update_speed: 5,
256
-
257
-            menu_curvature: 3,
258
-            menu_max_curvature: 5,
255
+            menu_volume: config.menu_volume,
256
+            menu_max_volume: config.menu_max_volume,
257
+            menu_crt_update_speed: config.menu_crt_update_speed,
258
+            menu_max_crt_update_speed: config.menu_max_crt_update_speed,
259
+            menu_curvature: config.menu_curvature,
260
+            menu_max_curvature: config.menu_max_curvature,
259 261
 
260 262
             is_game_over: false,
261 263
 
@@ -285,9 +287,21 @@ impl Game {
285 287
             t_count: 0,
286 288
             z_count: 0,
287 289
 
290
+            debug_mode: false,
288 291
             debug_flash: true,
289 292
             debug_delta: 0.0,
290
-        }
293
+        };
294
+
295
+        // set state according to initial config values
296
+        update_audio_volume(&game);
297
+        update_crt_update_speed(&mut game);
298
+
299
+        game
300
+    }
301
+
302
+    pub fn get_curvature(&self) -> f32 {
303
+        let f = self.menu_curvature as f32 / self.menu_max_curvature as f32;
304
+        f * (1.0 / 8.0)
291 305
     }
292 306
 
293 307
     pub fn reconfigure(&mut self, config: &Config) {
@@ -359,12 +373,14 @@ impl Game {
359 373
             }
360 374
         }
361 375
 
362
-        if self.debug_flash {
363
-            geometry.push_text("X", Block2D { x: 11, y: 1 }, Col::new(1.0, 1.0, 1.0, 1.0));
364
-        } else {
365
-            geometry.push_text("-", Block2D { x: 11, y: 1 }, Col::new(1.0, 1.0, 1.0, 1.0));
376
+        if self.debug_mode {
377
+            if self.debug_flash {
378
+                geometry.push_text("X", Block2D { x: 11, y: 1 }, Col::new(1.0, 1.0, 1.0, 1.0));
379
+            } else {
380
+                geometry.push_text("-", Block2D { x: 11, y: 1 }, Col::new(1.0, 1.0, 1.0, 1.0));
381
+            }
382
+            geometry.push_text(&format!("{}", self.debug_delta), Block2D { x: 12, y: 1 }, Col::new(1.0, 1.0, 1.0, 1.0));
366 383
         }
367
-        geometry.push_text(&format!("{}", self.debug_delta), Block2D { x: 12, y: 1 }, Col::new(1.0, 1.0, 1.0, 1.0));
368 384
     }
369 385
 
370 386
     pub fn tick(&mut self, controller: &Controller, delta: f32, random: f32) -> bool {
@@ -379,6 +395,10 @@ impl Game {
379 395
                 self.resume_from_paused = true;
380 396
             }
381 397
         }
398
+        if controller.just_released(ControllerButton::Select) {
399
+            self.debug_mode = !self.debug_mode;
400
+        }
401
+
382 402
 
383 403
         // always followed by a render
384 404
         // returns true if tick should be called again
@@ -393,10 +413,12 @@ impl Game {
393 413
             corrected_delta = delta;
394 414
         }
395 415
 
396
-        self.debug_flash = !self.debug_flash;
397
-        self.debug_delta = corrected_delta;
398
-        if self.debug_delta > 0.0 && self.debug_delta < 15.0 {
399
-            log(&format!("{}", self.debug_delta));
416
+        if self.debug_mode {
417
+            self.debug_flash = !self.debug_flash;
418
+            self.debug_delta = corrected_delta;
419
+            if self.debug_delta > 0.0 && self.debug_delta < 15.0 {
420
+                log(&format!("{}", self.debug_delta));
421
+            }
400 422
         }
401 423
 
402 424
         match self.mode {
@@ -1011,13 +1033,11 @@ fn update_pause_menu(game: &mut Game, controller: &Controller) -> bool {
1011 1033
 
1012 1034
     if controller.just_pressed(ControllerButton::Down) {
1013 1035
         game.menu_active_item += 1;
1014
-        play_sound(SoundEffect::MenuMove);
1015 1036
         update = true;
1016 1037
     }
1017 1038
 
1018 1039
     if controller.just_pressed(ControllerButton::Up) {
1019 1040
         game.menu_active_item -= 1;
1020
-        play_sound(SoundEffect::MenuMove);
1021 1041
         update = true;
1022 1042
     }
1023 1043
 
@@ -1025,52 +1045,82 @@ fn update_pause_menu(game: &mut Game, controller: &Controller) -> bool {
1025 1045
 
1026 1046
     // volume
1027 1047
     if game.menu_active_item == 1 {
1048
+        let mut volume_changed = false;
1049
+
1028 1050
         if controller.just_pressed(ControllerButton::Left) {
1051
+            volume_changed = true;
1029 1052
             game.menu_volume -= 1;
1030
-            play_sound(SoundEffect::MenuMove);
1031
-            update = true;
1032 1053
         }
1033 1054
         if controller.just_pressed(ControllerButton::Right) {
1055
+            volume_changed = true;
1034 1056
             game.menu_volume += 1;
1035
-            play_sound(SoundEffect::MenuMove);
1057
+        }
1058
+        if volume_changed {
1059
+            game.menu_volume = clamp(game.menu_volume, 0, game.menu_max_volume);
1036 1060
             update = true;
1061
+
1062
+            update_audio_volume(game);
1037 1063
         }
1038
-        game.menu_volume = clamp(game.menu_volume, 0, game.menu_max_volume);
1039 1064
     }
1040 1065
 
1041 1066
     // crt update speed
1042 1067
     if game.menu_active_item == 2 {
1068
+        let mut crt_update_speed_changed = false;
1069
+
1043 1070
         if controller.just_pressed(ControllerButton::Left) {
1071
+            crt_update_speed_changed = true;
1044 1072
             game.menu_crt_update_speed -= 1;
1045
-            play_sound(SoundEffect::MenuMove);
1046
-            update = true;
1047 1073
         }
1048 1074
         if controller.just_pressed(ControllerButton::Right) {
1075
+            crt_update_speed_changed = true;
1049 1076
             game.menu_crt_update_speed += 1;
1050
-            play_sound(SoundEffect::MenuMove);
1077
+        }
1078
+
1079
+        if crt_update_speed_changed {
1080
+            game.menu_crt_update_speed = clamp(game.menu_crt_update_speed, 0, game.menu_max_crt_update_speed);
1051 1081
             update = true;
1082
+
1083
+            update_crt_update_speed(game);
1052 1084
         }
1053
-        game.menu_crt_update_speed = clamp(game.menu_crt_update_speed, 0, game.menu_max_crt_update_speed);
1054 1085
     }
1055 1086
 
1056 1087
     // curvature
1057 1088
     if game.menu_active_item == 3 {
1058 1089
         if controller.just_pressed(ControllerButton::Left) {
1059 1090
             game.menu_curvature -= 1;
1060
-            play_sound(SoundEffect::MenuMove);
1061 1091
             update = true;
1062 1092
         }
1063 1093
         if controller.just_pressed(ControllerButton::Right) {
1064 1094
             game.menu_curvature += 1;
1065
-            play_sound(SoundEffect::MenuMove);
1066 1095
             update = true;
1067 1096
         }
1068 1097
         game.menu_curvature = clamp(game.menu_curvature, 0, game.menu_max_curvature);
1069 1098
     }
1070 1099
 
1100
+    if update {
1101
+        play_sound(SoundEffect::MenuMove);
1102
+    }
1071 1103
     update
1072 1104
 }
1073 1105
 
1106
+fn update_audio_volume(game: &Game) {
1107
+    let volume = game.menu_volume as f32 / game.menu_max_volume as f32;
1108
+    audio_volume(volume);
1109
+}
1110
+
1111
+fn update_crt_update_speed(game: &mut Game) {
1112
+    if game.menu_crt_update_speed == game.menu_max_crt_update_speed {
1113
+        // switch off any ghosting
1114
+        game.board.set_crt_update_speed(1.0);
1115
+    } else {
1116
+        // 0.00 -> ~1.0
1117
+        // 0.01 ->  0.2
1118
+        let percent = game.menu_crt_update_speed as f32 / game.menu_max_crt_update_speed as f32;
1119
+        let new_crt_update_speed = (percent * 0.2) + 0.01;
1120
+        game.board.set_crt_update_speed(new_crt_update_speed);
1121
+    }
1122
+}
1123
+
1074 1124
 fn wrap(val: i32, min: i32, max: i32) -> i32 {
1075 1125
     if val < min {
1076 1126
         return max

+ 18
- 2
src/lib.rs View File

@@ -44,6 +44,7 @@ extern "C" {
44 44
 #[wasm_bindgen]
45 45
 extern {
46 46
     fn audio_play(id: i32);
47
+    fn audio_volume(volume: f32);
47 48
 }
48 49
 
49 50
 #[wasm_bindgen]
@@ -100,7 +101,13 @@ pub struct Config {
100 101
     pub block_inactive_col_b: f32,
101 102
     pub block_inactive_col_a: f32,
102 103
 
103
-    pub crt_update_speed: f32, // higher the value, the less 'ghosting'
104
+    pub menu_volume: i32,
105
+    pub menu_max_volume: i32,
106
+    pub menu_crt_update_speed: i32,
107
+    pub menu_max_crt_update_speed: i32,
108
+    pub menu_curvature: i32,
109
+    pub menu_max_curvature: i32,
110
+
104 111
 }
105 112
 
106 113
 #[wasm_bindgen]
@@ -130,7 +137,12 @@ impl Config {
130 137
             block_inactive_col_g: 0.6,
131 138
             block_inactive_col_b: 0.0,
132 139
             block_inactive_col_a: 0.1,
133
-            crt_update_speed: 0.15,
140
+            menu_volume: 5,
141
+            menu_max_volume: 5,
142
+            menu_crt_update_speed: 4,
143
+            menu_max_crt_update_speed: 5,
144
+            menu_curvature: 3,
145
+            menu_max_curvature: 5,
134 146
         }
135 147
     }
136 148
 }
@@ -212,6 +224,10 @@ impl Bridge {
212 224
         self.geometry.crt_ptr()
213 225
     }
214 226
 
227
+    pub fn crt_curvature(&self) -> f32 {
228
+        self.game.get_curvature()
229
+    }
230
+
215 231
     fn input_event(&mut self, key: String, action: ControllerAction) -> KeyEventReturn {
216 232
         let mut prevent_default = true;
217 233
         let mut call_tick = false;

+ 4
- 0
web/js/Audio.js View File

@@ -45,6 +45,10 @@ export class Audio {
45 45
     case 9: this.menu_move.play(); break;
46 46
     default: console.error(`Audio: unable to play: ${label}`);
47 47
     }
48
+  }
48 49
 
50
+  volume(amount) {
51
+    // volume is in range 0..1
52
+    Howler.volume(amount);
49 53
   }
50 54
 }

+ 6
- 3
web/js/GLRenderer.js View File

@@ -120,6 +120,7 @@ function setupCRTShader(gl) {
120 120
   uniform sampler2D texture;
121 121
   uniform vec2 res;
122 122
   uniform vec2 uv_max;
123
+  uniform vec2 warp2;
123 124
 
124 125
   // Amount of shadow mask.
125 126
   float maskDark=0.7;
@@ -128,12 +129,12 @@ function setupCRTShader(gl) {
128 129
   // Display warp.
129 130
   // 0.0 = none
130 131
   // 1.0/8.0 = extreme
131
-  vec2 warp=vec2(1.0/20.0, 1.0/20.0);
132
+  // vec2 warp=vec2(1.0/20.0, 1.0/20.0);
132 133
 
133 134
   // Distortion of scanlines, and end of screen alpha.
134 135
   vec2 Warp(vec2 pos) {
135 136
     pos = pos * 2.0 - 1.0;
136
-    pos *= vec2(1.0 + (pos.y * pos.y) * warp.x, 1.0 + (pos.x * pos.x) * warp.y);
137
+    pos *= vec2(1.0 + (pos.y * pos.y) * warp2.x, 1.0 + (pos.x * pos.x) * warp2.y);
137 138
     return pos * 0.5 + 0.5;
138 139
   }
139 140
 
@@ -211,6 +212,7 @@ function setupCRTShader(gl) {
211 212
 
212 213
   shader.resolutionLocation = gl.getUniformLocation(shader.program, 'res');
213 214
   shader.uvMaxLocation = gl.getUniformLocation(shader.program, 'uv_max');
215
+  shader.warp2 = gl.getUniformLocation(shader.program, 'warp2');
214 216
 
215 217
   return shader;
216 218
 }
@@ -424,7 +426,7 @@ export class GLRenderer {
424 426
     gl.drawArrays(gl.TRIANGLE_STRIP, 0, geo_len / totalSize);
425 427
   }
426 428
 
427
-  renderTextureToScreen(canvasWidth, canvasHeight, memoryF32, geo_len, geo_ptr) {
429
+  renderTextureToScreen(canvasWidth, canvasHeight, memoryF32, geo_len, geo_ptr, curvature) {
428 430
     const gl = this.gl;
429 431
 
430 432
     let shader = this.crtShader;
@@ -461,6 +463,7 @@ export class GLRenderer {
461 463
 
462 464
     gl.uniform2f(shader.resolutionLocation, canvasWidth, canvasHeight);
463 465
     gl.uniform2f(shader.uvMaxLocation, 0.5000, 0.6875);
466
+    gl.uniform2f(shader.warp2, curvature, curvature);
464 467
 
465 468
     const glVertexBuffer = this.glVertexBuffer;
466 469
     const glColourBuffer = this.glColourBuffer;

+ 13
- 2
web/js/index.js View File

@@ -53,8 +53,9 @@ const render = () => {
53 53
   // apply a CRT effect
54 54
   const crt_len = bridge.crt_len();
55 55
   const crt_ptr = bridge.crt_ptr();
56
+  const curvature = bridge.crt_curvature();
56 57
   renderer.renderTextureToScreen(gState.config.canvas_width, gState.config.canvas_height,
57
-                                 memoryF32, crt_len, crt_ptr);
58
+                                 memoryF32, crt_len, crt_ptr, curvature);
58 59
 };
59 60
 
60 61
 function reconfigure() {
@@ -65,9 +66,14 @@ function audio_play(id) {
65 66
   gState.audio.play(id);
66 67
 }
67 68
 
69
+function audio_volume(volume) {
70
+  gState.audio.volume(volume);
71
+}
72
+
68 73
 window.reconfigure = reconfigure;
69 74
 window.config = gState.config;
70 75
 window.audio_play = audio_play;
76
+window.audio_volume = audio_volume;
71 77
 
72 78
 function get_canvas_aspect_ratio(config) {
73 79
 
@@ -136,7 +142,12 @@ function main() {
136 142
   gState.config.block_inactive_col_b = 0.0,
137 143
   gState.config.block_inactive_col_a = 0.1,
138 144
 
139
-  gState.config.crt_update_speed = 0.15;
145
+  gState.config.menu_volume = 5;
146
+  gState.config.menu_max_volume = 5;
147
+  gState.config.menu_crt_update_speed = 4;
148
+  gState.config.menu_max_crt_update_speed = 5;
149
+  gState.config.menu_curvature = 3;
150
+  gState.config.menu_max_curvature = 5;
140 151
 
141 152
   resize_canvas_element(canvasElement, gState.config);
142 153
 

Loading…
Cancel
Save