mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-01-06 02:56:41 +00:00
netplay shit, visual tweaks
This commit is contained in:
parent
01e35a09eb
commit
68cf299e96
11
Cargo.toml
11
Cargo.toml
|
@ -17,8 +17,8 @@ bench = false
|
|||
required-features = ["exe"]
|
||||
|
||||
[profile.release]
|
||||
lto = 'thin'
|
||||
panic = 'abort'
|
||||
lto = "off"
|
||||
panic = "abort"
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 3
|
||||
|
@ -33,13 +33,13 @@ category = "Game"
|
|||
osx_minimum_system_version = "10.12"
|
||||
|
||||
[features]
|
||||
default = ["scripting", "backend-sdl", "render-opengl", "ogg-playback", "exe"]
|
||||
default = ["scripting", "backend-sdl", "render-opengl", "ogg-playback", "exe", "netplay"]
|
||||
ogg-playback = ["lewton"]
|
||||
backend-sdl = ["sdl2", "sdl2-sys"]
|
||||
backend-glutin = ["winit", "glutin", "render-opengl"]
|
||||
render-opengl = []
|
||||
scripting = ["lua-ffi"]
|
||||
netplay = []
|
||||
netplay = ["tokio", "serde_cbor"]
|
||||
editor = []
|
||||
hooks = ["libc"]
|
||||
exe = []
|
||||
|
@ -73,11 +73,12 @@ sdl2 = { version = "=0.34.2", optional = true, features = ["unsafe_textures", "b
|
|||
sdl2-sys = { version = "=0.34.2", optional = true, features = ["bundled", "static-link"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_derive = "1"
|
||||
serde_cbor = { version = "0.11.2", optional = true }
|
||||
serde_json = "1.0"
|
||||
serde_yaml = "0.8"
|
||||
simple_logger = { version = "1.13" }
|
||||
strum = "0.20"
|
||||
strum_macros = "0.20"
|
||||
tokio = { version = "1.12.0", features = ["net"], optional = true }
|
||||
# remove and replace when drain_filter is in stable
|
||||
vec_mut_scan = "0.4"
|
||||
webbrowser = "0.5.5"
|
||||
|
|
|
@ -6,7 +6,7 @@ plugins {
|
|||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.3"
|
||||
//ndkVersion "21.3.6528147"
|
||||
ndkVersion "22.1.7171670"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "io.github.doukutsu_rs"
|
||||
|
@ -65,6 +65,7 @@ dependencies {
|
|||
}
|
||||
|
||||
println("cargo target: ${project.buildDir.getAbsolutePath()}/rust-target")
|
||||
println("ndk dir: ${android.ndkDirectory}")
|
||||
|
||||
cargoNdk {
|
||||
targets = [
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#[cfg(target_os = "android")]
|
||||
#[cfg_attr(target_os = "android", ndk_glue::main())]
|
||||
pub fn android_main() {
|
||||
doukutsu_rs::init().unwrap();
|
||||
let options = doukutsu_rs::LaunchOptions { server_mode: false };
|
||||
|
||||
doukutsu_rs::init(options).unwrap();
|
||||
}
|
||||
|
|
|
@ -260,10 +260,12 @@ pub struct Rect<T: Num + PartialOrd + Copy = isize> {
|
|||
}
|
||||
|
||||
impl<T: Num + PartialOrd + Copy> Rect<T> {
|
||||
#[inline(always)]
|
||||
pub fn new(left: T, top: T, right: T, bottom: T) -> Rect<T> {
|
||||
Rect { left, top, right, bottom }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn new_size(x: T, y: T, width: T, height: T) -> Rect<T> {
|
||||
Rect { left: x, top: y, right: x.add(width), bottom: y.add(height) }
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ pub struct HUD {
|
|||
max_ammo: u16,
|
||||
xp: u16,
|
||||
max_xp: u16,
|
||||
xp_bar_counter: u8,
|
||||
max_level: bool,
|
||||
life: u16,
|
||||
max_life: u16,
|
||||
|
@ -42,6 +43,7 @@ impl HUD {
|
|||
max_ammo: 0,
|
||||
xp: 0,
|
||||
max_xp: 0,
|
||||
xp_bar_counter: 0,
|
||||
max_level: false,
|
||||
life: 0,
|
||||
max_life: 0,
|
||||
|
@ -66,6 +68,7 @@ impl GameEntity<(&Player, &mut Inventory)> for HUD {
|
|||
self.max_ammo = max_ammo;
|
||||
self.xp = xp;
|
||||
self.max_xp = max_xp;
|
||||
self.xp_bar_counter = if player.xp_counter != 0 { self.xp_bar_counter.wrapping_add(1) } else { 0 };
|
||||
self.max_level = max_level;
|
||||
|
||||
self.life = player.life;
|
||||
|
@ -138,7 +141,7 @@ impl GameEntity<(&Player, &mut Inventory)> for HUD {
|
|||
pos_x -= 48;
|
||||
}
|
||||
|
||||
let wtype = unsafe { *self.weapon_types.get_unchecked(a) };
|
||||
let wtype = self.weapon_types[a];
|
||||
if wtype != 0 {
|
||||
rect = Rect::new_size(pos_x + weapon_offset - 4, 16 - 4, 24, 24);
|
||||
|
||||
|
@ -203,6 +206,10 @@ impl GameEntity<(&Player, &mut Inventory)> for HUD {
|
|||
batch.add_rect(bar_offset + weap_x + 24.0, 32.0 + top, &Rect::new_size(0, 80, bar_width, 8));
|
||||
}
|
||||
|
||||
if (self.xp_bar_counter & 0x02) != 0 {
|
||||
batch.add_rect(bar_offset + weap_x + 24.0, 32.0 + top, &Rect::new_size(40, 80, 40, 8));
|
||||
}
|
||||
|
||||
if self.max_life != 0 {
|
||||
let yellow_bar_width = (self.life_bar as f32 / self.max_life as f32 * 39.0) as u16;
|
||||
let bar_width = (self.life as f32 / self.max_life as f32 * 39.0) as u16;
|
||||
|
@ -252,8 +259,7 @@ impl GameEntity<(&Player, &mut Inventory)> for HUD {
|
|||
pos_x -= 96.0 + self.weapon_count as f32 * 16.0;
|
||||
}
|
||||
|
||||
let wtype = unsafe { *self.weapon_types.get_unchecked(a) };
|
||||
|
||||
let wtype = self.weapon_types[a];
|
||||
if wtype != 0 {
|
||||
rect.left = wtype as u16 * 16;
|
||||
rect.right = rect.left + 16;
|
||||
|
|
|
@ -480,7 +480,7 @@ impl EngineConstants {
|
|||
question_right_rect: Rect { left: 48, top: 64, right: 64, bottom: 80 },
|
||||
},
|
||||
world: WorldConsts { snack_rect: Rect { left: 256, top: 48, right: 272, bottom: 64 } },
|
||||
npc: serde_yaml::from_str("dummy: \"lol\"").unwrap(),
|
||||
npc: serde_json::from_str("{}").unwrap(),
|
||||
weapon: WeaponConsts {
|
||||
bullet_table: vec![
|
||||
// Null
|
||||
|
|
|
@ -75,7 +75,11 @@ pub trait BackendTexture {
|
|||
}
|
||||
|
||||
#[allow(unreachable_code)]
|
||||
pub fn init_backend() -> GameResult<Box<dyn Backend>> {
|
||||
pub fn init_backend(headless: bool) -> GameResult<Box<dyn Backend>> {
|
||||
if headless {
|
||||
return crate::framework::backend_null::NullBackend::new();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "backend-glutin"))]
|
||||
{
|
||||
return crate::framework::backend_glutin::GlutinBackend::new();
|
||||
|
|
|
@ -50,9 +50,9 @@ impl BackendEventLoop for NullEventLoop {
|
|||
game.loops = 0;
|
||||
state_ref.frame_time = 0.0;
|
||||
}
|
||||
std::thread::sleep(std::time::Duration::from_millis(5));
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
|
||||
//game.draw(ctx).unwrap();
|
||||
game.draw(ctx).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::framework::keyboard::KeyboardContext;
|
|||
use crate::Game;
|
||||
|
||||
pub struct Context {
|
||||
pub headless: bool,
|
||||
pub(crate) filesystem: Filesystem,
|
||||
pub(crate) renderer: Option<Box<dyn BackendRenderer>>,
|
||||
pub(crate) keyboard_context: KeyboardContext,
|
||||
|
@ -15,6 +16,7 @@ pub struct Context {
|
|||
impl Context {
|
||||
pub fn new() -> Context {
|
||||
Context {
|
||||
headless: false,
|
||||
filesystem: Filesystem::new(),
|
||||
renderer: None,
|
||||
keyboard_context: KeyboardContext::new(),
|
||||
|
@ -24,7 +26,7 @@ impl Context {
|
|||
}
|
||||
|
||||
pub fn run(&mut self, game: &mut Game) -> GameResult {
|
||||
let backend = init_backend()?;
|
||||
let backend = init_backend(self.headless)?;
|
||||
let mut event_loop = backend.create_event_loop()?;
|
||||
self.renderer = Some(event_loop.new_renderer()?);
|
||||
|
||||
|
|
|
@ -93,9 +93,9 @@ impl From<std::string::FromUtf8Error> for GameError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<serde_yaml::Error> for GameError {
|
||||
fn from(e: serde_yaml::Error) -> Self {
|
||||
let errstr = format!("Yaml error: {:?}", e);
|
||||
impl From<serde_json::Error> for GameError {
|
||||
fn from(e: serde_json::Error) -> Self {
|
||||
let errstr = format!("JSON error: {:?}", e);
|
||||
GameError::ParseError(errstr)
|
||||
}
|
||||
}
|
||||
|
|
17
src/lib.rs
17
src/lib.rs
|
@ -67,6 +67,10 @@ mod text_script;
|
|||
mod texture_set;
|
||||
mod weapon;
|
||||
|
||||
pub struct LaunchOptions {
|
||||
pub server_mode: bool,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref GAME_SUSPENDED: Mutex<bool> = Mutex::new(false);
|
||||
}
|
||||
|
@ -142,6 +146,12 @@ impl Game {
|
|||
fn draw(&mut self, ctx: &mut Context) -> GameResult {
|
||||
let state_ref = unsafe { &mut *self.state.get() };
|
||||
|
||||
if ctx.headless {
|
||||
self.loops = 0;
|
||||
state_ref.frame_time = 1.0;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if state_ref.timing_mode != TimingMode::FrameSynchronized {
|
||||
let mut elapsed = self.start_time.elapsed().as_nanos();
|
||||
|
||||
|
@ -185,7 +195,7 @@ impl Game {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn init() -> GameResult {
|
||||
pub fn init(options: LaunchOptions) -> GameResult {
|
||||
let _ = simple_logger::init_with_level(log::Level::Info);
|
||||
|
||||
#[cfg(not(target_os = "android"))]
|
||||
|
@ -264,6 +274,11 @@ pub fn init() -> GameResult {
|
|||
}
|
||||
}
|
||||
|
||||
if options.server_mode {
|
||||
log::info!("Running in server mode...");
|
||||
context.headless = true;
|
||||
}
|
||||
|
||||
let game = UnsafeCell::new(Game::new(&mut context)?);
|
||||
let state_ref = unsafe { &mut *((&mut *game.get()).state.get()) };
|
||||
#[cfg(feature = "scripting")]
|
||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -3,7 +3,18 @@
|
|||
use std::process::exit;
|
||||
|
||||
fn main() {
|
||||
let result = doukutsu_rs::init();
|
||||
let args = std::env::args();
|
||||
let mut options = doukutsu_rs::LaunchOptions {
|
||||
server_mode: false
|
||||
};
|
||||
|
||||
for arg in args {
|
||||
if arg == "--server-mode" {
|
||||
options.server_mode = true;
|
||||
}
|
||||
}
|
||||
|
||||
let result = doukutsu_rs::init(options);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
unsafe {
|
||||
|
@ -28,7 +39,7 @@ fn main() {
|
|||
}
|
||||
|
||||
if let Err(e) = result {
|
||||
println!("Initialization error: {}", e);
|
||||
eprintln!("Initialization error: {}", e);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
pub mod packets;
|
||||
pub mod server;
|
||||
pub mod server_config;
|
8
src/netplay/packets.rs
Normal file
8
src/netplay/packets.rs
Normal file
|
@ -0,0 +1,8 @@
|
|||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "i")]
|
||||
pub enum DRSPacket {
|
||||
#[serde(rename = "\x01")]
|
||||
Ping(u16),
|
||||
#[serde(rename = "\x02")]
|
||||
Pong(u16),
|
||||
}
|
34
src/netplay/server.rs
Normal file
34
src/netplay/server.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use tokio::net::UdpSocket;
|
||||
use crate::framework::error::GameResult;
|
||||
use crate::netplay::server_config::ServerConfiguration;
|
||||
|
||||
pub struct Server {
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn start(config: ServerConfiguration) -> GameResult<Server> {
|
||||
let context = ServerContext::new(config);
|
||||
|
||||
|
||||
Ok(Server {
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct ServerContext {
|
||||
config: ServerConfiguration,
|
||||
}
|
||||
|
||||
impl ServerContext {
|
||||
pub fn new(config: ServerConfiguration) -> ServerContext {
|
||||
ServerContext {
|
||||
config
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(self) {
|
||||
let socket = UdpSocket::bind(&self.config.bind_to).await.unwrap();
|
||||
|
||||
}
|
||||
}
|
10
src/netplay/server_config.rs
Normal file
10
src/netplay/server_config.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
pub struct ServerConfiguration {
|
||||
#[serde(default = "default_bind")]
|
||||
pub bind_to: String,
|
||||
}
|
||||
|
||||
// 'RS' = 0x5253 = 21075
|
||||
fn default_bind() -> String {
|
||||
"0.0.0.0:21075".to_string()
|
||||
}
|
|
@ -4,7 +4,8 @@ use num_derive::FromPrimitive;
|
|||
use num_traits::clamp;
|
||||
|
||||
use crate::caret::CaretType;
|
||||
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::frame::Frame;
|
||||
use crate::framework::context::Context;
|
||||
|
@ -13,13 +14,13 @@ use crate::input::dummy_player_controller::DummyPlayerController;
|
|||
use crate::input::player_controller::PlayerController;
|
||||
use crate::npc::list::NPCList;
|
||||
use crate::npc::NPC;
|
||||
use crate::player::skin::{PlayerAnimationState, PlayerAppearanceState, PlayerSkin};
|
||||
use crate::player::skin::basic::BasicPlayerSkin;
|
||||
use crate::player::skin::{PlayerAnimationState, PlayerAppearanceState, PlayerSkin};
|
||||
use crate::rng::RNG;
|
||||
use crate::shared_game_state::SharedGameState;
|
||||
use crate::components::number_popup::NumberPopup;
|
||||
|
||||
mod player_hit;
|
||||
pub mod player_list;
|
||||
pub mod skin;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive)]
|
||||
|
@ -42,6 +43,14 @@ impl TargetPlayer {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone)]
|
||||
enum BoosterSwitch {
|
||||
None,
|
||||
Up,
|
||||
Sides,
|
||||
Down,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Player {
|
||||
pub x: i32,
|
||||
|
@ -68,6 +77,7 @@ pub struct Player {
|
|||
pub up: bool,
|
||||
pub down: bool,
|
||||
pub shock_counter: u8,
|
||||
pub xp_counter: u8,
|
||||
pub current_weapon: u8,
|
||||
pub stars: u8,
|
||||
pub damage: u16,
|
||||
|
@ -79,7 +89,7 @@ pub struct Player {
|
|||
weapon_offset_y: i8,
|
||||
splash: bool,
|
||||
tick: u8,
|
||||
booster_switch: u8,
|
||||
booster_switch: BoosterSwitch,
|
||||
damage_counter: u16,
|
||||
damage_taken: i16,
|
||||
pub anim_num: u16,
|
||||
|
@ -121,8 +131,9 @@ impl Player {
|
|||
current_weapon: 0,
|
||||
weapon_offset_y: 0,
|
||||
shock_counter: 0,
|
||||
xp_counter: 0,
|
||||
tick: 0,
|
||||
booster_switch: 0,
|
||||
booster_switch: BoosterSwitch::None,
|
||||
stars: 0,
|
||||
damage: 0,
|
||||
air_counter: 0,
|
||||
|
@ -185,12 +196,12 @@ impl Player {
|
|||
self.question = false;
|
||||
|
||||
if !state.control_flags.control_enabled() {
|
||||
self.booster_switch = 0;
|
||||
self.booster_switch = BoosterSwitch::None;
|
||||
}
|
||||
|
||||
// ground movement
|
||||
if self.flags.hit_bottom_wall() || self.flags.hit_right_slope() || self.flags.hit_left_slope() {
|
||||
self.booster_switch = 0;
|
||||
self.booster_switch = BoosterSwitch::None;
|
||||
|
||||
if state.settings.infinite_booster {
|
||||
self.booster_fuel = u32::MAX;
|
||||
|
@ -258,31 +269,30 @@ impl Player {
|
|||
if state.control_flags.control_enabled() {
|
||||
if self.controller.trigger_jump() && self.booster_fuel != 0 {
|
||||
if self.equip.has_booster_0_8() {
|
||||
self.booster_switch = 1;
|
||||
self.booster_switch = BoosterSwitch::Sides;
|
||||
|
||||
if self.vel_y > 0x100 {
|
||||
// 0.5fix9
|
||||
self.vel_y /= 2;
|
||||
}
|
||||
} else if state.settings.infinite_booster || self.equip.has_booster_2_0() {
|
||||
if self.controller.move_up() {
|
||||
self.booster_switch = 2;
|
||||
self.booster_switch = BoosterSwitch::Up;
|
||||
self.vel_x = 0;
|
||||
self.vel_y = state.constants.booster.b2_0_up;
|
||||
} else if self.controller.move_left() {
|
||||
self.booster_switch = 1;
|
||||
self.booster_switch = BoosterSwitch::Sides;
|
||||
self.vel_x = state.constants.booster.b2_0_left;
|
||||
self.vel_y = 0;
|
||||
} else if self.controller.move_right() {
|
||||
self.booster_switch = 1;
|
||||
self.booster_switch = BoosterSwitch::Sides;
|
||||
self.vel_x = state.constants.booster.b2_0_right;
|
||||
self.vel_y = 0;
|
||||
} else if self.controller.move_down() {
|
||||
self.booster_switch = 3;
|
||||
self.booster_switch = BoosterSwitch::Down;
|
||||
self.vel_x = 0;
|
||||
self.vel_y = state.constants.booster.b2_0_down;
|
||||
} else {
|
||||
self.booster_switch = 2;
|
||||
self.booster_switch = BoosterSwitch::Up;
|
||||
self.vel_x = 0;
|
||||
self.vel_y = state.constants.booster.b2_0_up_nokey;
|
||||
}
|
||||
|
@ -307,18 +317,18 @@ impl Player {
|
|||
}
|
||||
|
||||
if (state.settings.infinite_booster || self.equip.has_booster_2_0())
|
||||
&& self.booster_switch != 0
|
||||
&& self.booster_switch != BoosterSwitch::None
|
||||
&& (!self.controller.jump() || self.booster_fuel == 0)
|
||||
{
|
||||
match self.booster_switch {
|
||||
1 => self.vel_x /= 2,
|
||||
2 => self.vel_y /= 2,
|
||||
_ => {}
|
||||
BoosterSwitch::Sides => self.vel_x /= 2,
|
||||
BoosterSwitch::Up => self.vel_y /= 2,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
if self.booster_fuel == 0 || !self.controller.jump() {
|
||||
self.booster_switch = 0;
|
||||
self.booster_switch = BoosterSwitch::None;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,7 +358,7 @@ impl Player {
|
|||
}
|
||||
|
||||
// booster losing fuel
|
||||
if self.booster_switch != 0 && self.booster_fuel != 0 {
|
||||
if self.booster_switch != BoosterSwitch::None && self.booster_fuel != 0 {
|
||||
self.booster_fuel -= 1;
|
||||
}
|
||||
|
||||
|
@ -367,18 +377,18 @@ impl Player {
|
|||
self.vel_y += 0x55;
|
||||
}
|
||||
|
||||
if (state.settings.infinite_booster || self.equip.has_booster_2_0()) && self.booster_switch != 0 {
|
||||
if (state.settings.infinite_booster || self.equip.has_booster_2_0()) && self.booster_switch != BoosterSwitch::None {
|
||||
match self.booster_switch {
|
||||
1 => {
|
||||
BoosterSwitch::Sides => {
|
||||
if self.flags.hit_left_wall() || self.flags.hit_right_wall() {
|
||||
self.vel_y = -0x100; // -0.5fix9
|
||||
self.vel_y = -0x100;
|
||||
}
|
||||
|
||||
if self.direction == Direction::Left {
|
||||
self.vel_x -= 0x20; // 0.1fix9
|
||||
self.vel_x -= 0x20;
|
||||
}
|
||||
if self.direction == Direction::Right {
|
||||
self.vel_x += 0x20; // 0.1fix9
|
||||
self.vel_x += 0x20;
|
||||
}
|
||||
|
||||
if self.controller.trigger_jump() || self.booster_fuel % 3 == 1 {
|
||||
|
@ -393,7 +403,7 @@ impl Player {
|
|||
state.sound_manager.play_sfx(113);
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
BoosterSwitch::Up => {
|
||||
self.vel_y -= 0x20;
|
||||
|
||||
if self.controller.trigger_jump() || self.booster_fuel % 3 == 1 {
|
||||
|
@ -401,7 +411,7 @@ impl Player {
|
|||
state.sound_manager.play_sfx(113);
|
||||
}
|
||||
}
|
||||
3 if self.controller.trigger_jump() || self.booster_fuel % 3 == 1 => {
|
||||
BoosterSwitch::Down if self.controller.trigger_jump() || self.booster_fuel % 3 == 1 => {
|
||||
state.create_caret(self.x, self.y + 0xc00, CaretType::Exhaust, Direction::Up);
|
||||
state.sound_manager.play_sfx(113);
|
||||
}
|
||||
|
@ -409,7 +419,7 @@ impl Player {
|
|||
}
|
||||
} else if self.flags.force_up() {
|
||||
self.vel_y += physics.gravity_air;
|
||||
} else if self.equip.has_booster_0_8() && self.booster_switch != 0 && self.vel_y > -0x400 {
|
||||
} else if self.equip.has_booster_0_8() && self.booster_switch != BoosterSwitch::None && self.vel_y > -0x400 {
|
||||
self.vel_y -= 0x20;
|
||||
|
||||
if self.booster_fuel % 3 == 0 {
|
||||
|
@ -725,13 +735,16 @@ impl GameEntity<&NPCList> for Player {
|
|||
self.damage_counter -= 1;
|
||||
}
|
||||
|
||||
if self.xp_counter != 0 {
|
||||
self.xp_counter -= 1;
|
||||
}
|
||||
|
||||
if self.shock_counter != 0 {
|
||||
self.shock_counter -= 1;
|
||||
} else if self.damage_taken != 0 {
|
||||
self.damage_taken = 0;
|
||||
}
|
||||
|
||||
// todo: add additional control modes like NXEngine has such as noclip?
|
||||
match self.control_mode {
|
||||
ControlMode::Normal => self.tick_normal(state, npc_list)?,
|
||||
ControlMode::IronHead => self.tick_ironhead(state)?,
|
||||
|
@ -813,7 +826,8 @@ impl GameEntity<&NPCList> for Player {
|
|||
self.prev_y - self.display_bounds.top as i32,
|
||||
self.y - self.display_bounds.top as i32,
|
||||
state.frame_time,
|
||||
) + self.weapon_offset_y as f32 + gun_off_y as f32
|
||||
) + self.weapon_offset_y as f32
|
||||
+ gun_off_y as f32
|
||||
- frame_y,
|
||||
&self.weapon_rect,
|
||||
);
|
||||
|
|
13
src/player/player_list.rs
Normal file
13
src/player/player_list.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use crate::player::Player;
|
||||
|
||||
pub struct RemotePlayerList {
|
||||
|
||||
}
|
||||
|
||||
impl RemotePlayerList {
|
||||
pub fn new() -> RemotePlayerList {
|
||||
RemotePlayerList {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,7 +33,7 @@ use crate::scene::Scene;
|
|||
use crate::shared_game_state::{SharedGameState, TileSize};
|
||||
use crate::stage::{BackgroundType, Stage};
|
||||
use crate::text_script::{ConfirmSelection, ScriptMode, TextScriptExecutionState, TextScriptLine, TextScriptVM};
|
||||
use crate::texture_set::SizedBatch;
|
||||
use crate::texture_set::SpriteBatch;
|
||||
use crate::weapon::bullet::BulletManager;
|
||||
use crate::weapon::{Weapon, WeaponType};
|
||||
|
||||
|
@ -661,7 +661,7 @@ impl GameScene {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_light(&self, x: f32, y: f32, size: f32, color: (u8, u8, u8), batch: &mut SizedBatch) {
|
||||
fn draw_light(&self, x: f32, y: f32, size: f32, color: (u8, u8, u8), batch: &mut Box<dyn SpriteBatch>) {
|
||||
batch.add_rect_scaled_tinted(
|
||||
x - size * 32.0,
|
||||
y - size * 32.0,
|
||||
|
@ -680,7 +680,7 @@ impl GameScene {
|
|||
(br, bg, bb): (u8, u8, u8),
|
||||
att: f32,
|
||||
angle: Range<i32>,
|
||||
batch: &mut SizedBatch,
|
||||
batch: &mut Box<dyn SpriteBatch>,
|
||||
) {
|
||||
let px = world_point_x as f32 / 512.0;
|
||||
let py = world_point_y as f32 / 512.0;
|
||||
|
@ -2001,11 +2001,12 @@ impl Scene for GameScene {
|
|||
}
|
||||
}
|
||||
|
||||
self.inventory_dim += 0.1 * if state.textscript_vm.mode == ScriptMode::Inventory {
|
||||
state.frame_time as f32
|
||||
} else {
|
||||
-(state.frame_time as f32)
|
||||
};
|
||||
self.inventory_dim += 0.1
|
||||
* if state.textscript_vm.mode == ScriptMode::Inventory {
|
||||
state.frame_time as f32
|
||||
} else {
|
||||
-(state.frame_time as f32)
|
||||
};
|
||||
|
||||
self.inventory_dim = self.inventory_dim.clamp(0.0, 1.0);
|
||||
|
||||
|
|
|
@ -37,7 +37,12 @@ impl LoadingScene {
|
|||
let stage_select_script = TextScript::load_from(stage_select_tsc, &state.constants)?;
|
||||
state.textscript_vm.set_stage_select_script(stage_select_script);
|
||||
|
||||
state.start_intro(ctx)?;
|
||||
if ctx.headless {
|
||||
log::info!("Headless mode detected, skipping intro and loading last saved game.");
|
||||
state.load_or_start_game(ctx)?;
|
||||
} else {
|
||||
state.start_intro(ctx)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ pub struct Settings {
|
|||
fn default_true() -> bool { true }
|
||||
|
||||
#[inline(always)]
|
||||
fn current_version() -> u32 { 2 }
|
||||
fn current_version() -> u32 { 3 }
|
||||
|
||||
#[inline(always)]
|
||||
fn default_interpolation() -> InterpolationMode { InterpolationMode::Linear }
|
||||
|
@ -55,9 +55,9 @@ fn default_speed() -> f64 {
|
|||
|
||||
impl Settings {
|
||||
pub fn load(ctx: &Context) -> GameResult<Settings> {
|
||||
if let Ok(file) = user_open(ctx, "/settings.yml") {
|
||||
match serde_yaml::from_reader::<_, Settings>(file) {
|
||||
Ok(settings) => return Ok(settings),
|
||||
if let Ok(file) = user_open(ctx, "/settings.json") {
|
||||
match serde_json::from_reader::<_, Settings>(file) {
|
||||
Ok(settings) => return Ok(settings.upgrade()),
|
||||
Err(err) => log::warn!("Failed to deserialize settings: {}", err),
|
||||
}
|
||||
}
|
||||
|
@ -65,9 +65,25 @@ impl Settings {
|
|||
Ok(Settings::default())
|
||||
}
|
||||
|
||||
fn upgrade(mut self) -> Self {
|
||||
let initial_version = self.version;
|
||||
|
||||
if self.version == 2 {
|
||||
self.version = 3;
|
||||
self.light_cone = true;
|
||||
}
|
||||
|
||||
if self.version != initial_version {
|
||||
log::info!("Upgraded configuration file from version {} to {}.", initial_version, self.version);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn save(&self, ctx: &Context) -> GameResult {
|
||||
let file = user_create(ctx, "/settings.yml")?;
|
||||
serde_yaml::to_writer(file, self)?;
|
||||
let file = user_create(ctx, "/settings.json")?;
|
||||
serde_json::to_writer(file, self)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -87,7 +103,7 @@ impl Settings {
|
|||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Settings {
|
||||
version: 2,
|
||||
version: current_version(),
|
||||
seasonal_textures: true,
|
||||
original_textures: false,
|
||||
shader_effects: true,
|
||||
|
|
|
@ -41,6 +41,7 @@ pub struct SoundManager {
|
|||
tx: Sender<PlaybackMessage>,
|
||||
prev_song_id: usize,
|
||||
current_song_id: usize,
|
||||
no_audio: bool,
|
||||
}
|
||||
|
||||
enum SongFormat {
|
||||
|
@ -64,6 +65,12 @@ impl SoundManager {
|
|||
pub fn new(ctx: &mut Context) -> GameResult<SoundManager> {
|
||||
let (tx, rx): (Sender<PlaybackMessage>, Receiver<PlaybackMessage>) = mpsc::channel();
|
||||
|
||||
if ctx.headless {
|
||||
log::info!("Running in headless mode, skipping initialization.");
|
||||
|
||||
return Ok(SoundManager { tx: tx.clone(), prev_song_id: 0, current_song_id: 0, no_audio: true });
|
||||
}
|
||||
|
||||
let host = cpal::default_host();
|
||||
let device =
|
||||
host.default_output_device().ok_or_else(|| AudioError(str!("Error initializing audio device.")))?;
|
||||
|
@ -81,22 +88,34 @@ impl SoundManager {
|
|||
}
|
||||
});
|
||||
|
||||
Ok(SoundManager { tx: tx.clone(), prev_song_id: 0, current_song_id: 0 })
|
||||
Ok(SoundManager { tx: tx.clone(), prev_song_id: 0, current_song_id: 0, no_audio: false })
|
||||
}
|
||||
|
||||
pub fn play_sfx(&self, id: u8) {
|
||||
if self.no_audio {
|
||||
return;
|
||||
}
|
||||
let _ = self.tx.send(PlaybackMessage::PlaySample(id));
|
||||
}
|
||||
|
||||
pub fn loop_sfx(&self, id: u8) {
|
||||
if self.no_audio {
|
||||
return;
|
||||
}
|
||||
let _ = self.tx.send(PlaybackMessage::LoopSample(id));
|
||||
}
|
||||
|
||||
pub fn stop_sfx(&self, id: u8) {
|
||||
if self.no_audio {
|
||||
return;
|
||||
}
|
||||
let _ = self.tx.send(PlaybackMessage::StopSample(id));
|
||||
}
|
||||
|
||||
pub fn set_org_interpolation(&self, interpolation: InterpolationMode) {
|
||||
if self.no_audio {
|
||||
return;
|
||||
}
|
||||
let _ = self.tx.send(PlaybackMessage::SetOrgInterpolation(interpolation));
|
||||
}
|
||||
|
||||
|
@ -107,7 +126,7 @@ impl SoundManager {
|
|||
settings: &Settings,
|
||||
ctx: &mut Context,
|
||||
) -> GameResult {
|
||||
if self.current_song_id == song_id {
|
||||
if self.current_song_id == song_id || self.no_audio {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -157,7 +176,8 @@ impl SoundManager {
|
|||
|
||||
self.prev_song_id = self.current_song_id;
|
||||
self.current_song_id = song_id;
|
||||
self.tx.send(PlaybackMessage::SetOrgInterpolation(settings.organya_interpolation))?;
|
||||
self.tx
|
||||
.send(PlaybackMessage::SetOrgInterpolation(settings.organya_interpolation))?;
|
||||
self.tx.send(PlaybackMessage::SaveState)?;
|
||||
self.tx.send(PlaybackMessage::PlayOrganyaSong(Box::new(org)))?;
|
||||
|
||||
|
@ -237,6 +257,10 @@ impl SoundManager {
|
|||
}
|
||||
|
||||
pub fn save_state(&mut self) -> GameResult {
|
||||
if self.no_audio {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.tx.send(PlaybackMessage::SaveState)?;
|
||||
self.prev_song_id = self.current_song_id;
|
||||
|
||||
|
@ -244,6 +268,10 @@ impl SoundManager {
|
|||
}
|
||||
|
||||
pub fn restore_state(&mut self) -> GameResult {
|
||||
if self.no_audio {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.tx.send(PlaybackMessage::RestoreState)?;
|
||||
self.current_song_id = self.prev_song_id;
|
||||
|
||||
|
@ -251,9 +279,14 @@ impl SoundManager {
|
|||
}
|
||||
|
||||
pub fn set_speed(&self, speed: f32) -> GameResult {
|
||||
if self.no_audio {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if speed <= 0.0 {
|
||||
return Err(InvalidValue(str!("Speed must be bigger than 0.0!")));
|
||||
}
|
||||
|
||||
self.tx.send(PlaybackMessage::SetSpeed(speed))?;
|
||||
|
||||
Ok(())
|
||||
|
@ -264,6 +297,10 @@ impl SoundManager {
|
|||
}
|
||||
|
||||
pub fn set_sample_params_from_file<R: io::Read>(&self, id: u8, data: R) -> GameResult {
|
||||
if self.no_audio {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut reader = BufReader::new(data).lines();
|
||||
let mut params = PixToneParameters::empty();
|
||||
|
||||
|
@ -325,6 +362,10 @@ impl SoundManager {
|
|||
}
|
||||
|
||||
pub fn set_sample_params(&self, id: u8, params: PixToneParameters) -> GameResult {
|
||||
if self.no_audio {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
self.tx.send(PlaybackMessage::SetSampleParams(id, params))?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1845,8 +1845,6 @@ impl TextScript {
|
|||
|
||||
/// Compiles a decrypted text script data into internal bytecode.
|
||||
pub fn compile(data: &[u8], strict: bool, encoding: TextScriptEncoding) -> GameResult<TextScript> {
|
||||
log::info!("data: {}", String::from_utf8_lossy(data));
|
||||
|
||||
let mut event_map = HashMap::new();
|
||||
let mut iter = data.iter().copied().peekable();
|
||||
let mut last_event = 0;
|
||||
|
|
|
@ -20,6 +20,117 @@ use crate::str;
|
|||
pub static mut I_MAG: f32 = 1.0;
|
||||
pub static mut G_MAG: f32 = 1.0;
|
||||
|
||||
pub trait SpriteBatch {
|
||||
fn width(&self) -> usize;
|
||||
|
||||
fn height(&self) -> usize;
|
||||
|
||||
fn dimensions(&self) -> (usize, usize);
|
||||
|
||||
fn real_dimensions(&self) -> (usize, usize);
|
||||
|
||||
fn scale(&self) -> (f32, f32);
|
||||
|
||||
fn has_glow_layer(&self) -> bool;
|
||||
|
||||
fn has_normal_layer(&self) -> bool;
|
||||
|
||||
fn to_rect(&self) -> common::Rect<usize>;
|
||||
|
||||
fn clear(&mut self);
|
||||
|
||||
fn add(&mut self, x: f32, y: f32);
|
||||
|
||||
fn add_rect(&mut self, x: f32, y: f32, rect: &common::Rect<u16>);
|
||||
|
||||
fn add_rect_flip(&mut self, x: f32, y: f32, flip_x: bool, flip_y: bool, rect: &common::Rect<u16>);
|
||||
|
||||
fn add_rect_tinted(&mut self, x: f32, y: f32, color: (u8, u8, u8, u8), rect: &common::Rect<u16>);
|
||||
|
||||
fn add_rect_scaled(&mut self, x: f32, y: f32, scale_x: f32, scale_y: f32, rect: &common::Rect<u16>);
|
||||
|
||||
fn add_rect_scaled_tinted(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
color: (u8, u8, u8, u8),
|
||||
scale_x: f32,
|
||||
scale_y: f32,
|
||||
rect: &common::Rect<u16>,
|
||||
);
|
||||
|
||||
fn draw(&mut self, ctx: &mut Context) -> GameResult;
|
||||
|
||||
fn draw_filtered(&mut self, _filter: FilterMode, _ctx: &mut Context) -> GameResult;
|
||||
}
|
||||
|
||||
pub struct DummyBatch;
|
||||
|
||||
impl SpriteBatch for DummyBatch {
|
||||
fn width(&self) -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn height(&self) -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn dimensions(&self) -> (usize, usize) {
|
||||
(1, 1)
|
||||
}
|
||||
|
||||
fn real_dimensions(&self) -> (usize, usize) {
|
||||
(1, 1)
|
||||
}
|
||||
|
||||
fn scale(&self) -> (f32, f32) {
|
||||
(1.0, 1.0)
|
||||
}
|
||||
|
||||
fn has_glow_layer(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn has_normal_layer(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn to_rect(&self) -> Rect<usize> {
|
||||
Rect::new(0, 0, 1, 1)
|
||||
}
|
||||
|
||||
fn clear(&mut self) {}
|
||||
|
||||
fn add(&mut self, _x: f32, _y: f32) {}
|
||||
|
||||
fn add_rect(&mut self, _x: f32, _y: f32, _rect: &Rect<u16>) {}
|
||||
|
||||
fn add_rect_flip(&mut self, _x: f32, _y: f32, _flip_x: bool, _flip_y: bool, _rect: &Rect<u16>) {}
|
||||
|
||||
fn add_rect_tinted(&mut self, _x: f32, _y: f32, _color: (u8, u8, u8, u8), _rect: &Rect<u16>) {}
|
||||
|
||||
fn add_rect_scaled(&mut self, _x: f32, _y: f32, _scale_x: f32, _scale_y: f32, _rect: &Rect<u16>) {}
|
||||
|
||||
fn add_rect_scaled_tinted(
|
||||
&mut self,
|
||||
_x: f32,
|
||||
_y: f32,
|
||||
_color: (u8, u8, u8, u8),
|
||||
_scale_x: f32,
|
||||
_scale_y: f32,
|
||||
_rect: &Rect<u16>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn draw(&mut self, _ctx: &mut Context) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_filtered(&mut self, _filter: FilterMode, _ctx: &mut Context) -> GameResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SizedBatch {
|
||||
batch: Box<dyn BackendTexture>,
|
||||
width: usize,
|
||||
|
@ -32,53 +143,53 @@ pub struct SizedBatch {
|
|||
has_normal_layer: bool,
|
||||
}
|
||||
|
||||
impl SizedBatch {
|
||||
impl SpriteBatch for SizedBatch {
|
||||
#[inline(always)]
|
||||
pub fn width(&self) -> usize {
|
||||
fn width(&self) -> usize {
|
||||
self.width
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn height(&self) -> usize {
|
||||
fn height(&self) -> usize {
|
||||
self.height
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn dimensions(&self) -> (usize, usize) {
|
||||
fn dimensions(&self) -> (usize, usize) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn real_dimensions(&self) -> (usize, usize) {
|
||||
fn real_dimensions(&self) -> (usize, usize) {
|
||||
(self.real_width, self.real_height)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn scale(&self) -> (f32, f32) {
|
||||
fn scale(&self) -> (f32, f32) {
|
||||
(self.scale_x, self.scale_y)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn has_glow_layer(&self) -> bool {
|
||||
fn has_glow_layer(&self) -> bool {
|
||||
self.has_glow_layer
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn has_normal_layer(&self) -> bool {
|
||||
fn has_normal_layer(&self) -> bool {
|
||||
self.has_normal_layer
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn to_rect(&self) -> common::Rect<usize> {
|
||||
fn to_rect(&self) -> common::Rect<usize> {
|
||||
common::Rect::<usize>::new(0, 0, self.width, self.height)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn clear(&mut self) {
|
||||
fn clear(&mut self) {
|
||||
self.batch.clear();
|
||||
}
|
||||
|
||||
pub fn add(&mut self, x: f32, y: f32) {
|
||||
fn add(&mut self, x: f32, y: f32) {
|
||||
let mag = unsafe { I_MAG };
|
||||
|
||||
self.batch.add(SpriteBatchCommand::DrawRect(
|
||||
|
@ -93,11 +204,11 @@ impl SizedBatch {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn add_rect(&mut self, x: f32, y: f32, rect: &common::Rect<u16>) {
|
||||
fn add_rect(&mut self, x: f32, y: f32, rect: &common::Rect<u16>) {
|
||||
self.add_rect_scaled(x, y, 1.0, 1.0, rect)
|
||||
}
|
||||
|
||||
pub fn add_rect_flip(&mut self, x: f32, y: f32, flip_x: bool, flip_y: bool, rect: &common::Rect<u16>) {
|
||||
fn add_rect_flip(&mut self, x: f32, y: f32, flip_x: bool, flip_y: bool, rect: &common::Rect<u16>) {
|
||||
if (rect.right - rect.left) == 0 || (rect.bottom - rect.top) == 0 {
|
||||
return;
|
||||
}
|
||||
|
@ -123,11 +234,11 @@ impl SizedBatch {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn add_rect_tinted(&mut self, x: f32, y: f32, color: (u8, u8, u8, u8), rect: &common::Rect<u16>) {
|
||||
fn add_rect_tinted(&mut self, x: f32, y: f32, color: (u8, u8, u8, u8), rect: &common::Rect<u16>) {
|
||||
self.add_rect_scaled_tinted(x, y, color, 1.0, 1.0, rect)
|
||||
}
|
||||
|
||||
pub fn add_rect_scaled(&mut self, x: f32, y: f32, scale_x: f32, scale_y: f32, rect: &common::Rect<u16>) {
|
||||
fn add_rect_scaled(&mut self, x: f32, y: f32, scale_x: f32, scale_y: f32, rect: &common::Rect<u16>) {
|
||||
if (rect.right.saturating_sub(rect.left)) == 0 || (rect.bottom.saturating_sub(rect.top)) == 0 {
|
||||
return;
|
||||
}
|
||||
|
@ -150,7 +261,7 @@ impl SizedBatch {
|
|||
));
|
||||
}
|
||||
|
||||
pub fn add_rect_scaled_tinted(
|
||||
fn add_rect_scaled_tinted(
|
||||
&mut self,
|
||||
x: f32,
|
||||
y: f32,
|
||||
|
@ -178,11 +289,11 @@ impl SizedBatch {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn draw(&mut self, ctx: &mut Context) -> GameResult {
|
||||
fn draw(&mut self, ctx: &mut Context) -> GameResult {
|
||||
self.draw_filtered(FilterMode::Nearest, ctx)
|
||||
}
|
||||
|
||||
pub fn draw_filtered(&mut self, _filter: FilterMode, _ctx: &mut Context) -> GameResult {
|
||||
fn draw_filtered(&mut self, _filter: FilterMode, _ctx: &mut Context) -> GameResult {
|
||||
//self.batch.set_filter(filter);
|
||||
self.batch.draw()?;
|
||||
self.batch.clear();
|
||||
|
@ -191,13 +302,18 @@ impl SizedBatch {
|
|||
}
|
||||
|
||||
pub struct TextureSet {
|
||||
pub tex_map: HashMap<String, SizedBatch>,
|
||||
pub tex_map: HashMap<String, Box<dyn SpriteBatch>>,
|
||||
pub paths: Vec<String>,
|
||||
dummy_batch: Box<dyn SpriteBatch>,
|
||||
}
|
||||
|
||||
impl TextureSet {
|
||||
pub fn new(base_path: &str) -> TextureSet {
|
||||
TextureSet { tex_map: HashMap::new(), paths: vec![base_path.to_string(), "".to_string()] }
|
||||
TextureSet {
|
||||
tex_map: HashMap::new(),
|
||||
paths: vec![base_path.to_string(), "".to_string()],
|
||||
dummy_batch: Box::new(DummyBatch),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_seasonal_content(&mut self, season: Season, settings: &Settings) {
|
||||
|
@ -239,14 +355,17 @@ impl TextureSet {
|
|||
create_texture(ctx, width as u16, height as u16, &img)
|
||||
}
|
||||
|
||||
pub fn load_texture(&self, ctx: &mut Context, constants: &EngineConstants, name: &str) -> GameResult<SizedBatch> {
|
||||
pub fn load_texture(
|
||||
&self,
|
||||
ctx: &mut Context,
|
||||
constants: &EngineConstants,
|
||||
name: &str,
|
||||
) -> GameResult<Box<dyn SpriteBatch>> {
|
||||
let path = self
|
||||
.paths
|
||||
.iter()
|
||||
.find_map(|s| {
|
||||
FILE_TYPES.iter().map(|ext| [s, name, ext].join("")).find(|path| {
|
||||
filesystem::exists(ctx, path)
|
||||
})
|
||||
FILE_TYPES.iter().map(|ext| [s, name, ext].join("")).find(|path| filesystem::exists(ctx, path))
|
||||
})
|
||||
.ok_or_else(|| GameError::ResourceLoadError(format!("Texture {} does not exist.", name)))?;
|
||||
|
||||
|
@ -254,25 +373,21 @@ impl TextureSet {
|
|||
.paths
|
||||
.iter()
|
||||
.find_map(|s| {
|
||||
FILE_TYPES.iter().map(|ext| [s, name, ".glow", ext].join("")).find(|path| {
|
||||
filesystem::exists(ctx, path)
|
||||
})
|
||||
}).is_some();
|
||||
FILE_TYPES.iter().map(|ext| [s, name, ".glow", ext].join("")).find(|path| filesystem::exists(ctx, path))
|
||||
})
|
||||
.is_some();
|
||||
|
||||
info!("Loading texture: {}", path);
|
||||
|
||||
let batch = self.load_image(ctx, &path)?;
|
||||
let size = batch.dimensions();
|
||||
|
||||
assert_ne!(size.0 as isize, 0, "size.width == 0");
|
||||
assert_ne!(size.1 as isize, 0, "size.height == 0");
|
||||
|
||||
let orig_dimensions = constants.tex_sizes.get(name).unwrap_or_else(|| &size);
|
||||
let scale = orig_dimensions.0 as f32 / size.0 as f32;
|
||||
let width = (size.0 as f32 * scale) as usize;
|
||||
let height = (size.1 as f32 * scale) as usize;
|
||||
|
||||
Ok(SizedBatch {
|
||||
Ok(Box::new(SizedBatch {
|
||||
batch,
|
||||
width,
|
||||
height,
|
||||
|
@ -282,7 +397,7 @@ impl TextureSet {
|
|||
real_height: size.1 as usize,
|
||||
has_glow_layer,
|
||||
has_normal_layer: false,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn get_or_load_batch(
|
||||
|
@ -290,7 +405,11 @@ impl TextureSet {
|
|||
ctx: &mut Context,
|
||||
constants: &EngineConstants,
|
||||
name: &str,
|
||||
) -> GameResult<&mut SizedBatch> {
|
||||
) -> GameResult<&mut Box<dyn SpriteBatch>> {
|
||||
if ctx.headless {
|
||||
return Ok(&mut self.dummy_batch);
|
||||
}
|
||||
|
||||
if !self.tex_map.contains_key(name) {
|
||||
let batch = self.load_texture(ctx, constants, name)?;
|
||||
self.tex_map.insert(str!(name), batch);
|
||||
|
|
|
@ -136,6 +136,12 @@ impl Weapon {
|
|||
state.create_caret(player.x, player.y, CaretType::LevelUp, Direction::Left);
|
||||
}
|
||||
}
|
||||
|
||||
player.xp_counter = if self.wtype != WeaponType::Spur {
|
||||
30
|
||||
} else {
|
||||
10
|
||||
};
|
||||
}
|
||||
|
||||
pub fn reset_xp(&mut self) {
|
||||
|
|
Loading…
Reference in a new issue