Skip to main content

kvas_engine/
script_game.rs

1//! Game-wide services on [`Ctx`]: the screen interface, saves and settings, files, cameras and
2//! asking the host to quit.
3//!
4//! ```
5//! use kvas_engine::prelude::*;
6//!
7//! #[derive(Default)]
8//! pub struct Menu { volume: f32 }
9//! impl Script for Menu {
10//!     fn update(&mut self, ctx: &mut Ctx) {
11//!         let ui = ctx.ui();
12//!         ui.panel([0.0, 0.0, ui.width(), 720.0], [0.0, 0.0, 0.0, 0.7], 0.0);
13//!         ui.text("PAUSED", [ui.width() / 2.0, 200.0], 48.0, [1.0; 4], "", Align::Center);
14//!         self.volume = ui.slider("volume", [540.0, 300.0, 200.0, 24.0], self.volume);
15//!         if ui.button("save", [540.0, 360.0, 200.0, 40.0], "SAVE") {
16//!             let _ = ctx.save_game("slot1", "Chapter 1", &serde_json::json!({ "volume": self.volume }));
17//!         }
18//!         if ui.button("quit", [540.0, 420.0, 200.0, 40.0], "QUIT") {
19//!             ctx.quit_game();
20//!         }
21//!     }
22//! }
23//! ```
24
25use crate::canvas::Canvas;
26use crate::components::{Camera, Transform};
27use crate::script::Ctx;
28use crate::storage::{SaveInfo, Storage};
29use kvas_ecs::Entity;
30
31impl Ctx<'_> {
32    /// The screen interface of this frame (see [`crate::canvas`]).
33    pub fn ui(&self) -> &Canvas {
34        &self.input.canvas
35    }
36    /// Width of the virtual interface screen (its height is always 720).
37    pub fn ui_width(&self) -> f32 {
38        self.input.canvas.width()
39    }
40
41    // ---- saves and settings
42    /// The game's data folder: saves, settings and files.
43    pub fn storage(&self) -> Storage {
44        self.input.storage()
45    }
46    /// Writes a save slot (`title` is shown in load menus; the scene is recorded too).
47    pub fn save_game(&self, slot: &str, title: &str, data: &serde_json::Value) -> Result<(), String> {
48        self.input.storage().save(slot, title, data)
49    }
50    /// Reads a save slot.
51    pub fn load_game(&self, slot: &str) -> Option<serde_json::Value> {
52        self.input.storage().load(slot)
53    }
54    /// Every save, newest first.
55    pub fn save_slots(&self) -> Vec<SaveInfo> {
56        self.input.storage().slots()
57    }
58    /// Whether a slot exists.
59    pub fn has_save(&self, slot: &str) -> bool {
60        self.input.storage().has_save(slot)
61    }
62    /// A persistent setting (kept across runs), or `default`.
63    pub fn setting_f32(&self, key: &str, default: f32) -> f32 {
64        self.input.storage().setting(key).and_then(|v| v.as_f64()).map_or(default, |v| v as f32)
65    }
66    /// A persistent on/off setting, or `default`.
67    pub fn setting_bool(&self, key: &str, default: bool) -> bool {
68        self.input.storage().setting(key).and_then(|v| v.as_bool()).unwrap_or(default)
69    }
70    /// A persistent text setting, or `default`.
71    pub fn setting_text(&self, key: &str, default: &str) -> String {
72        self.input.storage().setting(key).and_then(|v| v.as_str().map(str::to_string)).unwrap_or_else(|| default.to_string())
73    }
74    /// Stores a persistent setting.
75    pub fn set_setting(&self, key: &str, value: serde_json::Value) {
76        if let Err(e) = self.input.storage().set_setting(key, value) {
77            eprintln!("setting {key}: {e}");
78        }
79    }
80
81    // ---- cameras
82    /// The camera the game looks through.
83    pub fn active_camera(&self) -> Option<Entity> {
84        crate::components::active_camera(&self.scene.world)
85    }
86    /// Looks through another camera from the next frame on (security monitors, cut-scenes...).
87    pub fn set_active_camera(&mut self, camera: Entity) -> bool {
88        crate::components::set_active_camera(&mut self.scene.world, camera)
89    }
90    /// Switches to the camera named `name`.
91    pub fn set_active_camera_named(&mut self, name: &str) -> bool {
92        match self.find(name) {
93            Some(e) => self.set_active_camera(e),
94            None => false,
95        }
96    }
97    /// Adds a camera shader (`fn effect`) to a camera; returns its index.
98    pub fn add_camera_effect(&mut self, camera: Entity, shader: &str, params: [f32; 4]) -> Option<usize> {
99        let c = self.scene.world.get_mut::<Camera>(camera)?;
100        c.effects.push(crate::fx::PostEffect { shader: shader.to_string(), enabled: true, params });
101        Some(c.effects.len() - 1)
102    }
103    /// Changes the numbers of a camera shader (fade a VHS, pulse damage...).
104    pub fn set_camera_effect(&mut self, camera: Entity, index: usize, enabled: bool, params: [f32; 4]) {
105        if let Some(fx) = self.scene.world.get_mut::<Camera>(camera).and_then(|c| c.effects.get_mut(index)) {
106            fx.enabled = enabled;
107            fx.params = params;
108        }
109    }
110    /// Field of view of a camera in degrees (zoom, sprint effect).
111    pub fn set_camera_fov(&mut self, camera: Entity, fov: f32) {
112        if let Some(c) = self.scene.world.get_mut::<Camera>(camera) {
113            c.fov = fov.clamp(5.0, 170.0);
114            c.focal_length = 0.0;
115        }
116    }
117    /// World position of the active camera.
118    pub fn camera_position(&self) -> Option<glam::Vec3> {
119        let e = self.active_camera()?;
120        Some(crate::hierarchy::global_matrix(&self.scene.world, e).w_axis.truncate())
121    }
122    /// Direction the active camera looks along.
123    pub fn camera_forward(&self) -> Option<glam::Vec3> {
124        let e = self.active_camera()?;
125        Some(-crate::hierarchy::global_matrix(&self.scene.world, e).z_axis.truncate().normalize_or_zero())
126    }
127    /// Places a camera and points it at a target (cut-scenes).
128    pub fn place_camera(&mut self, camera: Entity, position: glam::Vec3, target: glam::Vec3) {
129        if let Some(t) = self.scene.world.get_mut::<Transform>(camera) {
130            t.position = position;
131            t.look_at(target, glam::Vec3::Y);
132        }
133    }
134
135    // ---- sound mix
136    /// Room echo for sounds started from now on: `amount` 0..1, `room_ms` first reflection delay
137    /// (20 small room .. 120 hall). Call when the player enters a space.
138    pub fn set_reverb(&mut self, amount: f32, room_ms: f32) {
139        self.scene.audio.reverb = amount.clamp(0.0, 1.0);
140        self.scene.audio.reverb_room_ms = room_ms.clamp(5.0, 400.0);
141    }
142    /// Volumes of the mix: everything, music, other sounds (0..1, kept up to 2).
143    pub fn set_volumes(&mut self, master: f32, music: f32, effects: f32) {
144        self.scene.audio.master_volume = master.clamp(0.0, 2.0);
145        self.scene.audio.music_volume = music.clamp(0.0, 2.0);
146        self.scene.audio.effects_volume = effects.clamp(0.0, 2.0);
147    }
148
149    // ---- host
150    /// Closes the game (in the editor it stops Play).
151    pub fn quit_game(&self) {
152        self.input.request_quit();
153    }
154    /// Switches the window to fullscreen or back.
155    pub fn set_fullscreen(&self, on: bool) {
156        self.input.request_fullscreen(on);
157    }
158}