Merge branch 'master' into horizon-os

This commit is contained in:
alula 2023-01-17 17:57:55 +01:00 committed by GitHub
commit c2a8bf52e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 199 additions and 83 deletions

View File

@ -337,6 +337,7 @@ pub struct EngineConstants {
pub music_table: Vec<String>,
pub organya_paths: Vec<String>,
pub credit_illustration_paths: Vec<String>,
pub player_skin_paths: Vec<String>,
pub animated_face_table: Vec<AnimatedFace>,
pub string_table: HashMap<String, String>,
pub missile_flags: Vec<u16>,
@ -369,6 +370,7 @@ impl Clone for EngineConstants {
music_table: self.music_table.clone(),
organya_paths: self.organya_paths.clone(),
credit_illustration_paths: self.credit_illustration_paths.clone(),
player_skin_paths: self.player_skin_paths.clone(),
animated_face_table: self.animated_face_table.clone(),
string_table: self.string_table.clone(),
missile_flags: self.missile_flags.clone(),
@ -1433,6 +1435,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),
@ -1665,6 +1668,7 @@ impl EngineConstants {
"Resource/BITMAP/".to_owned(), // CSE2E
"endpic/".to_owned(), // NXEngine
],
player_skin_paths: vec!["MyChar".to_owned()],
animated_face_table: vec![AnimatedFace { face_id: 0, anim_id: 0, anim_frames: vec![(0, 0)] }],
string_table: HashMap::new(),
missile_flags: vec![200, 201, 202, 218, 550, 766, 880, 920, 1551],
@ -1769,6 +1773,7 @@ impl EngineConstants {
self.textscript.fade_ticks = 21;
self.game.tile_offset_x = 3;
self.game.new_game_player_pos = (13, 8);
self.player_skin_paths.push("mychar_p2".to_owned());
}
pub fn apply_csdemo_patches(&mut self) {

View File

@ -497,14 +497,15 @@ struct SDL2Gamepad {
}
impl SDL2Gamepad {
fn new(inner: GameController) -> Box<dyn BackendGamepad> {
pub fn new(inner: GameController) -> Box<dyn BackendGamepad> {
Box::new(SDL2Gamepad { inner })
}
}
impl BackendGamepad for SDL2Gamepad {
fn set_rumble(&mut self, low_freq: u16, high_freq: u16, duration_ms: u32) -> GameResult {
self.inner.set_rumble(low_freq, high_freq, duration_ms).map_err(|e| GameError::GamepadError(e.to_string()))
let _ = self.inner.set_rumble(low_freq, high_freq, duration_ms);
Ok(())
}
fn instance_id(&self) -> u32 {

View File

@ -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 {

View File

@ -251,6 +251,24 @@ impl TileSize {
}
}
#[derive(PartialEq, Eq, Copy, Clone)]
pub struct PlayerSkinLocation {
pub texture_index: u16,
pub offset: u16,
}
impl PlayerSkinLocation {
pub const fn new(texture_index: u16, offset: u16) -> PlayerSkinLocation {
PlayerSkinLocation { texture_index, offset }
}
}
impl Default for PlayerSkinLocation {
fn default() -> PlayerSkinLocation {
PlayerSkinLocation::new(0, 0)
}
}
pub struct SharedGameState {
pub control_flags: ControlFlags,
pub game_flags: BitVec,
@ -301,7 +319,7 @@ pub struct SharedGameState {
pub difficulty: GameDifficulty,
pub player_count: PlayerCount,
pub player_count_modified_in_game: bool,
pub player2_skin: u16,
pub player2_skin_location: PlayerSkinLocation,
pub replay_state: ReplayState,
pub mod_requirements: ModRequirements,
pub loc: Locale,
@ -455,7 +473,7 @@ impl SharedGameState {
difficulty: GameDifficulty::Normal,
player_count: PlayerCount::One,
player_count_modified_in_game: false,
player2_skin: 0,
player2_skin_location: PlayerSkinLocation::default(),
replay_state: ReplayState::None,
mod_requirements,
loc: locale,

View File

@ -3,9 +3,9 @@ use itertools::Itertools;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::game::scripting::tsc::text_script::TextScriptExecutionState;
use crate::game::shared_game_state::SharedGameState;
use crate::scene::game_scene::GameScene;
use crate::game::scripting::tsc::text_script::TextScriptExecutionState;
use self::command_line::CommandLineParser;
@ -187,22 +187,22 @@ impl LiveDebugger {
}
#[cfg(feature = "scripting-lua")]
{
ui.same_line();
if ui.button("Reload Lua Scripts") {
if let Err(err) = state.lua.reload_scripts(ctx) {
log::error!("Error reloading scripts: {:?}", err);
self.error = Some(ImString::new(err.to_string()));
}
{
ui.same_line();
if ui.button("Reload Lua Scripts") {
if let Err(err) = state.lua.reload_scripts(ctx) {
log::error!("Error reloading scripts: {:?}", err);
self.error = Some(ImString::new(err.to_string()));
}
}
}
if game_scene.player2.cond.alive() {
if ui.button("Drop Player 2") {
game_scene.drop_player2();
}
} else if ui.button("Add Player 2") {
game_scene.add_player2(state);
game_scene.add_player2(state, ctx);
}
ui.same_line();
@ -216,7 +216,8 @@ impl LiveDebugger {
let _ = state.save_game(game_scene, ctx);
state.sound_manager.play_sfx(18);
}
} else if ui.button("Busy") {}
} else if ui.button("Busy") {
}
ui.same_line();
if ui.button("Hotkey List") {

View File

@ -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()));
@ -136,7 +142,24 @@ impl PlayerCountMenu {
}
}
MenuSelectionResult::Selected(SkinMenuEntry::Skin, _) => {
state.player2_skin += 2;
state.player2_skin_location.offset += 2;
let current_skin_spritesheet_name =
state.constants.player_skin_paths[state.player2_skin_location.texture_index as usize].as_str();
if let Some(tex_size) = state.constants.tex_sizes.get(current_skin_spritesheet_name) {
// TODO: should probably have a way to figure out the height from the spritesheet ahead of time
if state.player2_skin_location.offset * 2 * 16 >= tex_size.1 {
state.player2_skin_location.offset = 0;
if state.player2_skin_location.texture_index == 1 {
state.player2_skin_location.texture_index = 0;
} else {
state.player2_skin_location.texture_index = 1;
}
}
}
}
MenuSelectionResult::Selected(SkinMenuEntry::Start, _) => {
state.player_count = PlayerCount::Two;

View File

@ -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,20 +497,29 @@ 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 =
state.constants.player_skin_paths[state.player2_skin_location.texture_index as usize].as_str();
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,
&Rect::new_size(0, (state.player2_skin).saturating_mul(2 * 16), 16, 16),
&Rect::new_size(0, (state.player2_skin_location.offset).saturating_mul(2 * 16), 16, 16),
);
batch.draw(ctx)?;
}
@ -496,9 +533,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 +569,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 +597,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)?;
}

View File

@ -138,13 +138,21 @@ impl GameScene {
Rc::new(RefCell::new(textures))
};
let mut player2 = Player::new(state, ctx);
if state.player2_skin_location.texture_index != 0 {
let skinsheet_name =
state.constants.player_skin_paths[state.player2_skin_location.texture_index as usize].as_str();
player2.load_skin(skinsheet_name.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(),
@ -181,10 +189,16 @@ impl GameScene {
pub fn display_map_name(&mut self, ticks: u16) {
self.map_name_counter = ticks;
}
pub fn add_player2(&mut self, state: &mut SharedGameState) {
pub fn add_player2(&mut self, state: &mut SharedGameState, ctx: &mut Context) {
self.player2.cond.set_alive(true);
self.player2.cond.set_hidden(self.player1.cond.hidden());
self.player2.skin.set_skinsheet_offset(state.player2_skin);
let skinsheet_name =
state.constants.player_skin_paths[state.player2_skin_location.texture_index as usize].as_str();
self.player2.load_skin(skinsheet_name.to_owned(), state, ctx);
self.player2.skin.set_skinsheet_offset(state.player2_skin_location.offset);
self.player2.x = self.player1.x;
self.player2.y = self.player1.y;
self.player2.vel_x = self.player1.vel_x;
@ -1631,7 +1645,7 @@ impl Scene for GameScene {
self.replay.initialize_recording(state);
}
if state.player_count == PlayerCount::Two {
self.add_player2(state);
self.add_player2(state, ctx);
} else {
self.drop_player2();
}
@ -1737,7 +1751,7 @@ impl Scene for GameScene {
if state.player_count_modified_in_game {
if state.player_count == PlayerCount::Two {
self.add_player2(state);
self.add_player2(state, ctx);
} else {
self.drop_player2();
}