mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-07-09 22:16:20 +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
|
||||
vec_mut_scan = "0.3.0"
|
||||
webbrowser = "0.5.5"
|
||||
winit = "0.23.0"
|
||||
winit = {version = "0.23.0", features = ["serde"]}
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
ndk = "0.2.0"
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::io::ErrorKind;
|
|||
use std::io::SeekFrom;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
|
||||
use ggez::{GameError, GameResult};
|
||||
use ggez::GameResult;
|
||||
use ggez::GameError::FilesystemError;
|
||||
use ggez::vfs::{OpenOptions, VFile, VFS, VMetadata};
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::cmp::Ordering;
|
||||
|
||||
use num_traits::{AsPrimitive, Num};
|
||||
use num_traits::real::Real;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::bitfield;
|
||||
|
@ -82,22 +81,6 @@ bitfield! {
|
|||
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! {
|
||||
#[derive(Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct ControlFlags(u16);
|
||||
|
|
|
@ -52,7 +52,7 @@ impl 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 {
|
||||
BossLifeTarget::NPC(npc_id) => {
|
||||
if let Some(npc_cell) = npc_map.npcs.get(&npc_id) {
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
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 crate::common::{KeyState, Rect};
|
||||
use winit::event::TouchPhase;
|
||||
|
||||
use crate::common::Rect;
|
||||
use crate::engine_constants::EngineConstants;
|
||||
use crate::texture_set::TextureSet;
|
||||
|
||||
struct TouchPoint {
|
||||
id: u64,
|
||||
|
@ -20,8 +21,8 @@ impl TouchControls {
|
|||
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 {
|
||||
TouchPhase::Started | TouchPhase::Moved => {
|
||||
if let Some(point) = self.points.iter_mut().find(|p| p.id == touch.id) {
|
||||
|
@ -31,7 +32,7 @@ impl TouchControls {
|
|||
self.points.push(TouchPoint {
|
||||
id: touch.id,
|
||||
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::time::Instant;
|
||||
|
||||
use ggez::{Context, ContextBuilder, filesystem, GameError, GameResult};
|
||||
use ggez::{Context, ContextBuilder, GameError, GameResult};
|
||||
use ggez::conf::{Backend, WindowMode, WindowSetup};
|
||||
use ggez::event::{KeyCode, KeyMods};
|
||||
use ggez::filesystem::mount_vfs;
|
||||
|
@ -20,7 +20,7 @@ use ggez::mint::ColumnMatrix4;
|
|||
use ggez::nalgebra::Vector2;
|
||||
use log::*;
|
||||
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 crate::builtin_fs::BuiltinFS;
|
||||
|
@ -42,6 +42,7 @@ mod engine_constants;
|
|||
mod entity;
|
||||
mod frame;
|
||||
mod inventory;
|
||||
mod input;
|
||||
mod live_debugger;
|
||||
mod macros;
|
||||
mod map;
|
||||
|
@ -49,16 +50,16 @@ mod menu;
|
|||
mod npc;
|
||||
mod physics;
|
||||
mod player;
|
||||
mod player_hit;
|
||||
mod profile;
|
||||
mod rng;
|
||||
mod scene;
|
||||
mod settings;
|
||||
mod shaders;
|
||||
mod shared_game_state;
|
||||
mod stage;
|
||||
mod sound;
|
||||
mod text_script;
|
||||
mod texture_set;
|
||||
mod touch_controls;
|
||||
mod ui;
|
||||
mod weapon;
|
||||
|
||||
|
@ -165,14 +166,6 @@ impl Game {
|
|||
// todo: proper keymaps?
|
||||
let state = &mut self.state;
|
||||
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::F8 => {
|
||||
if state.settings.speed > 0.2 {
|
||||
|
@ -191,21 +184,8 @@ impl Game {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
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) => {
|
||||
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 {
|
||||
|
|
|
@ -64,10 +64,10 @@ impl LiveDebugger {
|
|||
.build(ui, || {
|
||||
ui.text(format!(
|
||||
"Player position: ({:.1},{:.1}), velocity: ({:.1},{:.1})",
|
||||
game_scene.player.x as f32 / 512.0,
|
||||
game_scene.player.y as f32 / 512.0,
|
||||
game_scene.player.vel_x as f32 / 512.0,
|
||||
game_scene.player.vel_y as f32 / 512.0,
|
||||
game_scene.player1.x as f32 / 512.0,
|
||||
game_scene.player1.y as f32 / 512.0,
|
||||
game_scene.player1.vel_x as f32 / 512.0,
|
||||
game_scene.player1.vel_y as f32 / 512.0,
|
||||
));
|
||||
|
||||
ui.text(format!(
|
||||
|
@ -77,7 +77,7 @@ impl LiveDebugger {
|
|||
));
|
||||
|
||||
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]) {
|
||||
match GameScene::new(state, ctx, self.selected_stage as usize) {
|
||||
Ok(mut scene) => {
|
||||
scene.inventory = game_scene.inventory.clone();
|
||||
scene.player = game_scene.player.clone();
|
||||
scene.player.x = (scene.stage.map.width / 2 * 16 * 0x200) as isize;
|
||||
scene.player.y = (scene.stage.map.height / 2 * 16 * 0x200) as isize;
|
||||
scene.inventory_player1 = game_scene.inventory_player1.clone();
|
||||
scene.player1 = game_scene.player1.clone();
|
||||
scene.player1.x = (scene.stage.map.width / 2 * 16 * 0x200) as isize;
|
||||
scene.player1.y = (scene.stage.map.height / 2 * 16 * 0x200) as isize;
|
||||
|
||||
if scene.player.life == 0 {
|
||||
scene.player.life = scene.player.max_life;
|
||||
if scene.player1.life == 0 {
|
||||
scene.player1.life = scene.player1.max_life;
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
ui.checkbox_flags(im_str!("Booster 0.8"), &mut game_scene.player.equip.0, 1);
|
||||
ui.checkbox_flags(im_str!("Map System"), &mut game_scene.player.equip.0, 2);
|
||||
ui.checkbox_flags(im_str!("Arms Barrier"), &mut game_scene.player.equip.0, 4);
|
||||
ui.checkbox_flags(im_str!("Turbocharge"), &mut game_scene.player.equip.0, 8);
|
||||
ui.checkbox_flags(im_str!("Air Tank"), &mut game_scene.player.equip.0, 16);
|
||||
ui.checkbox_flags(im_str!("Booster 2.0"), &mut game_scene.player.equip.0, 32);
|
||||
ui.checkbox_flags(im_str!("Mimiga Mask"), &mut game_scene.player.equip.0, 64);
|
||||
ui.checkbox_flags(im_str!("Whimsical Star"), &mut game_scene.player.equip.0, 128);
|
||||
ui.checkbox_flags(im_str!("Nikumaru Counter"), &mut game_scene.player.equip.0, 256);
|
||||
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.player1.equip.0, 2);
|
||||
ui.checkbox_flags(im_str!("Arms Barrier"), &mut game_scene.player1.equip.0, 4);
|
||||
ui.checkbox_flags(im_str!("Turbocharge"), &mut game_scene.player1.equip.0, 8);
|
||||
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.player1.equip.0, 32);
|
||||
ui.checkbox_flags(im_str!("Mimiga Mask"), &mut game_scene.player1.equip.0, 64);
|
||||
ui.checkbox_flags(im_str!("Whimsical Star"), &mut game_scene.player1.equip.0, 128);
|
||||
ui.checkbox_flags(im_str!("Nikumaru Counter"), &mut game_scene.player1.equip.0, 256);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::common::Rect;
|
||||
use ggez::{Context, GameResult};
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||
|
||||
pub enum MenuEntry {
|
||||
Active(String),
|
||||
|
@ -177,18 +178,18 @@ impl Menu {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, state: &mut SharedGameState) -> MenuSelectionResult {
|
||||
state.update_key_trigger();
|
||||
pub fn tick(&mut self, controller: &mut CombinedMenuController, state: &mut SharedGameState) -> MenuSelectionResult {
|
||||
controller.update_trigger();
|
||||
|
||||
if state.key_trigger.fire() {
|
||||
if controller.trigger_back() {
|
||||
state.sound_manager.play_sfx(5);
|
||||
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);
|
||||
loop {
|
||||
if state.key_trigger.down() {
|
||||
if controller.trigger_down() {
|
||||
self.selected += 1;
|
||||
if self.selected == self.entries.len() {
|
||||
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) {
|
||||
match entry {
|
||||
MenuEntry::Active(_) | MenuEntry::Toggle(_, _) => {
|
|
@ -37,6 +37,8 @@ pub trait PhysicalEntity {
|
|||
fn direction(&self) -> Direction;
|
||||
fn is_player(&self) -> bool;
|
||||
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) {
|
||||
let bounds_x = if self.is_player() { 5 } else { 8 };
|
||||
|
@ -53,7 +55,7 @@ pub trait PhysicalEntity {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +75,7 @@ pub trait PhysicalEntity {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
use std::clone::Clone;
|
||||
|
||||
use ggez::{Context, GameResult};
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::clamp;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::{Condition, Direction, Equipment, Flag, interpolate_fix9_scale, Rect};
|
||||
use crate::entity::GameEntity;
|
||||
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::shared_game_state::SharedGameState;
|
||||
|
||||
mod player_hit;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum ControlMode {
|
||||
|
@ -31,6 +34,19 @@ pub enum PlayerAppearance {
|
|||
Curly,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
pub enum TargetPlayer {
|
||||
Player1,
|
||||
Player2,
|
||||
}
|
||||
|
||||
impl TargetPlayer {
|
||||
#[inline]
|
||||
pub fn index(self) -> usize {
|
||||
self as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Player {
|
||||
pub x: isize,
|
||||
|
@ -61,6 +77,7 @@ pub struct Player {
|
|||
pub air_counter: u16,
|
||||
pub air: u16,
|
||||
pub appearance: PlayerAppearance,
|
||||
pub controller: Box<dyn PlayerController>,
|
||||
weapon_offset_y: i8,
|
||||
index_x: isize,
|
||||
index_y: isize,
|
||||
|
@ -113,6 +130,7 @@ impl Player {
|
|||
air_counter: 0,
|
||||
air: 0,
|
||||
appearance: PlayerAppearance::Quote,
|
||||
controller: Box::new(DummyPlayerController::new()),
|
||||
bubble: 0,
|
||||
damage_counter: 0,
|
||||
damage_taken: 0,
|
||||
|
@ -181,23 +199,33 @@ impl Player {
|
|||
}
|
||||
|
||||
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.question = true;
|
||||
} 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if state.key_state.left() {
|
||||
if self.controller.move_left() {
|
||||
self.direction = Direction::Left;
|
||||
}
|
||||
|
||||
if state.key_state.right() {
|
||||
if self.controller.move_right() {
|
||||
self.direction = Direction::Right;
|
||||
}
|
||||
}
|
||||
|
@ -221,7 +249,7 @@ impl Player {
|
|||
}
|
||||
} else { // air movement
|
||||
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() {
|
||||
self.booster_switch = 1;
|
||||
|
||||
|
@ -229,19 +257,19 @@ impl Player {
|
|||
self.vel_y /= 2;
|
||||
}
|
||||
} 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.vel_x = 0;
|
||||
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.vel_x = state.constants.booster.b2_0_left;
|
||||
self.vel_y = 0;
|
||||
} else if state.key_state.right() {
|
||||
} else if self.controller.move_right() {
|
||||
self.booster_switch = 1;
|
||||
self.vel_x = state.constants.booster.b2_0_right;
|
||||
self.vel_y = 0;
|
||||
} else if state.key_state.down() {
|
||||
} else if self.controller.move_down() {
|
||||
self.booster_switch = 3;
|
||||
self.vel_x = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if state.key_state.left() {
|
||||
if self.controller.look_left() {
|
||||
self.direction = Direction::Left;
|
||||
}
|
||||
|
||||
if state.key_state.right() {
|
||||
if self.controller.look_right() {
|
||||
self.direction = Direction::Right;
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
1 => { self.vel_x /= 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;
|
||||
}
|
||||
}
|
||||
|
||||
// jumping
|
||||
if state.control_flags.control_enabled() {
|
||||
self.up = state.key_state.up();
|
||||
self.down = state.key_state.down() && !self.flags.hit_bottom_wall();
|
||||
self.up = self.controller.move_up();
|
||||
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;
|
||||
state.sound_manager.play_sfx(15);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -334,7 +369,7 @@ impl Player {
|
|||
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 {
|
||||
state.create_caret(self.x + 0x400, self.y + 0x400, CaretType::Exhaust, self.direction.opposite());
|
||||
}
|
||||
|
@ -344,12 +379,12 @@ impl Player {
|
|||
2 => {
|
||||
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.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.sound_manager.play_sfx(113);
|
||||
}
|
||||
|
@ -369,13 +404,13 @@ impl Player {
|
|||
if self.flags.hit_top_wall() {
|
||||
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;
|
||||
} else {
|
||||
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 {
|
||||
self.vel_y = -self.vel_x;
|
||||
}
|
||||
|
@ -442,12 +477,12 @@ impl Player {
|
|||
// camera
|
||||
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
|
||||
if self.index_y < -0x8000 { // -64.0fix9
|
||||
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
|
||||
if self.index_y > 0x8000 { // -64.0fix9
|
||||
self.index_y = 0x8000;
|
||||
|
@ -487,7 +522,7 @@ impl Player {
|
|||
if self.flags.hit_bottom_wall() {
|
||||
if self.cond.interacted() {
|
||||
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.anim_counter += 1;
|
||||
|
@ -503,7 +538,7 @@ impl Player {
|
|||
if self.anim_num > 9 || 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.anim_counter += 1;
|
||||
|
@ -519,7 +554,7 @@ impl Player {
|
|||
if self.anim_num > 4 || 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() {
|
||||
state.sound_manager.play_sfx(24);
|
||||
}
|
||||
|
@ -534,9 +569,9 @@ impl Player {
|
|||
self.cond.set_fallen(false);
|
||||
self.anim_num = 0;
|
||||
}
|
||||
} else if state.key_state.up() {
|
||||
} else if self.controller.look_up() {
|
||||
self.anim_num = 6;
|
||||
} else if state.key_state.down() {
|
||||
} else if self.controller.look_down() {
|
||||
self.anim_num = 10;
|
||||
} else {
|
||||
self.anim_num = if self.vel_y > 0 { 1 } else { 3 };
|
|
@ -79,6 +79,14 @@ impl PhysicalEntity for Player {
|
|||
fn is_player(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn player_left_pressed(&self) -> bool {
|
||||
self.controller.move_left()
|
||||
}
|
||||
|
||||
fn player_right_pressed(&self) -> bool {
|
||||
self.controller.move_right()
|
||||
}
|
||||
}
|
||||
|
||||
impl Player {
|
|
@ -53,14 +53,14 @@ impl GameProfile {
|
|||
|
||||
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.current_item = self.current_item as u16;
|
||||
game_scene.inventory_player1.current_weapon = self.current_weapon as u16;
|
||||
game_scene.inventory_player1.current_item = self.current_item as u16;
|
||||
for weapon in self.weapon_data.iter() {
|
||||
if weapon.weapon_id == 0 { continue; }
|
||||
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon.weapon_id as u8);
|
||||
|
||||
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.level = match weapon.level {
|
||||
2 => { WeaponLevel::Level2 }
|
||||
|
@ -74,7 +74,7 @@ impl GameProfile {
|
|||
for item in self.items.iter().copied() {
|
||||
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() {
|
||||
|
@ -94,32 +94,32 @@ impl GameProfile {
|
|||
if flags & 0b10000000 != 0 { state.game_flags.set(idx * 8 + 7, true); }
|
||||
}
|
||||
|
||||
game_scene.player.equip.0 = self.equipment as u16;
|
||||
game_scene.player.cond.0 = 0x80;
|
||||
game_scene.player1.equip.0 = self.equipment as u16;
|
||||
game_scene.player1.cond.0 = 0x80;
|
||||
|
||||
game_scene.player.x = self.pos_x as isize;
|
||||
game_scene.player.y = self.pos_y as isize;
|
||||
game_scene.player1.x = self.pos_x 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.player.direction = self.direction;
|
||||
game_scene.player.life = self.life;
|
||||
game_scene.player.max_life = self.max_life;
|
||||
game_scene.player.stars = clamp(self.stars, 0, 3) as u8;
|
||||
game_scene.player1.control_mode = if self.control_mode == 1 { ControlMode::IronHead } else { ControlMode::Normal };
|
||||
game_scene.player1.direction = self.direction;
|
||||
game_scene.player1.life = self.life;
|
||||
game_scene.player1.max_life = self.max_life;
|
||||
game_scene.player1.stars = clamp(self.stars, 0, 3) as u8;
|
||||
}
|
||||
|
||||
pub fn dump(state: &mut SharedGameState, game_scene: &mut GameScene) -> GameProfile {
|
||||
let current_map = game_scene.stage_id as u32;
|
||||
let current_song = state.sound_manager.current_song() as u32;
|
||||
let pos_x = game_scene.player.x as i32;
|
||||
let pos_y = game_scene.player.y as i32;
|
||||
let direction = game_scene.player.direction;
|
||||
let max_life = game_scene.player.max_life;
|
||||
let stars = game_scene.player.stars as u16;
|
||||
let life = game_scene.player.life;
|
||||
let current_weapon = game_scene.inventory.current_weapon as u32;
|
||||
let current_item = game_scene.inventory.current_item as u32;
|
||||
let equipment = game_scene.player.equip.0 as u32;
|
||||
let control_mode = game_scene.player.control_mode as u32;
|
||||
let pos_x = game_scene.player1.x as i32;
|
||||
let pos_y = game_scene.player1.y as i32;
|
||||
let direction = game_scene.player1.direction;
|
||||
let max_life = game_scene.player1.max_life;
|
||||
let stars = game_scene.player1.stars as u16;
|
||||
let life = game_scene.player1.life;
|
||||
let current_weapon = game_scene.inventory_player1.current_weapon as u32;
|
||||
let current_item = game_scene.inventory_player1.current_item as u32;
|
||||
let equipment = game_scene.player1.equip.0 as u32;
|
||||
let control_mode = game_scene.player1.control_mode as u32;
|
||||
let counter = 0; // TODO
|
||||
let mut weapon_data = [
|
||||
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() {
|
||||
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.level = weapon.level as u32;
|
||||
weap.exp = weapon.experience as u32;
|
||||
|
@ -154,7 +154,7 @@ impl GameProfile {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::cell::Cell;
|
||||
|
||||
/// Deterministic XorShift-based random number generator
|
||||
pub struct RNG(Cell<(u64, u64, u64, u64)>);
|
||||
|
||||
#[inline]
|
||||
|
@ -48,6 +49,14 @@ impl RNG {
|
|||
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 {
|
||||
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::common::{Direction, FadeDirection, FadeState, fix9_scale, interpolate_fix9_scale, Rect};
|
||||
use crate::components::boss_life_bar::BossLifeBar;
|
||||
use crate::components::stage_select::StageSelect;
|
||||
use crate::entity::GameEntity;
|
||||
use crate::frame::{Frame, UpdateTarget};
|
||||
use crate::inventory::{Inventory, TakeExperienceResult};
|
||||
|
@ -30,13 +31,15 @@ pub struct GameScene {
|
|||
pub tick: usize,
|
||||
pub stage: Stage,
|
||||
pub boss_life_bar: BossLifeBar,
|
||||
pub stage_select: StageSelect,
|
||||
pub frame: Frame,
|
||||
pub player: Player,
|
||||
pub inventory: Inventory,
|
||||
pub player1: Player,
|
||||
pub player2: Player,
|
||||
pub inventory_player1: Inventory,
|
||||
pub inventory_player2: Inventory,
|
||||
pub stage_id: usize,
|
||||
pub npc_map: NPCMap,
|
||||
pub bullet_manager: BulletManager,
|
||||
pub current_teleport_slot: u8,
|
||||
pub intro_mode: bool,
|
||||
water_visible: bool,
|
||||
tex_background_name: String,
|
||||
|
@ -44,7 +47,6 @@ pub struct GameScene {
|
|||
life_bar: u16,
|
||||
life_bar_counter: u16,
|
||||
map_name_counter: u16,
|
||||
stage_select_text_y_pos: usize,
|
||||
weapon_x_pos: isize,
|
||||
}
|
||||
|
||||
|
@ -77,9 +79,12 @@ impl GameScene {
|
|||
Ok(Self {
|
||||
tick: 0,
|
||||
stage,
|
||||
player: Player::new(state),
|
||||
inventory: Inventory::new(),
|
||||
player1: Player::new(state),
|
||||
player2: Player::new(state),
|
||||
inventory_player1: Inventory::new(),
|
||||
inventory_player2: Inventory::new(),
|
||||
boss_life_bar: BossLifeBar::new(),
|
||||
stage_select: StageSelect::new(),
|
||||
frame: Frame {
|
||||
x: 0,
|
||||
y: 0,
|
||||
|
@ -93,7 +98,6 @@ impl GameScene {
|
|||
stage_id: id,
|
||||
npc_map: NPCMap::new(),
|
||||
bullet_manager: BulletManager::new(),
|
||||
current_teleport_slot: 0,
|
||||
intro_mode: false,
|
||||
water_visible: true,
|
||||
tex_background_name,
|
||||
|
@ -101,7 +105,6 @@ impl GameScene {
|
|||
life_bar: 0,
|
||||
life_bar_counter: 0,
|
||||
map_name_counter: 0,
|
||||
stage_select_text_y_pos: 54,
|
||||
weapon_x_pos: 16,
|
||||
})
|
||||
}
|
||||
|
@ -127,8 +130,8 @@ impl GameScene {
|
|||
fn draw_hud(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
// none
|
||||
let weap_x = self.weapon_x_pos as f32;
|
||||
let (ammo, max_ammo) = self.inventory.get_current_ammo();
|
||||
let (xp, max_xp, max_level) = self.inventory.get_current_max_exp(&state.constants);
|
||||
let (ammo, max_ammo) = self.inventory_player1.get_current_ammo();
|
||||
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")?;
|
||||
|
||||
if max_ammo == 0 {
|
||||
|
@ -159,20 +162,20 @@ impl GameScene {
|
|||
&Rect::new_size(0, 80, bar_width, 8));
|
||||
}
|
||||
|
||||
if self.player.max_life != 0 {
|
||||
if self.player1.max_life != 0 {
|
||||
// life box
|
||||
batch.add_rect(16.0, 40.0,
|
||||
&Rect::new_size(0, 40, 64, 8));
|
||||
// yellow bar
|
||||
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
|
||||
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 {
|
||||
let rect = if self.player.air % 30 > 10 {
|
||||
if self.player1.air_counter > 0 {
|
||||
let rect = if self.player1.air % 30 > 10 {
|
||||
Rect::new_size(112, 72, 32, 8)
|
||||
} else {
|
||||
Rect::new_size(112, 80, 32, 8)
|
||||
|
@ -185,9 +188,9 @@ impl GameScene {
|
|||
batch.draw(ctx)?;
|
||||
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 {
|
||||
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);
|
||||
|
||||
for a in 0..weapon_count {
|
||||
|
@ -205,7 +208,7 @@ impl GameScene {
|
|||
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.right = rect.left + 16;
|
||||
batch.add_rect(pos_x, 16.0, &rect);
|
||||
|
@ -215,17 +218,17 @@ impl GameScene {
|
|||
|
||||
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,
|
||||
(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 {
|
||||
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 + 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)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -614,9 +617,9 @@ impl GameScene {
|
|||
let scale = state.scale;
|
||||
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() {
|
||||
self.draw_light(fix9_scale(self.player.x - self.frame.x, scale),
|
||||
fix9_scale(self.player.y - self.frame.y, scale),
|
||||
if !self.player1.cond.hidden() && self.inventory_player1.get_current_weapon().is_some() {
|
||||
self.draw_light(fix9_scale(self.player1.x - self.frame.x, scale),
|
||||
fix9_scale(self.player1.y - self.frame.y, scale),
|
||||
4.0, (140, 140, 140), batch);
|
||||
}
|
||||
|
||||
|
@ -943,7 +946,7 @@ impl GameScene {
|
|||
// 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_interactions_disabled(true);
|
||||
state.textscript_vm.start_script(npc.event_num);
|
||||
|
@ -1030,7 +1033,7 @@ impl GameScene {
|
|||
if npc.life == 0 {
|
||||
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_interactions_disabled(true);
|
||||
state.textscript_vm.start_script(npc.event_num);
|
||||
|
@ -1078,51 +1081,48 @@ impl GameScene {
|
|||
}
|
||||
|
||||
if !dead_npcs.is_empty() {
|
||||
let missile = self.inventory.has_weapon(WeaponType::MissileLauncher)
|
||||
| self.inventory.has_weapon(WeaponType::SuperMissileLauncher);
|
||||
self.npc_map.process_dead_npcs(&dead_npcs, missile, &self.player, state);
|
||||
let missile = self.inventory_player1.has_weapon(WeaponType::MissileLauncher)
|
||||
|| self.inventory_player1.has_weapon(WeaponType::SuperMissileLauncher);
|
||||
self.npc_map.process_dead_npcs(&dead_npcs, missile, &self.player1, state);
|
||||
self.npc_map.garbage_collect();
|
||||
}
|
||||
}
|
||||
|
||||
fn tick_world(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
self.stage_select_text_y_pos = 54;
|
||||
self.current_teleport_slot = 0;
|
||||
|
||||
self.player.current_weapon = {
|
||||
if let Some(weapon) = self.inventory.get_current_weapon_mut() {
|
||||
self.player1.current_weapon = {
|
||||
if let Some(weapon) = self.inventory_player1.get_current_weapon_mut() {
|
||||
weapon.wtype as u8
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
self.player.tick(state, ())?;
|
||||
self.player1.tick(state, ())?;
|
||||
|
||||
if self.player.damage > 0 {
|
||||
let xp_loss = self.player.damage * if self.player.equip.has_arms_barrier() { 1 } else { 2 };
|
||||
match self.inventory.take_xp(xp_loss, state) {
|
||||
TakeExperienceResult::LevelDown if self.player.life > 0 => {
|
||||
state.create_caret(self.player.x, self.player.y, CaretType::LevelUp, Direction::Right);
|
||||
if self.player1.damage > 0 {
|
||||
let xp_loss = self.player1.damage * if self.player1.equip.has_arms_barrier() { 1 } else { 2 };
|
||||
match self.inventory_player1.take_xp(xp_loss, state) {
|
||||
TakeExperienceResult::LevelDown if self.player1.life > 0 => {
|
||||
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() {
|
||||
let mut npc = npc_cell.borrow_mut();
|
||||
|
||||
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.process_npc_changes(&self.player, state);
|
||||
self.npc_map.boss_map.tick(state, (&mut self.player1, &self.npc_map.npcs, &mut self.stage))?;
|
||||
self.npc_map.process_npc_changes(&self.player1, state);
|
||||
self.npc_map.garbage_collect();
|
||||
|
||||
self.player.tick_map_collisions(state, &mut self.stage);
|
||||
self.player.tick_npc_collisions(state, &mut self.npc_map, &mut self.inventory);
|
||||
self.player1.tick_map_collisions(state, &mut self.stage);
|
||||
self.player1.tick_npc_collisions(state, &mut self.npc_map, &mut self.inventory_player1);
|
||||
|
||||
for npc_cell in self.npc_map.npcs.values() {
|
||||
let mut npc = npc_cell.borrow_mut();
|
||||
|
@ -1136,19 +1136,19 @@ impl GameScene {
|
|||
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.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();
|
||||
|
||||
match self.frame.update_target {
|
||||
UpdateTarget::Player => {
|
||||
self.frame.target_x = self.player.target_x;
|
||||
self.frame.target_y = self.player.target_y;
|
||||
self.frame.target_x = self.player1.target_x;
|
||||
self.frame.target_y = self.player1.target_y;
|
||||
}
|
||||
UpdateTarget::NPC(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);
|
||||
|
||||
if state.control_flags.control_enabled() {
|
||||
if let Some(weapon) = self.inventory.get_current_weapon_mut() {
|
||||
weapon.shoot_bullet(&self.player, &mut self.bullet_manager, state);
|
||||
if let Some(weapon) = self.inventory_player1.get_current_weapon_mut() {
|
||||
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);
|
||||
self.inventory.next_weapon();
|
||||
self.inventory_player1.next_weapon();
|
||||
self.weapon_x_pos = 32;
|
||||
}
|
||||
|
||||
if state.key_trigger.weapon_prev() {
|
||||
if self.player1.controller.trigger_prev_weapon() {
|
||||
state.sound_manager.play_sfx(4);
|
||||
self.inventory.prev_weapon();
|
||||
self.inventory_player1.prev_weapon();
|
||||
self.weapon_x_pos = 0;
|
||||
}
|
||||
|
||||
// update health bar
|
||||
if self.life_bar < self.player.life as u16 {
|
||||
self.life_bar = self.player.life as u16;
|
||||
if self.life_bar < self.player1.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;
|
||||
if self.life_bar_counter > 30 {
|
||||
self.life_bar -= 1;
|
||||
|
@ -1208,88 +1208,6 @@ impl GameScene {
|
|||
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 {
|
||||
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)
|
||||
|
@ -1351,15 +1269,18 @@ impl GameScene {
|
|||
|
||||
impl Scene for GameScene {
|
||||
fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
let seed = (self.player.max_life as i32)
|
||||
.wrapping_add(self.player.x as i32)
|
||||
.wrapping_add(self.player.y as i32)
|
||||
let seed = (self.player1.max_life as i32)
|
||||
.wrapping_add(self.player1.x as i32)
|
||||
.wrapping_add(self.player1.y as i32)
|
||||
.wrapping_add(self.stage_id as i32)
|
||||
.wrapping_mul(7);
|
||||
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.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)?;
|
||||
for npc_data in npcs.iter() {
|
||||
log::info!("creating npc: {:?}", npc_data);
|
||||
|
@ -1384,32 +1305,33 @@ impl Scene for GameScene {
|
|||
|
||||
if state.constants.is_cs_plus {
|
||||
match state.season {
|
||||
Season::Halloween => self.player.appearance = PlayerAppearance::HalloweenQuote,
|
||||
Season::Christmas => self.player.appearance = PlayerAppearance::ReindeerQuote,
|
||||
Season::Halloween => self.player1.appearance = PlayerAppearance::HalloweenQuote,
|
||||
Season::Christmas => self.player1.appearance = PlayerAppearance::ReindeerQuote,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.npc_map.boss_map.boss_type = self.stage.data.boss_no as u16;
|
||||
self.player.target_x = self.player.x;
|
||||
self.player.target_y = self.player.y;
|
||||
self.frame.target_x = self.player.target_x;
|
||||
self.frame.target_y = self.player.target_y;
|
||||
self.player1.target_x = self.player1.x;
|
||||
self.player1.target_y = self.player1.y;
|
||||
self.frame.target_x = self.player1.target_x;
|
||||
self.frame.target_y = self.player1.target_y;
|
||||
self.frame.immediate_update(state, &self.stage);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
match state.textscript_vm.mode {
|
||||
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 {
|
||||
self.frame.prev_x = self.frame.x;
|
||||
self.frame.prev_y = self.frame.y;
|
||||
self.player.prev_x = self.player.x;
|
||||
self.player.prev_y = self.player.y;
|
||||
self.player1.prev_x = self.player1.x;
|
||||
self.player1.prev_y = self.player1.y;
|
||||
|
||||
for npc_cell in self.npc_map.npcs.values() {
|
||||
let mut npc = npc_cell.borrow_mut();
|
||||
|
@ -1509,7 +1431,7 @@ impl Scene for GameScene {
|
|||
npc.draw(state, ctx, &self.frame)?;
|
||||
}
|
||||
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 {
|
||||
self.draw_water(state, ctx)?;
|
||||
}
|
||||
|
@ -1534,7 +1456,7 @@ impl Scene for GameScene {
|
|||
}
|
||||
|
||||
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)?;
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::common::Rect;
|
|||
use crate::menu::{Menu, MenuEntry, MenuSelectionResult};
|
||||
use crate::scene::Scene;
|
||||
use crate::shared_game_state::{SharedGameState, TimingMode};
|
||||
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
|
@ -17,6 +18,7 @@ enum CurrentMenu {
|
|||
|
||||
pub struct TitleScene {
|
||||
tick: usize,
|
||||
controller: CombinedMenuController,
|
||||
current_menu: CurrentMenu,
|
||||
main_menu: Menu,
|
||||
option_menu: Menu,
|
||||
|
@ -26,6 +28,7 @@ impl TitleScene {
|
|||
pub fn new() -> Self {
|
||||
Self {
|
||||
tick: 0,
|
||||
controller: CombinedMenuController::new(),
|
||||
current_menu: CurrentMenu::MainMenu,
|
||||
main_menu: Menu::new(0, 0, 100, 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";
|
||||
|
||||
impl Scene for TitleScene {
|
||||
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
if self.tick == 0 {
|
||||
state.sound_manager.play_song(24, &state.constants, ctx)?;
|
||||
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;
|
||||
fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
self.controller.add(state.settings.create_player1_controller());
|
||||
self.controller.add(state.settings.create_player2_controller());
|
||||
|
||||
self.option_menu.push_entry(MenuEntry::Toggle("Original timing (50TPS)".to_string(), state.timing_mode == TimingMode::_50Hz));
|
||||
self.option_menu.push_entry(MenuEntry::Toggle("Lighting effects".to_string(), state.settings.shader_effects));
|
||||
if state.constants.supports_og_textures {
|
||||
self.option_menu.push_entry(MenuEntry::Toggle("Original textures".to_string(), state.settings.original_textures));
|
||||
} else {
|
||||
self.option_menu.push_entry(MenuEntry::Disabled("Original textures".to_string()));
|
||||
}
|
||||
state.sound_manager.play_song(24, &state.constants, ctx)?;
|
||||
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;
|
||||
|
||||
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;
|
||||
self.option_menu.push_entry(MenuEntry::Toggle("Original timing (50TPS)".to_string(), state.timing_mode == TimingMode::_50Hz));
|
||||
self.option_menu.push_entry(MenuEntry::Toggle("Lighting effects".to_string(), state.settings.shader_effects));
|
||||
if state.constants.supports_og_textures {
|
||||
self.option_menu.push_entry(MenuEntry::Toggle("Original textures".to_string(), state.settings.original_textures));
|
||||
} else {
|
||||
self.option_menu.push_entry(MenuEntry::Disabled("Original textures".to_string()));
|
||||
}
|
||||
|
||||
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.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 {
|
||||
CurrentMenu::MainMenu => {
|
||||
match self.main_menu.tick(state) {
|
||||
match self.main_menu.tick(&mut self.controller, state) {
|
||||
MenuSelectionResult::Selected(0, _) => {
|
||||
state.reset();
|
||||
state.sound_manager.play_song(0, &state.constants, ctx)?;
|
||||
|
@ -141,7 +151,7 @@ impl Scene for TitleScene {
|
|||
}
|
||||
}
|
||||
CurrentMenu::OptionMenu => {
|
||||
match self.option_menu.tick(state) {
|
||||
match self.option_menu.tick(&mut self.controller, state) {
|
||||
MenuSelectionResult::Selected(0, toggle) => {
|
||||
if let MenuEntry::Toggle(_, value) = toggle {
|
||||
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 chrono::{Datelike, Local};
|
||||
use gfx::{self, *};
|
||||
use ggez::{Context, filesystem, GameResult, graphics};
|
||||
use ggez::filesystem::OpenOptions;
|
||||
use ggez::graphics::{Canvas, Shader};
|
||||
use ggez::graphics::Canvas;
|
||||
use num_traits::clamp;
|
||||
|
||||
use crate::bmfont_renderer::BMFontRenderer;
|
||||
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::input::touch_controls::TouchControls;
|
||||
use crate::npc::{NPC, NPCTable};
|
||||
use crate::profile::GameProfile;
|
||||
use crate::rng::RNG;
|
||||
use crate::scene::game_scene::GameScene;
|
||||
use crate::scene::Scene;
|
||||
use crate::settings::Settings;
|
||||
use crate::shaders::Shaders;
|
||||
use crate::sound::SoundManager;
|
||||
use crate::stage::StageData;
|
||||
use crate::str;
|
||||
use crate::text_script::{ScriptMode, TextScriptExecutionState, TextScriptVM};
|
||||
use crate::texture_set::{TextureSet, g_mag};
|
||||
use crate::touch_controls::TouchControls;
|
||||
use crate::texture_set::{g_mag, TextureSet};
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
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 timing_mode: TimingMode,
|
||||
pub control_flags: ControlFlags,
|
||||
|
@ -137,8 +93,6 @@ pub struct SharedGameState {
|
|||
pub quake_counter: u16,
|
||||
pub teleporter_slots: Vec<(u16, u16)>,
|
||||
pub carets: Vec<Caret>,
|
||||
pub key_state: KeyState,
|
||||
pub key_trigger: KeyState,
|
||||
pub touch_controls: TouchControls,
|
||||
pub base_path: String,
|
||||
pub npc_table: NPCTable,
|
||||
|
@ -162,7 +116,6 @@ pub struct SharedGameState {
|
|||
pub sound_manager: SoundManager,
|
||||
pub settings: Settings,
|
||||
pub shutdown: bool,
|
||||
key_old: u16,
|
||||
}
|
||||
|
||||
impl SharedGameState {
|
||||
|
@ -175,7 +128,7 @@ impl SharedGameState {
|
|||
|
||||
let mut constants = EngineConstants::defaults();
|
||||
let mut base_path = "/";
|
||||
let settings = SharedGameState::load_settings(ctx)?;
|
||||
let settings = Settings::load(ctx)?;
|
||||
|
||||
if filesystem::exists(ctx, "/base/Nicalis.bmp") {
|
||||
info!("Cave Story+ (PC) data files detected.");
|
||||
|
@ -213,8 +166,6 @@ impl SharedGameState {
|
|||
quake_counter: 0,
|
||||
teleporter_slots: Vec::with_capacity(8),
|
||||
carets: Vec::with_capacity(32),
|
||||
key_state: KeyState(0),
|
||||
key_trigger: KeyState(0),
|
||||
touch_controls: TouchControls::new(),
|
||||
base_path: str!(base_path),
|
||||
npc_table: NPCTable::new(),
|
||||
|
@ -238,21 +189,6 @@ impl SharedGameState {
|
|||
sound_manager: SoundManager::new(ctx)?,
|
||||
settings,
|
||||
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 {
|
||||
let mut next_scene = GameScene::new(self, ctx, 13)?;
|
||||
next_scene.player.x = 10 * 16 * 0x200;
|
||||
next_scene.player.y = 8 * 16 * 0x200;
|
||||
next_scene.player1.x = 10 * 16 * 0x200;
|
||||
next_scene.player1.y = 8 * 16 * 0x200;
|
||||
self.fade_state = FadeState::Hidden;
|
||||
self.textscript_vm.state = TextScriptExecutionState::Running(200, 0);
|
||||
|
||||
|
@ -280,9 +216,9 @@ impl SharedGameState {
|
|||
|
||||
pub fn start_intro(&mut self, ctx: &mut Context) -> GameResult {
|
||||
let mut next_scene = GameScene::new(self, ctx, 72)?;
|
||||
next_scene.player.cond.set_hidden(true);
|
||||
next_scene.player.x = 3 * 16 * 0x200;
|
||||
next_scene.player.y = 3 * 16 * 0x200;
|
||||
next_scene.player1.cond.set_hidden(true);
|
||||
next_scene.player1.x = 3 * 16 * 0x200;
|
||||
next_scene.player1.y = 3 * 16 * 0x200;
|
||||
next_scene.intro_mode = true;
|
||||
self.fade_state = FadeState::Hidden;
|
||||
self.textscript_vm.state = TextScriptExecutionState::Running(100, 0);
|
||||
|
@ -334,9 +270,6 @@ impl SharedGameState {
|
|||
self.teleporter_slots.clear();
|
||||
self.quake_counter = 0;
|
||||
self.carets.clear();
|
||||
self.key_state.0 = 0;
|
||||
self.key_trigger.0 = 0;
|
||||
self.key_old = 0;
|
||||
self.new_npcs.clear();
|
||||
self.textscript_vm.set_mode(ScriptMode::Map);
|
||||
self.textscript_vm.suspend = true;
|
||||
|
@ -353,13 +286,6 @@ impl SharedGameState {
|
|||
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) {
|
||||
for caret in self.carets.iter_mut() {
|
||||
caret.tick(&self.effect_rng, &self.constants);
|
||||
|
|
|
@ -561,7 +561,10 @@ impl TextScriptVM {
|
|||
if remaining > 1 {
|
||||
let ticks = if state.textscript_vm.flags.fast() {
|
||||
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
|
||||
} else {
|
||||
4
|
||||
|
@ -593,13 +596,16 @@ impl TextScriptVM {
|
|||
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.textscript_vm.state = TextScriptExecutionState::WaitConfirmation(event, ip, no_event, 0, !selection);
|
||||
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);
|
||||
match selection {
|
||||
ConfirmSelection::Yes => {
|
||||
|
@ -614,13 +620,16 @@ impl TextScriptVM {
|
|||
break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
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);
|
||||
}
|
||||
break;
|
||||
|
@ -694,7 +703,7 @@ impl TextScriptVM {
|
|||
state.textscript_vm.flags.set_background_visible(false);
|
||||
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;
|
||||
|
||||
exec_state = TextScriptExecutionState::Ended;
|
||||
|
@ -702,7 +711,7 @@ impl TextScriptVM {
|
|||
OpCode::SLP => {
|
||||
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
|
||||
} else {
|
||||
1000
|
||||
|
@ -726,7 +735,7 @@ impl TextScriptVM {
|
|||
state.control_flags.set_tick_world(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);
|
||||
}
|
||||
|
@ -734,8 +743,8 @@ impl TextScriptVM {
|
|||
state.control_flags.set_tick_world(true);
|
||||
state.control_flags.set_control_enabled(false);
|
||||
|
||||
game_scene.player.up = false;
|
||||
game_scene.player.shock_counter = 0;
|
||||
game_scene.player1.up = false;
|
||||
game_scene.player1.shock_counter = 0;
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
|
@ -748,7 +757,7 @@ impl TextScriptVM {
|
|||
OpCode::MYD => {
|
||||
let new_direction = read_cur_varint(&mut cursor)? as usize;
|
||||
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);
|
||||
|
@ -756,14 +765,14 @@ impl TextScriptVM {
|
|||
OpCode::MYB => {
|
||||
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) {
|
||||
match direction {
|
||||
Direction::Left => game_scene.player.vel_x = 0x200,
|
||||
Direction::Up => game_scene.player.vel_y = -0x200,
|
||||
Direction::Right => game_scene.player.vel_x = -0x200,
|
||||
Direction::Bottom => game_scene.player.vel_y = 0x200,
|
||||
Direction::Left => game_scene.player1.vel_x = 0x200,
|
||||
Direction::Up => game_scene.player1.vel_y = -0x200,
|
||||
Direction::Right => game_scene.player1.vel_x = -0x200,
|
||||
Direction::Bottom => game_scene.player1.vel_y = 0x200,
|
||||
Direction::FacingPlayer => {
|
||||
// todo npc direction dependent bump
|
||||
}
|
||||
|
@ -773,12 +782,12 @@ impl TextScriptVM {
|
|||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
@ -823,7 +832,7 @@ impl TextScriptVM {
|
|||
let item_id = 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);
|
||||
} else {
|
||||
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 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);
|
||||
} else {
|
||||
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 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);
|
||||
} else {
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
|
@ -893,7 +902,7 @@ impl TextScriptVM {
|
|||
}
|
||||
}
|
||||
OpCode::MM0 => {
|
||||
game_scene.player.vel_x = 0;
|
||||
game_scene.player1.vel_x = 0;
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
|
@ -925,8 +934,8 @@ impl TextScriptVM {
|
|||
}
|
||||
OpCode::MLp => {
|
||||
let life = read_cur_varint(&mut cursor)? as u16;
|
||||
game_scene.player.life += life;
|
||||
game_scene.player.max_life += life;
|
||||
game_scene.player1.life += life;
|
||||
game_scene.player1.max_life += life;
|
||||
|
||||
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)?;
|
||||
new_scene.intro_mode = game_scene.intro_mode;
|
||||
new_scene.inventory = game_scene.inventory.clone();
|
||||
new_scene.player = game_scene.player.clone();
|
||||
new_scene.player.vel_x = 0;
|
||||
new_scene.player.vel_y = 0;
|
||||
new_scene.player.x = pos_x;
|
||||
new_scene.player.y = pos_y;
|
||||
new_scene.inventory_player1 = game_scene.inventory_player1.clone();
|
||||
new_scene.player1 = game_scene.player1.clone();
|
||||
new_scene.player1.vel_x = 0;
|
||||
new_scene.player1.vel_y = 0;
|
||||
new_scene.player1.x = pos_x;
|
||||
new_scene.player1.y = pos_y;
|
||||
|
||||
state.control_flags.set_tick_world(true);
|
||||
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_y = read_cur_varint(&mut cursor)? as isize * 16 * 0x200;
|
||||
|
||||
game_scene.player.vel_x = 0;
|
||||
game_scene.player.vel_y = 0;
|
||||
game_scene.player.x = pos_x;
|
||||
game_scene.player.y = pos_y;
|
||||
game_scene.player1.vel_x = 0;
|
||||
game_scene.player1.vel_y = 0;
|
||||
game_scene.player1.x = pos_x;
|
||||
game_scene.player1.y = pos_y;
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
|
@ -1036,7 +1045,7 @@ impl TextScriptVM {
|
|||
|
||||
let mode: Option<ControlMode> = FromPrimitive::from_u8(control_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);
|
||||
|
@ -1177,7 +1186,7 @@ impl TextScriptVM {
|
|||
npc.tsc_direction = tsc_direction as u16;
|
||||
|
||||
if direction == Direction::FacingPlayer {
|
||||
npc.direction = if game_scene.player.x < npc.x {
|
||||
npc.direction = if game_scene.player1.x < npc.x {
|
||||
Direction::Right
|
||||
} else {
|
||||
Direction::Left
|
||||
|
@ -1234,7 +1243,7 @@ impl TextScriptVM {
|
|||
npc.tsc_direction = tsc_direction as u16;
|
||||
|
||||
if direction == Direction::FacingPlayer {
|
||||
npc.direction = if game_scene.player.x < npc.x {
|
||||
npc.direction = if game_scene.player1.x < npc.x {
|
||||
Direction::Right
|
||||
} else {
|
||||
Direction::Left
|
||||
|
@ -1265,7 +1274,7 @@ impl TextScriptVM {
|
|||
npc.tsc_direction = tsc_direction as u16;
|
||||
|
||||
if direction == Direction::FacingPlayer {
|
||||
npc.direction = if game_scene.player.x < npc.x {
|
||||
npc.direction = if game_scene.player1.x < npc.x {
|
||||
Direction::Right
|
||||
} else {
|
||||
Direction::Left
|
||||
|
@ -1294,7 +1303,7 @@ impl TextScriptVM {
|
|||
npc.tsc_direction = tsc_direction as u16;
|
||||
|
||||
if direction == Direction::FacingPlayer {
|
||||
npc.direction = if game_scene.player.x < npc.x {
|
||||
npc.direction = if game_scene.player1.x < npc.x {
|
||||
Direction::Right
|
||||
} else {
|
||||
Direction::Left
|
||||
|
@ -1310,15 +1319,15 @@ impl TextScriptVM {
|
|||
OpCode::LIp => {
|
||||
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);
|
||||
}
|
||||
OpCode::ITp => {
|
||||
let item_id = read_cur_varint(&mut cursor)? as u16;
|
||||
|
||||
if !game_scene.inventory.has_item(item_id) {
|
||||
game_scene.inventory.add_item(item_id);
|
||||
if !game_scene.inventory_player1.has_item(item_id) {
|
||||
game_scene.inventory_player1.add_item(item_id);
|
||||
}
|
||||
|
||||
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 amount = read_cur_varint(&mut cursor)? as u16;
|
||||
|
||||
if game_scene.inventory.has_item_amount(item_id, Ordering::Less, amount) {
|
||||
game_scene.inventory.add_item(item_id);
|
||||
if game_scene.inventory_player1.has_item_amount(item_id, Ordering::Less, amount) {
|
||||
game_scene.inventory_player1.add_item(item_id);
|
||||
}
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
|
@ -1336,7 +1345,7 @@ impl TextScriptVM {
|
|||
OpCode::ITm => {
|
||||
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);
|
||||
}
|
||||
|
@ -1346,7 +1355,7 @@ impl TextScriptVM {
|
|||
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon_id);
|
||||
|
||||
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);
|
||||
|
@ -1356,32 +1365,32 @@ impl TextScriptVM {
|
|||
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon_id);
|
||||
|
||||
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);
|
||||
}
|
||||
OpCode::AEp => {
|
||||
game_scene.inventory.refill_all_ammo();
|
||||
game_scene.inventory_player1.refill_all_ammo();
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
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);
|
||||
}
|
||||
OpCode::EQp => {
|
||||
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);
|
||||
}
|
||||
OpCode::EQm => {
|
||||
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);
|
||||
}
|
||||
|
@ -1455,7 +1464,7 @@ impl TextScriptVM {
|
|||
|
||||
if tick_npc != 0 {
|
||||
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::{Context, GameError, GameResult, graphics};
|
||||
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::nalgebra::{Point2, Vector2};
|
||||
use image::RgbaImage;
|
||||
|
@ -14,7 +14,8 @@ use log::info;
|
|||
use crate::common;
|
||||
use crate::common::FILE_TYPES;
|
||||
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;
|
||||
|
||||
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) {
|
||||
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 {
|
||||
WeaponLevel::Level1 => { 1 }
|
||||
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) {
|
||||
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 {
|
||||
WeaponLevel::Level1 => { 4 }
|
||||
WeaponLevel::Level2 => { 5 }
|
||||
|
@ -203,7 +203,7 @@ impl Weapon {
|
|||
|
||||
fn shoot_bullet_fireball(&mut self, player: &Player, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
|
||||
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 {
|
||||
WeaponLevel::Level1 => { 7 }
|
||||
WeaponLevel::Level2 => { 8 }
|
||||
|
|
Loading…
Reference in a new issue