1use crate::audio::{AudioManager, UiAudioEvent};
5use crate::input::{GameAction, InputState};
6use crate::sprite::{BlendMode, Rect, Sprite};
7use glam::Vec2;
8
9pub struct Context<A: GameAction> {
17 pub input: InputState<A>,
18 pub delta_time: f32,
19 pub screen_width: f32,
20 pub screen_height: f32,
21 pub camera_offset_x: f32,
22 pub camera_offset_y: f32,
23 pub fps: f32,
24 pub frame_time_ms: f32,
25 pub fixed_tick_rate: u32,
26 pub target_fps: u32,
27 pub interpolation_alpha: f32,
28 pub freeze_frames: u16,
29 pub pending_shakes: Vec<(f32, f32)>,
30 pub request_exit: bool,
31 pub fullscreen_enabled: bool,
32 pub request_fullscreen: Option<bool>,
33 pub hdr_enabled: bool,
34 pub request_hdr: Option<bool>,
35 pub audio: AudioManager,
36 pub pending_ui_audio: Vec<UiAudioEvent>,
37
38 pub(crate) sprite_batch: Vec<Sprite>,
41 pub(crate) pending_textures: Vec<PendingTexture>,
42}
43
44pub(crate) struct PendingTexture {
46 pub bytes: &'static [u8],
47 pub label: String,
48}
49
50impl<A: GameAction> Context<A> {
51 pub fn new(screen_width: f32, screen_height: f32) -> Self {
54 Self {
55 input: InputState::new(),
56 delta_time: 0.0,
57 screen_width,
58 screen_height,
59 sprite_batch: Vec::new(),
60 camera_offset_x: 0.0,
61 camera_offset_y: 0.0,
62 fps: 0.0,
63 frame_time_ms: 0.0,
64 fixed_tick_rate: crate::time::DEFAULT_FIXED_HZ,
65 target_fps: 60, interpolation_alpha: 0.0,
67 freeze_frames: 0,
68 pending_shakes: Vec::new(),
69 pending_textures: Vec::new(),
70 request_exit: false,
71 fullscreen_enabled: true,
72 request_fullscreen: None,
73 hdr_enabled: false,
74 request_hdr: None,
75 audio: AudioManager::new(),
76 pending_ui_audio: Vec::new(),
77 }
78 }
79
80 pub fn load_texture(&mut self, bytes: &'static [u8], label: &str) -> usize {
84 let id = self.pending_textures.len() + 1; self.pending_textures.push(PendingTexture {
86 bytes,
87 label: label.to_string(),
88 });
89 id
90 }
91
92 pub fn trigger_freeze(&mut self, frames: u16) {
94 self.freeze_frames = self.freeze_frames.max(frames);
95 }
96
97 pub fn trigger_shake(&mut self, intensity: f32, duration: f32) {
99 self.pending_shakes.push((intensity, duration));
100 }
101
102 pub fn draw_sprite(&mut self, position: Vec2, size: Vec2, color: [f32; 4], flip_x: bool) {
109 self.sprite_batch
110 .push(Sprite::new(position, size, color).with_flip(flip_x));
111 }
113
114 pub fn draw_rect(&mut self, position: Vec2, size: Vec2, color: [f32; 4]) {
116 self.sprite_batch.push(Sprite::new(position, size, color));
117 }
118
119 pub fn draw_sprite_from_sheet(
128 &mut self,
129 position: Vec2,
130 size: Vec2,
131 color: [f32; 4],
132 source_rect: Rect,
133 flip_x: bool,
134 texture_id: usize,
135 ) {
136 self.sprite_batch.push(
137 Sprite::new(position, size, color)
138 .with_source(source_rect)
139 .with_flip(flip_x)
140 .with_texture_id(texture_id),
141 );
142 }
143
144 pub fn draw_sprite_from_sheet_additive(
146 &mut self,
147 position: Vec2,
148 size: Vec2,
149 color: [f32; 4],
150 source_rect: Rect,
151 flip_x: bool,
152 texture_id: usize,
153 ) {
154 self.sprite_batch.push(
155 Sprite::new(position, size, color)
156 .with_source(source_rect)
157 .with_flip(flip_x)
158 .with_texture_id(texture_id)
159 .with_blend_mode(BlendMode::Additive),
160 );
161 }
162
163 pub fn screen_center(&self) -> Vec2 {
164 Vec2::new(self.screen_width / 2.0, self.screen_height / 2.0)
165 }
166
167 pub(crate) fn drain_ui_audio_events(&mut self) {
169 if self.pending_ui_audio.is_empty() {
170 return;
171 }
172 self.pending_ui_audio.sort_unstable();
173 self.pending_ui_audio.dedup();
174 }
175
176 pub(crate) fn clear_sprites(&mut self) {
179 self.sprite_batch.clear();
180 }
181
182 #[allow(dead_code)] pub(crate) fn resize(&mut self, width: f32, height: f32) {
185 self.screen_width = width;
186 self.screen_height = height;
187 }
188
189 pub fn set_fullscreen_enabled(&mut self, enabled: bool) {
190 self.request_fullscreen = Some(enabled);
191 }
192
193 pub fn set_hdr_enabled(&mut self, enabled: bool) {
194 self.request_hdr = Some(enabled);
195 }
196}