romy_core/lib.rs
1use serde_derive::{Deserialize, Serialize};
2
3pub mod input;
4pub mod output;
5pub mod runtime;
6pub mod serial;
7
8use input::*;
9use output::*;
10
11/// Holds information about the Game
12#[derive(Serialize, Deserialize, Debug, Clone)]
13pub struct Info {
14 name: String,
15 step_interval: u32,
16 players: Vec<Player>,
17}
18
19impl Info {
20 /// Create a new structure of info about the game being played
21 /// # Arguments
22 /// * `name` - The title of the game
23 /// * `steps_per_second` - The number of times Game::Step() should be called per second
24 /// * `number_of_players` - How many players this games should have
25 /// * `input` - The device type to use for each player
26 pub fn new(
27 name: &str,
28 steps_per_second: i32,
29 number_of_players: i32,
30 input: InputDeviceType,
31 ) -> Self {
32 let player = Player { input };
33
34 let mut players = Vec::with_capacity(number_of_players as usize);
35 for _ in 0..number_of_players {
36 players.push(player.clone());
37 }
38
39 Self {
40 name: name.to_string(),
41 step_interval: Self::steps_per_second_to_interval(steps_per_second),
42 players,
43 }
44 }
45
46 /// Gets the name of the game
47 pub fn name(&self) -> &str {
48 &self.name
49 }
50
51 /// Gets time between steps of the game, in nanoseconds
52 pub fn step_interval(&self) -> u32 {
53 self.step_interval
54 }
55
56 // Gets the number of steps per second
57 pub fn steps_per_second(&self) -> u32 {
58 1_000_000_000 / self.step_interval
59 }
60
61 /// Converts from steps per second to a time interval in nanoseconds
62 pub fn steps_per_second_to_interval(steps: i32) -> u32 {
63 1_000_000_000 / steps as u32
64 }
65}
66
67#[derive(Serialize, Deserialize, Debug, Clone)]
68struct Player {
69 input: InputDeviceType,
70}
71
72/// The core trait used by Romy, games need to implement this. Romy will use these methods to run
73/// the game.
74pub trait Game {
75 /// Simulates the game by one step
76 ///
77 /// # Arguments
78 /// * `arguments` - Info, such as inputs, to be used in this step
79 fn step(&mut self, arguments: &StepArguments);
80
81 /// Renders an image for Romy to display, can be called many times per step.
82 ///
83 /// This function can return any image.
84 ///
85 /// # Arguments
86 /// * `arguments` - Info, such as the width of the frame Romy is rendering too, to be used in
87 /// this step
88 fn draw(&self, arguments: &DrawArguments) -> Image;
89
90 /// Renders some audio for Romy to play, called once per step.
91 ///
92 /// The sound returned currently needs to be at a sample rate of 44100hz, and have enough
93 /// samples to cover the amount of time between calls to step.
94 fn render_audio(&self, arguments: &RenderAudioArguments) -> Sound;
95}
96
97// Input Arguments /////////////////////////////////////////////////////////////////////////////////
98
99/// Arguments passed for each step of the game
100#[derive(Serialize, Deserialize, Default)]
101pub struct StepArguments {
102 input: InputArguments,
103}
104
105impl StepArguments {
106 pub fn new(input: InputArguments) -> Self {
107 Self { input }
108 }
109
110 /// Get the input for this step
111 pub fn input(&self) -> &InputArguments {
112 &self.input
113 }
114}
115
116#[derive(Serialize, Deserialize, Default)]
117pub struct InputArguments {
118 players: Vec<Option<PlayerInputArguments>>,
119}
120
121impl InputArguments {
122 pub fn new(players: Vec<Option<PlayerInputArguments>>) -> Self {
123 Self { players }
124 }
125
126 /// Get the input for a specific player, will be None if there is no available player
127 pub fn player(&self, player: i32) -> Option<&PlayerInputArguments> {
128 let player = self.players.get(player as usize);
129
130 if let Some(player) = player {
131 return player.as_ref();
132 }
133
134 None
135 }
136}
137
138#[derive(Serialize, Deserialize)]
139pub struct PlayerInputArguments {
140 input: InputDevice,
141}
142
143impl PlayerInputArguments {
144 /// Get the players NES style controller, will be None if there is no suitable input device, or
145 /// one wasn't asked for in the supplied game info.
146 pub fn nes(&self) -> Option<&Nes> {
147 if let InputDevice::Nes(ref nes) = self.input {
148 return Some(nes);
149 }
150 None
151 }
152
153 /// Get the players standard style controller, will be None if there is no suitable input device
154 /// or one wasn't asked for in the supplied game info.
155 pub fn controller(&self) -> Option<&Controller> {
156 if let InputDevice::Controller(ref nes) = self.input {
157 return Some(nes);
158 }
159 None
160 }
161
162 /// Get the players keyboard, will be None if there is no suitable input device or one wasn't
163 /// asked for in the supplied game info.
164 pub fn keyboard(&self) -> Option<&Keyboard> {
165 if let InputDevice::Keyboard(ref nes) = self.input {
166 return Some(nes);
167 }
168 None
169 }
170}
171
172// Draw arguments //////////////////////////////////////////////////////////////////////////////////
173
174/// Arguments passed for each draw of the game
175#[derive(Serialize, Deserialize, Debug)]
176pub struct DrawArguments {
177 width: i32,
178 height: i32,
179 step_offset: f32,
180}
181
182impl DrawArguments {
183 pub fn new(width: i32, height: i32, step_offset: f32) -> Self {
184 Self {
185 width,
186 height,
187 step_offset,
188 }
189 }
190
191 /// The horizontal width in pixels of the display area being used by Romy
192 pub fn width(&self) -> i32 {
193 self.width
194 }
195
196 /// The vertical height in pixels of the display area being used by Romy
197 pub fn height(&self) -> i32 {
198 self.height
199 }
200
201 /// The fraction of time since the last step call in a range of 0.0 - 1.0. 0.0 no time has
202 /// passed, 0.5 = half way to the next step, 0.99 = almost all the way to the next step.
203 pub fn step_offset(&self) -> f32 {
204 self.step_offset
205 }
206}
207
208// Render audio arguments //////////////////////////////////////////////////////////////////////////
209
210/// Arguments passed for each audio render of the game
211#[derive(Serialize, Deserialize, Debug)]
212pub struct RenderAudioArguments {}