Initial commit
This commit is contained in:
commit
c66d65d8af
7 changed files with 3024 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
.vscode
|
2465
Cargo.lock
generated
Normal file
2465
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[package]
|
||||||
|
name = "kroz"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bracket-lib = { git = "https://github.com/amethyst/bracket-lib" }
|
||||||
|
specs = "0.16.1"
|
||||||
|
specs-derive = "0.4.1"
|
||||||
|
cpal = "0.13"
|
||||||
|
oddio = "0.5"
|
||||||
|
rand = "0.8"
|
||||||
|
spin_sleep = "1.0"
|
286
src/main.rs
Normal file
286
src/main.rs
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
#![windows_subsystem = "windows"]
|
||||||
|
|
||||||
|
use bracket_lib::prelude::*;
|
||||||
|
use sidebar::draw_sidebar;
|
||||||
|
use sound::{Sound, SoundEffect, SoundEffectSamples, SoundSystem};
|
||||||
|
use specs::prelude::*;
|
||||||
|
use specs_derive::Component;
|
||||||
|
use std::{
|
||||||
|
cmp::{max, min},
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
use vga_color::*;
|
||||||
|
|
||||||
|
use crate::sound::SoundType;
|
||||||
|
|
||||||
|
mod sidebar;
|
||||||
|
mod sound;
|
||||||
|
pub mod vga_color;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct LevelNumber(u32);
|
||||||
|
|
||||||
|
struct SoundEffects {
|
||||||
|
step_sound: SoundEffectSamples,
|
||||||
|
pickup_sound: SoundEffectSamples,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Position {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct Renderable {
|
||||||
|
glyph: FontCharType,
|
||||||
|
fg: RGB,
|
||||||
|
bg: RGB,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Component)]
|
||||||
|
struct LeftMover {}
|
||||||
|
|
||||||
|
#[derive(Component, Debug)]
|
||||||
|
struct Player {
|
||||||
|
last_moved: Instant,
|
||||||
|
score: u32,
|
||||||
|
gems: u32,
|
||||||
|
whips: u32,
|
||||||
|
teleports: u32,
|
||||||
|
keys: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
ecs: World,
|
||||||
|
sound_system: SoundSystem,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World, sound_system: &mut SoundSystem) {
|
||||||
|
let mut positions = ecs.write_storage::<Position>();
|
||||||
|
let mut players = ecs.write_storage::<Player>();
|
||||||
|
|
||||||
|
let mut stepped = false;
|
||||||
|
|
||||||
|
for (player, pos) in (&mut players, &mut positions).join() {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now - player.last_moved > Duration::from_secs_f32(1. / 7.5) {
|
||||||
|
pos.x = min(65, max(0, pos.x + delta_x));
|
||||||
|
pos.y = min(24, max(0, pos.y + delta_y));
|
||||||
|
stepped = true;
|
||||||
|
player.last_moved = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if stepped {
|
||||||
|
let sound_effects = ecs.fetch::<SoundEffects>();
|
||||||
|
let step_sound = sound_effects.step_sound.clone();
|
||||||
|
let _ = sound_system.play_sound(step_sound);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn player_input(gs: &mut State, ctx: &mut BTerm) {
|
||||||
|
// Player movement
|
||||||
|
match ctx.key {
|
||||||
|
None => {} // Nothing happened
|
||||||
|
Some(key) => match key {
|
||||||
|
VirtualKeyCode::Left | VirtualKeyCode::J => {
|
||||||
|
try_move_player(-1, 0, &mut gs.ecs, &mut gs.sound_system)
|
||||||
|
}
|
||||||
|
VirtualKeyCode::U => try_move_player(-1, -1, &mut gs.ecs, &mut gs.sound_system),
|
||||||
|
VirtualKeyCode::Up | VirtualKeyCode::I => {
|
||||||
|
try_move_player(0, -1, &mut gs.ecs, &mut gs.sound_system)
|
||||||
|
}
|
||||||
|
VirtualKeyCode::O => try_move_player(1, -1, &mut gs.ecs, &mut gs.sound_system),
|
||||||
|
VirtualKeyCode::Right | VirtualKeyCode::K => {
|
||||||
|
try_move_player(1, 0, &mut gs.ecs, &mut gs.sound_system)
|
||||||
|
}
|
||||||
|
VirtualKeyCode::Comma => try_move_player(1, 1, &mut gs.ecs, &mut gs.sound_system),
|
||||||
|
VirtualKeyCode::Down | VirtualKeyCode::M => {
|
||||||
|
try_move_player(0, 1, &mut gs.ecs, &mut gs.sound_system)
|
||||||
|
}
|
||||||
|
VirtualKeyCode::N => try_move_player(-1, 1, &mut gs.ecs, &mut gs.sound_system),
|
||||||
|
VirtualKeyCode::S => {
|
||||||
|
let sound_effects = gs.ecs.fetch::<SoundEffects>();
|
||||||
|
let step_sound = sound_effects.pickup_sound.clone();
|
||||||
|
let _ = gs.sound_system.play_sound(step_sound);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameState for State {
|
||||||
|
fn tick(&mut self, ctx: &mut BTerm) {
|
||||||
|
ctx.cls();
|
||||||
|
|
||||||
|
player_input(self, ctx);
|
||||||
|
self.run_systems();
|
||||||
|
|
||||||
|
let positions = self.ecs.read_storage::<Position>();
|
||||||
|
let renderables = self.ecs.read_storage::<Renderable>();
|
||||||
|
|
||||||
|
for (pos, render) in (&positions, &renderables).join() {
|
||||||
|
ctx.set(pos.x, pos.y, render.fg, render.bg, render.glyph);
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_sidebar(&self.ecs, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LeftWalker {}
|
||||||
|
|
||||||
|
impl<'a> System<'a> for LeftWalker {
|
||||||
|
type SystemData = (ReadStorage<'a, LeftMover>, WriteStorage<'a, Position>);
|
||||||
|
|
||||||
|
fn run(&mut self, (lefty, mut pos): Self::SystemData) {
|
||||||
|
for (_lefty, pos) in (&lefty, &mut pos).join() {
|
||||||
|
pos.x -= 1;
|
||||||
|
if pos.x < 0 {
|
||||||
|
pos.x = 65;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn run_systems(&mut self) {
|
||||||
|
let mut lw = LeftWalker {};
|
||||||
|
lw.run_now(&self.ecs);
|
||||||
|
self.ecs.maintain();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> BError {
|
||||||
|
let context = BTermBuilder::simple(80, 25)?
|
||||||
|
.with_fps_cap(60.0)
|
||||||
|
.with_title("Kroz")
|
||||||
|
.with_tile_dimensions(8, 16)
|
||||||
|
.build()?;
|
||||||
|
let ss = SoundSystem::new();
|
||||||
|
let step_sound = ss.render_sound_effect(SoundEffect {
|
||||||
|
sounds: vec![
|
||||||
|
Sound {
|
||||||
|
sound_type: SoundType::Noise(350, 900),
|
||||||
|
duration: Duration::from_millis(6),
|
||||||
|
},
|
||||||
|
Sound {
|
||||||
|
sound_type: SoundType::Silence,
|
||||||
|
duration: Duration::from_millis(120),
|
||||||
|
},
|
||||||
|
Sound {
|
||||||
|
sound_type: SoundType::Noise(150, 200),
|
||||||
|
duration: Duration::from_millis(6),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
let pickup_sound = ss.render_sound_effect(SoundEffect {
|
||||||
|
sounds: vec![
|
||||||
|
Sound {
|
||||||
|
sound_type: SoundType::Noise(350, 900),
|
||||||
|
duration: Duration::from_millis(6),
|
||||||
|
},
|
||||||
|
Sound {
|
||||||
|
sound_type: SoundType::Silence,
|
||||||
|
duration: Duration::from_millis(120),
|
||||||
|
},
|
||||||
|
Sound {
|
||||||
|
sound_type: SoundType::Noise(1000, 2000),
|
||||||
|
duration: Duration::from_millis(20),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut gs = State {
|
||||||
|
ecs: World::new(),
|
||||||
|
// sound_sender: tx,
|
||||||
|
sound_system: ss,
|
||||||
|
//step_sound,
|
||||||
|
};
|
||||||
|
|
||||||
|
gs.ecs.insert(LevelNumber(0));
|
||||||
|
gs.ecs.insert(SoundEffects {
|
||||||
|
step_sound,
|
||||||
|
pickup_sound,
|
||||||
|
});
|
||||||
|
|
||||||
|
gs.ecs.register::<Position>();
|
||||||
|
gs.ecs.register::<Renderable>();
|
||||||
|
gs.ecs.register::<LeftMover>();
|
||||||
|
gs.ecs.register::<Player>();
|
||||||
|
|
||||||
|
gs.ecs
|
||||||
|
.create_entity()
|
||||||
|
.with(Position { x: 40, y: 24 })
|
||||||
|
.with(Renderable {
|
||||||
|
glyph: to_cp437('☻'),
|
||||||
|
fg: RGB::named(VGA_YELLOW_BRIGHT),
|
||||||
|
bg: RGB::named(VGA_BLACK),
|
||||||
|
})
|
||||||
|
.with(Player {
|
||||||
|
last_moved: Instant::now(),
|
||||||
|
score: 1290,
|
||||||
|
gems: 14,
|
||||||
|
whips: 7,
|
||||||
|
teleports: 0,
|
||||||
|
keys: 0,
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
for i in 0..10 {
|
||||||
|
gs.ecs
|
||||||
|
.create_entity()
|
||||||
|
.with(Position { x: i * 7, y: 20 })
|
||||||
|
.with(Renderable {
|
||||||
|
glyph: to_cp437('Ä'),
|
||||||
|
fg: RGB::named(VGA_RED_BRIGHT),
|
||||||
|
bg: RGB::named(VGA_BLACK),
|
||||||
|
})
|
||||||
|
.with(LeftMover {})
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// let descent_sounds: Vec<Sound> = (20..100)
|
||||||
|
// .rev()
|
||||||
|
// .flat_map(|x| {
|
||||||
|
// (1..10).rev().flat_map(move |y| {
|
||||||
|
// vec![Sound {
|
||||||
|
// sound_type: SoundType::Tone(x * y * y),
|
||||||
|
// duration: Duration::from_millis((y as f64 / 1.5) as u64),
|
||||||
|
// }]
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
// .collect();
|
||||||
|
|
||||||
|
// let descent_effect = gs.sound_system.render_sound_effect(SoundEffect {
|
||||||
|
// sounds: descent_sounds,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// let _ = gs.sound_system.play_sound(descent_effect);
|
||||||
|
|
||||||
|
let start_sounds: Vec<Sound> = (30..400)
|
||||||
|
.step_by(8)
|
||||||
|
.map(|x| Sound {
|
||||||
|
sound_type: SoundType::Tone(x),
|
||||||
|
duration: Duration::from_millis(24),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let swoop_effect = gs.sound_system.render_sound_effect(SoundEffect {
|
||||||
|
sounds: start_sounds,
|
||||||
|
});
|
||||||
|
|
||||||
|
let _ = gs.sound_system.play_sound(swoop_effect);
|
||||||
|
|
||||||
|
// let effect = gs.sound_system.render_sound_effect(SoundEffect {
|
||||||
|
// sounds: vec![Sound {
|
||||||
|
// sound_type: SoundType::Tone(3500),
|
||||||
|
// duration: Duration::from_millis(4000),
|
||||||
|
// }],
|
||||||
|
// });
|
||||||
|
|
||||||
|
// let _ = gs.sound_system.play_sound(effect);
|
||||||
|
|
||||||
|
main_loop(context, gs)
|
||||||
|
}
|
106
src/sidebar.rs
Normal file
106
src/sidebar.rs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
use crate::{vga_color::*, LevelNumber, Player};
|
||||||
|
use bracket_lib::prelude::*;
|
||||||
|
use specs::prelude::*;
|
||||||
|
|
||||||
|
const SIDEBAR_POS_X: i32 = 66;
|
||||||
|
const SIDEBAR_POS_Y: i32 = 0;
|
||||||
|
|
||||||
|
pub fn draw_sidebar(ecs: &World, ctx: &mut BTerm) {
|
||||||
|
// Blue background
|
||||||
|
ctx.fill_region(
|
||||||
|
Rect {
|
||||||
|
x1: SIDEBAR_POS_X,
|
||||||
|
x2: SIDEBAR_POS_X + 14,
|
||||||
|
y1: SIDEBAR_POS_Y,
|
||||||
|
y2: SIDEBAR_POS_Y + 19,
|
||||||
|
},
|
||||||
|
to_cp437(' '),
|
||||||
|
RGB::named(VGA_YELLOW_BRIGHT),
|
||||||
|
RGB::named(VGA_BLUE),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Gray number boxes
|
||||||
|
(1..17).step_by(3).for_each(|y| {
|
||||||
|
ctx.fill_region(
|
||||||
|
Rect {
|
||||||
|
x1: SIDEBAR_POS_X + 3,
|
||||||
|
x2: SIDEBAR_POS_X + 10,
|
||||||
|
y1: SIDEBAR_POS_Y + y,
|
||||||
|
y2: SIDEBAR_POS_Y + y + 1,
|
||||||
|
},
|
||||||
|
to_cp437(' '),
|
||||||
|
RGB::named(VGA_RED),
|
||||||
|
RGB::named(VGA_WHITE),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
ctx.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y, "Score");
|
||||||
|
ctx.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y + 3, "Level");
|
||||||
|
ctx.print_centered_at(
|
||||||
|
SIDEBAR_POS_X + 6,
|
||||||
|
SIDEBAR_POS_Y + 4,
|
||||||
|
ecs.read_resource::<LevelNumber>().0,
|
||||||
|
);
|
||||||
|
ctx.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y + 6, "Gems");
|
||||||
|
ctx.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y + 9, "Whips");
|
||||||
|
ctx.print(SIDEBAR_POS_X + 2, SIDEBAR_POS_Y + 12, "Teleports");
|
||||||
|
ctx.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y + 15, "Keys");
|
||||||
|
|
||||||
|
let players = ecs.read_storage::<Player>();
|
||||||
|
for player in players.join() {
|
||||||
|
ctx.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 1, player.score);
|
||||||
|
ctx.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 7, player.gems);
|
||||||
|
ctx.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 10, player.whips);
|
||||||
|
ctx.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 13, player.teleports);
|
||||||
|
ctx.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 16, player.keys);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hotkey list
|
||||||
|
ctx.print_color(
|
||||||
|
SIDEBAR_POS_X + 3,
|
||||||
|
SIDEBAR_POS_Y + 18,
|
||||||
|
RGB::named(VGA_CYAN_BRIGHT),
|
||||||
|
RGB::named(VGA_RED),
|
||||||
|
"OPTIONS",
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.fill_region(
|
||||||
|
Rect {
|
||||||
|
x1: SIDEBAR_POS_X,
|
||||||
|
x2: SIDEBAR_POS_X + 14,
|
||||||
|
y1: SIDEBAR_POS_Y + 19,
|
||||||
|
y2: SIDEBAR_POS_Y + 25,
|
||||||
|
},
|
||||||
|
to_cp437(' '),
|
||||||
|
RGB::named(VGA_WHITE),
|
||||||
|
RGB::named(VGA_BLUE),
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.fill_region(
|
||||||
|
Rect {
|
||||||
|
x1: SIDEBAR_POS_X + 3,
|
||||||
|
x2: SIDEBAR_POS_X + 4,
|
||||||
|
y1: SIDEBAR_POS_Y + 19,
|
||||||
|
y2: SIDEBAR_POS_Y + 25,
|
||||||
|
},
|
||||||
|
to_cp437(' '),
|
||||||
|
RGB::named(VGA_WHITE_BRIGHT),
|
||||||
|
RGB::named(VGA_BLUE),
|
||||||
|
);
|
||||||
|
|
||||||
|
ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 19, "Whip");
|
||||||
|
ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 20, "Teleport");
|
||||||
|
ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 21, "Pause");
|
||||||
|
ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 22, "Quit");
|
||||||
|
ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 23, "Save");
|
||||||
|
ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 24, "Restore");
|
||||||
|
|
||||||
|
ctx.print_color_right(
|
||||||
|
SIDEBAR_POS_X + 14,
|
||||||
|
SIDEBAR_POS_Y,
|
||||||
|
RGB::named(VGA_GREEN_BRIGHT),
|
||||||
|
RGB::named(VGA_BLACK),
|
||||||
|
&format!("{}", ctx.fps),
|
||||||
|
);
|
||||||
|
}
|
133
src/sound.rs
Normal file
133
src/sound.rs
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
use std::{f32::consts::PI, sync::Arc, time::Duration};
|
||||||
|
|
||||||
|
use cpal::{
|
||||||
|
traits::{DeviceTrait, HostTrait, StreamTrait},
|
||||||
|
SampleRate, Stream,
|
||||||
|
};
|
||||||
|
use oddio::{Frames, Handle, Mixer};
|
||||||
|
use rand::Rng;
|
||||||
|
|
||||||
|
type Frequency = u32;
|
||||||
|
type MinFrequency = u32;
|
||||||
|
type MaxFrequency = u32;
|
||||||
|
|
||||||
|
pub type SoundEffectSamples = Arc<Frames<f32>>;
|
||||||
|
|
||||||
|
pub enum SoundType {
|
||||||
|
Silence,
|
||||||
|
Tone(Frequency),
|
||||||
|
Noise(MinFrequency, MaxFrequency),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Sound {
|
||||||
|
pub sound_type: SoundType,
|
||||||
|
pub duration: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SoundEffect {
|
||||||
|
pub sounds: Vec<Sound>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SoundSystem {
|
||||||
|
mixer_handle: Handle<Mixer<[f32; 2]>>,
|
||||||
|
sample_rate: SampleRate,
|
||||||
|
_stream: Stream,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SoundSystem {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let host = cpal::default_host();
|
||||||
|
let device = host
|
||||||
|
.default_output_device()
|
||||||
|
.expect("no output device available");
|
||||||
|
let sample_rate = device.default_output_config().unwrap().sample_rate();
|
||||||
|
let config = cpal::StreamConfig {
|
||||||
|
channels: 2,
|
||||||
|
sample_rate,
|
||||||
|
buffer_size: cpal::BufferSize::Default,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mixer_handle, mixer) = oddio::split(oddio::Mixer::new());
|
||||||
|
|
||||||
|
let stream = device
|
||||||
|
.build_output_stream(
|
||||||
|
&config,
|
||||||
|
move |out_flat: &mut [f32], _: &cpal::OutputCallbackInfo| {
|
||||||
|
let out_stereo: &mut [[f32; 2]] = oddio::frame_stereo(out_flat);
|
||||||
|
oddio::run(&mixer, sample_rate.0, out_stereo);
|
||||||
|
},
|
||||||
|
move |err| {
|
||||||
|
eprintln!("{}", err);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
stream.play().unwrap();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
mixer_handle,
|
||||||
|
sample_rate,
|
||||||
|
_stream: stream,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_sound_effect(&self, effect: SoundEffect) -> SoundEffectSamples {
|
||||||
|
let effect_buffer: Vec<f32> = effect
|
||||||
|
.sounds
|
||||||
|
.iter()
|
||||||
|
.flat_map(|sound| match sound.sound_type {
|
||||||
|
SoundType::Silence => (0
|
||||||
|
..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize)
|
||||||
|
.map(|_| 0f32)
|
||||||
|
.collect::<Vec<f32>>(),
|
||||||
|
SoundType::Tone(freq) => {
|
||||||
|
if freq == 0 {
|
||||||
|
return (0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32())
|
||||||
|
as usize)
|
||||||
|
.map(|_| 0f32)
|
||||||
|
.collect::<Vec<f32>>();
|
||||||
|
}
|
||||||
|
let num_harmonics = self.sample_rate.0 / (freq as u32 * 2);
|
||||||
|
let coefficients = (0..num_harmonics + 1)
|
||||||
|
.map(|i| {
|
||||||
|
if i == 0 {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
(i as f32 * 0.5 * PI).sin() * 2.0 / (i as f32 * PI)
|
||||||
|
})
|
||||||
|
.collect::<Vec<f32>>();
|
||||||
|
let scaler = freq as f32 * PI * 2.0 / self.sample_rate.0 as f32;
|
||||||
|
(0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize)
|
||||||
|
.map(|i| {
|
||||||
|
let temp = scaler * i as f32;
|
||||||
|
coefficients
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(j, coef)| coef * (j as f32 * temp).cos())
|
||||||
|
.sum::<f32>()
|
||||||
|
})
|
||||||
|
.collect::<Vec<f32>>()
|
||||||
|
}
|
||||||
|
SoundType::Noise(min, max) => {
|
||||||
|
(0..(self.sample_rate.0 as f32 * sound.duration.as_secs_f32()) as usize)
|
||||||
|
.map(|i| {
|
||||||
|
let t = i as f32 / self.sample_rate.0 as f32;
|
||||||
|
(t * (rand::thread_rng().gen_range(min as f32..max as f32))
|
||||||
|
* 2.0
|
||||||
|
* std::f32::consts::PI)
|
||||||
|
.sin()
|
||||||
|
.signum()
|
||||||
|
})
|
||||||
|
.collect::<Vec<f32>>()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
oddio::Frames::from_iter(self.sample_rate.0, effect_buffer.iter().copied())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn play_sound(&mut self, samples: SoundEffectSamples) {
|
||||||
|
self.mixer_handle
|
||||||
|
.control::<oddio::Mixer<_>, _>()
|
||||||
|
.play(oddio::MonoToStereo::new(oddio::FramesSignal::from(samples)));
|
||||||
|
}
|
||||||
|
}
|
19
src/vga_color.rs
Normal file
19
src/vga_color.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// VGA 4-bit Colors
|
||||||
|
pub const VGA_BLACK: (u8, u8, u8) = (0, 0, 0);
|
||||||
|
pub const VGA_RED: (u8, u8, u8) = (170, 0, 0);
|
||||||
|
pub const VGA_GREEN: (u8, u8, u8) = (0, 170, 0);
|
||||||
|
pub const VGA_YELLOW: (u8, u8, u8) = (170, 85, 0);
|
||||||
|
pub const VGA_BLUE: (u8, u8, u8) = (0, 0, 170);
|
||||||
|
pub const VGA_MAGENTA: (u8, u8, u8) = (170, 0, 170);
|
||||||
|
pub const VGA_CYAN: (u8, u8, u8) = (0, 170, 170);
|
||||||
|
pub const VGA_WHITE: (u8, u8, u8) = (170, 170, 170);
|
||||||
|
|
||||||
|
// "Bold" VGA 4-bit Colors
|
||||||
|
pub const VGA_BLACK_BRIGHT: (u8, u8, u8) = (85, 85, 85);
|
||||||
|
pub const VGA_RED_BRIGHT: (u8, u8, u8) = (255, 85, 85);
|
||||||
|
pub const VGA_GREEN_BRIGHT: (u8, u8, u8) = (85, 255, 85);
|
||||||
|
pub const VGA_YELLOW_BRIGHT: (u8, u8, u8) = (255, 255, 85);
|
||||||
|
pub const VGA_BLUE_BRIGHT: (u8, u8, u8) = (85, 85, 255);
|
||||||
|
pub const VGA_MAGENTA_BRIGHT: (u8, u8, u8) = (255, 85, 255);
|
||||||
|
pub const VGA_CYAN_BRIGHT: (u8, u8, u8) = (85, 255, 255);
|
||||||
|
pub const VGA_WHITE_BRIGHT: (u8, u8, u8) = (255, 255, 255);
|
Loading…
Add table
Reference in a new issue