mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-07-12 07:46:48 +00:00
Refactor input system and other stuff
This commit is contained in:
parent
65bc0b6b88
commit
605a858c90
|
@ -77,7 +77,7 @@ strum_macros = "0.18.0"
|
||||||
# remove and replace when drain_filter is in stable
|
# remove and replace when drain_filter is in stable
|
||||||
vec_mut_scan = "0.3.0"
|
vec_mut_scan = "0.3.0"
|
||||||
webbrowser = "0.5.5"
|
webbrowser = "0.5.5"
|
||||||
winit = "0.23.0"
|
winit = {version = "0.23.0", features = ["serde"]}
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
ndk = "0.2.0"
|
ndk = "0.2.0"
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::io::ErrorKind;
|
||||||
use std::io::SeekFrom;
|
use std::io::SeekFrom;
|
||||||
use std::path::{Component, Path, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
|
|
||||||
use ggez::{GameError, GameResult};
|
use ggez::GameResult;
|
||||||
use ggez::GameError::FilesystemError;
|
use ggez::GameError::FilesystemError;
|
||||||
use ggez::vfs::{OpenOptions, VFile, VFS, VMetadata};
|
use ggez::vfs::{OpenOptions, VFile, VFS, VMetadata};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use num_traits::{AsPrimitive, Num};
|
use num_traits::{AsPrimitive, Num};
|
||||||
use num_traits::real::Real;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::bitfield;
|
use crate::bitfield;
|
||||||
|
@ -82,22 +81,6 @@ bitfield! {
|
||||||
pub drs_destroyed, set_drs_destroyed: 15;
|
pub drs_destroyed, set_drs_destroyed: 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
bitfield! {
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct KeyState(u16);
|
|
||||||
impl Debug;
|
|
||||||
|
|
||||||
pub left, set_left: 0;
|
|
||||||
pub right, set_right: 1;
|
|
||||||
pub up, set_up: 2;
|
|
||||||
pub down, set_down: 3;
|
|
||||||
pub map, set_map: 4;
|
|
||||||
pub jump, set_jump: 5;
|
|
||||||
pub fire, set_fire: 6;
|
|
||||||
pub weapon_next, set_weapon_next: 7;
|
|
||||||
pub weapon_prev, set_weapon_prev: 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
bitfield! {
|
bitfield! {
|
||||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct ControlFlags(u16);
|
pub struct ControlFlags(u16);
|
||||||
|
|
|
@ -52,7 +52,7 @@ impl BossLifeBar {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameEntity<&NPCMap> for BossLifeBar {
|
impl GameEntity<&NPCMap> for BossLifeBar {
|
||||||
fn tick(&mut self, state: &mut SharedGameState, npc_map: &NPCMap) -> GameResult<()> {
|
fn tick(&mut self, _state: &mut SharedGameState, npc_map: &NPCMap) -> GameResult<()> {
|
||||||
match self.target {
|
match self.target {
|
||||||
BossLifeTarget::NPC(npc_id) => {
|
BossLifeTarget::NPC(npc_id) => {
|
||||||
if let Some(npc_cell) = npc_map.npcs.get(&npc_id) {
|
if let Some(npc_cell) = npc_map.npcs.get(&npc_id) {
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
pub mod boss_life_bar;
|
pub mod boss_life_bar;
|
||||||
|
pub mod stage_select;
|
||||||
|
|
123
src/components/stage_select.rs
Normal file
123
src/components/stage_select.rs
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
use ggez::{Context, GameResult};
|
||||||
|
|
||||||
|
use crate::common::Rect;
|
||||||
|
use crate::entity::GameEntity;
|
||||||
|
use crate::frame::Frame;
|
||||||
|
use crate::player::Player;
|
||||||
|
use crate::shared_game_state::SharedGameState;
|
||||||
|
use crate::text_script::ScriptMode;
|
||||||
|
|
||||||
|
pub struct StageSelect {
|
||||||
|
pub current_teleport_slot: u8,
|
||||||
|
stage_select_text_y_pos: usize,
|
||||||
|
tick: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StageSelect {
|
||||||
|
pub fn new() -> StageSelect {
|
||||||
|
StageSelect {
|
||||||
|
current_teleport_slot: 0,
|
||||||
|
stage_select_text_y_pos: 54,
|
||||||
|
tick: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
self.stage_select_text_y_pos = 54;
|
||||||
|
self.current_teleport_slot = 0;
|
||||||
|
self.tick = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameEntity<(&Player, &Player)> for StageSelect {
|
||||||
|
fn tick(&mut self, state: &mut SharedGameState, (player1, player2): (&Player, &Player)) -> GameResult {
|
||||||
|
let slot_count = state.teleporter_slots.iter()
|
||||||
|
.filter(|&&(index, _event_num)| index != 0)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
if self.stage_select_text_y_pos > 46 {
|
||||||
|
self.stage_select_text_y_pos -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let left_pressed = player1.controller.trigger_left() || player2.controller.trigger_left();
|
||||||
|
let right_pressed = player1.controller.trigger_right() || player2.controller.trigger_right();
|
||||||
|
let ok_pressed = player1.controller.trigger_jump() || player1.controller.trigger_menu_ok()
|
||||||
|
|| player2.controller.trigger_jump() || player2.controller.trigger_menu_ok();
|
||||||
|
let cancel_pressed = player1.controller.trigger_shoot() || player2.controller.trigger_shoot();
|
||||||
|
|
||||||
|
if left_pressed {
|
||||||
|
if self.current_teleport_slot == 0 {
|
||||||
|
self.current_teleport_slot = slot_count.saturating_sub(1) as u8;
|
||||||
|
} else {
|
||||||
|
self.current_teleport_slot -= 1;
|
||||||
|
}
|
||||||
|
} else if right_pressed {
|
||||||
|
if self.current_teleport_slot == slot_count.saturating_sub(1) as u8 {
|
||||||
|
self.current_teleport_slot = 0;
|
||||||
|
} else {
|
||||||
|
self.current_teleport_slot += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if left_pressed || right_pressed {
|
||||||
|
state.sound_manager.play_sfx(1);
|
||||||
|
if let Some(&(index, _event_num)) = state.teleporter_slots.get(self.current_teleport_slot as usize) {
|
||||||
|
state.textscript_vm.start_script(1000 + index);
|
||||||
|
} else {
|
||||||
|
state.textscript_vm.start_script(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok_pressed || cancel_pressed {
|
||||||
|
self.reset();
|
||||||
|
state.textscript_vm.set_mode(ScriptMode::Map);
|
||||||
|
state.control_flags.set_tick_world(true);
|
||||||
|
state.control_flags.set_control_enabled(true);
|
||||||
|
state.control_flags.set_interactions_disabled(false);
|
||||||
|
|
||||||
|
if ok_pressed {
|
||||||
|
if let Some(&(_index, event_num)) = state.teleporter_slots.get(self.current_teleport_slot as usize) {
|
||||||
|
state.textscript_vm.start_script(event_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tick += 1;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, _frame: &Frame) -> GameResult {
|
||||||
|
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "StageImage")?;
|
||||||
|
|
||||||
|
let slot_count = state.teleporter_slots.iter()
|
||||||
|
.filter(|&&(index, _event_num)| index != 0)
|
||||||
|
.count();
|
||||||
|
let slot_offset = ((state.canvas_size.0 - 40.0 * slot_count as f32) / 2.0).floor();
|
||||||
|
let mut slot_rect = Rect::new(0, 0, 0, 0);
|
||||||
|
|
||||||
|
for i in 0..slot_count {
|
||||||
|
let index = state.teleporter_slots[i].0;
|
||||||
|
|
||||||
|
slot_rect.left = 32 * (index as u16 % 8);
|
||||||
|
slot_rect.top = 16 * (index as u16 / 8);
|
||||||
|
slot_rect.right = slot_rect.left + 32;
|
||||||
|
slot_rect.bottom = slot_rect.top + 16;
|
||||||
|
|
||||||
|
batch.add_rect(slot_offset + i as f32 * 40.0, 64.0, &slot_rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
batch.draw(ctx)?;
|
||||||
|
|
||||||
|
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;
|
||||||
|
|
||||||
|
batch.add_rect(128.0, self.stage_select_text_y_pos as f32, &state.constants.textscript.stage_select_text);
|
||||||
|
if slot_count > 0 {
|
||||||
|
batch.add_rect(slot_offset + self.current_teleport_slot as f32 * 40.0, 64.0, &state.constants.textscript.cursor[self.tick / 2 % 2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
batch.draw(ctx)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
73
src/input/combined_menu_controller.rs
Normal file
73
src/input/combined_menu_controller.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
use crate::input::player_controller::PlayerController;
|
||||||
|
use crate::shared_game_state::SharedGameState;
|
||||||
|
use ggez::{GameResult, Context};
|
||||||
|
|
||||||
|
pub struct CombinedMenuController {
|
||||||
|
controllers: Vec<Box<dyn PlayerController>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CombinedMenuController {
|
||||||
|
pub fn new() -> CombinedMenuController {
|
||||||
|
CombinedMenuController {
|
||||||
|
controllers: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||||
|
for cont in self.controllers.iter_mut() {
|
||||||
|
cont.update(state, ctx)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_trigger(&mut self) {
|
||||||
|
for cont in self.controllers.iter_mut() {
|
||||||
|
cont.update_trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, controller: Box<dyn PlayerController>) {
|
||||||
|
self.controllers.push(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trigger_up(&self) -> bool {
|
||||||
|
for cont in self.controllers.iter() {
|
||||||
|
if cont.trigger_up() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trigger_down(&self) -> bool {
|
||||||
|
for cont in self.controllers.iter() {
|
||||||
|
if cont.trigger_down() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trigger_ok(&self) -> bool {
|
||||||
|
for cont in self.controllers.iter() {
|
||||||
|
if cont.trigger_menu_ok() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trigger_back(&self) -> bool {
|
||||||
|
for cont in self.controllers.iter() {
|
||||||
|
if cont.trigger_menu_back() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
122
src/input/dummy_player_controller.rs
Normal file
122
src/input/dummy_player_controller.rs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
use ggez::{Context, GameResult};
|
||||||
|
|
||||||
|
use crate::input::player_controller::PlayerController;
|
||||||
|
use crate::shared_game_state::SharedGameState;
|
||||||
|
|
||||||
|
/// A no-op implementation of player controller.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct DummyPlayerController;
|
||||||
|
|
||||||
|
impl DummyPlayerController {
|
||||||
|
pub fn new() -> DummyPlayerController {
|
||||||
|
DummyPlayerController
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerController for DummyPlayerController {
|
||||||
|
fn update(&mut self, _state: &SharedGameState, _ctx: &mut Context) -> GameResult {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_trigger(&mut self) {}
|
||||||
|
|
||||||
|
fn move_up(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_left(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_down(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_right(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prev_weapon(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_weapon(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jump(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shoot(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_up(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_left(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_down(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_right(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_prev_weapon(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_next_weapon(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_jump(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_shoot(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_menu_ok(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_menu_back(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_menu_pause(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn look_up(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn look_left(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn look_down(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn look_right(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_analog_x(&self) -> f64 {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_analog_y(&self) -> f64 {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
193
src/input/keyboard_player_controller.rs
Normal file
193
src/input/keyboard_player_controller.rs
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
use ggez::{Context, GameResult};
|
||||||
|
use ggez::input::keyboard;
|
||||||
|
use winit::event::VirtualKeyCode;
|
||||||
|
|
||||||
|
use crate::bitfield;
|
||||||
|
use crate::input::player_controller::PlayerController;
|
||||||
|
use crate::player::TargetPlayer;
|
||||||
|
use crate::shared_game_state::SharedGameState;
|
||||||
|
|
||||||
|
bitfield! {
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct KeyState(u16);
|
||||||
|
impl Debug;
|
||||||
|
|
||||||
|
pub left, set_left: 0;
|
||||||
|
pub right, set_right: 1;
|
||||||
|
pub up, set_up: 2;
|
||||||
|
pub down, set_down: 3;
|
||||||
|
pub map, set_map: 4;
|
||||||
|
pub inventory, set_inventory: 5;
|
||||||
|
pub jump, set_jump: 6;
|
||||||
|
pub shoot, set_shoot: 7;
|
||||||
|
pub next_weapon, set_next_weapon: 8;
|
||||||
|
pub prev_weapon, set_prev_weapon: 9;
|
||||||
|
pub escape, set_escape: 10;
|
||||||
|
pub enter, set_enter: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct KeyboardController {
|
||||||
|
target: TargetPlayer,
|
||||||
|
state: KeyState,
|
||||||
|
old_state: KeyState,
|
||||||
|
trigger: KeyState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyboardController {
|
||||||
|
pub fn new(target: TargetPlayer) -> KeyboardController {
|
||||||
|
KeyboardController {
|
||||||
|
target,
|
||||||
|
state: KeyState(0),
|
||||||
|
old_state: KeyState(0),
|
||||||
|
trigger: KeyState(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlayerController for KeyboardController {
|
||||||
|
fn update(&mut self, state: &SharedGameState, ctx: &mut Context) -> GameResult {
|
||||||
|
let keymap = match self.target {
|
||||||
|
TargetPlayer::Player1 => &state.settings.player1_key_map,
|
||||||
|
TargetPlayer::Player2 => &state.settings.player2_key_map,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.state.set_left(keyboard::is_key_pressed(ctx, keymap.left));
|
||||||
|
self.state.set_up(keyboard::is_key_pressed(ctx, keymap.up));
|
||||||
|
self.state.set_right(keyboard::is_key_pressed(ctx, keymap.right));
|
||||||
|
self.state.set_down(keyboard::is_key_pressed(ctx, keymap.down));
|
||||||
|
self.state.set_map(keyboard::is_key_pressed(ctx, keymap.map));
|
||||||
|
self.state.set_inventory(keyboard::is_key_pressed(ctx, keymap.inventory));
|
||||||
|
self.state.set_jump(keyboard::is_key_pressed(ctx, keymap.jump));
|
||||||
|
self.state.set_shoot(keyboard::is_key_pressed(ctx, keymap.shoot));
|
||||||
|
self.state.set_prev_weapon(keyboard::is_key_pressed(ctx, keymap.prev_weapon));
|
||||||
|
self.state.set_next_weapon(keyboard::is_key_pressed(ctx, keymap.next_weapon));
|
||||||
|
self.state.set_enter(keyboard::is_key_pressed(ctx, VirtualKeyCode::Return));
|
||||||
|
self.state.set_escape(keyboard::is_key_pressed(ctx, VirtualKeyCode::Escape));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_trigger(&mut self) {
|
||||||
|
let mut trigger = self.state.0 ^ self.old_state.0;
|
||||||
|
trigger &= self.state.0;
|
||||||
|
self.old_state = self.state;
|
||||||
|
self.trigger = KeyState(trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_up(&self) -> bool {
|
||||||
|
self.state.up()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_left(&self) -> bool {
|
||||||
|
self.state.left()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_down(&self) -> bool {
|
||||||
|
self.state.down()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_right(&self) -> bool {
|
||||||
|
self.state.right()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prev_weapon(&self) -> bool {
|
||||||
|
self.state.prev_weapon()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_weapon(&self) -> bool {
|
||||||
|
self.state.next_weapon()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jump(&self) -> bool {
|
||||||
|
self.state.jump()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shoot(&self) -> bool {
|
||||||
|
self.state.shoot()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_up(&self) -> bool {
|
||||||
|
self.trigger.up()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_left(&self) -> bool {
|
||||||
|
self.trigger.left()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_down(&self) -> bool {
|
||||||
|
self.trigger.down()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_right(&self) -> bool {
|
||||||
|
self.trigger.right()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_prev_weapon(&self) -> bool {
|
||||||
|
self.trigger.prev_weapon()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_next_weapon(&self) -> bool {
|
||||||
|
self.trigger.next_weapon()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_jump(&self) -> bool {
|
||||||
|
self.trigger.jump()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_shoot(&self) -> bool {
|
||||||
|
self.trigger.shoot()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_menu_ok(&self) -> bool {
|
||||||
|
self.trigger.jump() || self.trigger.enter()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_menu_back(&self) -> bool {
|
||||||
|
self.trigger.shoot() || self.trigger.escape()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_menu_pause(&self) -> bool {
|
||||||
|
self.trigger.escape()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn look_up(&self) -> bool {
|
||||||
|
self.state.up()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn look_left(&self) -> bool {
|
||||||
|
self.state.left()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn look_down(&self) -> bool {
|
||||||
|
self.state.down()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn look_right(&self) -> bool {
|
||||||
|
self.state.right()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_analog_x(&self) -> f64 {
|
||||||
|
if self.state.left() && self.state.right() {
|
||||||
|
0.0
|
||||||
|
} else if self.state.left() {
|
||||||
|
-1.0
|
||||||
|
} else if self.state.right() {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn move_analog_y(&self) -> f64 {
|
||||||
|
if self.state.up() && self.state.down() {
|
||||||
|
0.0
|
||||||
|
} else if self.state.up() {
|
||||||
|
-1.0
|
||||||
|
} else if self.state.down() {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
5
src/input/mod.rs
Normal file
5
src/input/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
pub mod combined_menu_controller;
|
||||||
|
pub mod dummy_player_controller;
|
||||||
|
pub mod keyboard_player_controller;
|
||||||
|
pub mod player_controller;
|
||||||
|
pub mod touch_controls;
|
95
src/input/player_controller.rs
Normal file
95
src/input/player_controller.rs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
use ggez::{Context, GameResult};
|
||||||
|
|
||||||
|
use crate::shared_game_state::SharedGameState;
|
||||||
|
|
||||||
|
pub trait PlayerController: PlayerControllerClone {
|
||||||
|
fn update(&mut self, state: &SharedGameState, ctx: &mut Context) -> GameResult;
|
||||||
|
|
||||||
|
fn update_trigger(&mut self);
|
||||||
|
|
||||||
|
/// True if "move up" button is down.
|
||||||
|
fn move_up(&self) -> bool;
|
||||||
|
|
||||||
|
/// True if "move left" button is down.
|
||||||
|
fn move_left(&self) -> bool;
|
||||||
|
|
||||||
|
/// True if "move down" button is down.
|
||||||
|
fn move_down(&self) -> bool;
|
||||||
|
|
||||||
|
/// True if "move right" button is down.
|
||||||
|
fn move_right(&self) -> bool;
|
||||||
|
|
||||||
|
/// True if "prev weapon" button is down.
|
||||||
|
fn prev_weapon(&self) -> bool;
|
||||||
|
|
||||||
|
/// True if "next weapon" button is down.
|
||||||
|
fn next_weapon(&self) -> bool;
|
||||||
|
|
||||||
|
/// True if "jump" button is down.
|
||||||
|
fn jump(&self) -> bool;
|
||||||
|
|
||||||
|
/// True if "shoot" button is down.
|
||||||
|
fn shoot(&self) -> bool;
|
||||||
|
|
||||||
|
fn trigger_up(&self) -> bool;
|
||||||
|
|
||||||
|
fn trigger_left(&self) -> bool;
|
||||||
|
|
||||||
|
fn trigger_down(&self) -> bool;
|
||||||
|
|
||||||
|
fn trigger_right(&self) -> bool;
|
||||||
|
|
||||||
|
fn trigger_prev_weapon(&self) -> bool;
|
||||||
|
|
||||||
|
fn trigger_next_weapon(&self) -> bool;
|
||||||
|
|
||||||
|
fn trigger_jump(&self) -> bool;
|
||||||
|
|
||||||
|
fn trigger_shoot(&self) -> bool;
|
||||||
|
|
||||||
|
fn trigger_menu_ok(&self) -> bool;
|
||||||
|
|
||||||
|
fn trigger_menu_back(&self) -> bool;
|
||||||
|
|
||||||
|
fn trigger_menu_pause(&self) -> bool;
|
||||||
|
|
||||||
|
/// Optional, useful for controllers with two analog sticks.
|
||||||
|
/// Returns true if player looks towards upper direction.
|
||||||
|
fn look_up(&self) -> bool;
|
||||||
|
|
||||||
|
/// Optional, useful for controllers with two analog sticks.
|
||||||
|
/// Returns true if player looks towards left direction.
|
||||||
|
fn look_left(&self) -> bool;
|
||||||
|
|
||||||
|
/// Optional, useful for controllers with two analog sticks.
|
||||||
|
/// Returns true if player looks towards bottom direction.
|
||||||
|
fn look_down(&self) -> bool;
|
||||||
|
|
||||||
|
/// Optional, useful for controllers with two analog sticks.
|
||||||
|
/// Returns true if player looks towards right direction.
|
||||||
|
fn look_right(&self) -> bool;
|
||||||
|
|
||||||
|
/// Returns movement analog stick state in X axis within (-1.0..=1.0) range
|
||||||
|
/// In case of non-analog controllers this should return -1.0, 0.0 or 1.0, depending on keys pressed.
|
||||||
|
fn move_analog_x(&self) -> f64;
|
||||||
|
|
||||||
|
/// Returns movement analog stick state in Y axis within (-1.0..=1.0) range
|
||||||
|
/// In case of non-analog controllers this should return -1.0, 0.0 or 1.0, depending on keys pressed.
|
||||||
|
fn move_analog_y(&self) -> f64;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait PlayerControllerClone {
|
||||||
|
fn clone_box(&self) -> Box<dyn PlayerController>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static + PlayerController + Clone> PlayerControllerClone for T {
|
||||||
|
fn clone_box(&self) -> Box<dyn PlayerController> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Box<dyn PlayerController> {
|
||||||
|
fn clone(&self) -> Box<dyn PlayerController> {
|
||||||
|
self.clone_box()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
use winit::event::TouchPhase;
|
|
||||||
use crate::texture_set::TextureSet;
|
|
||||||
use ggez::{Context, GameResult};
|
use ggez::{Context, GameResult};
|
||||||
use crate::common::{KeyState, Rect};
|
use winit::event::TouchPhase;
|
||||||
|
|
||||||
|
use crate::common::Rect;
|
||||||
use crate::engine_constants::EngineConstants;
|
use crate::engine_constants::EngineConstants;
|
||||||
|
use crate::texture_set::TextureSet;
|
||||||
|
|
||||||
struct TouchPoint {
|
struct TouchPoint {
|
||||||
id: u64,
|
id: u64,
|
||||||
|
@ -20,8 +21,8 @@ impl TouchControls {
|
||||||
points: Vec::with_capacity(8),
|
points: Vec::with_capacity(8),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_winit_event(&mut self, scale: f32, key_state: &mut KeyState, touch: winit::event::Touch) {
|
pub fn process_winit_event(&mut self, scale: f32, touch: winit::event::Touch) {
|
||||||
match touch.phase {
|
match touch.phase {
|
||||||
TouchPhase::Started | TouchPhase::Moved => {
|
TouchPhase::Started | TouchPhase::Moved => {
|
||||||
if let Some(point) = self.points.iter_mut().find(|p| p.id == touch.id) {
|
if let Some(point) = self.points.iter_mut().find(|p| p.id == touch.id) {
|
||||||
|
@ -31,7 +32,7 @@ impl TouchControls {
|
||||||
self.points.push(TouchPoint {
|
self.points.push(TouchPoint {
|
||||||
id: touch.id,
|
id: touch.id,
|
||||||
position: (touch.location.x, touch.location.y),
|
position: (touch.location.x, touch.location.y),
|
||||||
last_position: (0.0, 0.0)
|
last_position: (0.0, 0.0),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
34
src/lib.rs
34
src/lib.rs
|
@ -8,7 +8,7 @@ use std::{env, mem};
|
||||||
use std::path;
|
use std::path;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use ggez::{Context, ContextBuilder, filesystem, GameError, GameResult};
|
use ggez::{Context, ContextBuilder, GameError, GameResult};
|
||||||
use ggez::conf::{Backend, WindowMode, WindowSetup};
|
use ggez::conf::{Backend, WindowMode, WindowSetup};
|
||||||
use ggez::event::{KeyCode, KeyMods};
|
use ggez::event::{KeyCode, KeyMods};
|
||||||
use ggez::filesystem::mount_vfs;
|
use ggez::filesystem::mount_vfs;
|
||||||
|
@ -20,7 +20,7 @@ use ggez::mint::ColumnMatrix4;
|
||||||
use ggez::nalgebra::Vector2;
|
use ggez::nalgebra::Vector2;
|
||||||
use log::*;
|
use log::*;
|
||||||
use pretty_env_logger::env_logger::Env;
|
use pretty_env_logger::env_logger::Env;
|
||||||
use winit::event::{ElementState, Event, KeyboardInput, TouchPhase, WindowEvent};
|
use winit::event::{ElementState, Event, KeyboardInput, WindowEvent};
|
||||||
use winit::event_loop::ControlFlow;
|
use winit::event_loop::ControlFlow;
|
||||||
|
|
||||||
use crate::builtin_fs::BuiltinFS;
|
use crate::builtin_fs::BuiltinFS;
|
||||||
|
@ -42,6 +42,7 @@ mod engine_constants;
|
||||||
mod entity;
|
mod entity;
|
||||||
mod frame;
|
mod frame;
|
||||||
mod inventory;
|
mod inventory;
|
||||||
|
mod input;
|
||||||
mod live_debugger;
|
mod live_debugger;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod map;
|
mod map;
|
||||||
|
@ -49,16 +50,16 @@ mod menu;
|
||||||
mod npc;
|
mod npc;
|
||||||
mod physics;
|
mod physics;
|
||||||
mod player;
|
mod player;
|
||||||
mod player_hit;
|
|
||||||
mod profile;
|
mod profile;
|
||||||
mod rng;
|
mod rng;
|
||||||
mod scene;
|
mod scene;
|
||||||
|
mod settings;
|
||||||
|
mod shaders;
|
||||||
mod shared_game_state;
|
mod shared_game_state;
|
||||||
mod stage;
|
mod stage;
|
||||||
mod sound;
|
mod sound;
|
||||||
mod text_script;
|
mod text_script;
|
||||||
mod texture_set;
|
mod texture_set;
|
||||||
mod touch_controls;
|
|
||||||
mod ui;
|
mod ui;
|
||||||
mod weapon;
|
mod weapon;
|
||||||
|
|
||||||
|
@ -165,14 +166,6 @@ impl Game {
|
||||||
// todo: proper keymaps?
|
// todo: proper keymaps?
|
||||||
let state = &mut self.state;
|
let state = &mut self.state;
|
||||||
match key_code {
|
match key_code {
|
||||||
KeyCode::Left => { state.key_state.set_left(true) }
|
|
||||||
KeyCode::Right => { state.key_state.set_right(true) }
|
|
||||||
KeyCode::Up => { state.key_state.set_up(true) }
|
|
||||||
KeyCode::Down => { state.key_state.set_down(true) }
|
|
||||||
KeyCode::Z => { state.key_state.set_jump(true) }
|
|
||||||
KeyCode::X => { state.key_state.set_fire(true) }
|
|
||||||
KeyCode::A => { state.key_state.set_weapon_prev(true) }
|
|
||||||
KeyCode::S => { state.key_state.set_weapon_next(true) }
|
|
||||||
KeyCode::F7 => { state.set_speed(1.0) }
|
KeyCode::F7 => { state.set_speed(1.0) }
|
||||||
KeyCode::F8 => {
|
KeyCode::F8 => {
|
||||||
if state.settings.speed > 0.2 {
|
if state.settings.speed > 0.2 {
|
||||||
|
@ -191,21 +184,8 @@ impl Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn key_up_event(&mut self, key_code: KeyCode, _key_mod: KeyMods) {
|
fn key_up_event(&mut self, key_code: KeyCode, _key_mod: KeyMods) {
|
||||||
let state = &mut self.state;
|
//
|
||||||
|
|
||||||
match key_code {
|
|
||||||
KeyCode::Left => { state.key_state.set_left(false) }
|
|
||||||
KeyCode::Right => { state.key_state.set_right(false) }
|
|
||||||
KeyCode::Up => { state.key_state.set_up(false) }
|
|
||||||
KeyCode::Down => { state.key_state.set_down(false) }
|
|
||||||
KeyCode::Z => { state.key_state.set_jump(false) }
|
|
||||||
KeyCode::X => { state.key_state.set_fire(false) }
|
|
||||||
KeyCode::A => { state.key_state.set_weapon_prev(false) }
|
|
||||||
KeyCode::S => { state.key_state.set_weapon_next(false) }
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -385,7 +365,7 @@ pub fn init() -> GameResult {
|
||||||
}
|
}
|
||||||
WindowEvent::Touch(touch) => {
|
WindowEvent::Touch(touch) => {
|
||||||
if let Some(game) = &mut game {
|
if let Some(game) = &mut game {
|
||||||
game.state.touch_controls.process_winit_event(game.state.scale, &mut game.state.key_state, touch);
|
game.state.touch_controls.process_winit_event(game.state.scale, touch);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::KeyboardInput {
|
WindowEvent::KeyboardInput {
|
||||||
|
|
|
@ -64,10 +64,10 @@ impl LiveDebugger {
|
||||||
.build(ui, || {
|
.build(ui, || {
|
||||||
ui.text(format!(
|
ui.text(format!(
|
||||||
"Player position: ({:.1},{:.1}), velocity: ({:.1},{:.1})",
|
"Player position: ({:.1},{:.1}), velocity: ({:.1},{:.1})",
|
||||||
game_scene.player.x as f32 / 512.0,
|
game_scene.player1.x as f32 / 512.0,
|
||||||
game_scene.player.y as f32 / 512.0,
|
game_scene.player1.y as f32 / 512.0,
|
||||||
game_scene.player.vel_x as f32 / 512.0,
|
game_scene.player1.vel_x as f32 / 512.0,
|
||||||
game_scene.player.vel_y as f32 / 512.0,
|
game_scene.player1.vel_y as f32 / 512.0,
|
||||||
));
|
));
|
||||||
|
|
||||||
ui.text(format!(
|
ui.text(format!(
|
||||||
|
@ -77,7 +77,7 @@ impl LiveDebugger {
|
||||||
));
|
));
|
||||||
|
|
||||||
ui.text(format!(
|
ui.text(format!(
|
||||||
"Booster fuel: {}", game_scene.player.booster_fuel
|
"Booster fuel: {}", game_scene.player1.booster_fuel
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
|
@ -139,13 +139,13 @@ impl LiveDebugger {
|
||||||
if ui.button(im_str!("Load"), [0.0, 0.0]) {
|
if ui.button(im_str!("Load"), [0.0, 0.0]) {
|
||||||
match GameScene::new(state, ctx, self.selected_stage as usize) {
|
match GameScene::new(state, ctx, self.selected_stage as usize) {
|
||||||
Ok(mut scene) => {
|
Ok(mut scene) => {
|
||||||
scene.inventory = game_scene.inventory.clone();
|
scene.inventory_player1 = game_scene.inventory_player1.clone();
|
||||||
scene.player = game_scene.player.clone();
|
scene.player1 = game_scene.player1.clone();
|
||||||
scene.player.x = (scene.stage.map.width / 2 * 16 * 0x200) as isize;
|
scene.player1.x = (scene.stage.map.width / 2 * 16 * 0x200) as isize;
|
||||||
scene.player.y = (scene.stage.map.height / 2 * 16 * 0x200) as isize;
|
scene.player1.y = (scene.stage.map.height / 2 * 16 * 0x200) as isize;
|
||||||
|
|
||||||
if scene.player.life == 0 {
|
if scene.player1.life == 0 {
|
||||||
scene.player.life = scene.player.max_life;
|
scene.player1.life = scene.player1.max_life;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.next_scene = Some(Box::new(scene));
|
state.next_scene = Some(Box::new(scene));
|
||||||
|
@ -252,19 +252,19 @@ impl LiveDebugger {
|
||||||
}
|
}
|
||||||
|
|
||||||
if CollapsingHeader::new(im_str!("Player condition flags")).default_open(false).build(&ui) {
|
if CollapsingHeader::new(im_str!("Player condition flags")).default_open(false).build(&ui) {
|
||||||
cond_flags(&ui, &mut game_scene.player.cond);
|
cond_flags(&ui, &mut game_scene.player1.cond);
|
||||||
}
|
}
|
||||||
|
|
||||||
if CollapsingHeader::new(im_str!("Player equipment")).default_open(false).build(&ui) {
|
if CollapsingHeader::new(im_str!("Player equipment")).default_open(false).build(&ui) {
|
||||||
ui.checkbox_flags(im_str!("Booster 0.8"), &mut game_scene.player.equip.0, 1);
|
ui.checkbox_flags(im_str!("Booster 0.8"), &mut game_scene.player1.equip.0, 1);
|
||||||
ui.checkbox_flags(im_str!("Map System"), &mut game_scene.player.equip.0, 2);
|
ui.checkbox_flags(im_str!("Map System"), &mut game_scene.player1.equip.0, 2);
|
||||||
ui.checkbox_flags(im_str!("Arms Barrier"), &mut game_scene.player.equip.0, 4);
|
ui.checkbox_flags(im_str!("Arms Barrier"), &mut game_scene.player1.equip.0, 4);
|
||||||
ui.checkbox_flags(im_str!("Turbocharge"), &mut game_scene.player.equip.0, 8);
|
ui.checkbox_flags(im_str!("Turbocharge"), &mut game_scene.player1.equip.0, 8);
|
||||||
ui.checkbox_flags(im_str!("Air Tank"), &mut game_scene.player.equip.0, 16);
|
ui.checkbox_flags(im_str!("Air Tank"), &mut game_scene.player1.equip.0, 16);
|
||||||
ui.checkbox_flags(im_str!("Booster 2.0"), &mut game_scene.player.equip.0, 32);
|
ui.checkbox_flags(im_str!("Booster 2.0"), &mut game_scene.player1.equip.0, 32);
|
||||||
ui.checkbox_flags(im_str!("Mimiga Mask"), &mut game_scene.player.equip.0, 64);
|
ui.checkbox_flags(im_str!("Mimiga Mask"), &mut game_scene.player1.equip.0, 64);
|
||||||
ui.checkbox_flags(im_str!("Whimsical Star"), &mut game_scene.player.equip.0, 128);
|
ui.checkbox_flags(im_str!("Whimsical Star"), &mut game_scene.player1.equip.0, 128);
|
||||||
ui.checkbox_flags(im_str!("Nikumaru Counter"), &mut game_scene.player.equip.0, 256);
|
ui.checkbox_flags(im_str!("Nikumaru Counter"), &mut game_scene.player1.equip.0, 256);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::common::Rect;
|
use crate::common::Rect;
|
||||||
use ggez::{Context, GameResult};
|
use ggez::{Context, GameResult};
|
||||||
use crate::shared_game_state::SharedGameState;
|
use crate::shared_game_state::SharedGameState;
|
||||||
|
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||||
|
|
||||||
pub enum MenuEntry {
|
pub enum MenuEntry {
|
||||||
Active(String),
|
Active(String),
|
||||||
|
@ -177,18 +178,18 @@ impl Menu {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tick(&mut self, state: &mut SharedGameState) -> MenuSelectionResult {
|
pub fn tick(&mut self, controller: &mut CombinedMenuController, state: &mut SharedGameState) -> MenuSelectionResult {
|
||||||
state.update_key_trigger();
|
controller.update_trigger();
|
||||||
|
|
||||||
if state.key_trigger.fire() {
|
if controller.trigger_back() {
|
||||||
state.sound_manager.play_sfx(5);
|
state.sound_manager.play_sfx(5);
|
||||||
return MenuSelectionResult::Canceled;
|
return MenuSelectionResult::Canceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_trigger.up() || state.key_trigger.down() && !self.entries.is_empty() {
|
if (controller.trigger_up() || controller.trigger_down()) && !self.entries.is_empty() {
|
||||||
state.sound_manager.play_sfx(1);
|
state.sound_manager.play_sfx(1);
|
||||||
loop {
|
loop {
|
||||||
if state.key_trigger.down() {
|
if controller.trigger_down() {
|
||||||
self.selected += 1;
|
self.selected += 1;
|
||||||
if self.selected == self.entries.len() {
|
if self.selected == self.entries.len() {
|
||||||
self.selected = 0;
|
self.selected = 0;
|
||||||
|
@ -212,7 +213,7 @@ impl Menu {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_trigger.jump() && !self.entries.is_empty() {
|
if controller.trigger_ok() && !self.entries.is_empty() {
|
||||||
if let Some(entry) = self.entries.get_mut(self.selected) {
|
if let Some(entry) = self.entries.get_mut(self.selected) {
|
||||||
match entry {
|
match entry {
|
||||||
MenuEntry::Active(_) | MenuEntry::Toggle(_, _) => {
|
MenuEntry::Active(_) | MenuEntry::Toggle(_, _) => {
|
|
@ -37,6 +37,8 @@ pub trait PhysicalEntity {
|
||||||
fn direction(&self) -> Direction;
|
fn direction(&self) -> Direction;
|
||||||
fn is_player(&self) -> bool;
|
fn is_player(&self) -> bool;
|
||||||
fn ignore_tile_44(&self) -> bool { true }
|
fn ignore_tile_44(&self) -> bool { true }
|
||||||
|
fn player_left_pressed(&self) -> bool { false }
|
||||||
|
fn player_right_pressed(&self) -> bool { false }
|
||||||
|
|
||||||
fn judge_hit_block(&mut self, state: &mut SharedGameState, x: isize, y: isize) {
|
fn judge_hit_block(&mut self, state: &mut SharedGameState, x: isize, y: isize) {
|
||||||
let bounds_x = if self.is_player() { 5 } else { 8 };
|
let bounds_x = if self.is_player() { 5 } else { 8 };
|
||||||
|
@ -53,7 +55,7 @@ pub trait PhysicalEntity {
|
||||||
self.set_vel_x(-0x180);
|
self.set_vel_x(-0x180);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !state.key_state.left() && self.vel_x() < 0 {
|
if !self.player_left_pressed() && self.vel_x() < 0 {
|
||||||
self.set_vel_x(0);
|
self.set_vel_x(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,7 +75,7 @@ pub trait PhysicalEntity {
|
||||||
self.set_vel_x(0x180);
|
self.set_vel_x(0x180);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !state.key_state.right() && self.vel_x() > 0 {
|
if !self.player_right_pressed() && self.vel_x() > 0 {
|
||||||
self.set_vel_x(0);
|
self.set_vel_x(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
use std::clone::Clone;
|
use std::clone::Clone;
|
||||||
|
|
||||||
|
use ggez::{Context, GameResult};
|
||||||
use num_derive::FromPrimitive;
|
use num_derive::FromPrimitive;
|
||||||
use num_traits::clamp;
|
use num_traits::clamp;
|
||||||
use num_traits::FromPrimitive;
|
|
||||||
|
|
||||||
use crate::caret::CaretType;
|
use crate::caret::CaretType;
|
||||||
use crate::common::{Condition, Direction, Equipment, Flag, interpolate_fix9_scale, Rect};
|
use crate::common::{Condition, Direction, Equipment, Flag, interpolate_fix9_scale, Rect};
|
||||||
use crate::entity::GameEntity;
|
use crate::entity::GameEntity;
|
||||||
use crate::frame::Frame;
|
use crate::frame::Frame;
|
||||||
use ggez::{Context, GameResult};
|
use crate::input::dummy_player_controller::DummyPlayerController;
|
||||||
|
use crate::input::player_controller::PlayerController;
|
||||||
use crate::npc::NPCMap;
|
use crate::npc::NPCMap;
|
||||||
use crate::shared_game_state::SharedGameState;
|
use crate::shared_game_state::SharedGameState;
|
||||||
|
|
||||||
|
mod player_hit;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum ControlMode {
|
pub enum ControlMode {
|
||||||
|
@ -31,6 +34,19 @@ pub enum PlayerAppearance {
|
||||||
Curly,
|
Curly,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub enum TargetPlayer {
|
||||||
|
Player1,
|
||||||
|
Player2,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TargetPlayer {
|
||||||
|
#[inline]
|
||||||
|
pub fn index(self) -> usize {
|
||||||
|
self as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
pub x: isize,
|
pub x: isize,
|
||||||
|
@ -61,6 +77,7 @@ pub struct Player {
|
||||||
pub air_counter: u16,
|
pub air_counter: u16,
|
||||||
pub air: u16,
|
pub air: u16,
|
||||||
pub appearance: PlayerAppearance,
|
pub appearance: PlayerAppearance,
|
||||||
|
pub controller: Box<dyn PlayerController>,
|
||||||
weapon_offset_y: i8,
|
weapon_offset_y: i8,
|
||||||
index_x: isize,
|
index_x: isize,
|
||||||
index_y: isize,
|
index_y: isize,
|
||||||
|
@ -113,6 +130,7 @@ impl Player {
|
||||||
air_counter: 0,
|
air_counter: 0,
|
||||||
air: 0,
|
air: 0,
|
||||||
appearance: PlayerAppearance::Quote,
|
appearance: PlayerAppearance::Quote,
|
||||||
|
controller: Box::new(DummyPlayerController::new()),
|
||||||
bubble: 0,
|
bubble: 0,
|
||||||
damage_counter: 0,
|
damage_counter: 0,
|
||||||
damage_taken: 0,
|
damage_taken: 0,
|
||||||
|
@ -181,23 +199,33 @@ impl Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.control_flags.control_enabled() {
|
if state.control_flags.control_enabled() {
|
||||||
if state.key_trigger.only_down() && state.key_state.only_down() && !self.cond.interacted() && !state.control_flags.interactions_disabled() {
|
let trigger_only_down = self.controller.trigger_down()
|
||||||
|
&& !self.controller.trigger_up()
|
||||||
|
&& !self.controller.trigger_left()
|
||||||
|
&& !self.controller.trigger_right();
|
||||||
|
|
||||||
|
let only_down = self.controller.move_down()
|
||||||
|
&& !self.controller.move_up()
|
||||||
|
&& !self.controller.move_left()
|
||||||
|
&& !self.controller.move_right();
|
||||||
|
|
||||||
|
if trigger_only_down && only_down && !self.cond.interacted() && !state.control_flags.interactions_disabled() {
|
||||||
self.cond.set_interacted(true);
|
self.cond.set_interacted(true);
|
||||||
self.question = true;
|
self.question = true;
|
||||||
} else {
|
} else {
|
||||||
if state.key_state.left() && self.vel_x > -physics.max_dash {
|
if self.controller.move_left() && self.vel_x > -physics.max_dash {
|
||||||
self.vel_x -= physics.dash_ground;
|
self.vel_x -= physics.dash_ground;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_state.right() && self.vel_x < physics.max_dash {
|
if self.controller.move_right() && self.vel_x < physics.max_dash {
|
||||||
self.vel_x += physics.dash_ground;
|
self.vel_x += physics.dash_ground;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_state.left() {
|
if self.controller.move_left() {
|
||||||
self.direction = Direction::Left;
|
self.direction = Direction::Left;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_state.right() {
|
if self.controller.move_right() {
|
||||||
self.direction = Direction::Right;
|
self.direction = Direction::Right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,7 +249,7 @@ impl Player {
|
||||||
}
|
}
|
||||||
} else { // air movement
|
} else { // air movement
|
||||||
if state.control_flags.control_enabled() {
|
if state.control_flags.control_enabled() {
|
||||||
if state.key_trigger.jump() && self.booster_fuel != 0 {
|
if self.controller.trigger_jump() && self.booster_fuel != 0 {
|
||||||
if self.equip.has_booster_0_8() {
|
if self.equip.has_booster_0_8() {
|
||||||
self.booster_switch = 1;
|
self.booster_switch = 1;
|
||||||
|
|
||||||
|
@ -229,19 +257,19 @@ impl Player {
|
||||||
self.vel_y /= 2;
|
self.vel_y /= 2;
|
||||||
}
|
}
|
||||||
} else if state.settings.infinite_booster || self.equip.has_booster_2_0() {
|
} else if state.settings.infinite_booster || self.equip.has_booster_2_0() {
|
||||||
if state.key_state.up() {
|
if self.controller.move_up() {
|
||||||
self.booster_switch = 2;
|
self.booster_switch = 2;
|
||||||
self.vel_x = 0;
|
self.vel_x = 0;
|
||||||
self.vel_y = state.constants.booster.b2_0_up;
|
self.vel_y = state.constants.booster.b2_0_up;
|
||||||
} else if state.key_state.left() {
|
} else if self.controller.move_left() {
|
||||||
self.booster_switch = 1;
|
self.booster_switch = 1;
|
||||||
self.vel_x = state.constants.booster.b2_0_left;
|
self.vel_x = state.constants.booster.b2_0_left;
|
||||||
self.vel_y = 0;
|
self.vel_y = 0;
|
||||||
} else if state.key_state.right() {
|
} else if self.controller.move_right() {
|
||||||
self.booster_switch = 1;
|
self.booster_switch = 1;
|
||||||
self.vel_x = state.constants.booster.b2_0_right;
|
self.vel_x = state.constants.booster.b2_0_right;
|
||||||
self.vel_y = 0;
|
self.vel_y = 0;
|
||||||
} else if state.key_state.down() {
|
} else if self.controller.move_down() {
|
||||||
self.booster_switch = 3;
|
self.booster_switch = 3;
|
||||||
self.vel_x = 0;
|
self.vel_x = 0;
|
||||||
self.vel_y = state.constants.booster.b2_0_down;
|
self.vel_y = state.constants.booster.b2_0_down;
|
||||||
|
@ -253,25 +281,25 @@ impl Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_state.left() && self.vel_x > -physics.max_dash {
|
if self.controller.move_left() && self.vel_x > -physics.max_dash {
|
||||||
self.vel_x -= physics.dash_air;
|
self.vel_x -= physics.dash_air;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_state.right() && self.vel_x < physics.max_dash {
|
if self.controller.move_right() && self.vel_x < physics.max_dash {
|
||||||
self.vel_x += physics.dash_air;
|
self.vel_x += physics.dash_air;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_state.left() {
|
if self.controller.look_left() {
|
||||||
self.direction = Direction::Left;
|
self.direction = Direction::Left;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_state.right() {
|
if self.controller.look_right() {
|
||||||
self.direction = Direction::Right;
|
self.direction = Direction::Right;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.settings.infinite_booster || self.equip.has_booster_2_0()) && self.booster_switch != 0
|
if (state.settings.infinite_booster || self.equip.has_booster_2_0()) && self.booster_switch != 0
|
||||||
&& (!state.key_state.jump() || self.booster_fuel == 0) {
|
&& (!self.controller.jump() || self.booster_fuel == 0) {
|
||||||
match self.booster_switch {
|
match self.booster_switch {
|
||||||
1 => { self.vel_x /= 2 }
|
1 => { self.vel_x /= 2 }
|
||||||
2 => { self.vel_y /= 2 }
|
2 => { self.vel_y /= 2 }
|
||||||
|
@ -279,24 +307,31 @@ impl Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.booster_fuel == 0 || !state.key_state.jump() {
|
if self.booster_fuel == 0 || !self.controller.jump() {
|
||||||
self.booster_switch = 0;
|
self.booster_switch = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// jumping
|
// jumping
|
||||||
if state.control_flags.control_enabled() {
|
if state.control_flags.control_enabled() {
|
||||||
self.up = state.key_state.up();
|
self.up = self.controller.move_up();
|
||||||
self.down = state.key_state.down() && !self.flags.hit_bottom_wall();
|
self.down = self.controller.move_down() && !self.flags.hit_bottom_wall();
|
||||||
|
|
||||||
if state.key_trigger.jump() && (self.flags.hit_bottom_wall() || self.flags.hit_right_slope() || self.flags.hit_left_slope()) && !self.flags.force_up() {
|
if self.controller.trigger_jump() && (self.flags.hit_bottom_wall()
|
||||||
|
|| self.flags.hit_right_slope()
|
||||||
|
|| self.flags.hit_left_slope())
|
||||||
|
&& !self.flags.force_up() {
|
||||||
self.vel_y = -physics.jump;
|
self.vel_y = -physics.jump;
|
||||||
state.sound_manager.play_sfx(15);
|
state.sound_manager.play_sfx(15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop interacting when moved
|
// stop interacting when moved
|
||||||
if state.control_flags.control_enabled() && (state.key_state.left() || state.key_state.right() || state.key_state.up() || state.key_state.jump() || state.key_state.fire()) {
|
if state.control_flags.control_enabled() && (self.controller.move_left()
|
||||||
|
|| self.controller.move_right()
|
||||||
|
|| self.controller.move_up()
|
||||||
|
|| self.controller.jump()
|
||||||
|
|| self.controller.shoot()) {
|
||||||
self.cond.set_interacted(false);
|
self.cond.set_interacted(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +369,7 @@ impl Player {
|
||||||
self.vel_x += 0x20; // 0.1fix9
|
self.vel_x += 0x20; // 0.1fix9
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_trigger.jump() || self.booster_fuel % 3 == 1 {
|
if self.controller.trigger_jump() || self.booster_fuel % 3 == 1 {
|
||||||
if self.direction == Direction::Left || self.direction == Direction::Right {
|
if self.direction == Direction::Left || self.direction == Direction::Right {
|
||||||
state.create_caret(self.x + 0x400, self.y + 0x400, CaretType::Exhaust, self.direction.opposite());
|
state.create_caret(self.x + 0x400, self.y + 0x400, CaretType::Exhaust, self.direction.opposite());
|
||||||
}
|
}
|
||||||
|
@ -344,12 +379,12 @@ impl Player {
|
||||||
2 => {
|
2 => {
|
||||||
self.vel_y -= 0x20;
|
self.vel_y -= 0x20;
|
||||||
|
|
||||||
if state.key_trigger.jump() || self.booster_fuel % 3 == 1 {
|
if self.controller.trigger_jump() || self.booster_fuel % 3 == 1 {
|
||||||
state.create_caret(self.x, self.y + 6 * 0x200, CaretType::Exhaust, Direction::Bottom);
|
state.create_caret(self.x, self.y + 6 * 0x200, CaretType::Exhaust, Direction::Bottom);
|
||||||
state.sound_manager.play_sfx(113);
|
state.sound_manager.play_sfx(113);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
3 if state.key_trigger.jump() || self.booster_fuel % 3 == 1 => {
|
3 if self.controller.trigger_jump() || self.booster_fuel % 3 == 1 => {
|
||||||
state.create_caret(self.x, self.y + 6 * 0x200, CaretType::Exhaust, Direction::Up);
|
state.create_caret(self.x, self.y + 6 * 0x200, CaretType::Exhaust, Direction::Up);
|
||||||
state.sound_manager.play_sfx(113);
|
state.sound_manager.play_sfx(113);
|
||||||
}
|
}
|
||||||
|
@ -369,13 +404,13 @@ impl Player {
|
||||||
if self.flags.hit_top_wall() {
|
if self.flags.hit_top_wall() {
|
||||||
self.vel_y = 0x200; // 1.0fix9
|
self.vel_y = 0x200; // 1.0fix9
|
||||||
}
|
}
|
||||||
} else if self.vel_y < 0 && state.control_flags.control_enabled() && state.key_state.jump() {
|
} else if self.vel_y < 0 && state.control_flags.control_enabled() && self.controller.jump() {
|
||||||
self.vel_y += physics.gravity_air;
|
self.vel_y += physics.gravity_air;
|
||||||
} else {
|
} else {
|
||||||
self.vel_y += physics.gravity_ground;
|
self.vel_y += physics.gravity_ground;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !state.control_flags.control_enabled() || !state.key_trigger.jump() {
|
if !state.control_flags.control_enabled() || !self.controller.trigger_jump() {
|
||||||
if self.flags.hit_right_slope() && self.vel_x < 0 {
|
if self.flags.hit_right_slope() && self.vel_x < 0 {
|
||||||
self.vel_y = -self.vel_x;
|
self.vel_y = -self.vel_x;
|
||||||
}
|
}
|
||||||
|
@ -442,12 +477,12 @@ impl Player {
|
||||||
// camera
|
// camera
|
||||||
self.index_x = clamp(self.index_x + self.direction.vector_x() * 0x200, -0x8000, 0x8000);
|
self.index_x = clamp(self.index_x + self.direction.vector_x() * 0x200, -0x8000, 0x8000);
|
||||||
|
|
||||||
if state.control_flags.control_enabled() && state.key_state.up() {
|
if state.control_flags.control_enabled() && self.controller.move_up() {
|
||||||
self.index_y -= 0x200; // 1.0fix9
|
self.index_y -= 0x200; // 1.0fix9
|
||||||
if self.index_y < -0x8000 { // -64.0fix9
|
if self.index_y < -0x8000 { // -64.0fix9
|
||||||
self.index_y = -0x8000;
|
self.index_y = -0x8000;
|
||||||
}
|
}
|
||||||
} else if state.control_flags.control_enabled() && state.key_state.down() {
|
} else if state.control_flags.control_enabled() && self.controller.move_down() {
|
||||||
self.index_y += 0x200; // 1.0fix9
|
self.index_y += 0x200; // 1.0fix9
|
||||||
if self.index_y > 0x8000 { // -64.0fix9
|
if self.index_y > 0x8000 { // -64.0fix9
|
||||||
self.index_y = 0x8000;
|
self.index_y = 0x8000;
|
||||||
|
@ -487,7 +522,7 @@ impl Player {
|
||||||
if self.flags.hit_bottom_wall() {
|
if self.flags.hit_bottom_wall() {
|
||||||
if self.cond.interacted() {
|
if self.cond.interacted() {
|
||||||
self.anim_num = 11;
|
self.anim_num = 11;
|
||||||
} else if state.control_flags.control_enabled() && state.key_state.up() && (state.key_state.left() || state.key_state.right()) {
|
} else if state.control_flags.control_enabled() && self.controller.move_up() && (self.controller.move_left() || self.controller.move_right()) {
|
||||||
self.cond.set_fallen(true);
|
self.cond.set_fallen(true);
|
||||||
|
|
||||||
self.anim_counter += 1;
|
self.anim_counter += 1;
|
||||||
|
@ -503,7 +538,7 @@ impl Player {
|
||||||
if self.anim_num > 9 || self.anim_num < 6 {
|
if self.anim_num > 9 || self.anim_num < 6 {
|
||||||
self.anim_num = 6;
|
self.anim_num = 6;
|
||||||
}
|
}
|
||||||
} else if state.control_flags.control_enabled() && (state.key_state.left() || state.key_state.right()) {
|
} else if state.control_flags.control_enabled() && (self.controller.move_left() || self.controller.move_right()) {
|
||||||
self.cond.set_fallen(true);
|
self.cond.set_fallen(true);
|
||||||
|
|
||||||
self.anim_counter += 1;
|
self.anim_counter += 1;
|
||||||
|
@ -519,7 +554,7 @@ impl Player {
|
||||||
if self.anim_num > 4 || self.anim_num < 1 {
|
if self.anim_num > 4 || self.anim_num < 1 {
|
||||||
self.anim_num = 1;
|
self.anim_num = 1;
|
||||||
}
|
}
|
||||||
} else if state.control_flags.control_enabled() && state.key_state.up() {
|
} else if state.control_flags.control_enabled() && self.controller.move_up() {
|
||||||
if self.cond.fallen() {
|
if self.cond.fallen() {
|
||||||
state.sound_manager.play_sfx(24);
|
state.sound_manager.play_sfx(24);
|
||||||
}
|
}
|
||||||
|
@ -534,9 +569,9 @@ impl Player {
|
||||||
self.cond.set_fallen(false);
|
self.cond.set_fallen(false);
|
||||||
self.anim_num = 0;
|
self.anim_num = 0;
|
||||||
}
|
}
|
||||||
} else if state.key_state.up() {
|
} else if self.controller.look_up() {
|
||||||
self.anim_num = 6;
|
self.anim_num = 6;
|
||||||
} else if state.key_state.down() {
|
} else if self.controller.look_down() {
|
||||||
self.anim_num = 10;
|
self.anim_num = 10;
|
||||||
} else {
|
} else {
|
||||||
self.anim_num = if self.vel_y > 0 { 1 } else { 3 };
|
self.anim_num = if self.vel_y > 0 { 1 } else { 3 };
|
|
@ -79,6 +79,14 @@ impl PhysicalEntity for Player {
|
||||||
fn is_player(&self) -> bool {
|
fn is_player(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn player_left_pressed(&self) -> bool {
|
||||||
|
self.controller.move_left()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn player_right_pressed(&self) -> bool {
|
||||||
|
self.controller.move_right()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
|
@ -53,14 +53,14 @@ impl GameProfile {
|
||||||
|
|
||||||
state.sound_manager.play_song(self.current_song as usize, &state.constants, ctx);
|
state.sound_manager.play_song(self.current_song as usize, &state.constants, ctx);
|
||||||
|
|
||||||
game_scene.inventory.current_weapon = self.current_weapon as u16;
|
game_scene.inventory_player1.current_weapon = self.current_weapon as u16;
|
||||||
game_scene.inventory.current_item = self.current_item as u16;
|
game_scene.inventory_player1.current_item = self.current_item as u16;
|
||||||
for weapon in self.weapon_data.iter() {
|
for weapon in self.weapon_data.iter() {
|
||||||
if weapon.weapon_id == 0 { continue; }
|
if weapon.weapon_id == 0 { continue; }
|
||||||
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon.weapon_id as u8);
|
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon.weapon_id as u8);
|
||||||
|
|
||||||
if let Some(wtype) = weapon_type {
|
if let Some(wtype) = weapon_type {
|
||||||
let w = game_scene.inventory.add_weapon(wtype, weapon.max_ammo as u16);
|
let w = game_scene.inventory_player1.add_weapon(wtype, weapon.max_ammo as u16);
|
||||||
w.ammo = weapon.ammo as u16;
|
w.ammo = weapon.ammo as u16;
|
||||||
w.level = match weapon.level {
|
w.level = match weapon.level {
|
||||||
2 => { WeaponLevel::Level2 }
|
2 => { WeaponLevel::Level2 }
|
||||||
|
@ -74,7 +74,7 @@ impl GameProfile {
|
||||||
for item in self.items.iter().copied() {
|
for item in self.items.iter().copied() {
|
||||||
if item == 0 { break; }
|
if item == 0 { break; }
|
||||||
|
|
||||||
game_scene.inventory.add_item(item as u16);
|
game_scene.inventory_player1.add_item(item as u16);
|
||||||
}
|
}
|
||||||
|
|
||||||
for slot in self.teleporter_slots.iter() {
|
for slot in self.teleporter_slots.iter() {
|
||||||
|
@ -94,32 +94,32 @@ impl GameProfile {
|
||||||
if flags & 0b10000000 != 0 { state.game_flags.set(idx * 8 + 7, true); }
|
if flags & 0b10000000 != 0 { state.game_flags.set(idx * 8 + 7, true); }
|
||||||
}
|
}
|
||||||
|
|
||||||
game_scene.player.equip.0 = self.equipment as u16;
|
game_scene.player1.equip.0 = self.equipment as u16;
|
||||||
game_scene.player.cond.0 = 0x80;
|
game_scene.player1.cond.0 = 0x80;
|
||||||
|
|
||||||
game_scene.player.x = self.pos_x as isize;
|
game_scene.player1.x = self.pos_x as isize;
|
||||||
game_scene.player.y = self.pos_y as isize;
|
game_scene.player1.y = self.pos_y as isize;
|
||||||
|
|
||||||
game_scene.player.control_mode = if self.control_mode == 1 { ControlMode::IronHead } else { ControlMode::Normal };
|
game_scene.player1.control_mode = if self.control_mode == 1 { ControlMode::IronHead } else { ControlMode::Normal };
|
||||||
game_scene.player.direction = self.direction;
|
game_scene.player1.direction = self.direction;
|
||||||
game_scene.player.life = self.life;
|
game_scene.player1.life = self.life;
|
||||||
game_scene.player.max_life = self.max_life;
|
game_scene.player1.max_life = self.max_life;
|
||||||
game_scene.player.stars = clamp(self.stars, 0, 3) as u8;
|
game_scene.player1.stars = clamp(self.stars, 0, 3) as u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dump(state: &mut SharedGameState, game_scene: &mut GameScene) -> GameProfile {
|
pub fn dump(state: &mut SharedGameState, game_scene: &mut GameScene) -> GameProfile {
|
||||||
let current_map = game_scene.stage_id as u32;
|
let current_map = game_scene.stage_id as u32;
|
||||||
let current_song = state.sound_manager.current_song() as u32;
|
let current_song = state.sound_manager.current_song() as u32;
|
||||||
let pos_x = game_scene.player.x as i32;
|
let pos_x = game_scene.player1.x as i32;
|
||||||
let pos_y = game_scene.player.y as i32;
|
let pos_y = game_scene.player1.y as i32;
|
||||||
let direction = game_scene.player.direction;
|
let direction = game_scene.player1.direction;
|
||||||
let max_life = game_scene.player.max_life;
|
let max_life = game_scene.player1.max_life;
|
||||||
let stars = game_scene.player.stars as u16;
|
let stars = game_scene.player1.stars as u16;
|
||||||
let life = game_scene.player.life;
|
let life = game_scene.player1.life;
|
||||||
let current_weapon = game_scene.inventory.current_weapon as u32;
|
let current_weapon = game_scene.inventory_player1.current_weapon as u32;
|
||||||
let current_item = game_scene.inventory.current_item as u32;
|
let current_item = game_scene.inventory_player1.current_item as u32;
|
||||||
let equipment = game_scene.player.equip.0 as u32;
|
let equipment = game_scene.player1.equip.0 as u32;
|
||||||
let control_mode = game_scene.player.control_mode as u32;
|
let control_mode = game_scene.player1.control_mode as u32;
|
||||||
let counter = 0; // TODO
|
let counter = 0; // TODO
|
||||||
let mut weapon_data = [
|
let mut weapon_data = [
|
||||||
WeaponData { weapon_id: 0, level: 0, exp: 0, max_ammo: 0, ammo: 0 },
|
WeaponData { weapon_id: 0, level: 0, exp: 0, max_ammo: 0, ammo: 0 },
|
||||||
|
@ -144,7 +144,7 @@ impl GameProfile {
|
||||||
];
|
];
|
||||||
|
|
||||||
for (idx, weap) in weapon_data.iter_mut().enumerate() {
|
for (idx, weap) in weapon_data.iter_mut().enumerate() {
|
||||||
if let Some(weapon) = game_scene.inventory.get_weapon(idx) {
|
if let Some(weapon) = game_scene.inventory_player1.get_weapon(idx) {
|
||||||
weap.weapon_id = weapon.wtype as u32;
|
weap.weapon_id = weapon.wtype as u32;
|
||||||
weap.level = weapon.level as u32;
|
weap.level = weapon.level as u32;
|
||||||
weap.exp = weapon.experience as u32;
|
weap.exp = weapon.experience as u32;
|
||||||
|
@ -154,7 +154,7 @@ impl GameProfile {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (idx, item) in items.iter_mut().enumerate() {
|
for (idx, item) in items.iter_mut().enumerate() {
|
||||||
if let Some(sitem) = game_scene.inventory.get_item_idx(idx) {
|
if let Some(sitem) = game_scene.inventory_player1.get_item_idx(idx) {
|
||||||
*item = sitem.0 as u32;
|
*item = sitem.0 as u32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
|
||||||
|
/// Deterministic XorShift-based random number generator
|
||||||
pub struct RNG(Cell<(u64, u64, u64, u64)>);
|
pub struct RNG(Cell<(u64, u64, u64, u64)>);
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -48,6 +49,14 @@ impl RNG {
|
||||||
self.next_u64() as u32
|
self.next_u64() as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dump_state(&self) -> (u64, u64, u64, u64) {
|
||||||
|
self.0.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_state(&mut self, saved_state: (u64, u64, u64, u64)) {
|
||||||
|
self.0.replace(saved_state);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn range(&self, range: std::ops::Range<i32>) -> i32 {
|
pub fn range(&self, range: std::ops::Range<i32>) -> i32 {
|
||||||
range.start.wrapping_add((self.next_u32() >> 2) as i32 % (range.end.wrapping_sub(range.start).wrapping_add(1)))
|
range.start.wrapping_add((self.next_u32() >> 2) as i32 % (range.end.wrapping_sub(range.start).wrapping_add(1)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::bullet::BulletManager;
|
||||||
use crate::caret::CaretType;
|
use crate::caret::CaretType;
|
||||||
use crate::common::{Direction, FadeDirection, FadeState, fix9_scale, interpolate_fix9_scale, Rect};
|
use crate::common::{Direction, FadeDirection, FadeState, fix9_scale, interpolate_fix9_scale, Rect};
|
||||||
use crate::components::boss_life_bar::BossLifeBar;
|
use crate::components::boss_life_bar::BossLifeBar;
|
||||||
|
use crate::components::stage_select::StageSelect;
|
||||||
use crate::entity::GameEntity;
|
use crate::entity::GameEntity;
|
||||||
use crate::frame::{Frame, UpdateTarget};
|
use crate::frame::{Frame, UpdateTarget};
|
||||||
use crate::inventory::{Inventory, TakeExperienceResult};
|
use crate::inventory::{Inventory, TakeExperienceResult};
|
||||||
|
@ -30,13 +31,15 @@ pub struct GameScene {
|
||||||
pub tick: usize,
|
pub tick: usize,
|
||||||
pub stage: Stage,
|
pub stage: Stage,
|
||||||
pub boss_life_bar: BossLifeBar,
|
pub boss_life_bar: BossLifeBar,
|
||||||
|
pub stage_select: StageSelect,
|
||||||
pub frame: Frame,
|
pub frame: Frame,
|
||||||
pub player: Player,
|
pub player1: Player,
|
||||||
pub inventory: Inventory,
|
pub player2: Player,
|
||||||
|
pub inventory_player1: Inventory,
|
||||||
|
pub inventory_player2: Inventory,
|
||||||
pub stage_id: usize,
|
pub stage_id: usize,
|
||||||
pub npc_map: NPCMap,
|
pub npc_map: NPCMap,
|
||||||
pub bullet_manager: BulletManager,
|
pub bullet_manager: BulletManager,
|
||||||
pub current_teleport_slot: u8,
|
|
||||||
pub intro_mode: bool,
|
pub intro_mode: bool,
|
||||||
water_visible: bool,
|
water_visible: bool,
|
||||||
tex_background_name: String,
|
tex_background_name: String,
|
||||||
|
@ -44,7 +47,6 @@ pub struct GameScene {
|
||||||
life_bar: u16,
|
life_bar: u16,
|
||||||
life_bar_counter: u16,
|
life_bar_counter: u16,
|
||||||
map_name_counter: u16,
|
map_name_counter: u16,
|
||||||
stage_select_text_y_pos: usize,
|
|
||||||
weapon_x_pos: isize,
|
weapon_x_pos: isize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,9 +79,12 @@ impl GameScene {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
tick: 0,
|
tick: 0,
|
||||||
stage,
|
stage,
|
||||||
player: Player::new(state),
|
player1: Player::new(state),
|
||||||
inventory: Inventory::new(),
|
player2: Player::new(state),
|
||||||
|
inventory_player1: Inventory::new(),
|
||||||
|
inventory_player2: Inventory::new(),
|
||||||
boss_life_bar: BossLifeBar::new(),
|
boss_life_bar: BossLifeBar::new(),
|
||||||
|
stage_select: StageSelect::new(),
|
||||||
frame: Frame {
|
frame: Frame {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
|
@ -93,7 +98,6 @@ impl GameScene {
|
||||||
stage_id: id,
|
stage_id: id,
|
||||||
npc_map: NPCMap::new(),
|
npc_map: NPCMap::new(),
|
||||||
bullet_manager: BulletManager::new(),
|
bullet_manager: BulletManager::new(),
|
||||||
current_teleport_slot: 0,
|
|
||||||
intro_mode: false,
|
intro_mode: false,
|
||||||
water_visible: true,
|
water_visible: true,
|
||||||
tex_background_name,
|
tex_background_name,
|
||||||
|
@ -101,7 +105,6 @@ impl GameScene {
|
||||||
life_bar: 0,
|
life_bar: 0,
|
||||||
life_bar_counter: 0,
|
life_bar_counter: 0,
|
||||||
map_name_counter: 0,
|
map_name_counter: 0,
|
||||||
stage_select_text_y_pos: 54,
|
|
||||||
weapon_x_pos: 16,
|
weapon_x_pos: 16,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -127,8 +130,8 @@ impl GameScene {
|
||||||
fn draw_hud(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
fn draw_hud(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||||
// none
|
// none
|
||||||
let weap_x = self.weapon_x_pos as f32;
|
let weap_x = self.weapon_x_pos as f32;
|
||||||
let (ammo, max_ammo) = self.inventory.get_current_ammo();
|
let (ammo, max_ammo) = self.inventory_player1.get_current_ammo();
|
||||||
let (xp, max_xp, max_level) = self.inventory.get_current_max_exp(&state.constants);
|
let (xp, max_xp, max_level) = self.inventory_player1.get_current_max_exp(&state.constants);
|
||||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;
|
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;
|
||||||
|
|
||||||
if max_ammo == 0 {
|
if max_ammo == 0 {
|
||||||
|
@ -159,20 +162,20 @@ impl GameScene {
|
||||||
&Rect::new_size(0, 80, bar_width, 8));
|
&Rect::new_size(0, 80, bar_width, 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.player.max_life != 0 {
|
if self.player1.max_life != 0 {
|
||||||
// life box
|
// life box
|
||||||
batch.add_rect(16.0, 40.0,
|
batch.add_rect(16.0, 40.0,
|
||||||
&Rect::new_size(0, 40, 64, 8));
|
&Rect::new_size(0, 40, 64, 8));
|
||||||
// yellow bar
|
// yellow bar
|
||||||
batch.add_rect(40.0, 40.0,
|
batch.add_rect(40.0, 40.0,
|
||||||
&Rect::new_size(0, 32, (self.life_bar * 40) / self.player.max_life, 8));
|
&Rect::new_size(0, 32, (self.life_bar * 40) / self.player1.max_life, 8));
|
||||||
// life
|
// life
|
||||||
batch.add_rect(40.0, 40.0,
|
batch.add_rect(40.0, 40.0,
|
||||||
&Rect::new_size(0, 24, (self.player.life * 40) / self.player.max_life, 8));
|
&Rect::new_size(0, 24, (self.player1.life * 40) / self.player1.max_life, 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.player.air_counter > 0 {
|
if self.player1.air_counter > 0 {
|
||||||
let rect = if self.player.air % 30 > 10 {
|
let rect = if self.player1.air % 30 > 10 {
|
||||||
Rect::new_size(112, 72, 32, 8)
|
Rect::new_size(112, 72, 32, 8)
|
||||||
} else {
|
} else {
|
||||||
Rect::new_size(112, 80, 32, 8)
|
Rect::new_size(112, 80, 32, 8)
|
||||||
|
@ -185,9 +188,9 @@ impl GameScene {
|
||||||
batch.draw(ctx)?;
|
batch.draw(ctx)?;
|
||||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "ArmsImage")?;
|
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "ArmsImage")?;
|
||||||
|
|
||||||
let weapon_count = self.inventory.get_weapon_count();
|
let weapon_count = self.inventory_player1.get_weapon_count();
|
||||||
if weapon_count != 0 {
|
if weapon_count != 0 {
|
||||||
let current_weapon = self.inventory.get_current_weapon_idx() as isize;
|
let current_weapon = self.inventory_player1.get_current_weapon_idx() as isize;
|
||||||
let mut rect = Rect::new(0, 0, 0, 16);
|
let mut rect = Rect::new(0, 0, 0, 16);
|
||||||
|
|
||||||
for a in 0..weapon_count {
|
for a in 0..weapon_count {
|
||||||
|
@ -205,7 +208,7 @@ impl GameScene {
|
||||||
pos_x -= 48.0;
|
pos_x -= 48.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(weapon) = self.inventory.get_weapon(a) {
|
if let Some(weapon) = self.inventory_player1.get_weapon(a) {
|
||||||
rect.left = weapon.wtype as u16 * 16;
|
rect.left = weapon.wtype as u16 * 16;
|
||||||
rect.right = rect.left + 16;
|
rect.right = rect.left + 16;
|
||||||
batch.add_rect(pos_x, 16.0, &rect);
|
batch.add_rect(pos_x, 16.0, &rect);
|
||||||
|
@ -215,17 +218,17 @@ impl GameScene {
|
||||||
|
|
||||||
batch.draw(ctx)?;
|
batch.draw(ctx)?;
|
||||||
|
|
||||||
if self.player.air_counter > 0 && self.player.air_counter % 6 < 4 {
|
if self.player1.air_counter > 0 && self.player1.air_counter % 6 < 4 {
|
||||||
self.draw_number((state.canvas_size.0 / 2.0).floor() + 8.0,
|
self.draw_number((state.canvas_size.0 / 2.0).floor() + 8.0,
|
||||||
(state.canvas_size.1 / 2.0).floor(),
|
(state.canvas_size.1 / 2.0).floor(),
|
||||||
(self.player.air / 10) as usize, Alignment::Left, state, ctx)?;
|
(self.player1.air / 10) as usize, Alignment::Left, state, ctx)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if max_ammo != 0 {
|
if max_ammo != 0 {
|
||||||
self.draw_number(weap_x + 64.0, 16.0, ammo as usize, Alignment::Right, state, ctx)?;
|
self.draw_number(weap_x + 64.0, 16.0, ammo as usize, Alignment::Right, state, ctx)?;
|
||||||
self.draw_number(weap_x + 64.0, 24.0, max_ammo as usize, Alignment::Right, state, ctx)?;
|
self.draw_number(weap_x + 64.0, 24.0, max_ammo as usize, Alignment::Right, state, ctx)?;
|
||||||
}
|
}
|
||||||
self.draw_number(weap_x + 24.0, 32.0, self.inventory.get_current_level() as usize, Alignment::Right, state, ctx)?;
|
self.draw_number(weap_x + 24.0, 32.0, self.inventory_player1.get_current_level() as usize, Alignment::Right, state, ctx)?;
|
||||||
self.draw_number(40.0, 40.0, self.life_bar as usize, Alignment::Right, state, ctx)?;
|
self.draw_number(40.0, 40.0, self.life_bar as usize, Alignment::Right, state, ctx)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -614,9 +617,9 @@ impl GameScene {
|
||||||
let scale = state.scale;
|
let scale = state.scale;
|
||||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "builtin/lightmap/spot")?;
|
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "builtin/lightmap/spot")?;
|
||||||
|
|
||||||
if !self.player.cond.hidden() && self.inventory.get_current_weapon().is_some() {
|
if !self.player1.cond.hidden() && self.inventory_player1.get_current_weapon().is_some() {
|
||||||
self.draw_light(fix9_scale(self.player.x - self.frame.x, scale),
|
self.draw_light(fix9_scale(self.player1.x - self.frame.x, scale),
|
||||||
fix9_scale(self.player.y - self.frame.y, scale),
|
fix9_scale(self.player1.y - self.frame.y, scale),
|
||||||
4.0, (140, 140, 140), batch);
|
4.0, (140, 140, 140), batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -943,7 +946,7 @@ impl GameScene {
|
||||||
// todo show damage
|
// todo show damage
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.player.cond.alive() && npc.npc_flags.event_when_killed() {
|
if self.player1.cond.alive() && npc.npc_flags.event_when_killed() {
|
||||||
state.control_flags.set_tick_world(true);
|
state.control_flags.set_tick_world(true);
|
||||||
state.control_flags.set_interactions_disabled(true);
|
state.control_flags.set_interactions_disabled(true);
|
||||||
state.textscript_vm.start_script(npc.event_num);
|
state.textscript_vm.start_script(npc.event_num);
|
||||||
|
@ -1030,7 +1033,7 @@ impl GameScene {
|
||||||
if npc.life == 0 {
|
if npc.life == 0 {
|
||||||
npc.life = npc.id;
|
npc.life = npc.id;
|
||||||
|
|
||||||
if self.player.cond.alive() && npc.npc_flags.event_when_killed() {
|
if self.player1.cond.alive() && npc.npc_flags.event_when_killed() {
|
||||||
state.control_flags.set_tick_world(true);
|
state.control_flags.set_tick_world(true);
|
||||||
state.control_flags.set_interactions_disabled(true);
|
state.control_flags.set_interactions_disabled(true);
|
||||||
state.textscript_vm.start_script(npc.event_num);
|
state.textscript_vm.start_script(npc.event_num);
|
||||||
|
@ -1078,51 +1081,48 @@ impl GameScene {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dead_npcs.is_empty() {
|
if !dead_npcs.is_empty() {
|
||||||
let missile = self.inventory.has_weapon(WeaponType::MissileLauncher)
|
let missile = self.inventory_player1.has_weapon(WeaponType::MissileLauncher)
|
||||||
| self.inventory.has_weapon(WeaponType::SuperMissileLauncher);
|
|| self.inventory_player1.has_weapon(WeaponType::SuperMissileLauncher);
|
||||||
self.npc_map.process_dead_npcs(&dead_npcs, missile, &self.player, state);
|
self.npc_map.process_dead_npcs(&dead_npcs, missile, &self.player1, state);
|
||||||
self.npc_map.garbage_collect();
|
self.npc_map.garbage_collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tick_world(&mut self, state: &mut SharedGameState) -> GameResult {
|
fn tick_world(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||||
self.stage_select_text_y_pos = 54;
|
self.player1.current_weapon = {
|
||||||
self.current_teleport_slot = 0;
|
if let Some(weapon) = self.inventory_player1.get_current_weapon_mut() {
|
||||||
|
|
||||||
self.player.current_weapon = {
|
|
||||||
if let Some(weapon) = self.inventory.get_current_weapon_mut() {
|
|
||||||
weapon.wtype as u8
|
weapon.wtype as u8
|
||||||
} else {
|
} else {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.player.tick(state, ())?;
|
self.player1.tick(state, ())?;
|
||||||
|
|
||||||
if self.player.damage > 0 {
|
if self.player1.damage > 0 {
|
||||||
let xp_loss = self.player.damage * if self.player.equip.has_arms_barrier() { 1 } else { 2 };
|
let xp_loss = self.player1.damage * if self.player1.equip.has_arms_barrier() { 1 } else { 2 };
|
||||||
match self.inventory.take_xp(xp_loss, state) {
|
match self.inventory_player1.take_xp(xp_loss, state) {
|
||||||
TakeExperienceResult::LevelDown if self.player.life > 0 => {
|
TakeExperienceResult::LevelDown if self.player1.life > 0 => {
|
||||||
state.create_caret(self.player.x, self.player.y, CaretType::LevelUp, Direction::Right);
|
state.create_caret(self.player1.x, self.player1.y, CaretType::LevelUp, Direction::Right);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.player.damage = 0;
|
self.player1.damage = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for npc_cell in self.npc_map.npcs.values() {
|
for npc_cell in self.npc_map.npcs.values() {
|
||||||
let mut npc = npc_cell.borrow_mut();
|
let mut npc = npc_cell.borrow_mut();
|
||||||
|
|
||||||
if npc.cond.alive() {
|
if npc.cond.alive() {
|
||||||
npc.tick(state, (&mut self.player, &self.npc_map.npcs, &mut self.stage))?;
|
npc.tick(state, (&mut self.player1, &self.npc_map.npcs, &mut self.stage))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.npc_map.boss_map.tick(state, (&mut self.player, &self.npc_map.npcs, &mut self.stage))?;
|
self.npc_map.boss_map.tick(state, (&mut self.player1, &self.npc_map.npcs, &mut self.stage))?;
|
||||||
self.npc_map.process_npc_changes(&self.player, state);
|
self.npc_map.process_npc_changes(&self.player1, state);
|
||||||
self.npc_map.garbage_collect();
|
self.npc_map.garbage_collect();
|
||||||
|
|
||||||
self.player.tick_map_collisions(state, &mut self.stage);
|
self.player1.tick_map_collisions(state, &mut self.stage);
|
||||||
self.player.tick_npc_collisions(state, &mut self.npc_map, &mut self.inventory);
|
self.player1.tick_npc_collisions(state, &mut self.npc_map, &mut self.inventory_player1);
|
||||||
|
|
||||||
for npc_cell in self.npc_map.npcs.values() {
|
for npc_cell in self.npc_map.npcs.values() {
|
||||||
let mut npc = npc_cell.borrow_mut();
|
let mut npc = npc_cell.borrow_mut();
|
||||||
|
@ -1136,19 +1136,19 @@ impl GameScene {
|
||||||
npc.tick_map_collisions(state, &mut self.stage);
|
npc.tick_map_collisions(state, &mut self.stage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.npc_map.process_npc_changes(&self.player, state);
|
self.npc_map.process_npc_changes(&self.player1, state);
|
||||||
self.npc_map.garbage_collect();
|
self.npc_map.garbage_collect();
|
||||||
|
|
||||||
self.tick_npc_bullet_collissions(state);
|
self.tick_npc_bullet_collissions(state);
|
||||||
self.npc_map.process_npc_changes(&self.player, state);
|
self.npc_map.process_npc_changes(&self.player1, state);
|
||||||
|
|
||||||
self.bullet_manager.tick_bullets(state, &self.player, &mut self.stage);
|
self.bullet_manager.tick_bullets(state, &self.player1, &mut self.stage);
|
||||||
state.tick_carets();
|
state.tick_carets();
|
||||||
|
|
||||||
match self.frame.update_target {
|
match self.frame.update_target {
|
||||||
UpdateTarget::Player => {
|
UpdateTarget::Player => {
|
||||||
self.frame.target_x = self.player.target_x;
|
self.frame.target_x = self.player1.target_x;
|
||||||
self.frame.target_y = self.player.target_y;
|
self.frame.target_y = self.player1.target_y;
|
||||||
}
|
}
|
||||||
UpdateTarget::NPC(npc_id) => {
|
UpdateTarget::NPC(npc_id) => {
|
||||||
if let Some(npc_cell) = self.npc_map.npcs.get(&npc_id) {
|
if let Some(npc_cell) = self.npc_map.npcs.get(&npc_id) {
|
||||||
|
@ -1172,28 +1172,28 @@ impl GameScene {
|
||||||
self.frame.update(state, &self.stage);
|
self.frame.update(state, &self.stage);
|
||||||
|
|
||||||
if state.control_flags.control_enabled() {
|
if state.control_flags.control_enabled() {
|
||||||
if let Some(weapon) = self.inventory.get_current_weapon_mut() {
|
if let Some(weapon) = self.inventory_player1.get_current_weapon_mut() {
|
||||||
weapon.shoot_bullet(&self.player, &mut self.bullet_manager, state);
|
weapon.shoot_bullet(&self.player1, &mut self.bullet_manager, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_trigger.weapon_next() {
|
if self.player1.controller.trigger_next_weapon() {
|
||||||
state.sound_manager.play_sfx(4);
|
state.sound_manager.play_sfx(4);
|
||||||
self.inventory.next_weapon();
|
self.inventory_player1.next_weapon();
|
||||||
self.weapon_x_pos = 32;
|
self.weapon_x_pos = 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_trigger.weapon_prev() {
|
if self.player1.controller.trigger_prev_weapon() {
|
||||||
state.sound_manager.play_sfx(4);
|
state.sound_manager.play_sfx(4);
|
||||||
self.inventory.prev_weapon();
|
self.inventory_player1.prev_weapon();
|
||||||
self.weapon_x_pos = 0;
|
self.weapon_x_pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// update health bar
|
// update health bar
|
||||||
if self.life_bar < self.player.life as u16 {
|
if self.life_bar < self.player1.life as u16 {
|
||||||
self.life_bar = self.player.life as u16;
|
self.life_bar = self.player1.life as u16;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.life_bar > self.player.life as u16 {
|
if self.life_bar > self.player1.life as u16 {
|
||||||
self.life_bar_counter += 1;
|
self.life_bar_counter += 1;
|
||||||
if self.life_bar_counter > 30 {
|
if self.life_bar_counter > 30 {
|
||||||
self.life_bar -= 1;
|
self.life_bar -= 1;
|
||||||
|
@ -1208,88 +1208,6 @@ impl GameScene {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tick_stage_select(&mut self, state: &mut SharedGameState) -> GameResult {
|
|
||||||
let slot_count = state.teleporter_slots.iter()
|
|
||||||
.filter(|&&(index, _event_num)| index != 0)
|
|
||||||
.count();
|
|
||||||
|
|
||||||
if self.stage_select_text_y_pos > 46 {
|
|
||||||
self.stage_select_text_y_pos -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.key_trigger.left() {
|
|
||||||
if self.current_teleport_slot == 0 {
|
|
||||||
self.current_teleport_slot = slot_count.saturating_sub(1) as u8;
|
|
||||||
} else {
|
|
||||||
self.current_teleport_slot -= 1;
|
|
||||||
}
|
|
||||||
} else if state.key_trigger.right() {
|
|
||||||
if self.current_teleport_slot == slot_count.saturating_sub(1) as u8 {
|
|
||||||
self.current_teleport_slot = 0;
|
|
||||||
} else {
|
|
||||||
self.current_teleport_slot += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.key_trigger.left() || state.key_trigger.right() {
|
|
||||||
state.sound_manager.play_sfx(1);
|
|
||||||
if let Some(&(index, _event_num)) = state.teleporter_slots.get(self.current_teleport_slot as usize) {
|
|
||||||
state.textscript_vm.start_script(1000 + index);
|
|
||||||
} else {
|
|
||||||
state.textscript_vm.start_script(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if state.key_trigger.jump() | state.key_trigger.fire() {
|
|
||||||
state.textscript_vm.set_mode(ScriptMode::Map);
|
|
||||||
state.control_flags.set_tick_world(true);
|
|
||||||
state.control_flags.set_control_enabled(true);
|
|
||||||
state.control_flags.set_interactions_disabled(false);
|
|
||||||
|
|
||||||
if state.key_trigger.jump() {
|
|
||||||
if let Some(&(_index, event_num)) = state.teleporter_slots.get(self.current_teleport_slot as usize) {
|
|
||||||
state.textscript_vm.start_script(event_num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_stage_select(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
|
||||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "StageImage")?;
|
|
||||||
|
|
||||||
let slot_count = state.teleporter_slots.iter()
|
|
||||||
.filter(|&&(index, _event_num)| index != 0)
|
|
||||||
.count();
|
|
||||||
let slot_offset = ((state.canvas_size.0 - 40.0 * slot_count as f32) / 2.0).floor();
|
|
||||||
let mut slot_rect = Rect::new(0, 0, 0, 0);
|
|
||||||
|
|
||||||
for i in 0..slot_count {
|
|
||||||
let index = state.teleporter_slots[i].0;
|
|
||||||
|
|
||||||
slot_rect.left = 32 * (index as u16 % 8);
|
|
||||||
slot_rect.top = 16 * (index as u16 / 8);
|
|
||||||
slot_rect.right = slot_rect.left + 32;
|
|
||||||
slot_rect.bottom = slot_rect.top + 16;
|
|
||||||
|
|
||||||
batch.add_rect(slot_offset + i as f32 * 40.0, 64.0, &slot_rect);
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.draw(ctx)?;
|
|
||||||
|
|
||||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;
|
|
||||||
|
|
||||||
batch.add_rect(128.0, self.stage_select_text_y_pos as f32, &state.constants.textscript.stage_select_text);
|
|
||||||
if slot_count > 0 {
|
|
||||||
batch.add_rect(slot_offset + self.current_teleport_slot as f32 * 40.0, 64.0, &state.constants.textscript.cursor[self.tick / 2 % 2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.draw(ctx)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_debug_npc(&self, npc: &NPC, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
fn draw_debug_npc(&self, npc: &NPC, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||||
if npc.x < (self.frame.x - 128 - npc.display_bounds.width() as isize * 0x200)
|
if npc.x < (self.frame.x - 128 - npc.display_bounds.width() as isize * 0x200)
|
||||||
|| npc.x > (self.frame.x + 128 + (state.canvas_size.0 as isize + npc.display_bounds.width() as isize) * 0x200)
|
|| npc.x > (self.frame.x + 128 + (state.canvas_size.0 as isize + npc.display_bounds.width() as isize) * 0x200)
|
||||||
|
@ -1351,15 +1269,18 @@ impl GameScene {
|
||||||
|
|
||||||
impl Scene for GameScene {
|
impl Scene for GameScene {
|
||||||
fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||||
let seed = (self.player.max_life as i32)
|
let seed = (self.player1.max_life as i32)
|
||||||
.wrapping_add(self.player.x as i32)
|
.wrapping_add(self.player1.x as i32)
|
||||||
.wrapping_add(self.player.y as i32)
|
.wrapping_add(self.player1.y as i32)
|
||||||
.wrapping_add(self.stage_id as i32)
|
.wrapping_add(self.stage_id as i32)
|
||||||
.wrapping_mul(7);
|
.wrapping_mul(7);
|
||||||
state.game_rng = RNG::new(seed);
|
state.game_rng = RNG::new(seed);
|
||||||
state.textscript_vm.set_scene_script(self.stage.load_text_script(&state.base_path, &state.constants, ctx)?);
|
state.textscript_vm.set_scene_script(self.stage.load_text_script(&state.base_path, &state.constants, ctx)?);
|
||||||
state.textscript_vm.suspend = false;
|
state.textscript_vm.suspend = false;
|
||||||
|
|
||||||
|
self.player1.controller = state.settings.create_player1_controller();
|
||||||
|
self.player2.controller = state.settings.create_player2_controller();
|
||||||
|
|
||||||
let npcs = self.stage.load_npcs(&state.base_path, ctx)?;
|
let npcs = self.stage.load_npcs(&state.base_path, ctx)?;
|
||||||
for npc_data in npcs.iter() {
|
for npc_data in npcs.iter() {
|
||||||
log::info!("creating npc: {:?}", npc_data);
|
log::info!("creating npc: {:?}", npc_data);
|
||||||
|
@ -1384,32 +1305,33 @@ impl Scene for GameScene {
|
||||||
|
|
||||||
if state.constants.is_cs_plus {
|
if state.constants.is_cs_plus {
|
||||||
match state.season {
|
match state.season {
|
||||||
Season::Halloween => self.player.appearance = PlayerAppearance::HalloweenQuote,
|
Season::Halloween => self.player1.appearance = PlayerAppearance::HalloweenQuote,
|
||||||
Season::Christmas => self.player.appearance = PlayerAppearance::ReindeerQuote,
|
Season::Christmas => self.player1.appearance = PlayerAppearance::ReindeerQuote,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.npc_map.boss_map.boss_type = self.stage.data.boss_no as u16;
|
self.npc_map.boss_map.boss_type = self.stage.data.boss_no as u16;
|
||||||
self.player.target_x = self.player.x;
|
self.player1.target_x = self.player1.x;
|
||||||
self.player.target_y = self.player.y;
|
self.player1.target_y = self.player1.y;
|
||||||
self.frame.target_x = self.player.target_x;
|
self.frame.target_x = self.player1.target_x;
|
||||||
self.frame.target_y = self.player.target_y;
|
self.frame.target_y = self.player1.target_y;
|
||||||
self.frame.immediate_update(state, &self.stage);
|
self.frame.immediate_update(state, &self.stage);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||||
state.update_key_trigger();
|
self.player1.controller.update(state, ctx)?;
|
||||||
|
self.player1.controller.update_trigger();
|
||||||
|
|
||||||
if self.intro_mode && (state.key_trigger.jump() || self.tick >= 500) {
|
if self.intro_mode && (self.player1.controller.trigger_menu_ok() || self.tick >= 500) {
|
||||||
state.next_scene = Some(Box::new(TitleScene::new()));
|
state.next_scene = Some(Box::new(TitleScene::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
match state.textscript_vm.mode {
|
match state.textscript_vm.mode {
|
||||||
ScriptMode::Map if state.control_flags.tick_world() => self.tick_world(state)?,
|
ScriptMode::Map if state.control_flags.tick_world() => self.tick_world(state)?,
|
||||||
ScriptMode::StageSelect => self.tick_stage_select(state)?,
|
ScriptMode::StageSelect => self.stage_select.tick(state, (&self.player1, &self.player2))?,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1447,8 +1369,8 @@ impl Scene for GameScene {
|
||||||
fn draw_tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
fn draw_tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||||
self.frame.prev_x = self.frame.x;
|
self.frame.prev_x = self.frame.x;
|
||||||
self.frame.prev_y = self.frame.y;
|
self.frame.prev_y = self.frame.y;
|
||||||
self.player.prev_x = self.player.x;
|
self.player1.prev_x = self.player1.x;
|
||||||
self.player.prev_y = self.player.y;
|
self.player1.prev_y = self.player1.y;
|
||||||
|
|
||||||
for npc_cell in self.npc_map.npcs.values() {
|
for npc_cell in self.npc_map.npcs.values() {
|
||||||
let mut npc = npc_cell.borrow_mut();
|
let mut npc = npc_cell.borrow_mut();
|
||||||
|
@ -1509,7 +1431,7 @@ impl Scene for GameScene {
|
||||||
npc.draw(state, ctx, &self.frame)?;
|
npc.draw(state, ctx, &self.frame)?;
|
||||||
}
|
}
|
||||||
self.draw_bullets(state, ctx)?;
|
self.draw_bullets(state, ctx)?;
|
||||||
self.player.draw(state, ctx, &self.frame)?;
|
self.player1.draw(state, ctx, &self.frame)?;
|
||||||
if state.settings.shader_effects && self.water_visible {
|
if state.settings.shader_effects && self.water_visible {
|
||||||
self.draw_water(state, ctx)?;
|
self.draw_water(state, ctx)?;
|
||||||
}
|
}
|
||||||
|
@ -1534,7 +1456,7 @@ impl Scene for GameScene {
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.textscript_vm.mode == ScriptMode::StageSelect {
|
if state.textscript_vm.mode == ScriptMode::StageSelect {
|
||||||
self.draw_stage_select(state, ctx)?;
|
self.stage_select.draw(state, ctx, &self.frame)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.draw_fade(state, ctx)?;
|
self.draw_fade(state, ctx)?;
|
||||||
|
|
|
@ -5,6 +5,7 @@ use crate::common::Rect;
|
||||||
use crate::menu::{Menu, MenuEntry, MenuSelectionResult};
|
use crate::menu::{Menu, MenuEntry, MenuSelectionResult};
|
||||||
use crate::scene::Scene;
|
use crate::scene::Scene;
|
||||||
use crate::shared_game_state::{SharedGameState, TimingMode};
|
use crate::shared_game_state::{SharedGameState, TimingMode};
|
||||||
|
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
@ -17,6 +18,7 @@ enum CurrentMenu {
|
||||||
|
|
||||||
pub struct TitleScene {
|
pub struct TitleScene {
|
||||||
tick: usize,
|
tick: usize,
|
||||||
|
controller: CombinedMenuController,
|
||||||
current_menu: CurrentMenu,
|
current_menu: CurrentMenu,
|
||||||
main_menu: Menu,
|
main_menu: Menu,
|
||||||
option_menu: Menu,
|
option_menu: Menu,
|
||||||
|
@ -26,6 +28,7 @@ impl TitleScene {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
tick: 0,
|
tick: 0,
|
||||||
|
controller: CombinedMenuController::new(),
|
||||||
current_menu: CurrentMenu::MainMenu,
|
current_menu: CurrentMenu::MainMenu,
|
||||||
main_menu: Menu::new(0, 0, 100, 1 * 14 + 6),
|
main_menu: Menu::new(0, 0, 100, 1 * 14 + 6),
|
||||||
option_menu: Menu::new(0, 0, 180, 1 * 14 + 6),
|
option_menu: Menu::new(0, 0, 180, 1 * 14 + 6),
|
||||||
|
@ -82,35 +85,42 @@ static COPYRIGHT_NICALIS_SWITCH: &str = "@2017 NICALIS INC.";
|
||||||
static DISCORD_LINK: &str = "https://discord.gg/fbRsNNB";
|
static DISCORD_LINK: &str = "https://discord.gg/fbRsNNB";
|
||||||
|
|
||||||
impl Scene for TitleScene {
|
impl Scene for TitleScene {
|
||||||
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||||
if self.tick == 0 {
|
self.controller.add(state.settings.create_player1_controller());
|
||||||
state.sound_manager.play_song(24, &state.constants, ctx)?;
|
self.controller.add(state.settings.create_player2_controller());
|
||||||
self.main_menu.push_entry(MenuEntry::Active("New game".to_string()));
|
|
||||||
self.main_menu.push_entry(MenuEntry::Active("Load game".to_string()));
|
|
||||||
self.main_menu.push_entry(MenuEntry::Active("Options".to_string()));
|
|
||||||
self.main_menu.push_entry(MenuEntry::Disabled("Editor".to_string()));
|
|
||||||
self.main_menu.push_entry(MenuEntry::Active("Quit".to_string()));
|
|
||||||
self.main_menu.height = self.main_menu.entries.len() as u16 * 14 + 6;
|
|
||||||
|
|
||||||
self.option_menu.push_entry(MenuEntry::Toggle("Original timing (50TPS)".to_string(), state.timing_mode == TimingMode::_50Hz));
|
state.sound_manager.play_song(24, &state.constants, ctx)?;
|
||||||
self.option_menu.push_entry(MenuEntry::Toggle("Lighting effects".to_string(), state.settings.shader_effects));
|
self.main_menu.push_entry(MenuEntry::Active("New game".to_string()));
|
||||||
if state.constants.supports_og_textures {
|
self.main_menu.push_entry(MenuEntry::Active("Load game".to_string()));
|
||||||
self.option_menu.push_entry(MenuEntry::Toggle("Original textures".to_string(), state.settings.original_textures));
|
self.main_menu.push_entry(MenuEntry::Active("Options".to_string()));
|
||||||
} else {
|
self.main_menu.push_entry(MenuEntry::Disabled("Editor".to_string()));
|
||||||
self.option_menu.push_entry(MenuEntry::Disabled("Original textures".to_string()));
|
self.main_menu.push_entry(MenuEntry::Active("Quit".to_string()));
|
||||||
}
|
self.main_menu.height = self.main_menu.entries.len() as u16 * 14 + 6;
|
||||||
|
|
||||||
if state.constants.is_cs_plus {
|
self.option_menu.push_entry(MenuEntry::Toggle("Original timing (50TPS)".to_string(), state.timing_mode == TimingMode::_50Hz));
|
||||||
self.option_menu.push_entry(MenuEntry::Toggle("Seasonal textures".to_string(), state.settings.seasonal_textures));
|
self.option_menu.push_entry(MenuEntry::Toggle("Lighting effects".to_string(), state.settings.shader_effects));
|
||||||
} else {
|
if state.constants.supports_og_textures {
|
||||||
self.option_menu.push_entry(MenuEntry::Disabled("Seasonal textures".to_string()));
|
self.option_menu.push_entry(MenuEntry::Toggle("Original textures".to_string(), state.settings.original_textures));
|
||||||
}
|
} else {
|
||||||
self.option_menu.push_entry(MenuEntry::Active("Join our Discord".to_string()));
|
self.option_menu.push_entry(MenuEntry::Disabled("Original textures".to_string()));
|
||||||
self.option_menu.push_entry(MenuEntry::Disabled(DISCORD_LINK.to_owned()));
|
|
||||||
self.option_menu.push_entry(MenuEntry::Active("Back".to_string()));
|
|
||||||
self.option_menu.height = self.option_menu.entries.len() as u16 * 14 + 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if state.constants.is_cs_plus {
|
||||||
|
self.option_menu.push_entry(MenuEntry::Toggle("Seasonal textures".to_string(), state.settings.seasonal_textures));
|
||||||
|
} else {
|
||||||
|
self.option_menu.push_entry(MenuEntry::Disabled("Seasonal textures".to_string()));
|
||||||
|
}
|
||||||
|
self.option_menu.push_entry(MenuEntry::Active("Join our Discord".to_string()));
|
||||||
|
self.option_menu.push_entry(MenuEntry::Disabled(DISCORD_LINK.to_owned()));
|
||||||
|
self.option_menu.push_entry(MenuEntry::Active("Back".to_string()));
|
||||||
|
self.option_menu.height = self.option_menu.entries.len() as u16 * 14 + 6;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||||
|
self.controller.update(state, ctx)?;
|
||||||
|
|
||||||
self.main_menu.x = ((state.canvas_size.0 - self.main_menu.width as f32) / 2.0).floor() as isize;
|
self.main_menu.x = ((state.canvas_size.0 - self.main_menu.width as f32) / 2.0).floor() as isize;
|
||||||
self.main_menu.y = ((state.canvas_size.1 + 70.0 - self.main_menu.height as f32) / 2.0).floor() as isize;
|
self.main_menu.y = ((state.canvas_size.1 + 70.0 - self.main_menu.height as f32) / 2.0).floor() as isize;
|
||||||
|
|
||||||
|
@ -119,7 +129,7 @@ impl Scene for TitleScene {
|
||||||
|
|
||||||
match self.current_menu {
|
match self.current_menu {
|
||||||
CurrentMenu::MainMenu => {
|
CurrentMenu::MainMenu => {
|
||||||
match self.main_menu.tick(state) {
|
match self.main_menu.tick(&mut self.controller, state) {
|
||||||
MenuSelectionResult::Selected(0, _) => {
|
MenuSelectionResult::Selected(0, _) => {
|
||||||
state.reset();
|
state.reset();
|
||||||
state.sound_manager.play_song(0, &state.constants, ctx)?;
|
state.sound_manager.play_song(0, &state.constants, ctx)?;
|
||||||
|
@ -141,7 +151,7 @@ impl Scene for TitleScene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CurrentMenu::OptionMenu => {
|
CurrentMenu::OptionMenu => {
|
||||||
match self.option_menu.tick(state) {
|
match self.option_menu.tick(&mut self.controller, state) {
|
||||||
MenuSelectionResult::Selected(0, toggle) => {
|
MenuSelectionResult::Selected(0, toggle) => {
|
||||||
if let MenuEntry::Toggle(_, value) = toggle {
|
if let MenuEntry::Toggle(_, value) = toggle {
|
||||||
match state.timing_mode {
|
match state.timing_mode {
|
||||||
|
|
102
src/settings.rs
Normal file
102
src/settings.rs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
use ggez::{Context, GameResult};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use winit::event::VirtualKeyCode;
|
||||||
|
|
||||||
|
use crate::input::keyboard_player_controller::KeyboardController;
|
||||||
|
use crate::input::player_controller::PlayerController;
|
||||||
|
use crate::player::TargetPlayer;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Settings {
|
||||||
|
pub seasonal_textures: bool,
|
||||||
|
pub original_textures: bool,
|
||||||
|
pub shader_effects: bool,
|
||||||
|
pub motion_interpolation: bool,
|
||||||
|
pub touch_controls: bool,
|
||||||
|
pub player1_key_map: PlayerKeyMap,
|
||||||
|
pub player2_key_map: PlayerKeyMap,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub speed: f64,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub god_mode: bool,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub infinite_booster: bool,
|
||||||
|
#[serde(skip)]
|
||||||
|
pub debug_outlines: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Settings {
|
||||||
|
pub fn load(_ctx: &mut Context) -> GameResult<Settings> {
|
||||||
|
Ok(Settings::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_player1_controller(&self) -> Box<dyn PlayerController> {
|
||||||
|
Box::new(KeyboardController::new(TargetPlayer::Player1))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_player2_controller(&self) -> Box<dyn PlayerController> {
|
||||||
|
Box::new(KeyboardController::new(TargetPlayer::Player2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Settings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Settings {
|
||||||
|
seasonal_textures: true,
|
||||||
|
original_textures: false,
|
||||||
|
shader_effects: true,
|
||||||
|
motion_interpolation: true,
|
||||||
|
touch_controls: cfg!(target_os = "android"),
|
||||||
|
player1_key_map: p1_default_keymap(),
|
||||||
|
player2_key_map: p2_default_keymap(),
|
||||||
|
speed: 1.0,
|
||||||
|
god_mode: false,
|
||||||
|
infinite_booster: false,
|
||||||
|
debug_outlines: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct PlayerKeyMap {
|
||||||
|
pub left: VirtualKeyCode,
|
||||||
|
pub up: VirtualKeyCode,
|
||||||
|
pub right: VirtualKeyCode,
|
||||||
|
pub down: VirtualKeyCode,
|
||||||
|
pub prev_weapon: VirtualKeyCode,
|
||||||
|
pub next_weapon: VirtualKeyCode,
|
||||||
|
pub jump: VirtualKeyCode,
|
||||||
|
pub shoot: VirtualKeyCode,
|
||||||
|
pub inventory: VirtualKeyCode,
|
||||||
|
pub map: VirtualKeyCode,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn p1_default_keymap() -> PlayerKeyMap {
|
||||||
|
PlayerKeyMap {
|
||||||
|
left: VirtualKeyCode::Left,
|
||||||
|
up: VirtualKeyCode::Up,
|
||||||
|
right: VirtualKeyCode::Right,
|
||||||
|
down: VirtualKeyCode::Down,
|
||||||
|
prev_weapon: VirtualKeyCode::A,
|
||||||
|
next_weapon: VirtualKeyCode::S,
|
||||||
|
jump: VirtualKeyCode::Z,
|
||||||
|
shoot: VirtualKeyCode::X,
|
||||||
|
inventory: VirtualKeyCode::Q,
|
||||||
|
map: VirtualKeyCode::W,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn p2_default_keymap() -> PlayerKeyMap {
|
||||||
|
PlayerKeyMap {
|
||||||
|
left: VirtualKeyCode::Comma,
|
||||||
|
up: VirtualKeyCode::L,
|
||||||
|
right: VirtualKeyCode::Slash,
|
||||||
|
down: VirtualKeyCode::Period,
|
||||||
|
prev_weapon: VirtualKeyCode::G,
|
||||||
|
next_weapon: VirtualKeyCode::H,
|
||||||
|
jump: VirtualKeyCode::B,
|
||||||
|
shoot: VirtualKeyCode::N,
|
||||||
|
inventory: VirtualKeyCode::T,
|
||||||
|
map: VirtualKeyCode::Y,
|
||||||
|
}
|
||||||
|
}
|
36
src/shaders.rs
Normal file
36
src/shaders.rs
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
use gfx::{self, *};
|
||||||
|
use ggez::graphics::Shader;
|
||||||
|
use ggez::{Context, GameResult};
|
||||||
|
|
||||||
|
gfx_defines! {
|
||||||
|
constant WaterShaderParams {
|
||||||
|
resolution: [f32; 2] = "u_Resolution",
|
||||||
|
t: f32 = "u_Tick",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Shaders {
|
||||||
|
pub water_shader: Shader<WaterShaderParams>,
|
||||||
|
pub water_shader_params: WaterShaderParams,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Shaders {
|
||||||
|
pub fn new(ctx: &mut Context) -> GameResult<Shaders> {
|
||||||
|
let water_shader_params = WaterShaderParams {
|
||||||
|
t: 0.0,
|
||||||
|
resolution: [0.0, 0.0],
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Shaders {
|
||||||
|
water_shader: Shader::new(
|
||||||
|
ctx,
|
||||||
|
"/builtin/shaders/basic_150.vert.glsl",
|
||||||
|
"/builtin/shaders/water_150.frag.glsl",
|
||||||
|
water_shader_params,
|
||||||
|
"WaterShaderParams",
|
||||||
|
None,
|
||||||
|
)?,
|
||||||
|
water_shader_params,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,27 +3,28 @@ use std::time::Instant;
|
||||||
|
|
||||||
use bitvec::vec::BitVec;
|
use bitvec::vec::BitVec;
|
||||||
use chrono::{Datelike, Local};
|
use chrono::{Datelike, Local};
|
||||||
use gfx::{self, *};
|
|
||||||
use ggez::{Context, filesystem, GameResult, graphics};
|
use ggez::{Context, filesystem, GameResult, graphics};
|
||||||
use ggez::filesystem::OpenOptions;
|
use ggez::filesystem::OpenOptions;
|
||||||
use ggez::graphics::{Canvas, Shader};
|
use ggez::graphics::Canvas;
|
||||||
use num_traits::clamp;
|
use num_traits::clamp;
|
||||||
|
|
||||||
use crate::bmfont_renderer::BMFontRenderer;
|
use crate::bmfont_renderer::BMFontRenderer;
|
||||||
use crate::caret::{Caret, CaretType};
|
use crate::caret::{Caret, CaretType};
|
||||||
use crate::common::{ControlFlags, Direction, FadeState, KeyState};
|
use crate::common::{ControlFlags, Direction, FadeState};
|
||||||
use crate::engine_constants::EngineConstants;
|
use crate::engine_constants::EngineConstants;
|
||||||
|
use crate::input::touch_controls::TouchControls;
|
||||||
use crate::npc::{NPC, NPCTable};
|
use crate::npc::{NPC, NPCTable};
|
||||||
use crate::profile::GameProfile;
|
use crate::profile::GameProfile;
|
||||||
use crate::rng::RNG;
|
use crate::rng::RNG;
|
||||||
use crate::scene::game_scene::GameScene;
|
use crate::scene::game_scene::GameScene;
|
||||||
use crate::scene::Scene;
|
use crate::scene::Scene;
|
||||||
|
use crate::settings::Settings;
|
||||||
|
use crate::shaders::Shaders;
|
||||||
use crate::sound::SoundManager;
|
use crate::sound::SoundManager;
|
||||||
use crate::stage::StageData;
|
use crate::stage::StageData;
|
||||||
use crate::str;
|
use crate::str;
|
||||||
use crate::text_script::{ScriptMode, TextScriptExecutionState, TextScriptVM};
|
use crate::text_script::{ScriptMode, TextScriptExecutionState, TextScriptVM};
|
||||||
use crate::texture_set::{TextureSet, g_mag};
|
use crate::texture_set::{g_mag, TextureSet};
|
||||||
use crate::touch_controls::TouchControls;
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||||
pub enum TimingMode {
|
pub enum TimingMode {
|
||||||
|
@ -80,51 +81,6 @@ impl Season {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Settings {
|
|
||||||
pub god_mode: bool,
|
|
||||||
pub infinite_booster: bool,
|
|
||||||
pub speed: f64,
|
|
||||||
pub seasonal_textures: bool,
|
|
||||||
pub original_textures: bool,
|
|
||||||
pub shader_effects: bool,
|
|
||||||
pub motion_interpolation: bool,
|
|
||||||
pub debug_outlines: bool,
|
|
||||||
pub touch_controls: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx_defines! {
|
|
||||||
constant WaterShaderParams {
|
|
||||||
resolution: [f32; 2] = "u_Resolution",
|
|
||||||
t: f32 = "u_Tick",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Shaders {
|
|
||||||
pub water_shader: Shader<WaterShaderParams>,
|
|
||||||
pub water_shader_params: WaterShaderParams,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Shaders {
|
|
||||||
pub fn new(ctx: &mut Context) -> GameResult<Shaders> {
|
|
||||||
let water_shader_params = WaterShaderParams {
|
|
||||||
t: 0.0,
|
|
||||||
resolution: [0.0, 0.0],
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Shaders {
|
|
||||||
water_shader: Shader::new(
|
|
||||||
ctx,
|
|
||||||
"/builtin/shaders/basic_150.vert.glsl",
|
|
||||||
"/builtin/shaders/water_150.frag.glsl",
|
|
||||||
water_shader_params,
|
|
||||||
"WaterShaderParams",
|
|
||||||
None,
|
|
||||||
)?,
|
|
||||||
water_shader_params,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SharedGameState {
|
pub struct SharedGameState {
|
||||||
pub timing_mode: TimingMode,
|
pub timing_mode: TimingMode,
|
||||||
pub control_flags: ControlFlags,
|
pub control_flags: ControlFlags,
|
||||||
|
@ -137,8 +93,6 @@ pub struct SharedGameState {
|
||||||
pub quake_counter: u16,
|
pub quake_counter: u16,
|
||||||
pub teleporter_slots: Vec<(u16, u16)>,
|
pub teleporter_slots: Vec<(u16, u16)>,
|
||||||
pub carets: Vec<Caret>,
|
pub carets: Vec<Caret>,
|
||||||
pub key_state: KeyState,
|
|
||||||
pub key_trigger: KeyState,
|
|
||||||
pub touch_controls: TouchControls,
|
pub touch_controls: TouchControls,
|
||||||
pub base_path: String,
|
pub base_path: String,
|
||||||
pub npc_table: NPCTable,
|
pub npc_table: NPCTable,
|
||||||
|
@ -162,7 +116,6 @@ pub struct SharedGameState {
|
||||||
pub sound_manager: SoundManager,
|
pub sound_manager: SoundManager,
|
||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
pub shutdown: bool,
|
pub shutdown: bool,
|
||||||
key_old: u16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SharedGameState {
|
impl SharedGameState {
|
||||||
|
@ -175,7 +128,7 @@ impl SharedGameState {
|
||||||
|
|
||||||
let mut constants = EngineConstants::defaults();
|
let mut constants = EngineConstants::defaults();
|
||||||
let mut base_path = "/";
|
let mut base_path = "/";
|
||||||
let settings = SharedGameState::load_settings(ctx)?;
|
let settings = Settings::load(ctx)?;
|
||||||
|
|
||||||
if filesystem::exists(ctx, "/base/Nicalis.bmp") {
|
if filesystem::exists(ctx, "/base/Nicalis.bmp") {
|
||||||
info!("Cave Story+ (PC) data files detected.");
|
info!("Cave Story+ (PC) data files detected.");
|
||||||
|
@ -213,8 +166,6 @@ impl SharedGameState {
|
||||||
quake_counter: 0,
|
quake_counter: 0,
|
||||||
teleporter_slots: Vec::with_capacity(8),
|
teleporter_slots: Vec::with_capacity(8),
|
||||||
carets: Vec::with_capacity(32),
|
carets: Vec::with_capacity(32),
|
||||||
key_state: KeyState(0),
|
|
||||||
key_trigger: KeyState(0),
|
|
||||||
touch_controls: TouchControls::new(),
|
touch_controls: TouchControls::new(),
|
||||||
base_path: str!(base_path),
|
base_path: str!(base_path),
|
||||||
npc_table: NPCTable::new(),
|
npc_table: NPCTable::new(),
|
||||||
|
@ -238,21 +189,6 @@ impl SharedGameState {
|
||||||
sound_manager: SoundManager::new(ctx)?,
|
sound_manager: SoundManager::new(ctx)?,
|
||||||
settings,
|
settings,
|
||||||
shutdown: false,
|
shutdown: false,
|
||||||
key_old: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_settings(ctx: &mut Context) -> GameResult<Settings> {
|
|
||||||
Ok(Settings {
|
|
||||||
god_mode: false,
|
|
||||||
infinite_booster: false,
|
|
||||||
speed: 1.0,
|
|
||||||
seasonal_textures: true,
|
|
||||||
original_textures: false,
|
|
||||||
shader_effects: true,
|
|
||||||
motion_interpolation: true,
|
|
||||||
debug_outlines: false,
|
|
||||||
touch_controls: cfg!(target_os = "android"),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,8 +204,8 @@ impl SharedGameState {
|
||||||
|
|
||||||
pub fn start_new_game(&mut self, ctx: &mut Context) -> GameResult {
|
pub fn start_new_game(&mut self, ctx: &mut Context) -> GameResult {
|
||||||
let mut next_scene = GameScene::new(self, ctx, 13)?;
|
let mut next_scene = GameScene::new(self, ctx, 13)?;
|
||||||
next_scene.player.x = 10 * 16 * 0x200;
|
next_scene.player1.x = 10 * 16 * 0x200;
|
||||||
next_scene.player.y = 8 * 16 * 0x200;
|
next_scene.player1.y = 8 * 16 * 0x200;
|
||||||
self.fade_state = FadeState::Hidden;
|
self.fade_state = FadeState::Hidden;
|
||||||
self.textscript_vm.state = TextScriptExecutionState::Running(200, 0);
|
self.textscript_vm.state = TextScriptExecutionState::Running(200, 0);
|
||||||
|
|
||||||
|
@ -280,9 +216,9 @@ impl SharedGameState {
|
||||||
|
|
||||||
pub fn start_intro(&mut self, ctx: &mut Context) -> GameResult {
|
pub fn start_intro(&mut self, ctx: &mut Context) -> GameResult {
|
||||||
let mut next_scene = GameScene::new(self, ctx, 72)?;
|
let mut next_scene = GameScene::new(self, ctx, 72)?;
|
||||||
next_scene.player.cond.set_hidden(true);
|
next_scene.player1.cond.set_hidden(true);
|
||||||
next_scene.player.x = 3 * 16 * 0x200;
|
next_scene.player1.x = 3 * 16 * 0x200;
|
||||||
next_scene.player.y = 3 * 16 * 0x200;
|
next_scene.player1.y = 3 * 16 * 0x200;
|
||||||
next_scene.intro_mode = true;
|
next_scene.intro_mode = true;
|
||||||
self.fade_state = FadeState::Hidden;
|
self.fade_state = FadeState::Hidden;
|
||||||
self.textscript_vm.state = TextScriptExecutionState::Running(100, 0);
|
self.textscript_vm.state = TextScriptExecutionState::Running(100, 0);
|
||||||
|
@ -334,9 +270,6 @@ impl SharedGameState {
|
||||||
self.teleporter_slots.clear();
|
self.teleporter_slots.clear();
|
||||||
self.quake_counter = 0;
|
self.quake_counter = 0;
|
||||||
self.carets.clear();
|
self.carets.clear();
|
||||||
self.key_state.0 = 0;
|
|
||||||
self.key_trigger.0 = 0;
|
|
||||||
self.key_old = 0;
|
|
||||||
self.new_npcs.clear();
|
self.new_npcs.clear();
|
||||||
self.textscript_vm.set_mode(ScriptMode::Map);
|
self.textscript_vm.set_mode(ScriptMode::Map);
|
||||||
self.textscript_vm.suspend = true;
|
self.textscript_vm.suspend = true;
|
||||||
|
@ -353,13 +286,6 @@ impl SharedGameState {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_key_trigger(&mut self) {
|
|
||||||
let mut trigger = self.key_state.0 ^ self.key_old;
|
|
||||||
trigger &= self.key_state.0;
|
|
||||||
self.key_old = self.key_state.0;
|
|
||||||
self.key_trigger = KeyState(trigger);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn tick_carets(&mut self) {
|
pub fn tick_carets(&mut self) {
|
||||||
for caret in self.carets.iter_mut() {
|
for caret in self.carets.iter_mut() {
|
||||||
caret.tick(&self.effect_rng, &self.constants);
|
caret.tick(&self.effect_rng, &self.constants);
|
||||||
|
|
|
@ -561,7 +561,10 @@ impl TextScriptVM {
|
||||||
if remaining > 1 {
|
if remaining > 1 {
|
||||||
let ticks = if state.textscript_vm.flags.fast() {
|
let ticks = if state.textscript_vm.flags.fast() {
|
||||||
0
|
0
|
||||||
} else if state.key_state.jump() || state.key_state.fire() {
|
} else if game_scene.player1.controller.jump()
|
||||||
|
|| game_scene.player1.controller.shoot()
|
||||||
|
|| game_scene.player2.controller.jump()
|
||||||
|
|| game_scene.player2.controller.shoot() {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
4
|
4
|
||||||
|
@ -593,13 +596,16 @@ impl TextScriptVM {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_trigger.left() || state.key_trigger.right() {
|
if game_scene.player1.controller.trigger_left()
|
||||||
|
|| game_scene.player1.controller.trigger_right()
|
||||||
|
|| game_scene.player2.controller.trigger_left()
|
||||||
|
|| game_scene.player2.controller.trigger_right() {
|
||||||
state.sound_manager.play_sfx(1);
|
state.sound_manager.play_sfx(1);
|
||||||
state.textscript_vm.state = TextScriptExecutionState::WaitConfirmation(event, ip, no_event, 0, !selection);
|
state.textscript_vm.state = TextScriptExecutionState::WaitConfirmation(event, ip, no_event, 0, !selection);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.key_trigger.jump() {
|
if game_scene.player1.controller.trigger_jump() || game_scene.player2.controller.trigger_jump() {
|
||||||
state.sound_manager.play_sfx(18);
|
state.sound_manager.play_sfx(18);
|
||||||
match selection {
|
match selection {
|
||||||
ConfirmSelection::Yes => {
|
ConfirmSelection::Yes => {
|
||||||
|
@ -614,13 +620,16 @@ impl TextScriptVM {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
TextScriptExecutionState::WaitStanding(event, ip) => {
|
TextScriptExecutionState::WaitStanding(event, ip) => {
|
||||||
if game_scene.player.flags.hit_bottom_wall() {
|
if game_scene.player1.flags.hit_bottom_wall() {
|
||||||
state.textscript_vm.state = TextScriptExecutionState::Running(event, ip);
|
state.textscript_vm.state = TextScriptExecutionState::Running(event, ip);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
TextScriptExecutionState::WaitInput(event, ip) => {
|
TextScriptExecutionState::WaitInput(event, ip) => {
|
||||||
if state.key_trigger.jump() || state.key_trigger.fire() {
|
if game_scene.player1.controller.trigger_jump()
|
||||||
|
|| game_scene.player1.controller.trigger_shoot()
|
||||||
|
|| game_scene.player2.controller.trigger_jump()
|
||||||
|
|| game_scene.player2.controller.trigger_shoot() {
|
||||||
state.textscript_vm.state = TextScriptExecutionState::Running(event, ip);
|
state.textscript_vm.state = TextScriptExecutionState::Running(event, ip);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -694,7 +703,7 @@ impl TextScriptVM {
|
||||||
state.textscript_vm.flags.set_background_visible(false);
|
state.textscript_vm.flags.set_background_visible(false);
|
||||||
state.textscript_vm.stack.clear();
|
state.textscript_vm.stack.clear();
|
||||||
|
|
||||||
game_scene.player.cond.set_interacted(false);
|
game_scene.player1.cond.set_interacted(false);
|
||||||
game_scene.frame.update_target = UpdateTarget::Player;
|
game_scene.frame.update_target = UpdateTarget::Player;
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Ended;
|
exec_state = TextScriptExecutionState::Ended;
|
||||||
|
@ -702,7 +711,7 @@ impl TextScriptVM {
|
||||||
OpCode::SLP => {
|
OpCode::SLP => {
|
||||||
state.textscript_vm.set_mode(ScriptMode::StageSelect);
|
state.textscript_vm.set_mode(ScriptMode::StageSelect);
|
||||||
|
|
||||||
let event_num = if let Some(slot) = state.teleporter_slots.get(game_scene.current_teleport_slot as usize) {
|
let event_num = if let Some(slot) = state.teleporter_slots.get(game_scene.stage_select.current_teleport_slot as usize) {
|
||||||
1000 + slot.0
|
1000 + slot.0
|
||||||
} else {
|
} else {
|
||||||
1000
|
1000
|
||||||
|
@ -726,7 +735,7 @@ impl TextScriptVM {
|
||||||
state.control_flags.set_tick_world(false);
|
state.control_flags.set_tick_world(false);
|
||||||
state.control_flags.set_control_enabled(false);
|
state.control_flags.set_control_enabled(false);
|
||||||
|
|
||||||
game_scene.player.shock_counter = 0;
|
game_scene.player1.shock_counter = 0;
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
|
@ -734,8 +743,8 @@ impl TextScriptVM {
|
||||||
state.control_flags.set_tick_world(true);
|
state.control_flags.set_tick_world(true);
|
||||||
state.control_flags.set_control_enabled(false);
|
state.control_flags.set_control_enabled(false);
|
||||||
|
|
||||||
game_scene.player.up = false;
|
game_scene.player1.up = false;
|
||||||
game_scene.player.shock_counter = 0;
|
game_scene.player1.shock_counter = 0;
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
|
@ -748,7 +757,7 @@ impl TextScriptVM {
|
||||||
OpCode::MYD => {
|
OpCode::MYD => {
|
||||||
let new_direction = read_cur_varint(&mut cursor)? as usize;
|
let new_direction = read_cur_varint(&mut cursor)? as usize;
|
||||||
if let Some(direction) = Direction::from_int(new_direction) {
|
if let Some(direction) = Direction::from_int(new_direction) {
|
||||||
game_scene.player.direction = direction;
|
game_scene.player1.direction = direction;
|
||||||
}
|
}
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
|
@ -756,14 +765,14 @@ impl TextScriptVM {
|
||||||
OpCode::MYB => {
|
OpCode::MYB => {
|
||||||
let new_direction = read_cur_varint(&mut cursor)? as usize;
|
let new_direction = read_cur_varint(&mut cursor)? as usize;
|
||||||
|
|
||||||
game_scene.player.vel_y = -0x200;
|
game_scene.player1.vel_y = -0x200;
|
||||||
|
|
||||||
if let Some(direction) = Direction::from_int_facing(new_direction) {
|
if let Some(direction) = Direction::from_int_facing(new_direction) {
|
||||||
match direction {
|
match direction {
|
||||||
Direction::Left => game_scene.player.vel_x = 0x200,
|
Direction::Left => game_scene.player1.vel_x = 0x200,
|
||||||
Direction::Up => game_scene.player.vel_y = -0x200,
|
Direction::Up => game_scene.player1.vel_y = -0x200,
|
||||||
Direction::Right => game_scene.player.vel_x = -0x200,
|
Direction::Right => game_scene.player1.vel_x = -0x200,
|
||||||
Direction::Bottom => game_scene.player.vel_y = 0x200,
|
Direction::Bottom => game_scene.player1.vel_y = 0x200,
|
||||||
Direction::FacingPlayer => {
|
Direction::FacingPlayer => {
|
||||||
// todo npc direction dependent bump
|
// todo npc direction dependent bump
|
||||||
}
|
}
|
||||||
|
@ -773,12 +782,12 @@ impl TextScriptVM {
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
OpCode::SMC => {
|
OpCode::SMC => {
|
||||||
game_scene.player.cond.set_hidden(false);
|
game_scene.player1.cond.set_hidden(false);
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
OpCode::HMC => {
|
OpCode::HMC => {
|
||||||
game_scene.player.cond.set_hidden(true);
|
game_scene.player1.cond.set_hidden(true);
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
|
@ -823,7 +832,7 @@ impl TextScriptVM {
|
||||||
let item_id = read_cur_varint(&mut cursor)? as u16;
|
let item_id = read_cur_varint(&mut cursor)? as u16;
|
||||||
let event_num = read_cur_varint(&mut cursor)? as u16;
|
let event_num = read_cur_varint(&mut cursor)? as u16;
|
||||||
|
|
||||||
if game_scene.inventory.has_item(item_id) {
|
if game_scene.inventory_player1.has_item(item_id) {
|
||||||
exec_state = TextScriptExecutionState::Running(event_num, 0);
|
exec_state = TextScriptExecutionState::Running(event_num, 0);
|
||||||
} else {
|
} else {
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
|
@ -834,7 +843,7 @@ impl TextScriptVM {
|
||||||
let amount = read_cur_varint(&mut cursor)? as u16;
|
let amount = read_cur_varint(&mut cursor)? as u16;
|
||||||
let event_num = read_cur_varint(&mut cursor)? as u16;
|
let event_num = read_cur_varint(&mut cursor)? as u16;
|
||||||
|
|
||||||
if game_scene.inventory.has_item_amount(item_id, Ordering::Equal, amount) {
|
if game_scene.inventory_player1.has_item_amount(item_id, Ordering::Equal, amount) {
|
||||||
exec_state = TextScriptExecutionState::Running(event_num, 0);
|
exec_state = TextScriptExecutionState::Running(event_num, 0);
|
||||||
} else {
|
} else {
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
|
@ -845,7 +854,7 @@ impl TextScriptVM {
|
||||||
let event_num = read_cur_varint(&mut cursor)? as u16;
|
let event_num = read_cur_varint(&mut cursor)? as u16;
|
||||||
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon);
|
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon);
|
||||||
|
|
||||||
if weapon_type.is_some() && game_scene.inventory.has_weapon(weapon_type.unwrap()) {
|
if weapon_type.is_some() && game_scene.inventory_player1.has_weapon(weapon_type.unwrap()) {
|
||||||
exec_state = TextScriptExecutionState::Running(event_num, 0);
|
exec_state = TextScriptExecutionState::Running(event_num, 0);
|
||||||
} else {
|
} else {
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
|
@ -893,7 +902,7 @@ impl TextScriptVM {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OpCode::MM0 => {
|
OpCode::MM0 => {
|
||||||
game_scene.player.vel_x = 0;
|
game_scene.player1.vel_x = 0;
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
|
@ -925,8 +934,8 @@ impl TextScriptVM {
|
||||||
}
|
}
|
||||||
OpCode::MLp => {
|
OpCode::MLp => {
|
||||||
let life = read_cur_varint(&mut cursor)? as u16;
|
let life = read_cur_varint(&mut cursor)? as u16;
|
||||||
game_scene.player.life += life;
|
game_scene.player1.life += life;
|
||||||
game_scene.player.max_life += life;
|
game_scene.player1.max_life += life;
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
|
@ -999,12 +1008,12 @@ impl TextScriptVM {
|
||||||
|
|
||||||
let mut new_scene = GameScene::new(state, ctx, map_id)?;
|
let mut new_scene = GameScene::new(state, ctx, map_id)?;
|
||||||
new_scene.intro_mode = game_scene.intro_mode;
|
new_scene.intro_mode = game_scene.intro_mode;
|
||||||
new_scene.inventory = game_scene.inventory.clone();
|
new_scene.inventory_player1 = game_scene.inventory_player1.clone();
|
||||||
new_scene.player = game_scene.player.clone();
|
new_scene.player1 = game_scene.player1.clone();
|
||||||
new_scene.player.vel_x = 0;
|
new_scene.player1.vel_x = 0;
|
||||||
new_scene.player.vel_y = 0;
|
new_scene.player1.vel_y = 0;
|
||||||
new_scene.player.x = pos_x;
|
new_scene.player1.x = pos_x;
|
||||||
new_scene.player.y = pos_y;
|
new_scene.player1.y = pos_y;
|
||||||
|
|
||||||
state.control_flags.set_tick_world(true);
|
state.control_flags.set_tick_world(true);
|
||||||
state.textscript_vm.flags.0 = 0;
|
state.textscript_vm.flags.0 = 0;
|
||||||
|
@ -1024,10 +1033,10 @@ impl TextScriptVM {
|
||||||
let pos_x = read_cur_varint(&mut cursor)? as isize * 16 * 0x200;
|
let pos_x = read_cur_varint(&mut cursor)? as isize * 16 * 0x200;
|
||||||
let pos_y = read_cur_varint(&mut cursor)? as isize * 16 * 0x200;
|
let pos_y = read_cur_varint(&mut cursor)? as isize * 16 * 0x200;
|
||||||
|
|
||||||
game_scene.player.vel_x = 0;
|
game_scene.player1.vel_x = 0;
|
||||||
game_scene.player.vel_y = 0;
|
game_scene.player1.vel_y = 0;
|
||||||
game_scene.player.x = pos_x;
|
game_scene.player1.x = pos_x;
|
||||||
game_scene.player.y = pos_y;
|
game_scene.player1.y = pos_y;
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
|
@ -1036,7 +1045,7 @@ impl TextScriptVM {
|
||||||
|
|
||||||
let mode: Option<ControlMode> = FromPrimitive::from_u8(control_mode);
|
let mode: Option<ControlMode> = FromPrimitive::from_u8(control_mode);
|
||||||
if let Some(mode) = mode {
|
if let Some(mode) = mode {
|
||||||
game_scene.player.control_mode = mode;
|
game_scene.player1.control_mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
|
@ -1177,7 +1186,7 @@ impl TextScriptVM {
|
||||||
npc.tsc_direction = tsc_direction as u16;
|
npc.tsc_direction = tsc_direction as u16;
|
||||||
|
|
||||||
if direction == Direction::FacingPlayer {
|
if direction == Direction::FacingPlayer {
|
||||||
npc.direction = if game_scene.player.x < npc.x {
|
npc.direction = if game_scene.player1.x < npc.x {
|
||||||
Direction::Right
|
Direction::Right
|
||||||
} else {
|
} else {
|
||||||
Direction::Left
|
Direction::Left
|
||||||
|
@ -1234,7 +1243,7 @@ impl TextScriptVM {
|
||||||
npc.tsc_direction = tsc_direction as u16;
|
npc.tsc_direction = tsc_direction as u16;
|
||||||
|
|
||||||
if direction == Direction::FacingPlayer {
|
if direction == Direction::FacingPlayer {
|
||||||
npc.direction = if game_scene.player.x < npc.x {
|
npc.direction = if game_scene.player1.x < npc.x {
|
||||||
Direction::Right
|
Direction::Right
|
||||||
} else {
|
} else {
|
||||||
Direction::Left
|
Direction::Left
|
||||||
|
@ -1265,7 +1274,7 @@ impl TextScriptVM {
|
||||||
npc.tsc_direction = tsc_direction as u16;
|
npc.tsc_direction = tsc_direction as u16;
|
||||||
|
|
||||||
if direction == Direction::FacingPlayer {
|
if direction == Direction::FacingPlayer {
|
||||||
npc.direction = if game_scene.player.x < npc.x {
|
npc.direction = if game_scene.player1.x < npc.x {
|
||||||
Direction::Right
|
Direction::Right
|
||||||
} else {
|
} else {
|
||||||
Direction::Left
|
Direction::Left
|
||||||
|
@ -1294,7 +1303,7 @@ impl TextScriptVM {
|
||||||
npc.tsc_direction = tsc_direction as u16;
|
npc.tsc_direction = tsc_direction as u16;
|
||||||
|
|
||||||
if direction == Direction::FacingPlayer {
|
if direction == Direction::FacingPlayer {
|
||||||
npc.direction = if game_scene.player.x < npc.x {
|
npc.direction = if game_scene.player1.x < npc.x {
|
||||||
Direction::Right
|
Direction::Right
|
||||||
} else {
|
} else {
|
||||||
Direction::Left
|
Direction::Left
|
||||||
|
@ -1310,15 +1319,15 @@ impl TextScriptVM {
|
||||||
OpCode::LIp => {
|
OpCode::LIp => {
|
||||||
let life = read_cur_varint(&mut cursor)? as u16;
|
let life = read_cur_varint(&mut cursor)? as u16;
|
||||||
|
|
||||||
game_scene.player.life = clamp(game_scene.player.life + life, 0, game_scene.player.max_life);
|
game_scene.player1.life = clamp(game_scene.player1.life + life, 0, game_scene.player1.max_life);
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
OpCode::ITp => {
|
OpCode::ITp => {
|
||||||
let item_id = read_cur_varint(&mut cursor)? as u16;
|
let item_id = read_cur_varint(&mut cursor)? as u16;
|
||||||
|
|
||||||
if !game_scene.inventory.has_item(item_id) {
|
if !game_scene.inventory_player1.has_item(item_id) {
|
||||||
game_scene.inventory.add_item(item_id);
|
game_scene.inventory_player1.add_item(item_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
|
@ -1327,8 +1336,8 @@ impl TextScriptVM {
|
||||||
let item_id = read_cur_varint(&mut cursor)? as u16;
|
let item_id = read_cur_varint(&mut cursor)? as u16;
|
||||||
let amount = read_cur_varint(&mut cursor)? as u16;
|
let amount = read_cur_varint(&mut cursor)? as u16;
|
||||||
|
|
||||||
if game_scene.inventory.has_item_amount(item_id, Ordering::Less, amount) {
|
if game_scene.inventory_player1.has_item_amount(item_id, Ordering::Less, amount) {
|
||||||
game_scene.inventory.add_item(item_id);
|
game_scene.inventory_player1.add_item(item_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
|
@ -1336,7 +1345,7 @@ impl TextScriptVM {
|
||||||
OpCode::ITm => {
|
OpCode::ITm => {
|
||||||
let item_id = read_cur_varint(&mut cursor)? as u16;
|
let item_id = read_cur_varint(&mut cursor)? as u16;
|
||||||
|
|
||||||
game_scene.inventory.consume_item(item_id);
|
game_scene.inventory_player1.consume_item(item_id);
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
|
@ -1346,7 +1355,7 @@ impl TextScriptVM {
|
||||||
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon_id);
|
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon_id);
|
||||||
|
|
||||||
if let Some(wtype) = weapon_type {
|
if let Some(wtype) = weapon_type {
|
||||||
game_scene.inventory.add_weapon(wtype, max_ammo);
|
game_scene.inventory_player1.add_weapon(wtype, max_ammo);
|
||||||
}
|
}
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
|
@ -1356,32 +1365,32 @@ impl TextScriptVM {
|
||||||
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon_id);
|
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon_id);
|
||||||
|
|
||||||
if let Some(wtype) = weapon_type {
|
if let Some(wtype) = weapon_type {
|
||||||
game_scene.inventory.remove_weapon(wtype);
|
game_scene.inventory_player1.remove_weapon(wtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
OpCode::AEp => {
|
OpCode::AEp => {
|
||||||
game_scene.inventory.refill_all_ammo();
|
game_scene.inventory_player1.refill_all_ammo();
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
OpCode::ZAM => {
|
OpCode::ZAM => {
|
||||||
game_scene.inventory.reset_all_weapon_xp();
|
game_scene.inventory_player1.reset_all_weapon_xp();
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
OpCode::EQp => {
|
OpCode::EQp => {
|
||||||
let mask = read_cur_varint(&mut cursor)? as u16;
|
let mask = read_cur_varint(&mut cursor)? as u16;
|
||||||
|
|
||||||
game_scene.player.equip.0 |= mask;
|
game_scene.player1.equip.0 |= mask;
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
OpCode::EQm => {
|
OpCode::EQm => {
|
||||||
let mask = read_cur_varint(&mut cursor)? as u16;
|
let mask = read_cur_varint(&mut cursor)? as u16;
|
||||||
|
|
||||||
game_scene.player.equip.0 &= !mask;
|
game_scene.player1.equip.0 &= !mask;
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
|
@ -1455,7 +1464,7 @@ impl TextScriptVM {
|
||||||
|
|
||||||
if tick_npc != 0 {
|
if tick_npc != 0 {
|
||||||
if let Some(npc) = game_scene.npc_map.npcs.get(&tick_npc) {
|
if let Some(npc) = game_scene.npc_map.npcs.get(&tick_npc) {
|
||||||
npc.borrow_mut().tick(state, (&mut game_scene.player, &game_scene.npc_map.npcs, &mut game_scene.stage))?;
|
npc.borrow_mut().tick(state, (&mut game_scene.player1, &game_scene.npc_map.npcs, &mut game_scene.stage))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::io::{BufReader, Read, Seek, SeekFrom};
|
||||||
use ggez;
|
use ggez;
|
||||||
use ggez::{Context, GameError, GameResult, graphics};
|
use ggez::{Context, GameError, GameResult, graphics};
|
||||||
use ggez::filesystem;
|
use ggez::filesystem;
|
||||||
use ggez::graphics::{Color, Drawable, DrawMode, DrawParam, FilterMode, Image, Mesh, Rect, mint};
|
use ggez::graphics::{Color, Drawable, DrawMode, DrawParam, FilterMode, Image, Mesh, mint, Rect};
|
||||||
use ggez::graphics::spritebatch::SpriteBatch;
|
use ggez::graphics::spritebatch::SpriteBatch;
|
||||||
use ggez::nalgebra::{Point2, Vector2};
|
use ggez::nalgebra::{Point2, Vector2};
|
||||||
use image::RgbaImage;
|
use image::RgbaImage;
|
||||||
|
@ -14,7 +14,8 @@ use log::info;
|
||||||
use crate::common;
|
use crate::common;
|
||||||
use crate::common::FILE_TYPES;
|
use crate::common::FILE_TYPES;
|
||||||
use crate::engine_constants::EngineConstants;
|
use crate::engine_constants::EngineConstants;
|
||||||
use crate::shared_game_state::{Season, Settings};
|
use crate::settings::Settings;
|
||||||
|
use crate::shared_game_state::Season;
|
||||||
use crate::str;
|
use crate::str;
|
||||||
|
|
||||||
pub static mut g_mag: f32 = 1.0;
|
pub static mut g_mag: f32 = 1.0;
|
||||||
|
|
|
@ -85,7 +85,7 @@ impl Weapon {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shoot_bullet_snake(&mut self, player: &Player, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
|
fn shoot_bullet_snake(&mut self, player: &Player, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
|
||||||
if state.key_trigger.fire() && bullet_manager.count_bullets_multi([1, 2, 3]) < 4 {
|
if player.controller.trigger_shoot() && bullet_manager.count_bullets_multi([1, 2, 3]) < 4 {
|
||||||
let btype = match self.level {
|
let btype = match self.level {
|
||||||
WeaponLevel::Level1 => { 1 }
|
WeaponLevel::Level1 => { 1 }
|
||||||
WeaponLevel::Level2 => { 2 }
|
WeaponLevel::Level2 => { 2 }
|
||||||
|
@ -141,7 +141,7 @@ impl Weapon {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shoot_bullet_polar_star(&mut self, player: &Player, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
|
fn shoot_bullet_polar_star(&mut self, player: &Player, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
|
||||||
if state.key_trigger.fire() && bullet_manager.count_bullets_multi([4, 5, 6]) < 2 {
|
if player.controller.trigger_shoot() && bullet_manager.count_bullets_multi([4, 5, 6]) < 2 {
|
||||||
let btype = match self.level {
|
let btype = match self.level {
|
||||||
WeaponLevel::Level1 => { 4 }
|
WeaponLevel::Level1 => { 4 }
|
||||||
WeaponLevel::Level2 => { 5 }
|
WeaponLevel::Level2 => { 5 }
|
||||||
|
@ -203,7 +203,7 @@ impl Weapon {
|
||||||
|
|
||||||
fn shoot_bullet_fireball(&mut self, player: &Player, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
|
fn shoot_bullet_fireball(&mut self, player: &Player, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
|
||||||
let max_bullets = self.level as usize + 1;
|
let max_bullets = self.level as usize + 1;
|
||||||
if state.key_trigger.fire() && bullet_manager.count_bullets_multi([7, 8, 9]) < max_bullets {
|
if player.controller.trigger_shoot() && bullet_manager.count_bullets_multi([7, 8, 9]) < max_bullets {
|
||||||
let btype = match self.level {
|
let btype = match self.level {
|
||||||
WeaponLevel::Level1 => { 7 }
|
WeaponLevel::Level1 => { 7 }
|
||||||
WeaponLevel::Level2 => { 8 }
|
WeaponLevel::Level2 => { 8 }
|
||||||
|
|
Loading…
Reference in a new issue