add basic support for switch P2 skins
This commit is contained in:
parent
5ed2d40e23
commit
4be3dd518b
|
@ -1433,6 +1433,7 @@ impl EngineConstants {
|
|||
"ItemImage" => (256, 128),
|
||||
"Loading" => (64, 8),
|
||||
"MyChar" => (200, 64),
|
||||
"mychar_p2" => (200, 384), // switch
|
||||
"Npc/Npc0" => (32, 32),
|
||||
"Npc/NpcAlmo1" => (320, 240),
|
||||
"Npc/NpcAlmo2" => (320, 240),
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::clone::Clone;
|
|||
use num_derive::FromPrimitive;
|
||||
use num_traits::clamp;
|
||||
|
||||
use crate::common::{Condition, Direction, Equipment, Flag, interpolate_fix9_scale, Rect};
|
||||
use crate::common::{interpolate_fix9_scale, Condition, Direction, Equipment, Flag, Rect};
|
||||
use crate::components::number_popup::NumberPopup;
|
||||
use crate::entity::GameEntity;
|
||||
use crate::framework::context::Context;
|
||||
|
@ -12,8 +12,8 @@ use crate::game::caret::CaretType;
|
|||
use crate::game::frame::Frame;
|
||||
use crate::game::npc::list::NPCList;
|
||||
use crate::game::npc::NPC;
|
||||
use crate::game::player::skin::{PlayerAnimationState, PlayerAppearanceState, PlayerSkin};
|
||||
use crate::game::player::skin::basic::BasicPlayerSkin;
|
||||
use crate::game::player::skin::{PlayerAnimationState, PlayerAppearanceState, PlayerSkin};
|
||||
use crate::game::shared_game_state::SharedGameState;
|
||||
use crate::input::dummy_player_controller::DummyPlayerController;
|
||||
use crate::input::player_controller::PlayerController;
|
||||
|
@ -189,6 +189,12 @@ impl Player {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn load_skin(&mut self, texture_name: String, state: &mut SharedGameState, ctx: &mut Context) {
|
||||
self.skin = Box::new(BasicPlayerSkin::new(texture_name, state, ctx));
|
||||
self.display_bounds = self.skin.get_display_bounds();
|
||||
self.hit_bounds = self.skin.get_hit_bounds();
|
||||
}
|
||||
|
||||
fn tick_normal(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult {
|
||||
if !state.control_flags.interactions_disabled() && state.control_flags.control_enabled() {
|
||||
if self.equip.has_air_tank() {
|
||||
|
@ -402,10 +408,10 @@ impl Player {
|
|||
// stop interacting when moved
|
||||
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.controller.move_right()
|
||||
|| self.controller.move_up()
|
||||
|| self.controller.jump()
|
||||
|| self.controller.shoot())
|
||||
{
|
||||
self.cond.set_interacted(false);
|
||||
}
|
||||
|
@ -524,8 +530,8 @@ impl Player {
|
|||
if (self.flags.hit_bottom_wall() && self.flags.hit_right_higher_half() && self.vel_x < 0)
|
||||
|| (self.flags.hit_bottom_wall() && self.flags.hit_left_higher_half() && self.vel_x > 0)
|
||||
|| (self.flags.hit_bottom_wall()
|
||||
&& self.flags.hit_left_lower_half()
|
||||
&& self.flags.hit_right_lower_half())
|
||||
&& self.flags.hit_left_lower_half()
|
||||
&& self.flags.hit_right_lower_half())
|
||||
{
|
||||
self.vel_y = 0x400; // 2.0fix9
|
||||
}
|
||||
|
@ -533,9 +539,9 @@ impl Player {
|
|||
|
||||
let max_move = if self.flags.in_water()
|
||||
&& !(self.flags.force_left()
|
||||
|| self.flags.force_up()
|
||||
|| self.flags.force_right()
|
||||
|| self.flags.force_down())
|
||||
|| self.flags.force_up()
|
||||
|| self.flags.force_right()
|
||||
|| self.flags.force_down())
|
||||
{
|
||||
state.constants.player.water_physics.max_move
|
||||
} else {
|
||||
|
|
|
@ -302,6 +302,7 @@ pub struct SharedGameState {
|
|||
pub player_count: PlayerCount,
|
||||
pub player_count_modified_in_game: bool,
|
||||
pub player2_skin: u16,
|
||||
pub player2_uses_p2_skinsheet: bool,
|
||||
pub replay_state: ReplayState,
|
||||
pub mod_requirements: ModRequirements,
|
||||
pub loc: Locale,
|
||||
|
@ -455,6 +456,7 @@ impl SharedGameState {
|
|||
player_count: PlayerCount::One,
|
||||
player_count_modified_in_game: false,
|
||||
player2_skin: 0,
|
||||
player2_uses_p2_skinsheet: false,
|
||||
replay_state: ReplayState::None,
|
||||
mod_requirements,
|
||||
loc: locale,
|
||||
|
|
|
@ -2,8 +2,8 @@ use crate::framework::context::Context;
|
|||
use crate::framework::error::GameResult;
|
||||
use crate::game::shared_game_state::{PlayerCount, SharedGameState};
|
||||
use crate::input::combined_menu_controller::CombinedMenuController;
|
||||
use crate::menu::{Menu, MenuSelectionResult};
|
||||
use crate::menu::MenuEntry;
|
||||
use crate::menu::{Menu, MenuSelectionResult};
|
||||
|
||||
pub enum CurrentMenu {
|
||||
CoopMenu,
|
||||
|
@ -59,20 +59,26 @@ impl PlayerCountMenu {
|
|||
self.coop_menu = Menu::new(0, 0, 130, 0);
|
||||
self.skin_menu = Menu::new(0, 0, 130, 0);
|
||||
|
||||
self.coop_menu.push_entry(CoopMenuEntry::Title, MenuEntry::Disabled(state.loc.t("menus.coop_menu.title").to_owned()));
|
||||
self.coop_menu
|
||||
.push_entry(CoopMenuEntry::Title, MenuEntry::Disabled(state.loc.t("menus.coop_menu.title").to_owned()));
|
||||
self.coop_menu.push_entry(CoopMenuEntry::One, MenuEntry::Active(state.loc.t("menus.coop_menu.one").to_owned()));
|
||||
self.coop_menu.push_entry(CoopMenuEntry::Two, MenuEntry::Active(state.loc.t("menus.coop_menu.two").to_owned()));
|
||||
self.coop_menu.push_entry(CoopMenuEntry::Back, MenuEntry::Active(state.loc.t("common.back").to_owned()));
|
||||
|
||||
self.coop_menu.selected = CoopMenuEntry::One;
|
||||
|
||||
self.skin_menu.push_entry(SkinMenuEntry::Title, MenuEntry::Disabled(state.loc.t("menus.skin_menu.title").to_owned()));
|
||||
self.skin_menu
|
||||
.push_entry(SkinMenuEntry::Title, MenuEntry::Disabled(state.loc.t("menus.skin_menu.title").to_owned()));
|
||||
self.skin_menu.push_entry(SkinMenuEntry::Skin, MenuEntry::PlayerSkin);
|
||||
|
||||
if self.on_title {
|
||||
self.skin_menu.push_entry(SkinMenuEntry::Start, MenuEntry::Active(state.loc.t("menus.main_menu.start").to_owned()));
|
||||
self.skin_menu
|
||||
.push_entry(SkinMenuEntry::Start, MenuEntry::Active(state.loc.t("menus.main_menu.start").to_owned()));
|
||||
} else {
|
||||
self.skin_menu.push_entry(SkinMenuEntry::Add, MenuEntry::Active(state.loc.t("menus.pause_menu.add_player2").to_owned()));
|
||||
self.skin_menu.push_entry(
|
||||
SkinMenuEntry::Add,
|
||||
MenuEntry::Active(state.loc.t("menus.pause_menu.add_player2").to_owned()),
|
||||
);
|
||||
}
|
||||
|
||||
self.skin_menu.push_entry(SkinMenuEntry::Back, MenuEntry::Active(state.loc.t("common.back").to_owned()));
|
||||
|
@ -137,6 +143,21 @@ impl PlayerCountMenu {
|
|||
}
|
||||
MenuSelectionResult::Selected(SkinMenuEntry::Skin, _) => {
|
||||
state.player2_skin += 2;
|
||||
|
||||
if state.player2_uses_p2_skinsheet && state.player2_skin == 2 {
|
||||
state.player2_skin += 2;
|
||||
}
|
||||
|
||||
let current_skin_spritesheet = if state.player2_uses_p2_skinsheet { "mychar_p2" } else { "mychar" };
|
||||
|
||||
if let Some(tex_size) = state.constants.tex_sizes.get(current_skin_spritesheet) {
|
||||
// TODO: should probably have a way to figure out the height from the spritesheet ahead of time
|
||||
|
||||
if state.player2_skin * 2 * 16 >= tex_size.1 {
|
||||
state.player2_skin = 0;
|
||||
state.player2_uses_p2_skinsheet = !state.player2_uses_p2_skinsheet;
|
||||
}
|
||||
}
|
||||
}
|
||||
MenuSelectionResult::Selected(SkinMenuEntry::Start, _) => {
|
||||
state.player_count = PlayerCount::Two;
|
||||
|
|
139
src/menu/mod.rs
139
src/menu/mod.rs
|
@ -375,62 +375,90 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
|||
for (_, entry) in &self.entries {
|
||||
match entry {
|
||||
MenuEntry::Active(name) | MenuEntry::DisabledWhite(name) => {
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 20.0, y)
|
||||
.draw(name, ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 20.0, y).draw(
|
||||
name,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
}
|
||||
MenuEntry::Disabled(name) => {
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 20.0, y)
|
||||
.color((0xa0, 0xa0, 0xff, 0xff))
|
||||
.draw(name, ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 20.0, y).color((0xa0, 0xa0, 0xff, 0xff)).draw(
|
||||
name,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
}
|
||||
MenuEntry::Toggle(name, value) => {
|
||||
let value_text = if *value { "ON" } else { "OFF" };
|
||||
let name_text_len = state.font.builder().compute_width(name);
|
||||
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 20.0, y)
|
||||
.draw(name, ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 20.0, y).draw(
|
||||
name,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 25.0 + name_text_len, y)
|
||||
.draw(value_text, ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 25.0 + name_text_len, y).draw(
|
||||
value_text,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
}
|
||||
MenuEntry::Options(name, index, value) => {
|
||||
let value_text = if let Some(text) = value.get(*index) { text } else { "???" };
|
||||
let name_text_len = state.font.builder().compute_width(name);
|
||||
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 20.0, y)
|
||||
.draw(name, ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 20.0, y).draw(
|
||||
name,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 25.0 + name_text_len, y)
|
||||
.draw(value_text, ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 25.0 + name_text_len, y).draw(
|
||||
value_text,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
}
|
||||
MenuEntry::DescriptiveOptions(name, index, value, description) => {
|
||||
let value_text = if let Some(text) = value.get(*index) { text } else { "???" };
|
||||
let description_text = if let Some(text) = description.get(*index) { text } else { "???" };
|
||||
let name_text_len = state.font.builder().compute_width(name);
|
||||
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 20.0, y)
|
||||
.draw(name, ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 20.0, y).draw(
|
||||
name,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 25.0 + name_text_len, y)
|
||||
.draw(value_text, ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 25.0 + name_text_len, y).draw(
|
||||
value_text,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
|
||||
state.font.builder()
|
||||
state
|
||||
.font
|
||||
.builder()
|
||||
.position(self.x as f32 + 20.0, y + 16.0)
|
||||
.color((0xc0, 0xc0, 0xff, 0xff))
|
||||
.draw(description_text, ctx, &state.constants, &mut state.texture_set)?;
|
||||
}
|
||||
MenuEntry::OptionsBar(name, percent) => {
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 20.0, y)
|
||||
.draw(name, ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 20.0, y).draw(
|
||||
name,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
|
||||
if state.constants.is_switch || state.constants.is_cs_plus {
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "ui")?;
|
||||
|
@ -469,16 +497,24 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
|||
}
|
||||
}
|
||||
MenuEntry::NewSave => {
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 20.0, y)
|
||||
.draw(state.loc.t("menus.save_menu.new"), ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 20.0, y).draw(
|
||||
state.loc.t("menus.save_menu.new"),
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
}
|
||||
MenuEntry::PlayerSkin => {
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 20.0, y)
|
||||
.draw(state.loc.t("menus.skin_menu.label"), ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 20.0, y).draw(
|
||||
state.loc.t("menus.skin_menu.label"),
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "MyChar")?;
|
||||
let spritesheet_name = if state.player2_uses_p2_skinsheet { "mychar_p2" } else { "MyChar" };
|
||||
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, spritesheet_name)?;
|
||||
batch.add_rect(
|
||||
self.x as f32 + 88.0,
|
||||
y - 4.0,
|
||||
|
@ -496,9 +532,12 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
|||
let bar_width = (save.life as f32 / save.max_life as f32 * 39.0) as u16;
|
||||
let right_edge = self.x as f32 + self.width as f32 - 4.0;
|
||||
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 20.0, y)
|
||||
.draw(name, ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 20.0, y).draw(
|
||||
name,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
|
||||
if valid_save {
|
||||
// Lifebar
|
||||
|
@ -529,9 +568,12 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
|||
_ => difficulty_name.push_str("(unknown)"),
|
||||
}
|
||||
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 20.0, y + 10.0)
|
||||
.draw(difficulty_name.as_str(), ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 20.0, y + 10.0).draw(
|
||||
difficulty_name.as_str(),
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Weapons
|
||||
|
@ -554,15 +596,20 @@ impl<T: std::cmp::PartialEq + std::default::Default + Clone> Menu<T> {
|
|||
}
|
||||
}
|
||||
MenuEntry::Control(name, data) => {
|
||||
state.font.builder()
|
||||
.position(self.x as f32 + 20.0, y)
|
||||
.draw(name, ctx, &state.constants, &mut state.texture_set)?;
|
||||
state.font.builder().position(self.x as f32 + 20.0, y).draw(
|
||||
name,
|
||||
ctx,
|
||||
&state.constants,
|
||||
&mut state.texture_set,
|
||||
)?;
|
||||
|
||||
match data {
|
||||
ControlMenuData::String(value) => {
|
||||
let text_width = state.font.builder().compute_width(value);
|
||||
|
||||
state.font.builder()
|
||||
state
|
||||
.font
|
||||
.builder()
|
||||
.position(self.x as f32 + self.width as f32 - 5.0 - text_width, y)
|
||||
.draw(value, ctx, &state.constants, &mut state.texture_set)?;
|
||||
}
|
||||
|
|
|
@ -138,13 +138,19 @@ impl GameScene {
|
|||
Rc::new(RefCell::new(textures))
|
||||
};
|
||||
|
||||
let mut player2 = Player::new(state, ctx);
|
||||
|
||||
if state.player2_uses_p2_skinsheet {
|
||||
player2.load_skin("mychar_p2".to_owned(), state, ctx);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
tick: 0,
|
||||
stage,
|
||||
water_params,
|
||||
water_renderer,
|
||||
player1: Player::new(state, ctx),
|
||||
player2: Player::new(state, ctx),
|
||||
player2: player2,
|
||||
inventory_player1: Inventory::new(),
|
||||
inventory_player2: Inventory::new(),
|
||||
boss_life_bar: BossLifeBar::new(),
|
||||
|
|
Loading…
Reference in New Issue