2020-09-20 01:05:41 +00:00
|
|
|
use crate::common::Rect;
|
|
|
|
use crate::ggez::{Context, GameResult};
|
2020-09-20 15:27:31 +00:00
|
|
|
use crate::shared_game_state::SharedGameState;
|
2020-09-20 01:05:41 +00:00
|
|
|
|
|
|
|
pub enum MenuEntry {
|
|
|
|
Active(String),
|
|
|
|
Disabled(String),
|
2020-09-21 22:33:16 +00:00
|
|
|
Toggle(String, bool),
|
2020-09-20 01:05:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum MenuSelectionResult<'a> {
|
|
|
|
None,
|
|
|
|
Canceled,
|
2020-09-21 22:33:16 +00:00
|
|
|
Selected(usize, &'a mut MenuEntry),
|
2020-09-20 01:05:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Menu {
|
|
|
|
pub x: isize,
|
|
|
|
pub y: isize,
|
|
|
|
pub width: usize,
|
|
|
|
pub height: usize,
|
2020-09-25 19:25:40 +00:00
|
|
|
pub selected: usize,
|
|
|
|
pub entries: Vec<MenuEntry>,
|
2020-09-20 01:05:41 +00:00
|
|
|
anim_num: u16,
|
|
|
|
anim_wait: u16,
|
|
|
|
}
|
|
|
|
|
|
|
|
static QUOTE_FRAMES: [usize; 4] = [0, 1, 0, 2];
|
|
|
|
|
|
|
|
impl Menu {
|
|
|
|
pub fn new(x: isize, y: isize, width: usize, height: usize) -> Menu {
|
|
|
|
Menu {
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
selected: 0,
|
|
|
|
anim_num: 0,
|
|
|
|
anim_wait: 0,
|
|
|
|
entries: Vec::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn push_entry(&mut self, entry: MenuEntry) {
|
|
|
|
self.entries.push(entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn draw(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
|
|
|
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;
|
|
|
|
|
|
|
|
let mut rect = Rect::new(0, 0, 0, 0);
|
|
|
|
let mut rect2 = Rect::new(0, 0, 0, 0);
|
|
|
|
|
|
|
|
rect = state.constants.title.menu_left_top;
|
|
|
|
batch.add_rect(self.x as f32 - rect.width() as f32, self.y as f32 - rect.height() as f32, &rect);
|
|
|
|
rect = state.constants.title.menu_right_top;
|
|
|
|
batch.add_rect(self.x as f32 + self.width as f32, self.y as f32 - rect.height() as f32, &rect);
|
|
|
|
rect = state.constants.title.menu_left_bottom;
|
|
|
|
batch.add_rect(self.x as f32 - rect.width() as f32, self.y as f32 + self.height as f32, &rect);
|
|
|
|
rect = state.constants.title.menu_right_bottom;
|
|
|
|
batch.add_rect(self.x as f32 + self.width as f32, self.y as f32 + self.height as f32, &rect);
|
|
|
|
|
|
|
|
rect = state.constants.title.menu_top;
|
|
|
|
rect2 = state.constants.title.menu_bottom;
|
|
|
|
let mut x = self.x as f32;
|
|
|
|
let mut y = self.y as f32;
|
|
|
|
let mut width = self.width;
|
|
|
|
let mut height = self.height;
|
|
|
|
|
|
|
|
while width > 0 {
|
|
|
|
rect.right = if width >= rect.width() {
|
|
|
|
width = width.saturating_sub(rect.width());
|
|
|
|
rect.right
|
|
|
|
} else {
|
|
|
|
let old_width = width;
|
|
|
|
width = 0;
|
|
|
|
rect.left + old_width
|
|
|
|
};
|
|
|
|
rect2.right = rect.right;
|
|
|
|
|
|
|
|
batch.add_rect(x, y - rect.height() as f32, &rect);
|
|
|
|
batch.add_rect(x, y + self.height as f32, &rect2);
|
|
|
|
x += rect.width() as f32;
|
|
|
|
}
|
|
|
|
|
|
|
|
x = self.x as f32;
|
|
|
|
rect = state.constants.title.menu_left;
|
|
|
|
rect2 = state.constants.title.menu_right;
|
|
|
|
while height > 0 {
|
|
|
|
rect.bottom = if height >= rect.height() {
|
|
|
|
height = height.saturating_sub(rect.height());
|
|
|
|
rect.bottom
|
|
|
|
} else {
|
|
|
|
let old_height = height;
|
|
|
|
height = 0;
|
|
|
|
rect.top + old_height
|
|
|
|
};
|
|
|
|
rect2.bottom = rect.bottom;
|
|
|
|
|
|
|
|
batch.add_rect(x - rect.width() as f32, y, &rect);
|
|
|
|
batch.add_rect(x + self.width as f32, y, &rect2);
|
|
|
|
y += rect.height() as f32;
|
|
|
|
}
|
|
|
|
|
|
|
|
height = self.height;
|
|
|
|
y = self.y as f32;
|
|
|
|
|
|
|
|
while height > 0 {
|
|
|
|
rect = state.constants.title.menu_middle;
|
|
|
|
width = self.width;
|
|
|
|
x = self.x as f32;
|
|
|
|
|
|
|
|
rect.bottom = if height >= rect.height() {
|
|
|
|
height = height.saturating_sub(rect.height());
|
|
|
|
rect.bottom
|
|
|
|
} else {
|
|
|
|
let old_height = height;
|
|
|
|
height = 0;
|
|
|
|
rect.top + old_height
|
|
|
|
};
|
|
|
|
|
|
|
|
while width > 0 {
|
|
|
|
rect.right = if width >= rect.width() {
|
|
|
|
width = width.saturating_sub(rect.width());
|
|
|
|
rect.right
|
|
|
|
} else {
|
|
|
|
let old_width = width;
|
|
|
|
width = 0;
|
|
|
|
rect.left + old_width
|
|
|
|
};
|
|
|
|
|
|
|
|
batch.add_rect(x, y, &rect);
|
|
|
|
|
|
|
|
x += rect.width() as f32;
|
|
|
|
}
|
|
|
|
|
|
|
|
y += rect.height() as f32;
|
|
|
|
}
|
|
|
|
|
|
|
|
batch.draw(ctx)?;
|
|
|
|
|
|
|
|
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "MyChar")?;
|
|
|
|
|
|
|
|
rect.left = QUOTE_FRAMES[self.anim_num as usize] * 16;
|
|
|
|
rect.top = 16;
|
|
|
|
rect.right = rect.left + 16;
|
|
|
|
rect.bottom = rect.top + 16;
|
|
|
|
|
|
|
|
batch.add_rect(self.x as f32,
|
|
|
|
self.y as f32 + 2.0 + (self.selected as f32 * 14.0),
|
2020-09-21 22:33:16 +00:00
|
|
|
&rect);
|
2020-09-20 01:05:41 +00:00
|
|
|
|
|
|
|
batch.draw(ctx)?;
|
|
|
|
|
|
|
|
y = self.y as f32 + 6.0;
|
|
|
|
for entry in self.entries.iter() {
|
|
|
|
match entry {
|
|
|
|
MenuEntry::Active(name) => {
|
|
|
|
state.font.draw_text(name.chars(), self.x as f32 + 20.0, y, &state.constants, &mut state.texture_set, ctx)?;
|
|
|
|
}
|
|
|
|
MenuEntry::Disabled(name) => {
|
2020-09-22 19:31:47 +00:00
|
|
|
state.font.draw_colored_text(name.chars(), self.x as f32 + 20.0, y, (0xa0, 0xa0, 0xff), &state.constants, &mut state.texture_set, ctx)?;
|
2020-09-20 01:05:41 +00:00
|
|
|
}
|
2020-09-21 22:33:16 +00:00
|
|
|
MenuEntry::Toggle(name, value) => {
|
|
|
|
let value_text = if *value { "ON" } else { "OFF" };
|
|
|
|
let val_text_len = state.font.text_width(value_text.chars(), &state.constants);
|
|
|
|
|
|
|
|
state.font.draw_text(name.chars(), self.x as f32 + 20.0, y, &state.constants, &mut state.texture_set, ctx)?;
|
|
|
|
|
|
|
|
state.font.draw_text(value_text.chars(), self.x as f32 + self.width as f32 - val_text_len, y, &state.constants, &mut state.texture_set, ctx)?;
|
|
|
|
}
|
2020-09-20 01:05:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
y += 14.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn tick(&mut self, state: &mut SharedGameState) -> MenuSelectionResult {
|
|
|
|
state.update_key_trigger();
|
|
|
|
|
|
|
|
if state.key_trigger.fire() {
|
|
|
|
state.sound_manager.play_sfx(5);
|
|
|
|
return MenuSelectionResult::Canceled;
|
|
|
|
}
|
|
|
|
|
|
|
|
if state.key_trigger.up() || state.key_trigger.down() && !self.entries.is_empty() {
|
|
|
|
state.sound_manager.play_sfx(1);
|
|
|
|
loop {
|
|
|
|
if state.key_trigger.down() {
|
|
|
|
self.selected += 1;
|
|
|
|
if self.selected == self.entries.len() {
|
|
|
|
self.selected = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if self.selected == 0 {
|
|
|
|
self.selected = self.entries.len();
|
|
|
|
}
|
|
|
|
self.selected -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(entry) = self.entries.get(self.selected) {
|
|
|
|
match entry {
|
|
|
|
MenuEntry::Active(_) => { break; }
|
2020-09-21 22:33:16 +00:00
|
|
|
MenuEntry::Toggle(_, _) => { break; }
|
2020-09-20 01:05:41 +00:00
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if state.key_trigger.jump() && !self.entries.is_empty() {
|
2020-09-21 22:33:16 +00:00
|
|
|
if let Some(entry) = self.entries.get_mut(self.selected) {
|
2020-09-20 01:05:41 +00:00
|
|
|
match entry {
|
2020-09-21 22:33:16 +00:00
|
|
|
MenuEntry::Active(_) | MenuEntry::Toggle(_, _) => {
|
2020-09-20 01:05:41 +00:00
|
|
|
state.sound_manager.play_sfx(18);
|
|
|
|
return MenuSelectionResult::Selected(self.selected, entry);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// todo nikumaru counter support
|
|
|
|
self.anim_wait += 1;
|
|
|
|
if self.anim_wait > 8 {
|
|
|
|
self.anim_wait = 0;
|
|
|
|
|
|
|
|
self.anim_num += 1;
|
|
|
|
if self.anim_num >= QUOTE_FRAMES.len() as u16 {
|
|
|
|
self.anim_num = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
MenuSelectionResult::None
|
|
|
|
}
|
|
|
|
}
|