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 {}