This commit is contained in:
Alula 2021-01-01 02:46:01 +01:00
parent 81a422502c
commit e092a9e2ea
No known key found for this signature in database
GPG Key ID: 3E00485503A1D8BA
42 changed files with 1286 additions and 756 deletions

View File

@ -40,11 +40,17 @@ opt-level = 1
[profile.release.build-override]
opt-level = 1
[features]
default = ["scripting"]
scripting = ["lua-ffi"]
editor = []
[dependencies]
#cpal = {path = "./3rdparty/cpal"}
#gfx_device_gl = {path = "./3rdparty/gfx/src/backend/gl"}
#ggez = {path = "./3rdparty/ggez"}
#glutin = {path = "./3rdparty/glutin/glutin"}
#lua-ffi = {path = "./3rdparty/luajit-rs", optional = true}
bitvec = "0.17.4"
byteorder = "1.3"
@ -64,6 +70,7 @@ image = {version = "0.22", default-features = false, features = ["png_codec", "p
itertools = "0.9.0"
lazy_static = "1.4.0"
log = "0.4"
lua-ffi = {git = "https://github.com/doukutsu-rs/lua-ffi.git", rev = "a1e7a62d7f583836b4703cfb8e54a68b79ceee11", optional = true}
lru = "0.6.0"
num-derive = "0.3.2"
num-traits = "0.2.12"

View File

@ -14,9 +14,9 @@ A re-implementation of Cave Story (Doukutsu Monogatari) engine written in [Rust]
This repository does not contain any copyrighted files.
For better user experience, pre-built binaries are distributed with slightly modified freeware game files.
For better user experience, pre-built binaries are distributed with slightly modified freeware game files.
*doukutsu-rs* should work fine with [CSE2-Enhanced](https://github.com/Clownacy/CSE2) or [NXEngine(-evo)](https://github.com/nxengine/nxengine-evo) freeware data files and [Cave Story+](https://www.nicalis.com/games/cavestory+) data files.
*doukutsu-rs* should work fine with pre-extracted and tweaked data files from [this repository](https://github.com/doukutsu-rs/game-data), [NXEngine(-evo)](https://github.com/nxengine/nxengine-evo) extracted freeware data files and [Cave Story+](https://www.nicalis.com/games/cavestory+) data files.
Vanilla Cave Story does not work yet because some important data files have been embedded inside the executable. and we don't have a loader/extractor implemented yet.
@ -25,30 +25,20 @@ Vanilla Cave Story does not work yet because some important data files have been
**Freeware**
- https://github.com/doukutsu-rs/game-data - Freeware game data distributed with CI builds, based on those two below.
- https://github.com/Clownacy/CSE2/archive/enhanced.zip - copy `game_english/data` from archive to the runtime directory (place you run the executable from, usually project root)
- ~~https://github.com/Clownacy/CSE2/archive/enhanced.zip - copy `game_english/data` from archive to the runtime directory (place you run the executable from, usually project root)~~
- https://github.com/nxengine/nxengine-evo/releases/download/v2.6.4/NXEngine-v2.6.4-Win32.zip - copy `NXEngine-evo-2.6.4-xxx/data` from the archive to runtime directory
**Cave Story+**
- PC release - (Tested only with Steam version, both Windows and Linux builds) Copy `data` folder from installation directory ([guide for Steam](https://steamcommunity.com/sharedfiles/filedetails/?id=760447682)) to the runtime directory.
- Switch release - **Not supported or actively tested.** Some of release-specific opcodes have been implemented (no code
decompilation was involved, just pure data file analysis), so you should be able to play it without any major issues.
Because methods used to extract game data from cartridge vary, you have to find that out on your own.
- PC release (Steam) - (Tested only with Steam version, both Windows and Linux builds) Copy `data` folder from installation directory ([guide for Steam](https://steamcommunity.com/sharedfiles/filedetails/?id=760447682)) to the runtime directory.
- PC release (EGS) - (Untested, but the game is essentially the same as Steam release) Same thing as with Steam version.
- Switch release - (Tested once, no guarantee to work) You need a hacked Switch and physical release. Google should help you.
#### Gameplay support roadmap
- [x] Checkmarked things = fully implemented
- [ ] Unmarked things = partially or not implemented yet.
- [x] Rendering
- [x] Backdrops
- [x] Tilemap
- [x] Player and it's animations
- [x] Carets
- [x] Bullets
- [x] NPCs
- [x] Text
- [x] HUD
- [ ] Text scripts (TSC)
- [x] Initial implementation
- [x] Full implementation of gameplay opcodes
@ -79,9 +69,6 @@ Because methods used to extract game data from cartridge vary, you have to find
- [ ] Last Cave
- [ ] Balcony
- [ ] Hell
- [ ] Cave Story+ specific NPCs
- [x] Dashing Gaudis (361)
- [ ] ??? (362)
- [ ] Weapons
- [x] Leveling / XP system
- [x] Initial implementation
@ -99,15 +86,14 @@ Because methods used to extract game data from cartridge vary, you have to find
- [ ] Support for different game editions
- [ ] Vanilla
- [x] Modified vanilla
- [ ] Cave Story+
- [ ] Cave Story+ (PC/Switch)
- [x] Base mod
- [ ] Mod loading
- [x] Curly Story
- [ ] Wind Fortress (~40%)
- [ ] Wind Fortress
- [ ] Boss Run
- [x] Seasonal graphics
- [x] Co-op gameplay
- [ ] Remastered soundtrack
*(tbd)*
@ -121,14 +107,6 @@ Because methods used to extract game data from cartridge vary, you have to find
![CS+ with enhanced graphics](https://i.imgur.com/YaPAs70.png)
#### Legal note
This project includes reverse engineered implementations of NPC and game physics algorithms, derived from freeware Cave Story and PC Cave Story+ executables.
Since the game's (non-existent, even for CS+) EULA does not prohibit reverse engineering,
[according to Secion 103(f)](https://www.law.cornell.edu/uscode/text/17/1201) we could legally revese engineer those parts
to achieve interoperability.
#### Credits
- Studio Pixel/Nicalis for Cave Story

View File

@ -23,7 +23,7 @@ impl BulletManager {
}
}
pub fn create_bullet(&mut self, x: isize, y: isize, btype: u16, owner: TargetPlayer, direction: Direction, constants: &EngineConstants) {
pub fn create_bullet(&mut self, x: i32, y: i32, btype: u16, owner: TargetPlayer, direction: Direction, constants: &EngineConstants) {
self.bullets.push(Bullet::new(x, y, btype, owner, direction, constants));
}
@ -56,14 +56,14 @@ impl BulletManager {
pub struct Bullet {
pub btype: u16,
pub x: isize,
pub y: isize,
pub vel_x: isize,
pub vel_y: isize,
pub target_x: isize,
pub target_y: isize,
pub prev_x: isize,
pub prev_y: isize,
pub x: i32,
pub y: i32,
pub vel_x: i32,
pub vel_y: i32,
pub target_x: i32,
pub target_y: i32,
pub prev_x: i32,
pub prev_y: i32,
pub life: u16,
pub lifetime: u16,
pub damage: u16,
@ -84,7 +84,7 @@ pub struct Bullet {
}
impl Bullet {
pub fn new(x: isize, y: isize, btype: u16, owner: TargetPlayer, direction: Direction, constants: &EngineConstants) -> Bullet {
pub fn new(x: i32, y: i32, btype: u16, owner: TargetPlayer, direction: Direction, constants: &EngineConstants) -> Bullet {
let bullet = constants.weapon.bullet_table
.get(btype as usize)
.unwrap_or_else(|| &BulletData {
@ -398,7 +398,7 @@ impl Bullet {
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Right);
}
fn judge_hit_block_destroy(&mut self, x: isize, y: isize, hit_attribs: &[u8; 4], state: &mut SharedGameState) {
fn judge_hit_block_destroy(&mut self, x: i32, y: i32, hit_attribs: &[u8; 4], state: &mut SharedGameState) {
let mut hits = [false; 4];
let block_x = (x * 16 + 8) * 0x200;
let block_y = (y * 16 + 8) * 0x200;
@ -413,77 +413,77 @@ impl Bullet {
// left wall
if hits[0] && hits[2] {
if (self.x - self.hit_bounds.left as isize) < block_x {
if (self.x - self.hit_bounds.left as i32) < block_x {
self.flags.set_hit_left_wall(true);
}
} else if hits[0] && !hits[2] {
if (self.x - self.hit_bounds.left as isize) < block_x
&& (self.y - self.hit_bounds.top as isize) < block_y - (3 * 0x200) {
if (self.x - self.hit_bounds.left as i32) < block_x
&& (self.y - self.hit_bounds.top as i32) < block_y - (3 * 0x200) {
self.flags.set_hit_left_wall(true);
}
} else if !hits[0] && hits[2]
&& (self.x - self.hit_bounds.left as isize) < block_x
&& (self.y + self.hit_bounds.top as isize) > block_y + (3 * 0x200) {
&& (self.x - self.hit_bounds.left as i32) < block_x
&& (self.y + self.hit_bounds.top as i32) > block_y + (3 * 0x200) {
self.flags.set_hit_left_wall(true);
}
// right wall
if hits[1] && hits[3] {
if (self.x + self.hit_bounds.right as isize) > block_x {
if (self.x + self.hit_bounds.right as i32) > block_x {
self.flags.set_hit_right_wall(true);
}
} else if hits[1] && !hits[3] {
if (self.x + self.hit_bounds.right as isize) > block_x
&& (self.y - self.hit_bounds.top as isize) < block_y - (3 * 0x200) {
if (self.x + self.hit_bounds.right as i32) > block_x
&& (self.y - self.hit_bounds.top as i32) < block_y - (3 * 0x200) {
self.flags.set_hit_right_wall(true);
}
} else if !hits[1] && hits[3]
&& (self.x + self.hit_bounds.right as isize) > block_x
&& (self.y + self.hit_bounds.top as isize) > block_y + (3 * 0x200) {
&& (self.x + self.hit_bounds.right as i32) > block_x
&& (self.y + self.hit_bounds.top as i32) > block_y + (3 * 0x200) {
self.flags.set_hit_right_wall(true);
}
// ceiling
if hits[0] && hits[1] {
if (self.y - self.hit_bounds.top as isize) < block_y {
if (self.y - self.hit_bounds.top as i32) < block_y {
self.flags.set_hit_top_wall(true);
}
} else if hits[0] && !hits[1] {
if (self.x - self.hit_bounds.left as isize) < block_x - (3 * 0x200)
&& (self.y - self.hit_bounds.top as isize) < block_y {
if (self.x - self.hit_bounds.left as i32) < block_x - (3 * 0x200)
&& (self.y - self.hit_bounds.top as i32) < block_y {
self.flags.set_hit_top_wall(true);
}
} else if !hits[0] && hits[1]
&& (self.x + self.hit_bounds.right as isize) > block_x + (3 * 0x200)
&& (self.y - self.hit_bounds.top as isize) < block_y {
&& (self.x + self.hit_bounds.right as i32) > block_x + (3 * 0x200)
&& (self.y - self.hit_bounds.top as i32) < block_y {
self.flags.set_hit_top_wall(true);
}
// ground
if hits[2] && hits[3] {
if (self.y + self.hit_bounds.bottom as isize) > block_y {
if (self.y + self.hit_bounds.bottom as i32) > block_y {
self.flags.set_hit_bottom_wall(true);
}
} else if hits[2] && !hits[3] {
if (self.x - self.hit_bounds.left as isize) < block_x - (3 * 0x200)
&& (self.y + self.hit_bounds.bottom as isize) > block_y {
if (self.x - self.hit_bounds.left as i32) < block_x - (3 * 0x200)
&& (self.y + self.hit_bounds.bottom as i32) > block_y {
self.flags.set_hit_bottom_wall(true);
}
} else if !hits[2] && hits[3]
&& (self.x + self.hit_bounds.right as isize) > block_x + (3 * 0x200)
&& (self.y + self.hit_bounds.bottom as isize) > block_y {
&& (self.x + self.hit_bounds.right as i32) > block_x + (3 * 0x200)
&& (self.y + self.hit_bounds.bottom as i32) > block_y {
self.flags.set_hit_bottom_wall(true);
}
if self.weapon_flags.flag_x08() {
if self.flags.hit_left_wall() {
self.x = block_x + self.hit_bounds.right as isize;
self.x = block_x + self.hit_bounds.right as i32;
} else if self.flags.hit_right_wall() {
self.x = block_x - self.hit_bounds.left as isize;
self.x = block_x - self.hit_bounds.left as i32;
} else if self.flags.hit_top_wall() {
self.y = block_y + self.hit_bounds.bottom as isize;
self.y = block_y + self.hit_bounds.bottom as i32;
} else if self.flags.hit_bottom_wall() {
self.y = block_y - self.hit_bounds.top as isize;
self.y = block_y - self.hit_bounds.top as i32;
}
} else if self.flags.hit_left_wall() || self.flags.hit_top_wall()
|| self.flags.hit_right_wall() || self.flags.hit_bottom_wall() {
@ -494,22 +494,22 @@ impl Bullet {
impl PhysicalEntity for Bullet {
#[inline(always)]
fn x(&self) -> isize {
fn x(&self) -> i32 {
self.x
}
#[inline(always)]
fn y(&self) -> isize {
fn y(&self) -> i32 {
self.y
}
#[inline(always)]
fn vel_x(&self) -> isize {
fn vel_x(&self) -> i32 {
self.vel_x
}
#[inline(always)]
fn vel_y(&self) -> isize {
fn vel_y(&self) -> i32 {
self.vel_y
}
@ -523,22 +523,22 @@ impl PhysicalEntity for Bullet {
}
#[inline(always)]
fn set_x(&mut self, x: isize) {
fn set_x(&mut self, x: i32) {
self.x = x;
}
#[inline(always)]
fn set_y(&mut self, y: isize) {
fn set_y(&mut self, y: i32) {
self.y = y;
}
#[inline(always)]
fn set_vel_x(&mut self, vel_x: isize) {
fn set_vel_x(&mut self, vel_x: i32) {
self.vel_x = vel_x;
}
#[inline(always)]
fn set_vel_y(&mut self, vel_y: isize) {
fn set_vel_y(&mut self, vel_y: i32) {
self.vel_y = vel_y;
}
@ -562,11 +562,11 @@ impl PhysicalEntity for Bullet {
false
}
fn judge_hit_block(&mut self, _state: &mut SharedGameState, x: isize, y: isize) {
if (self.x - self.hit_bounds.left as isize) < (x * 16 + 8) * 0x200
&& (self.x + self.hit_bounds.right as isize) > (x * 16 - 8) * 0x200
&& (self.y - self.hit_bounds.top as isize) < (y * 16 + 8) * 0x200
&& (self.y + self.hit_bounds.bottom as isize) > (y * 16 - 8) * 0x200
fn judge_hit_block(&mut self, _state: &mut SharedGameState, x: i32, y: i32) {
if (self.x - self.hit_bounds.left as i32) < (x * 16 + 8) * 0x200
&& (self.x + self.hit_bounds.right as i32) > (x * 16 - 8) * 0x200
&& (self.y - self.hit_bounds.top as i32) < (y * 16 + 8) * 0x200
&& (self.y + self.hit_bounds.bottom as i32) > (y * 16 - 8) * 0x200
{
self.flags.set_weapon_hit_block(true);
}
@ -578,8 +578,8 @@ impl PhysicalEntity for Bullet {
return;
}
let x = clamp(self.x() / 16 / 0x200, 0, stage.map.width as isize);
let y = clamp(self.y() / 16 / 0x200, 0, stage.map.height as isize);
let x = clamp(self.x() / 16 / 0x200, 0, stage.map.width as i32);
let y = clamp(self.y() / 16 / 0x200, 0, stage.map.height as i32);
let mut hit_attribs = [0u8; 4];
for (idx, (&ox, &oy)) in OFF_X.iter().zip(OFF_Y.iter()).enumerate() {
@ -615,13 +615,13 @@ impl PhysicalEntity for Bullet {
npc.y = (y * 16 + 8) * 0x200;
for _ in 0..4 {
npc.vel_x = state.game_rng.range(-0x200..0x200) as isize;
npc.vel_y = state.game_rng.range(-0x200..0x200) as isize;
npc.vel_x = state.game_rng.range(-0x200..0x200) as i32;
npc.vel_y = state.game_rng.range(-0x200..0x200) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
if let Some(tile) = stage.map.tiles.get_mut(stage.map.width * (y + oy) as usize + (x + ox) as usize) {
if let Some(tile) = stage.map.tiles.get_mut(stage.map.width as usize * (y + oy) as usize + (x + ox) as usize) {
*tile = tile.wrapping_sub(1);
}
}

View File

@ -26,14 +26,14 @@ pub enum CaretType {
pub struct Caret {
pub ctype: CaretType,
pub x: isize,
pub y: isize,
pub vel_x: isize,
pub vel_y: isize,
pub offset_x: isize,
pub offset_y: isize,
pub prev_x: isize,
pub prev_y: isize,
pub x: i32,
pub y: i32,
pub vel_x: i32,
pub vel_y: i32,
pub offset_x: i32,
pub offset_y: i32,
pub prev_x: i32,
pub prev_y: i32,
pub cond: Condition,
pub direction: Direction,
pub anim_rect: Rect<u16>,
@ -43,7 +43,7 @@ pub struct Caret {
}
impl Caret {
pub fn new(x: isize, y: isize, ctype: CaretType, direct: Direction, constants: &EngineConstants) -> Caret {
pub fn new(x: i32, y: i32, ctype: CaretType, direct: Direction, constants: &EngineConstants) -> Caret {
let (offset_x, offset_y) = constants.caret.offsets[ctype as usize];
Caret {
@ -236,8 +236,8 @@ impl Caret {
if self.action_num == 0 {
self.action_num = 1;
let angle = rng.range(0..255) as f64 * CDEG_RAD;
self.vel_x = (angle.cos() * 1024.0) as isize;
self.vel_y = (angle.sin() * 1024.0) as isize;
self.vel_x = (angle.cos() * 1024.0) as i32;
self.vel_y = (angle.sin() * 1024.0) as i32;
}
self.x += self.vel_x;
@ -276,11 +276,11 @@ impl Caret {
if self.anim_num == 0 {
match self.direction {
Direction::Left => {
self.vel_x = rng.range(-0x600..0x600) as isize; // -3.0fix9..3.0fix9
self.vel_y = rng.range(-0x200..0x200) as isize; // -1.0fix9..1.0fix9
self.vel_x = rng.range(-0x600..0x600) as i32; // -3.0fix9..3.0fix9
self.vel_y = rng.range(-0x200..0x200) as i32; // -1.0fix9..1.0fix9
}
Direction::Up => {
self.vel_y = rng.range(-3..-1) as isize * 0x200;
self.vel_y = rng.range(-3..-1) as i32 * 0x200;
}
_ => {}
}

View File

@ -213,7 +213,7 @@ impl Direction {
}
}
pub fn vector_x(&self) -> isize {
pub fn vector_x(&self) -> i32 {
match self {
Direction::Left => { -1 }
Direction::Up => { 0 }
@ -223,7 +223,7 @@ impl Direction {
}
}
pub fn vector_y(&self) -> isize {
pub fn vector_y(&self) -> i32 {
match self {
Direction::Left => { 0 }
Direction::Up => { -1 }
@ -350,11 +350,12 @@ macro_rules! rect_deserialze {
rect_deserialze!(u8);
rect_deserialze!(u16);
rect_deserialze!(i32);
rect_deserialze!(isize);
rect_deserialze!(usize);
#[inline(always)]
pub fn fix9_scale(val: isize, scale: f32) -> f32 {
pub fn fix9_scale(val: i32, scale: f32) -> f32 {
(val as f64 * scale as f64 / 512.0).floor() as f32 / scale
}
@ -363,11 +364,10 @@ fn lerp_f64(v1: f64, v2: f64, t: f64) -> f64 {
v1 * (1.0 - t.fract()) + v2 * t.fract()
}
pub fn interpolate_fix9_scale(old_val: isize, val: isize, frame_delta: f64) -> f32 {
pub fn interpolate_fix9_scale(old_val: i32, val: i32, frame_delta: f64) -> f32 {
if (frame_delta - 1.0).abs() < 0.001 {
return (val / 0x200) as f32;
}
(lerp_f64(old_val as f64, val as f64, frame_delta) / 512.0) as f32
//((lerp_f64(old_val as f64, val as f64, frame_delta) * scale as f64 / 512.0).floor() / (scale as f64)) as f32
}

View File

@ -12,24 +12,24 @@ mod npcs;
#[derive(Debug, Copy, Clone)]
pub struct PhysicsConsts {
pub max_dash: isize,
pub max_move: isize,
pub gravity_ground: isize,
pub gravity_air: isize,
pub dash_ground: isize,
pub dash_air: isize,
pub resist: isize,
pub jump: isize,
pub max_dash: i32,
pub max_move: i32,
pub gravity_ground: i32,
pub gravity_air: i32,
pub dash_ground: i32,
pub dash_air: i32,
pub resist: i32,
pub jump: i32,
}
#[derive(Debug, Copy, Clone)]
pub struct BoosterConsts {
pub fuel: usize,
pub b2_0_up: isize,
pub b2_0_up_nokey: isize,
pub b2_0_down: isize,
pub b2_0_left: isize,
pub b2_0_right: isize,
pub fuel: u32,
pub b2_0_up: i32,
pub b2_0_up_nokey: i32,
pub b2_0_down: i32,
pub b2_0_left: i32,
pub b2_0_right: i32,
}
#[derive(Debug, Copy, Clone)]
@ -47,7 +47,7 @@ pub struct MyCharConsts {
#[derive(Debug)]
pub struct CaretConsts {
pub offsets: [(isize, isize); 18],
pub offsets: [(i32, i32); 18],
pub bubble_left_rects: Vec<Rect<u16>>,
pub bubble_right_rects: Vec<Rect<u16>>,
pub projectile_dissipation_left_rects: Vec<Rect<u16>>,
@ -707,7 +707,7 @@ impl EngineConstants {
"Title" => (320, 48),
},
textscript: TextScriptConsts {
encoding: TextScriptEncoding::UTF8,
encoding: TextScriptEncoding::ShiftJIS,
encrypted: true,
animated_face_pics: false,
textbox_rect_top: Rect { left: 0, top: 0, right: 244, bottom: 8 },

View File

@ -12,14 +12,14 @@ pub enum UpdateTarget {
}
pub struct Frame {
pub x: isize,
pub y: isize,
pub prev_x: isize,
pub prev_y: isize,
pub x: i32,
pub y: i32,
pub prev_x: i32,
pub prev_y: i32,
pub update_target: UpdateTarget,
pub target_x: isize,
pub target_y: isize,
pub wait: isize,
pub target_x: i32,
pub target_y: i32,
pub wait: i32,
}
impl Frame {
@ -35,31 +35,31 @@ impl Frame {
}
pub fn immediate_update(&mut self, state: &mut SharedGameState, stage: &Stage) {
if (stage.map.width - 1) * 16 < state.canvas_size.0 as usize {
self.x = -(((state.canvas_size.0 as isize - ((stage.map.width - 1) * 16) as isize) * 0x200) / 2);
if (stage.map.width as usize).saturating_sub(1) * 16 < state.canvas_size.0 as usize {
self.x = -(((state.canvas_size.0 as i32 - ((stage.map.width - 1) * 16) as i32) * 0x200) / 2);
} else {
self.x = self.target_x - (state.canvas_size.0 as isize * 0x200 / 2);
self.x = self.target_x - (state.canvas_size.0 as i32 * 0x200 / 2);
if self.x < 0 {
self.x = 0;
}
let max_x = (((stage.map.width as isize - 1) * 16) - state.canvas_size.0 as isize) * 0x200;
let max_x = (((stage.map.width as i32 - 1) * 16) - state.canvas_size.0 as i32) * 0x200;
if self.x > max_x {
self.x = max_x;
}
}
if (stage.map.height - 1) * 16 < state.canvas_size.1 as usize {
self.y = -(((state.canvas_size.1 as isize - ((stage.map.height - 1) * 16) as isize) * 0x200) / 2);
if (stage.map.height as usize).saturating_sub(1) * 16 < state.canvas_size.1 as usize {
self.y = -(((state.canvas_size.1 as i32 - ((stage.map.height - 1) * 16) as i32) * 0x200) / 2);
} else {
self.y = self.target_y - (state.canvas_size.1 as isize * 0x200 / 2);
self.y = self.target_y - (state.canvas_size.1 as i32 * 0x200 / 2);
if self.y < 0 {
self.y = 0;
}
let max_y = (((stage.map.height as isize - 1) * 16) - state.canvas_size.1 as isize) * 0x200;
let max_y = (((stage.map.height as i32 - 1) * 16) - state.canvas_size.1 as i32) * 0x200;
if self.y > max_y {
self.y = max_y;
}
@ -70,31 +70,31 @@ impl Frame {
}
pub fn update(&mut self, state: &mut SharedGameState, stage: &Stage) {
if (stage.map.width - 1) * 16 < state.canvas_size.0 as usize {
self.x = -(((state.canvas_size.0 as isize - ((stage.map.width - 1) * 16) as isize) * 0x200) / 2);
if (stage.map.width as usize).saturating_sub(1) * 16 < state.canvas_size.0 as usize {
self.x = -(((state.canvas_size.0 as i32 - ((stage.map.width - 1) * 16) as i32) * 0x200) / 2);
} else {
self.x += (self.target_x - (state.canvas_size.0 as isize * 0x200 / 2) - self.x) / self.wait;
self.x += (self.target_x - (state.canvas_size.0 as i32 * 0x200 / 2) - self.x) / self.wait;
if self.x < 0 {
self.x = 0;
}
let max_x = (((stage.map.width as isize - 1) * 16) - state.canvas_size.0 as isize) * 0x200;
let max_x = (((stage.map.width as i32 - 1) * 16) - state.canvas_size.0 as i32) * 0x200;
if self.x > max_x {
self.x = max_x;
}
}
if (stage.map.height - 1) * 16 < state.canvas_size.1 as usize {
self.y = -(((state.canvas_size.1 as isize - ((stage.map.height - 1) * 16) as isize) * 0x200) / 2);
if (stage.map.height as usize).saturating_sub(1) * 16 < state.canvas_size.1 as usize {
self.y = -(((state.canvas_size.1 as i32 - ((stage.map.height - 1) * 16) as i32) * 0x200) / 2);
} else {
self.y += (self.target_y - (state.canvas_size.1 as isize * 0x200 / 2) - self.y) / self.wait;
self.y += (self.target_y - (state.canvas_size.1 as i32 * 0x200 / 2) - self.y) / self.wait;
if self.y < 0 {
self.y = 0;
}
let max_y = (((stage.map.height as isize - 1) * 16) - state.canvas_size.1 as isize) * 0x200;
let max_y = (((stage.map.height as i32 - 1) * 16) - state.canvas_size.1 as i32) * 0x200;
if self.y > max_y {
self.y = max_y;
}
@ -103,8 +103,8 @@ impl Frame {
if state.quake_counter > 0 {
state.quake_counter -= 1;
self.x += state.effect_rng.range(-0x300..0x300) as isize;
self.y += state.effect_rng.range(-0x300..0x300) as isize;
self.x += state.effect_rng.range(-0x300..0x300) as i32;
self.y += state.effect_rng.range(-0x300..0x300) as i32;
}
}
}

View File

@ -1,10 +1,14 @@
#[macro_use]
extern crate log;
#[cfg_attr(feature = "scripting", macro_use)]
#[cfg(feature = "scripting")]
extern crate lua_ffi;
extern crate strum;
#[macro_use]
extern crate strum_macros;
use std::{env, mem};
use std::cell::UnsafeCell;
use std::path;
use std::time::Instant;
@ -53,6 +57,8 @@ mod player;
mod profile;
mod rng;
mod scene;
#[cfg(feature = "scripting")]
mod scripting;
mod settings;
mod shaders;
mod shared_game_state;
@ -65,7 +71,7 @@ mod weapon;
struct Game {
scene: Option<Box<dyn Scene>>,
state: SharedGameState,
state: UnsafeCell<SharedGameState>,
ui: UI,
def_matrix: ColumnMatrix4<f32>,
start_time: Instant,
@ -80,7 +86,7 @@ impl Game {
scene: None,
ui: UI::new(ctx)?,
def_matrix: DrawParam::new().to_matrix(),
state: SharedGameState::new(ctx)?,
state: UnsafeCell::new(SharedGameState::new(ctx)?),
start_time: Instant::now(),
last_tick: 0,
next_tick: 0,
@ -92,15 +98,17 @@ impl Game {
fn update(&mut self, ctx: &mut Context) -> GameResult {
if let Some(scene) = self.scene.as_mut() {
match self.state.timing_mode {
let state_ref = unsafe { &mut *self.state.get() };
match state_ref.timing_mode {
TimingMode::_50Hz | TimingMode::_60Hz => {
let last_tick = self.next_tick;
while self.start_time.elapsed().as_nanos() >= self.next_tick && self.loops < 10 {
if (self.state.settings.speed - 1.0).abs() < 0.01 {
self.next_tick += self.state.timing_mode.get_delta() as u128;
if (state_ref.settings.speed - 1.0).abs() < 0.01 {
self.next_tick += state_ref.timing_mode.get_delta() as u128;
} else {
self.next_tick += (self.state.timing_mode.get_delta() as f64 / self.state.settings.speed) as u128;
self.next_tick += (state_ref.timing_mode.get_delta() as f64 / state_ref.settings.speed) as u128;
}
self.loops += 1;
}
@ -108,21 +116,21 @@ impl Game {
if self.loops == 10 {
log::warn!("Frame skip is way too high, a long system lag occurred?");
self.last_tick = self.start_time.elapsed().as_nanos();
self.next_tick = self.last_tick + (self.state.timing_mode.get_delta() as f64 / self.state.settings.speed) as u128;
self.next_tick = self.last_tick + (state_ref.timing_mode.get_delta() as f64 / state_ref.settings.speed) as u128;
self.loops = 0;
}
if self.loops != 0 {
scene.draw_tick(&mut self.state)?;
scene.draw_tick(state_ref)?;
self.last_tick = last_tick;
}
for _ in 0..self.loops {
scene.tick(&mut self.state, ctx)?;
scene.tick(state_ref, ctx)?;
}
}
TimingMode::FrameSynchronized => {
scene.tick(&mut self.state, ctx)?;
scene.tick(state_ref, ctx)?;
}
}
}
@ -130,28 +138,30 @@ impl Game {
}
fn draw(&mut self, ctx: &mut Context) -> GameResult {
if self.state.timing_mode != TimingMode::FrameSynchronized {
let state_ref = unsafe { &mut *self.state.get() };
if state_ref.timing_mode != TimingMode::FrameSynchronized {
let n1 = (self.start_time.elapsed().as_nanos() - self.last_tick) as f64;
let n2 = (self.next_tick - self.last_tick) as f64;
self.state.frame_time = n1 / n2;
state_ref.frame_time = n1 / n2;
}
self.loops = 0;
graphics::clear(ctx, [0.0, 0.0, 0.0, 1.0].into());
graphics::set_transform(ctx, DrawParam::new()
.scale(Vector2::new(self.state.scale, self.state.scale))
.scale(Vector2::new(state_ref.scale, state_ref.scale))
.to_matrix());
graphics::apply_transformations(ctx)?;
if let Some(scene) = self.scene.as_mut() {
scene.draw(&mut self.state, ctx)?;
if self.state.settings.touch_controls {
self.state.touch_controls.draw(self.state.canvas_size, &self.state.constants, &mut self.state.texture_set, ctx)?;
scene.draw(state_ref, ctx)?;
if state_ref.settings.touch_controls {
state_ref.touch_controls.draw(state_ref.canvas_size, &state_ref.constants, &mut state_ref.texture_set, ctx)?;
}
graphics::set_transform(ctx, self.def_matrix);
graphics::apply_transformations(ctx)?;
self.ui.draw(&mut self.state, ctx, scene)?;
self.ui.draw(state_ref, ctx, scene)?;
}
graphics::present(ctx)?;
@ -161,7 +171,7 @@ impl Game {
fn key_down_event(&mut self, key_code: KeyCode, _key_mod: KeyMods, repeat: bool) {
if repeat { return; }
let state = &mut self.state;
let state = unsafe { &mut *self.state.get() };
match key_code {
KeyCode::F7 => { state.set_speed(1.0) }
KeyCode::F8 => {
@ -328,8 +338,19 @@ pub fn init() -> GameResult {
game.ui.handle_events(ctx, &event);
} else {
let mut new_game = Game::new(ctx).unwrap();
new_game.state.next_scene = Some(Box::new(LoadingScene::new()));
let state_ref = unsafe { &mut *new_game.state.get() };
state_ref.next_scene = Some(Box::new(LoadingScene::new()));
game = Some(new_game);
#[cfg(feature = "scripting")]
{
unsafe {
let game_ref = game.as_mut().unwrap();
let state_ref = game_ref.state.get();
(&mut *state_ref).lua.update_refs(game_ref.state.get(), ctx as *mut Context);
}
}
}
}
@ -358,22 +379,26 @@ pub fn init() -> GameResult {
match event {
WindowEvent::CloseRequested => {
if let Some(game) = &mut game {
game.state.shutdown();
let state_ref = unsafe { &mut *game.state.get() };
state_ref.shutdown();
}
*flow = ControlFlow::Exit;
}
WindowEvent::Resized(_) => {
if let (Some(ctx), Some(game)) = (&mut context, &mut game) {
game.state.tmp_canvas = Canvas::with_window_size(ctx).unwrap();
game.state.game_canvas = Canvas::with_window_size(ctx).unwrap();
game.state.lightmap_canvas = Canvas::with_window_size(ctx).unwrap();
game.state.handle_resize(ctx).unwrap();
let state_ref = unsafe { &mut *game.state.get() };
state_ref.tmp_canvas = Canvas::with_window_size(ctx).unwrap();
state_ref.game_canvas = Canvas::with_window_size(ctx).unwrap();
state_ref.lightmap_canvas = Canvas::with_window_size(ctx).unwrap();
state_ref.handle_resize(ctx).unwrap();
graphics::window(ctx).update_gfx(&mut game.ui.main_color, &mut game.ui.main_depth);
}
}
WindowEvent::Touch(touch) => {
if let Some(game) = &mut game {
game.state.touch_controls.process_winit_event(game.state.scale, touch);
let state_ref = unsafe { &mut *game.state.get() };
state_ref.touch_controls.process_winit_event(state_ref.scale, touch);
}
}
WindowEvent::KeyboardInput {
@ -420,19 +445,21 @@ pub fn init() -> GameResult {
}
window(ctx).window().request_redraw();
if game.state.shutdown {
let state_ref = unsafe { &mut *game.state.get() };
if state_ref.shutdown {
log::info!("Shutting down...");
*flow = ControlFlow::Exit;
return;
}
if game.state.next_scene.is_some() {
mem::swap(&mut game.scene, &mut game.state.next_scene);
game.state.next_scene = None;
if state_ref.next_scene.is_some() {
mem::swap(&mut game.scene, &mut state_ref.next_scene);
state_ref.next_scene = None;
game.scene.as_mut().unwrap().init(&mut game.state, ctx).unwrap();
game.scene.as_mut().unwrap().init(state_ref, ctx).unwrap();
game.loops = 0;
game.state.frame_time = 0.0;
state_ref.frame_time = 0.0;
}
}
}

View File

@ -4,6 +4,7 @@ use itertools::Itertools;
use crate::scene::game_scene::GameScene;
use crate::shared_game_state::SharedGameState;
use crate::text_script::TextScriptExecutionState;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[repr(u8)]
@ -95,7 +96,7 @@ impl LiveDebugger {
state.set_speed(speed);
}
if ui.button(im_str!("Map Selector"), [0.0, 0.0]) {
if ui.button(im_str!("Maps"), [0.0, 0.0]) {
self.map_selector_visible = !self.map_selector_visible;
}
@ -104,16 +105,22 @@ impl LiveDebugger {
self.events_visible = !self.events_visible;
}
ui.same_line(0.0);
if ui.button(im_str!("Hacks"), [0.0, 0.0]) {
self.hacks_visible = !self.hacks_visible;
}
ui.same_line(0.0);
if ui.button(im_str!("Flags"), [0.0, 0.0]) {
self.flags_visible = !self.flags_visible;
}
#[cfg(feature = "scripting")]
{
ui.same_line(0.0);
if ui.button(im_str!("Reload Scripts"), [0.0, 0.0]) {
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(0.0);
if game_scene.player2.cond.alive() {
if ui.button(im_str!("Drop Player 2"), [0.0, 0.0]) {
@ -152,21 +159,23 @@ impl LiveDebugger {
scene.inventory_player2 = game_scene.inventory_player2.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;
scene.player1.x = scene.stage.map.width as i32 / 2 * 16 * 0x200;
scene.player1.y = scene.stage.map.height as i32 / 2 * 16 * 0x200;
if scene.player1.life == 0 {
scene.player1.life = scene.player1.max_life;
}
scene.player2 = game_scene.player2.clone();
scene.player2.x = (scene.stage.map.width / 2 * 16 * 0x200) as isize;
scene.player2.y = (scene.stage.map.height / 2 * 16 * 0x200) as isize;
scene.player2.x = scene.stage.map.width as i32 / 2 * 16 * 0x200;
scene.player2.y = scene.stage.map.height as i32 / 2 * 16 * 0x200;
if scene.player2.life == 0 {
scene.player2.life = scene.player1.max_life;
}
state.textscript_vm.suspend = true;
state.textscript_vm.state = TextScriptExecutionState::Running(94, 0);
state.next_scene = Some(Box::new(scene));
}
Err(e) => {

View File

@ -10,8 +10,8 @@ static SUPPORTED_PXM_VERSIONS: [u8; 1] = [0x10];
static SUPPORTED_PXE_VERSIONS: [u8; 2] = [0, 0x10];
pub struct Map {
pub width: usize,
pub height: usize,
pub width: u16,
pub height: u16,
pub tiles: Vec<u8>,
pub attrib: [u8; 0x100],
}
@ -33,9 +33,9 @@ impl Map {
return Err(ResourceLoadError(format!("Unsupported PXM version: {:#x}", version)));
}
let width = map_data.read_u16::<LE>()? as usize;
let height = map_data.read_u16::<LE>()? as usize;
let mut tiles = vec![0u8; width * height];
let width = map_data.read_u16::<LE>()?;
let height = map_data.read_u16::<LE>()?;
let mut tiles = vec![0u8; (width * height) as usize];
let mut attrib = [0u8; 0x100];
log::info!("Map size: {}x{}", width, height);
@ -54,11 +54,11 @@ impl Map {
}
pub fn get_attribute(&self, x: usize, y: usize) -> u8 {
if x >= self.width || y >= self.height {
if x >= self.width as usize || y >= self.height as usize {
return 0;
}
self.attrib[*self.tiles.get(self.width * y + x).unwrap_or_else(|| &0u8) as usize]
self.attrib[*self.tiles.get(self.width as usize * y + x).unwrap_or_else(|| &0u8) as usize]
}
}

View File

@ -4,15 +4,31 @@ use crate::common::Rect;
use crate::input::combined_menu_controller::CombinedMenuController;
use crate::shared_game_state::SharedGameState;
pub struct MenuSaveInfo {
}
pub enum MenuEntry {
Hidden,
Active(String),
Disabled(String),
Toggle(String, bool),
Options(String, usize, Vec<String>),
SaveData(MenuSaveInfo),
NewSave,
}
impl MenuEntry {
pub fn height(&self) -> f64 {
14.0
match self {
MenuEntry::Hidden => 0.0,
MenuEntry::Active(_) => 14.0,
MenuEntry::Disabled(_) => 14.0,
MenuEntry::Toggle(_, _) => 14.0,
MenuEntry::Options(_, _, _) => 14.0,
MenuEntry::SaveData(_) => 30.0,
MenuEntry::NewSave => 30.0,
}
}
}
@ -29,6 +45,7 @@ pub struct Menu {
pub height: u16,
pub selected: usize,
pub entries: Vec<MenuEntry>,
entry_y: u16,
anim_num: u16,
anim_wait: u16,
}
@ -43,6 +60,7 @@ impl Menu {
width,
height,
selected: 0,
entry_y: 0,
anim_num: 0,
anim_wait: 0,
entries: Vec::new(),
@ -53,6 +71,16 @@ impl Menu {
self.entries.push(entry);
}
pub fn update_height(&mut self) {
let mut height = 6.0;
for entry in self.entries.iter() {
height += entry.height();
}
self.height = height.max(6.0) as u16;
}
pub fn draw(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;
@ -155,7 +183,7 @@ impl Menu {
rect.bottom = rect.top + 16;
batch.add_rect(self.x as f32,
self.y as f32 + 2.0 + (self.selected as f32 * 14.0),
self.y as f32 + 2.0 + self.entry_y as f32,
&rect);
batch.draw(ctx)?;
@ -177,6 +205,8 @@ impl Menu {
state.font.draw_text(value_text.chars(), self.x as f32 + self.width as f32 - val_text_len, y, &state.constants, &mut state.texture_set, ctx)?;
}
MenuEntry::Hidden => {}
_ => {}
}
y += entry.height() as f32;
@ -218,6 +248,14 @@ impl Menu {
}
}
if !self.entries.is_empty() {
self.entry_y = self.entries[0..(self.selected)]
.iter()
.map(|e| e.height())
.sum::<f64>()
.max(0.0) as u16;
}
let mut y = self.y as f32 + 6.0;
for (idx, entry) in self.entries.iter_mut().enumerate() {
let entry_bounds = Rect::new_size(self.x, y as isize, self.width as isize, entry.height() as isize);

View File

@ -34,10 +34,10 @@ impl NPC {
npc.direction = Direction::Left;
for _ in 0..3 {
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -104,8 +104,8 @@ impl NPC {
let mut angle = ((self.y + 4 * 0x200 - player.y) as f64 / (self.x - player.y) as f64).atan();
angle += self.rng.range(-16..16) as f64 * std::f64::consts::FRAC_PI_8;
npc.vel_x = (angle.cos() * 512.0) as isize; // 1.0fix9
npc.vel_y = (angle.sin() * 512.0) as isize;
npc.vel_x = (angle.cos() * 512.0) as i32; // 1.0fix9
npc.vel_y = (angle.sin() * 512.0) as i32;
let _ = npc_list.spawn(0x100, npc);
@ -293,10 +293,10 @@ impl NPC {
for _ in 0..3 {
npc.cond.set_alive(true);
npc.direction = Direction::Left;
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -475,8 +475,8 @@ impl NPC {
}
}
102 => {
let x = clamp(self.x / (16 * 0x200), 0, stage.map.width as isize) as usize;
let y = clamp(self.y / (16 * 0x200), 0, stage.map.height as isize) as usize;
let x = clamp(self.x / (16 * 0x200), 0, stage.map.width as i32) as usize;
let y = clamp(self.y / (16 * 0x200), 0, stage.map.height as i32) as usize;
if y <= 34 && stage.change_tile(x, y, 0) {
state.sound_manager.play_sfx(44);
@ -484,20 +484,20 @@ impl NPC {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
npc.x = x as isize * 16 * 0x200;
npc.y = y as isize * 16 * 0x200;
npc.x = x as i32 * 16 * 0x200;
npc.y = y as i32 * 16 * 0x200;
let _ = npc_list.spawn(0x100, npc.clone());
let _ = npc_list.spawn(0x100, npc.clone());
if x > 0 && stage.change_tile(x - 1, y, 0) {
npc.x = (x - 1) as isize * 16 * 0x200;
npc.x = (x - 1) as i32 * 16 * 0x200;
let _ = npc_list.spawn(0x100, npc.clone());
let _ = npc_list.spawn(0x100, npc.clone());
}
if x < stage.map.width && stage.change_tile(x + 1, y, 0) {
npc.x = (x + 1) as isize * 16 * 0x200;
if x < stage.map.width as usize && stage.change_tile(x + 1, y, 0) {
npc.x = (x + 1) as i32 * 16 * 0x200;
let _ = npc_list.spawn(0x100, npc.clone());
let _ = npc_list.spawn(0x100, npc);
}
@ -516,10 +516,10 @@ impl NPC {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
npc.direction = Direction::Left;
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc);
}
@ -554,10 +554,10 @@ impl NPC {
npc.direction = Direction::Left;
for _ in 0..16 {
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -682,8 +682,8 @@ impl NPC {
let mut npc = NPC::create(11, &state.npc_table);
npc.cond.set_alive(true);
npc.vel_x = (angle.cos() * -512.0) as isize;
npc.vel_y = (angle.sin() * -512.0) as isize;
npc.vel_x = (angle.cos() * -512.0) as i32;
npc.vel_y = (angle.sin() * -512.0) as i32;
npc.x = self.x;
npc.y = self.y + 4 * 0x200;
@ -766,16 +766,16 @@ impl NPC {
npc_proj.direction = Direction::Left;
for _ in 0..8 {
npc_smoke.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc_smoke.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc_smoke.vel_x = self.rng.range(-0x155..0x155) as isize;
npc_smoke.vel_y = self.rng.range(-0x600..0) as isize;
npc_smoke.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc_smoke.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc_smoke.vel_x = self.rng.range(-0x155..0x155) as i32;
npc_smoke.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc_smoke.clone());
npc_proj.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc_proj.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc_proj.vel_x = self.rng.range(-0x400..0x400) as isize;
npc_proj.vel_y = self.rng.range(-0x400..0) as isize;
npc_proj.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc_proj.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc_proj.vel_x = self.rng.range(-0x400..0x400) as i32;
npc_proj.vel_y = self.rng.range(-0x400..0) as i32;
let _ = npc_list.spawn(0x100, npc_proj.clone());
}
@ -865,7 +865,7 @@ impl NPC {
&& self.y - 12 * 0x200 < players[pi].y && self.y + 8 * 0x200 > players[pi].y { // 12.0fix9 / 8.0fix9
self.action_num = 10;
self.anim_num = 5;
self.vel_y2 = pi as isize;
self.vel_y2 = pi as i32;
players[pi].cond.set_hidden(true);
players[pi].damage(2, state, npc_list);
} else {
@ -894,7 +894,7 @@ impl NPC {
&& self.y - 12 * 0x200 < players[pi].y && self.y + 8 * 0x200 > players[pi].y {
self.action_num = 10;
self.anim_num = 5;
self.vel_y2 = pi as isize;
self.vel_y2 = pi as i32;
players[pi].cond.set_hidden(true);
players[pi].damage(2, state, npc_list);
}

View File

@ -628,8 +628,8 @@ impl NPC {
npc.cond.set_alive(true);
npc.x = self.x;
npc.y = self.y;
npc.vel_x = (angle.cos() * -1024.0) as isize;
npc.vel_y = (angle.sin() * -1024.0) as isize;
npc.vel_x = (angle.cos() * -1024.0) as i32;
npc.vel_y = (angle.sin() * -1024.0) as i32;
let _ = npc_list.spawn(0x100, npc);
state.sound_manager.play_sfx(39);

View File

@ -142,12 +142,12 @@ impl NPC {
0 | 1 => {
if self.action_num == 0 {
let angle = self.rng.range(0..0xff);
self.vel_x = ((angle as f64 * 1.40625).cos() * 512.0) as isize;
self.target_x = self.x + ((angle as f64 * 1.40625 + std::f64::consts::FRAC_2_PI).cos() * 8.0 * 512.0) as isize;
self.vel_x = ((angle as f64 * 1.40625).cos() * 512.0) as i32;
self.target_x = self.x + ((angle as f64 * 1.40625 + std::f64::consts::FRAC_2_PI).cos() * 8.0 * 512.0) as i32;
let angle = self.rng.range(0..0xff);
self.vel_y = ((angle as f64 * 1.40625).sin() * 512.0) as isize;
self.target_y = self.y + ((angle as f64 * 1.40625 + std::f64::consts::FRAC_2_PI).sin() * 8.0 * 512.0) as isize;
self.vel_y = ((angle as f64 * 1.40625).sin() * 512.0) as i32;
self.target_y = self.y + ((angle as f64 * 1.40625 + std::f64::consts::FRAC_2_PI).sin() * 8.0 * 512.0) as i32;
self.action_num = 1;
self.action_counter2 = 120;
@ -873,8 +873,8 @@ impl NPC {
npc.x = self.x;
npc.y = self.y;
for _ in 0..4 {
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -938,8 +938,8 @@ impl NPC {
npc.x = self.x;
npc.y = self.y;
for _ in 0..8 {
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -978,8 +978,8 @@ impl NPC {
npc.x = self.x;
npc.y = self.y;
for _ in 0..4 {
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -1059,8 +1059,8 @@ impl NPC {
npc.x = self.x;
npc.y = self.y;
for _ in 0..8 {
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -1224,8 +1224,8 @@ impl NPC {
self.target_y = self.y;
}
self.x = self.target_x + self.rng.range(-1..1) as isize * 0x200;
self.y = self.target_y + self.rng.range(-1..1) as isize * 0x200;
self.x = self.target_x + self.rng.range(-1..1) as i32 * 0x200;
self.y = self.target_y + self.rng.range(-1..1) as i32 * 0x200;
self.action_counter += 1;
if self.action_counter > 30 {
@ -1245,7 +1245,7 @@ impl NPC {
self.vel_x += 0x20;
self.x += self.vel_x;
self.y = self.target_y + self.rng.range(-1..1) as isize * 0x200;
self.y = self.target_y + self.rng.range(-1..1) as i32 * 0x200;
self.action_counter += 1;
if self.action_counter > 10 {

View File

@ -204,10 +204,10 @@ impl NPC {
npc.cond.set_alive(true);
for _ in 0..4 {
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -235,8 +235,8 @@ impl NPC {
self.action_counter += 1;
if self.action_counter > 100 && self.action_counter % 6 == 1 {
let deg = (if self.direction == Direction::Left { 0x88 } else { 0xf8 } + self.rng.range(-16..16)) as f64 * CDEG_RAD;
let vel_x = (deg.cos() * 1536.0) as isize;
let vel_y = (deg.sin() * 1536.0) as isize;
let vel_x = (deg.cos() * 1536.0) as i32;
let vel_y = (deg.sin() * 1536.0) as i32;
let mut npc = NPC::create(11, &state.npc_table);
@ -289,10 +289,10 @@ impl NPC {
npc.cond.set_alive(true);
for _ in 0..8 {
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -309,10 +309,10 @@ impl NPC {
npc.cond.set_alive(true);
npc.direction = Direction::Left;
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc);
}
@ -349,10 +349,10 @@ impl NPC {
npc.cond.set_alive(true);
npc.direction = Direction::Left;
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc);
}

View File

@ -122,7 +122,7 @@ impl NPC {
self.anim_counter += 1;
if (self.anim_counter % 8) == 1 {
state.create_caret(self.x + state.effect_rng.range(-8..8) as isize * 0x200,
state.create_caret(self.x + state.effect_rng.range(-8..8) as i32 * 0x200,
self.y + 8 * 0x200,
CaretType::LittleParticles, Direction::Up);
}

View File

@ -31,85 +31,4 @@ impl NPC {
Ok(())
}
pub(crate) fn tick_n361_gaudi_dashing(&mut self, state: &mut SharedGameState, players: [&mut Player; 2], npc_list: &NPCList) -> GameResult {
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.vel_x2 = 0;
self.vel_y2 = 0;
self.action_num = 1;
}
let player = self.get_closest_player_mut(players);
if (self.direction == Direction::Right && player.x > self.x + 272 * 0x200 && player.x < self.x + 288 * 0x200)
|| (self.direction == Direction::Left && player.x < self.x - 272 * 0x200 && player.x > self.x - 288 * 0x200) {
self.action_num = 10;
} else {
return Ok(());
}
}
10 | 11 => {
if self.action_num == 10 {
self.npc_flags.set_shootable(true);
self.action_num = 11;
self.damage = 5;
}
let player = self.get_closest_player_mut(players);
if self.x > player.x {
self.direction = Direction::Left;
} else {
self.direction = Direction::Right;
}
self.anim_counter += 1;
if self.anim_counter > 1 {
self.anim_counter = 0;
self.anim_num += 1;
if self.anim_num > 1 {
self.anim_num = 0;
}
}
self.vel_x2 += self.direction.vector_x() * 0x10;
self.vel_y2 += (player.y - self.y).signum() * 0x10;
if self.vel_x2 < 0 && self.flags.hit_left_wall() {
self.vel_x2 /= -2;
}
if self.vel_x2 > 0 && self.flags.hit_right_wall() {
self.vel_x2 /= -2;
}
if self.vel_y2 < 0 && self.flags.hit_top_wall() {
self.vel_y2 *= -1;
}
if self.vel_y2 > 0 && self.flags.hit_bottom_wall() {
self.vel_y2 /= -2;
}
self.vel_x2 = clamp(self.vel_x2, -0x5ff, 0x5ff);
self.vel_y2 = clamp(self.vel_y2, -0x5ff, 0x5ff);
self.x += self.vel_x2;
self.y += self.vel_y2;
}
_ => {}
}
if self.life <= 985 {
npc_list.create_death_smoke(self.x, self.y, 0, 2, state, &self.rng);
self.npc_type = 154;
self.action_num = 0;
}
let dir_offset = if self.direction == Direction::Left { 0 } else { 2 };
self.anim_rect = state.constants.npc.n361_gaudi_dashing[self.anim_num as usize + dir_offset];
Ok(())
}
}

View File

@ -54,8 +54,8 @@ impl NPC {
if self.direction == Direction::Left || self.direction == Direction::Up {
let angle = self.rng.range(0..31415) as f32 / 5000.0;
self.vel_x = (angle.cos() * self.rng.range(0x200..0x5ff) as f32) as isize;
self.vel_y = (angle.sin() * self.rng.range(0x200..0x5ff) as f32) as isize;
self.vel_x = (angle.cos() * self.rng.range(0x200..0x5ff) as f32) as i32;
self.vel_y = (angle.sin() * self.rng.range(0x200..0x5ff) as f32) as i32;
}
} else {
self.vel_x = (self.vel_x * 20) / 21;
@ -147,10 +147,10 @@ impl NPC {
npc.cond.set_alive(true);
for _ in 0..4 {
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -307,8 +307,8 @@ impl NPC {
npc.x = self.x;
npc.y = self.y;
for _ in 0..4 {
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -571,13 +571,13 @@ impl NPC {
droplet.direction = Direction::Left;
droplet.x = self.x;
droplet.y = self.y;
droplet.vel_x = 2 * self.rng.range(-0x200..0x200) as isize;
droplet.vel_y = 3 * self.rng.range(-0x200..0x80) as isize;
droplet.vel_x = 2 * self.rng.range(-0x200..0x200) as i32;
droplet.vel_y = 3 * self.rng.range(-0x200..0x80) as i32;
let _ = npc_list.spawn(0x100, droplet.clone());
if self.action_counter % 2 == 0 {
droplet.vel_x = 2 * self.rng.range(-0x200..0x200) as isize;
droplet.vel_y = 3 * self.rng.range(-0x200..0x80) as isize;
droplet.vel_x = 2 * self.rng.range(-0x200..0x200) as i32;
droplet.vel_y = 3 * self.rng.range(-0x200..0x80) as i32;
let _ = npc_list.spawn(0x100, droplet);
}
}
@ -610,7 +610,7 @@ impl NPC {
self.cond.set_alive(false);
}
if self.y > stage.map.height as isize * 16 * 0x200 {
if self.y > stage.map.height as i32 * 16 * 0x200 {
// out of map
self.cond.set_alive(false);
}
@ -698,7 +698,7 @@ impl NPC {
particle.cond.set_alive(true);
particle.direction = Direction::Left;
particle.x = self.x;
particle.y = self.y + (self.rng.range(-8..8) * 0x200) as isize;
particle.y = self.y + (self.rng.range(-8..8) * 0x200) as i32;
let _ = npc_list.spawn(0x100, particle);
}
}
@ -750,7 +750,7 @@ impl NPC {
let mut particle = NPC::create(199, &state.npc_table);
particle.cond.set_alive(true);
particle.direction = Direction::Up;
particle.x = self.x + (self.rng.range(-8..8) * 0x200) as isize;
particle.x = self.x + (self.rng.range(-8..8) * 0x200) as i32;
particle.y = self.y;
let _ = npc_list.spawn(0x100, particle);
}
@ -803,7 +803,7 @@ impl NPC {
particle.cond.set_alive(true);
particle.direction = Direction::Right;
particle.x = self.x;
particle.y = self.y + (self.rng.range(-8..8) * 0x200) as isize;
particle.y = self.y + (self.rng.range(-8..8) * 0x200) as i32;
let _ = npc_list.spawn(0x100, particle);
}
}
@ -851,7 +851,7 @@ impl NPC {
let mut particle = NPC::create(199, &state.npc_table);
particle.cond.set_alive(true);
particle.direction = Direction::Bottom;
particle.x = self.x + (self.rng.range(-8..8) * 0x200) as isize;
particle.x = self.x + (self.rng.range(-8..8) * 0x200) as i32;
particle.y = self.y;
let _ = npc_list.spawn(0x100, particle);
}
@ -944,8 +944,8 @@ impl NPC {
npc.y = self.y;
for _ in 0..4 {
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -1051,10 +1051,10 @@ impl NPC {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
for _ in 0..3 {
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -1099,10 +1099,10 @@ impl NPC {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
for _ in 0..3 {
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -1170,10 +1170,10 @@ impl NPC {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
for _ in 0..3 {
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -1218,10 +1218,10 @@ impl NPC {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
for _ in 0..3 {
npc.x = self.x + self.rng.range(-12..12) as isize * 0x200;
npc.y = self.y + self.rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.x = self.x + self.rng.range(-12..12) as i32 * 0x200;
npc.y = self.y + self.rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -1267,8 +1267,8 @@ impl NPC {
if self.action_num == 0 {
self.action_num = 1;
self.anim_num = self.rng.range(0..2) as u16;
self.vel_x = self.direction.vector_x() * (self.rng.range(4..8) * 0x200 / 2) as isize;
self.vel_y = self.direction.vector_y() * (self.rng.range(4..8) * 0x200 / 2) as isize;
self.vel_x = self.direction.vector_x() * (self.rng.range(4..8) * 0x200 / 2) as i32;
self.vel_y = self.direction.vector_y() * (self.rng.range(4..8) * 0x200 / 2) as i32;
}
self.anim_counter += 1;

View File

@ -18,8 +18,8 @@ impl NPC {
self.target_y = npc.y;
let angle = ((self.y - self.target_y) as f64 / (self.x - self.target_x) as f64).atan();
self.vel_x = (angle.cos() * 1024.0) as isize; // 2.0fix9
self.vel_y = (angle.sin() * 1024.0) as isize;
self.vel_x = (angle.cos() * 1024.0) as i32; // 2.0fix9
self.vel_y = (angle.sin() * 1024.0) as i32;
}
if self.action_counter2 == 0 {
@ -78,7 +78,7 @@ impl NPC {
state.sound_manager.play_sfx(29);
}
self.x = self.target_x + self.rng.range(-1..1) as isize * 0x200;
self.x = self.target_x + self.rng.range(-1..1) as i32 * 0x200;
self.action_counter += 1;
if self.action_counter >= 32 {
@ -341,7 +341,7 @@ impl NPC {
npc.x = self.x + 8 * 0x200;
npc.y = self.y - 8 * 0x200;
npc.vel_x = 0x600;
npc.vel_y = self.rng.range(-0x200..0) as isize;
npc.vel_y = self.rng.range(-0x200..0) as i32;
npc.cond.set_alive(true);
let _ = npc_list.spawn(0x100, npc);

View File

@ -12,8 +12,8 @@ impl NPC {
if self.action_num == 0 {
self.action_num = 1;
self.vel_x = self.rng.range(-0x80..0x80) as isize;
self.vel_y = self.rng.range(-0x7f..0x100) as isize;
self.vel_x = self.rng.range(-0x80..0x80) as i32;
self.vel_y = self.rng.range(-0x7f..0x100) as i32;
}
self.vel_x -= 0x8;
@ -43,8 +43,8 @@ impl NPC {
self.action_num = 1;
self.anim_num = self.rng.range(0..4) as u16;
self.vel_x = self.rng.range(-0x200..0x200) as isize;
self.vel_y = self.rng.range(-0x400..0) as isize;
self.vel_x = self.rng.range(-0x200..0x200) as i32;
self.vel_y = self.rng.range(-0x400..0) as i32;
self.direction = if self.rng.range(0..1) != 0 {
Direction::Left
@ -162,8 +162,8 @@ impl NPC {
if state.control_flags.wind() {
if self.action_num == 0 {
self.action_num = 1;
self.vel_x = self.rng.range(0x7f..0x100) as isize;
self.vel_y = self.rng.range(-0x20..0x20) as isize;
self.vel_x = self.rng.range(0x7f..0x100) as i32;
self.vel_y = self.rng.range(-0x20..0x20) as i32;
}
self.vel_x -= 0x08;
@ -230,8 +230,8 @@ impl NPC {
if state.control_flags.wind() {
if self.action_num == 0 {
self.action_num = 1;
self.vel_x = self.rng.range(0x7f..0x100) as isize;
self.vel_y = self.rng.range(-0x20..0x20) as isize;
self.vel_x = self.rng.range(0x7f..0x100) as i32;
self.vel_y = self.rng.range(-0x20..0x20) as i32;
}
self.vel_x -= 0x08;

View File

@ -167,8 +167,8 @@ impl NPC {
npc.y = self.y;
for _ in 0..4 {
npc.vel_x = self.rng.range(-0x155..0x155) as isize;
npc.vel_y = self.rng.range(-0x600..0) as isize;
npc.vel_x = self.rng.range(-0x155..0x155) as i32;
npc.vel_y = self.rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -216,8 +216,8 @@ impl NPC {
}
self.target_y += 0x100;
self.x = self.target_x + self.rng.range(-1..1) as isize * 0x200;
self.y = self.target_y + self.rng.range(-1..1) as isize * 0x200;
self.x = self.target_x + self.rng.range(-1..1) as i32 * 0x200;
self.y = self.target_y + self.rng.range(-1..1) as i32 * 0x200;
}
70 | 71 => {
if self.action_num == 70 {
@ -227,7 +227,7 @@ impl NPC {
self.anim_counter = 0;
}
self.x += (self.direction.vector_x() as isize | 1) * 0x100;
self.x += (self.direction.vector_x() as i32 | 1) * 0x100;
self.anim_counter += 1;
if self.anim_counter > 8 {

View File

@ -153,14 +153,14 @@ impl NPC {
if self.action_num == 0 {
self.action_num = 2;
self.vel_x = if self.rng.next_u16() & 1 != 0 {
self.rng.range(-0x200..-0x100) as isize
self.rng.range(-0x200..-0x100) as i32
} else {
self.rng.range(0x100..0x200) as isize
self.rng.range(0x100..0x200) as i32
};
self.vel_y = if self.rng.next_u16() & 1 != 0 {
self.rng.range(-0x200..-0x100) as isize
self.rng.range(-0x200..-0x100) as i32
} else {
self.rng.range(0x100..0x200) as isize
self.rng.range(0x100..0x200) as i32
};
self.vel_x2 = self.vel_x;
self.vel_y2 = self.vel_y;

View File

@ -80,10 +80,10 @@ impl BossNPC {
for _ in 0..8 {
npc.cond.set_alive(true);
npc.direction = Direction::Left;
npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as isize * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize;
npc.vel_y = self.parts[0].rng.range(-0x600..0) as isize;
npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as i32 * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
npc.vel_y = self.parts[0].rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -168,8 +168,8 @@ impl BossNPC {
let mut npc = NPC::create(110, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.parts[0].rng.range(4..16) as isize * 16 * 0x200;
npc.y = self.parts[0].rng.range(0..4) as isize * 16 * 0x200;
npc.x = self.parts[0].rng.range(4..16) as i32 * 16 * 0x200;
npc.y = self.parts[0].rng.range(0..4) as i32 * 16 * 0x200;
npc.direction = Direction::FacingPlayer;
let _ = npc_list.spawn(0x80, npc);
@ -179,10 +179,10 @@ impl BossNPC {
for _ in 0..4 {
npc.cond.set_alive(true);
npc.direction = Direction::Left;
npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as isize * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize;
npc.vel_y = self.parts[0].rng.range(-0x600..0) as isize;
npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as i32 * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
npc.vel_y = self.parts[0].rng.range(-0x600..0) as i32;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -216,7 +216,7 @@ impl BossNPC {
self.parts[0].action_counter = 0;
self.parts[0].vel_x2 = 16;
self.parts[0].anim_num = 3;
self.parts[0].target_x = self.parts[0].life as isize;
self.parts[0].target_x = self.parts[0].life as i32;
self.parts[1].npc_flags.set_shootable(true);
}
}
@ -250,14 +250,14 @@ impl BossNPC {
npc.cond.set_alive(true);
npc.x = self.parts[0].x + self.parts[0].direction.vector_x() * 2 * 16 * 0x200;
npc.y = self.parts[0].y - 8 * 0x200;
npc.vel_x = (deg.cos() * -512.0) as isize;
npc.vel_y = (deg.sin() * -512.0) as isize;
npc.vel_x = (deg.cos() * -512.0) as i32;
npc.vel_y = (deg.sin() * -512.0) as i32;
let _ = npc_list.spawn(0x100, npc);
state.sound_manager.play_sfx(39);
if self.parts[0].vel_x2 == 0 || (self.parts[0].life as isize) < self.parts[0].target_x - 90 {
if self.parts[0].vel_x2 == 0 || (self.parts[0].life as i32) < self.parts[0].target_x - 90 {
self.parts[0].action_num = 114;
self.parts[0].action_counter = 0;
self.parts[0].anim_num = 2;
@ -326,8 +326,8 @@ impl BossNPC {
let mut npc = NPC::create(104, &state.npc_table);
for _ in 0..2 {
npc.cond.set_alive(true);
npc.x = self.parts[0].rng.range(4..16) as isize * 16 * 0x200;
npc.y = self.parts[0].rng.range(0..4) as isize * 16 * 0x200;
npc.x = self.parts[0].rng.range(4..16) as i32 * 16 * 0x200;
npc.y = self.parts[0].rng.range(0..4) as i32 * 16 * 0x200;
npc.direction = Direction::FacingPlayer;
let _ = npc_list.spawn(0x80, npc.clone());
@ -336,8 +336,8 @@ impl BossNPC {
let mut npc = NPC::create(110, &state.npc_table);
for _ in 0..6 {
npc.cond.set_alive(true);
npc.x = self.parts[0].rng.range(4..16) as isize * 16 * 0x200;
npc.y = self.parts[0].rng.range(0..4) as isize * 16 * 0x200;
npc.x = self.parts[0].rng.range(4..16) as i32 * 16 * 0x200;
npc.y = self.parts[0].rng.range(0..4) as i32 * 16 * 0x200;
npc.direction = Direction::FacingPlayer;
let _ = npc_list.spawn(0x80, npc.clone());
@ -346,10 +346,10 @@ impl BossNPC {
let mut npc = NPC::create(4, &state.npc_table);
for _ in 0..8 {
npc.cond.set_alive(true);
npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as isize * 0x200;
npc.y = self.parts[0].y + self.parts[0].hit_bounds.bottom as isize;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize;
npc.vel_y = self.parts[0].rng.range(-0x600..0) as isize;
npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as i32 * 0x200;
npc.y = self.parts[0].y + self.parts[0].hit_bounds.bottom as i32;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
npc.vel_y = self.parts[0].rng.range(-0x600..0) as i32;
npc.direction = Direction::Left;
let _ = npc_list.spawn(0x100, npc.clone());
@ -385,10 +385,10 @@ impl BossNPC {
let mut npc = NPC::create(4, &state.npc_table);
for _ in 0..8 {
npc.cond.set_alive(true);
npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as isize * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize;
npc.vel_y = self.parts[0].rng.range(-0x600..0) as isize;
npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as i32 * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
npc.vel_y = self.parts[0].rng.range(-0x600..0) as i32;
npc.direction = Direction::Left;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -398,10 +398,10 @@ impl BossNPC {
if (self.parts[0].action_counter % 5) == 0 {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as isize * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize;
npc.vel_y = self.parts[0].rng.range(-0x600..0) as isize;
npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as i32 * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
npc.vel_y = self.parts[0].rng.range(-0x600..0) as i32;
npc.direction = Direction::Left;
let _ = npc_list.spawn(0x100, npc);
}
@ -440,10 +440,10 @@ impl BossNPC {
if (self.parts[0].action_counter % 9) == 0 {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as isize * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as isize * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize;
npc.vel_y = self.parts[0].rng.range(-0x600..0) as isize;
npc.x = self.parts[0].x + self.parts[0].rng.range(-12..12) as i32 * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-12..12) as i32 * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
npc.vel_y = self.parts[0].rng.range(-0x600..0) as i32;
npc.direction = Direction::Left;
let _ = npc_list.spawn(0x100, npc);
}

View File

@ -96,17 +96,17 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager)> for Bo
continue;
}
let off_x = if npc.direction == Direction::Left { npc.display_bounds.left } else { npc.display_bounds.right } as isize;
let off_x = if npc.direction == Direction::Left { npc.display_bounds.left } else { npc.display_bounds.right } as i32;
let shock = if npc.shock > 0 {
(2 * ((npc.shock as isize / 2) % 2) - 1) as f32
(2 * ((npc.shock as i32 / 2) % 2) - 1) as f32
} else { 0.0 };
batch.add_rect(
interpolate_fix9_scale(npc.prev_x - off_x - frame.prev_x,
npc.x - off_x - frame.x,
state.frame_time) + shock,
interpolate_fix9_scale(npc.prev_y - npc.display_bounds.top as isize - frame.prev_y,
npc.y - npc.display_bounds.top as isize - frame.y,
interpolate_fix9_scale(npc.prev_y - npc.display_bounds.top as i32 - frame.prev_y,
npc.y - npc.display_bounds.top as i32 - frame.y,
state.frame_time),
&npc.anim_rect,
);

View File

@ -27,8 +27,8 @@ impl NPC {
}
let radians = self.action_counter2 as f64 * CDEG_RAD;
self.vel_x = 2 * (radians.cos() * 512.0) as isize;
self.vel_y = 2 * (radians.sin() * 512.0) as isize;
self.vel_x = 2 * (radians.cos() * 512.0) as i32;
self.vel_y = 2 * (radians.sin() * 512.0) as i32;
self.x += self.vel_x;
self.y += self.vel_y;
@ -391,13 +391,13 @@ impl BossNPC {
if self.parts[0].action_num == 600 {
self.parts[0].action_num = 601;
self.parts[0].action_counter = 0;
self.parts[0].vel_y2 = self.parts[0].life as isize;
self.parts[0].vel_y2 = self.parts[0].life as i32;
self.parts[1].action_num = 30;
self.parts[2].action_num = 30;
}
self.parts[0].action_counter += 1;
if (self.parts[0].life as isize) < self.parts[0].vel_y2.saturating_sub(200)
if (self.parts[0].life as i32) < self.parts[0].vel_y2.saturating_sub(200)
|| self.parts[0].action_counter > 300 {
self.parts[0].action_num = 602;
self.parts[0].action_counter = 0;
@ -429,8 +429,8 @@ impl BossNPC {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.parts[0].x + self.parts[0].rng.range(-72..72) as isize * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-64..64) as isize * 0x200;
npc.x = self.parts[0].x + self.parts[0].rng.range(-72..72) as i32 * 0x200;
npc.y = self.parts[0].y + self.parts[0].rng.range(-64..64) as i32 * 0x200;
let _ = npc_list.spawn(0x100, npc);
if self.parts[0].action_counter > 100 {
@ -805,8 +805,8 @@ impl BossNPC {
npc.cond.set_alive(true);
npc.x = self.parts[i].x;
npc.y = self.parts[i].y;
npc.vel_x = (deg.cos() * -1536.0) as isize;
npc.vel_y = (deg.sin() * -1536.0) as isize;
npc.vel_x = (deg.cos() * -1536.0) as i32;
npc.vel_y = (deg.sin() * -1536.0) as i32;
let _ = npc_list.spawn(0x100, npc);

View File

@ -187,7 +187,7 @@ impl BossNPC {
npc.cond.set_alive(true);
npc.x = self.parts[0].x;
npc.y = self.parts[0].y - 16 * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x100..0x100) as isize;
npc.vel_x = self.parts[0].rng.range(-0x100..0x100) as i32;
npc.vel_y = -0x333;
npc.direction = if self.parts[0].rng.range(0..9) <= 7 {
Direction::Left
@ -253,7 +253,7 @@ impl BossNPC {
if self.parts[0].action_counter == 120 {
self.parts[0].action_num = 30;
self.parts[0].action_counter = 0;
self.parts[0].x = self.parts[0].target_x + self.parts[0].rng.range(-0x40..0x40) as isize * 0x200;
self.parts[0].x = self.parts[0].target_x + self.parts[0].rng.range(-0x40..0x40) as i32 * 0x200;
self.parts[0].y = self.parts[0].target_y;
}
}
@ -285,7 +285,7 @@ impl BossNPC {
npc.cond.set_alive(true);
npc.x = self.parts[0].x;
npc.y = self.parts[0].y - 16 * 0x200;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as isize;
npc.vel_x = self.parts[0].rng.range(-0x155..0x155) as i32;
npc.vel_y = -0x333;
npc.direction = Direction::Left;
@ -350,8 +350,8 @@ impl BossNPC {
state.sound_manager.play_sfx(52);
}
let dest_x = self.parts[0].x + self.parts[0].rng.range(-0x30..0x30) as isize * 0x200;
let dest_y = self.parts[0].y + self.parts[0].rng.range(-0x30..0x18) as isize * 0x200;
let dest_x = self.parts[0].x + self.parts[0].rng.range(-0x30..0x30) as i32 * 0x200;
let dest_y = self.parts[0].y + self.parts[0].rng.range(-0x30..0x18) as i32 * 0x200;
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);

View File

@ -53,22 +53,22 @@ bitfield! {
pub struct NPC {
pub id: u16,
pub npc_type: u16,
pub x: isize,
pub y: isize,
pub x: i32,
pub y: i32,
/// X velocity, affected by physics code
pub vel_x: isize,
pub vel_x: i32,
/// Y velocity, affected by physics code
pub vel_y: isize,
pub vel_y: i32,
/// X velocity, unaffected by physics code
pub vel_x2: isize,
pub vel_x2: i32,
/// Y velocity, unaffected by physics code
pub vel_y2: isize,
pub target_x: isize,
pub target_y: isize,
pub vel_y2: i32,
pub target_x: i32,
pub target_y: i32,
/// Previous X position, used by frame interpolator
pub prev_x: isize,
pub prev_x: i32,
/// Previous Y position, used by frame interpolator
pub prev_y: isize,
pub prev_y: i32,
pub exp: u16,
pub size: u8,
pub shock: u16,
@ -276,7 +276,6 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager)> for NP
298 => self.tick_n298_intro_doctor(state),
299 => self.tick_n299_intro_balrog_misery(state),
300 => self.tick_n300_intro_demon_crown(state),
361 => self.tick_n361_gaudi_dashing(state, players, npc_list),
_ => Ok(()),
}?;
@ -302,17 +301,17 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager)> for NP
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, state.npc_table.get_texture_name(self.npc_type))?;
let off_x = if self.direction == Direction::Left { self.display_bounds.left } else { self.display_bounds.right } as isize;
let off_x = if self.direction == Direction::Left { self.display_bounds.left } else { self.display_bounds.right } as i32;
let shock = if self.shock > 0 {
(2 * ((self.shock as isize / 2) % 2) - 1) as f32
(2 * ((self.shock as i32 / 2) % 2) - 1) as f32
} else { 0.0 };
batch.add_rect(
interpolate_fix9_scale(self.prev_x - off_x - frame.prev_x,
self.x - off_x - frame.x,
state.frame_time) + shock,
interpolate_fix9_scale(self.prev_y - self.display_bounds.top as isize - frame.prev_y,
self.y - self.display_bounds.top as isize - frame.y,
interpolate_fix9_scale(self.prev_y - self.display_bounds.top as i32 - frame.prev_y,
self.y - self.display_bounds.top as i32 - frame.y,
state.frame_time),
&self.anim_rect,
);
@ -324,16 +323,16 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager)> for NP
impl PhysicalEntity for NPC {
#[inline(always)]
fn x(&self) -> isize { self.x }
fn x(&self) -> i32 { self.x }
#[inline(always)]
fn y(&self) -> isize { self.y }
fn y(&self) -> i32 { self.y }
#[inline(always)]
fn vel_x(&self) -> isize { self.vel_x }
fn vel_x(&self) -> i32 { self.vel_x }
#[inline(always)]
fn vel_y(&self) -> isize { self.vel_y }
fn vel_y(&self) -> i32 { self.vel_y }
#[inline(always)]
fn hit_rect_size(&self) -> usize {
@ -345,10 +344,10 @@ impl PhysicalEntity for NPC {
}
#[inline(always)]
fn offset_x(&self) -> isize { if self.size >= 3 && !self.cond.drs_boss() { -0x1000 } else { 0 } }
fn offset_x(&self) -> i32 { if self.size >= 3 && !self.cond.drs_boss() { -0x1000 } else { 0 } }
#[inline(always)]
fn offset_y(&self) -> isize { if self.size >= 3 && !self.cond.drs_boss() { -0x1000 } else { 0 } }
fn offset_y(&self) -> i32 { if self.size >= 3 && !self.cond.drs_boss() { -0x1000 } else { 0 } }
#[inline(always)]
fn hit_bounds(&self) -> &Rect<usize> {
@ -356,22 +355,22 @@ impl PhysicalEntity for NPC {
}
#[inline(always)]
fn set_x(&mut self, x: isize) {
fn set_x(&mut self, x: i32) {
self.x = x;
}
#[inline(always)]
fn set_y(&mut self, y: isize) {
fn set_y(&mut self, y: i32) {
self.y = y;
}
#[inline(always)]
fn set_vel_x(&mut self, vel_x: isize) {
fn set_vel_x(&mut self, vel_x: i32) {
self.vel_x = vel_x;
}
#[inline(always)]
fn set_vel_y(&mut self, vel_y: isize) {
fn set_vel_y(&mut self, vel_y: i32) {
self.vel_y = vel_y;
}

View File

@ -82,8 +82,8 @@ impl NPC {
NPC {
id: data.id,
npc_type: data.npc_type,
x: data.x as isize * 16 * 0x200,
y: data.y as isize * 16 * 0x200,
x: data.x as i32 * 16 * 0x200,
y: data.y as i32 * 16 * 0x200,
vel_x: 0,
vel_y: 0,
vel_x2: 0,
@ -172,16 +172,16 @@ impl NPC {
pub fn collides_with_bullet(&self, bullet: &Bullet) -> bool {
(
self.npc_flags.shootable()
&& (self.x - self.hit_bounds.right as isize) < (bullet.x + bullet.enemy_hit_width as isize)
&& (self.x + self.hit_bounds.right as isize) > (bullet.x - bullet.enemy_hit_width as isize)
&& (self.y - self.hit_bounds.top as isize) < (bullet.y + bullet.enemy_hit_height as isize)
&& (self.y + self.hit_bounds.bottom as isize) > (bullet.y - bullet.enemy_hit_height as isize)
&& (self.x - self.hit_bounds.right as i32) < (bullet.x + bullet.enemy_hit_width as i32)
&& (self.x + self.hit_bounds.right as i32) > (bullet.x - bullet.enemy_hit_width as i32)
&& (self.y - self.hit_bounds.top as i32) < (bullet.y + bullet.enemy_hit_height as i32)
&& (self.y + self.hit_bounds.bottom as i32) > (bullet.y - bullet.enemy_hit_height as i32)
) || (
self.npc_flags.invulnerable()
&& (self.x - self.hit_bounds.right as isize) < (bullet.x + bullet.hit_bounds.right as isize)
&& (self.x + self.hit_bounds.right as isize) > (bullet.x - bullet.hit_bounds.left as isize)
&& (self.y - self.hit_bounds.top as isize) < (bullet.y + bullet.hit_bounds.bottom as isize)
&& (self.y + self.hit_bounds.bottom as isize) > (bullet.y - bullet.hit_bounds.top as isize)
&& (self.x - self.hit_bounds.right as i32) < (bullet.x + bullet.hit_bounds.right as i32)
&& (self.x + self.hit_bounds.right as i32) > (bullet.x - bullet.hit_bounds.left as i32)
&& (self.y - self.hit_bounds.top as i32) < (bullet.y + bullet.hit_bounds.bottom as i32)
&& (self.y + self.hit_bounds.bottom as i32) > (bullet.y - bullet.hit_bounds.top as i32)
)
}
@ -323,18 +323,18 @@ impl NPCList {
/// Creates NPC death smoke diffusing in random directions.
#[inline]
pub fn create_death_smoke(&self, x: isize, y: isize, radius: usize, amount: usize, state: &mut SharedGameState, rng: &dyn RNG) {
pub fn create_death_smoke(&self, x: i32, y: i32, radius: usize, amount: usize, state: &mut SharedGameState, rng: &dyn RNG) {
self.create_death_smoke_common(x, y, radius, amount, Direction::Left, state, rng)
}
/// Creates NPC death smoke diffusing upwards.
#[inline]
pub fn create_death_smoke_up(&self, x: isize, y: isize, radius: usize, amount: usize, state: &mut SharedGameState, rng: &dyn RNG) {
pub fn create_death_smoke_up(&self, x: i32, y: i32, radius: usize, amount: usize, state: &mut SharedGameState, rng: &dyn RNG) {
self.create_death_smoke_common(x, y, radius, amount, Direction::Up, state, rng)
}
#[allow(clippy::too_many_arguments)]
fn create_death_smoke_common(&self, x: isize, y: isize, radius: usize, amount: usize, direction: Direction, state: &mut SharedGameState, rng: &dyn RNG) {
fn create_death_smoke_common(&self, x: i32, y: i32, radius: usize, amount: usize, direction: Direction, state: &mut SharedGameState, rng: &dyn RNG) {
let radius = (radius / 0x200) as i32;
let mut npc = NPC::create(4, &state.npc_table);
@ -342,8 +342,8 @@ impl NPCList {
npc.direction = direction;
for _ in 0..amount {
let off_x = rng.range(-radius..radius) as isize * 0x200;
let off_y = rng.range(-radius..radius) as isize * 0x200;
let off_x = rng.range(-radius..radius) as i32 * 0x200;
let off_y = rng.range(-radius..radius) as i32 * 0x200;
npc.x = x + off_x;
npc.y = y + off_y;

View File

@ -12,25 +12,25 @@ use crate::npc::list::NPCList;
// 0 | 11 1 2 5
// 1 | 12 3 4 6
// 2 | 13 8 9 7
pub const OFF_X: [isize; 16] = [0, 1, 0, 1, 2, 2, 2, 0, 1, -1, -1, -1, -1, 0, 1, 2];
pub const OFF_Y: [isize; 16] = [0, 0, 1, 1, 0, 1, 2, 2, 2, -1, 0, 1, 2, -1, -1, -1];
pub const OFF_X: [i32; 16] = [0, 1, 0, 1, 2, 2, 2, 0, 1, -1, -1, -1, -1, 0, 1, 2];
pub const OFF_Y: [i32; 16] = [0, 0, 1, 1, 0, 1, 2, 2, 2, -1, 0, 1, 2, -1, -1, -1];
pub trait PhysicalEntity {
fn x(&self) -> isize;
fn y(&self) -> isize;
fn vel_x(&self) -> isize;
fn vel_y(&self) -> isize;
fn x(&self) -> i32;
fn y(&self) -> i32;
fn vel_x(&self) -> i32;
fn vel_y(&self) -> i32;
fn hit_rect_size(&self) -> usize;
fn offset_x(&self) -> isize { 0 }
fn offset_y(&self) -> isize { 0 }
fn offset_x(&self) -> i32 { 0 }
fn offset_y(&self) -> i32 { 0 }
fn hit_bounds(&self) -> &Rect<usize>;
fn set_x(&mut self, x: isize);
fn set_y(&mut self, y: isize);
fn set_vel_x(&mut self, x: isize);
fn set_vel_y(&mut self, y: isize);
fn set_x(&mut self, x: i32);
fn set_y(&mut self, y: i32);
fn set_vel_x(&mut self, x: i32);
fn set_vel_y(&mut self, y: i32);
fn cond(&mut self) -> &mut Condition;
fn flags(&mut self) -> &mut Flag;
@ -41,15 +41,15 @@ pub trait PhysicalEntity {
fn player_left_pressed(&self) -> bool { false }
fn player_right_pressed(&self) -> bool { false }
fn judge_hit_block(&mut self, state: &mut SharedGameState, x: isize, y: isize) {
fn judge_hit_block(&mut self, state: &mut SharedGameState, x: i32, y: i32) {
let bounds_x = if self.is_player() { 5 } else { 8 };
let bounds_y = if self.is_player() { 4 } else { 5 };
// left wall
if (self.y() - self.hit_bounds().top as isize) < (y * 16 + bounds_y) * 0x200
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 16 - bounds_y) * 0x200
&& (self.x() - self.hit_bounds().right as isize) < (x * 16 + 8) * 0x200
&& (self.x() - self.hit_bounds().right as isize) > x * 16 * 0x200 {
self.set_x(((x * 16 + 8) * 0x200) + self.hit_bounds().right as isize);
if (self.y() - self.hit_bounds().top as i32) < (y * 16 + bounds_y) * 0x200
&& (self.y() + self.hit_bounds().bottom as i32) > (y * 16 - bounds_y) * 0x200
&& (self.x() - self.hit_bounds().right as i32) < (x * 16 + 8) * 0x200
&& (self.x() - self.hit_bounds().right as i32) > x * 16 * 0x200 {
self.set_x(((x * 16 + 8) * 0x200) + self.hit_bounds().right as i32);
if self.is_player() {
if self.vel_x() < -0x180 {
@ -65,11 +65,11 @@ pub trait PhysicalEntity {
}
// right wall
if (self.y() - self.hit_bounds().top as isize) < (y * 16 + bounds_y) * 0x200
&& self.y() + self.hit_bounds().bottom as isize > (y * 16 - bounds_y) * 0x200
&& (self.x() + self.hit_bounds().right as isize) > (x * 16 - 8) * 0x200
&& (self.x() + self.hit_bounds().right as isize) < x * 16 * 0x200 {
self.set_x(((x * 16 - 8) * 0x200) - self.hit_bounds().right as isize);
if (self.y() - self.hit_bounds().top as i32) < (y * 16 + bounds_y) * 0x200
&& self.y() + self.hit_bounds().bottom as i32 > (y * 16 - bounds_y) * 0x200
&& (self.x() + self.hit_bounds().right as i32) > (x * 16 - 8) * 0x200
&& (self.x() + self.hit_bounds().right as i32) < x * 16 * 0x200 {
self.set_x(((x * 16 - 8) * 0x200) - self.hit_bounds().right as i32);
if self.is_player() {
if self.vel_x() > 0x180 {
@ -85,17 +85,17 @@ pub trait PhysicalEntity {
}
// ceiling
if (self.x() - self.hit_bounds().right as isize) < (x * 16 + bounds_x) * 0x200
&& (self.x() + self.hit_bounds().right as isize) > (x * 16 - bounds_x) * 0x200
&& (self.y() - self.hit_bounds().top as isize) < (y * 16 + 8) * 0x200
&& (self.y() - self.hit_bounds().top as isize) > y * 16 * 0x200 {
self.set_y(((y * 16 + 8) * 0x200) + self.hit_bounds().top as isize);
if (self.x() - self.hit_bounds().right as i32) < (x * 16 + bounds_x) * 0x200
&& (self.x() + self.hit_bounds().right as i32) > (x * 16 - bounds_x) * 0x200
&& (self.y() - self.hit_bounds().top as i32) < (y * 16 + 8) * 0x200
&& (self.y() - self.hit_bounds().top as i32) > y * 16 * 0x200 {
self.set_y(((y * 16 + 8) * 0x200) + self.hit_bounds().top as i32);
if self.is_player() {
if !self.cond().hidden() && self.vel_y() < -0x200 {
state.sound_manager.play_sfx(3);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as isize, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as isize, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as i32, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as i32, CaretType::LittleParticles, Direction::Left);
}
if self.vel_y() < 0 {
@ -109,11 +109,11 @@ pub trait PhysicalEntity {
}
// floor
if ((self.x() - self.hit_bounds().right as isize) < (x * 16 + bounds_x) * 0x200)
&& ((self.x() + self.hit_bounds().right as isize) > (x * 16 - bounds_x) * 0x200)
&& ((self.y() + self.hit_bounds().bottom as isize) > ((y * 16 - 8) * 0x200))
&& ((self.y() + self.hit_bounds().bottom as isize) < (y * 16 * 0x200)) {
self.set_y(((y * 16 - 8) * 0x200) - self.hit_bounds().bottom as isize);
if ((self.x() - self.hit_bounds().right as i32) < (x * 16 + bounds_x) * 0x200)
&& ((self.x() + self.hit_bounds().right as i32) > (x * 16 - bounds_x) * 0x200)
&& ((self.y() + self.hit_bounds().bottom as i32) > ((y * 16 - 8) * 0x200))
&& ((self.y() + self.hit_bounds().bottom as i32) < (y * 16 * 0x200)) {
self.set_y(((y * 16 - 8) * 0x200) - self.hit_bounds().bottom as i32);
if self.is_player() {
if self.vel_y() > 0x400 {
@ -132,17 +132,17 @@ pub trait PhysicalEntity {
}
// upper left slope (bigger half)
fn judge_hit_triangle_a(&mut self, state: &mut SharedGameState, x: isize, y: isize) {
fn judge_hit_triangle_a(&mut self, state: &mut SharedGameState, x: i32, y: i32) {
if self.x() < (x * 16 + 8) * 0x200
&& self.x() > (x * 16 - 8) * 0x200
&& (self.y() - self.hit_bounds().top as isize) < (y * 16 * 0x200) - (self.x() - x * 16 * 0x200) / 2 + 0x800
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 16 - 8) * 0x200 {
self.set_y((y * 16 * 0x200) - ((self.x() - x * 16 * 0x200) / 2) + 0x800 + self.hit_bounds().top as isize);
&& (self.y() - self.hit_bounds().top as i32) < (y * 16 * 0x200) - (self.x() - x * 16 * 0x200) / 2 + 0x800
&& (self.y() + self.hit_bounds().bottom as i32) > (y * 16 - 8) * 0x200 {
self.set_y((y * 16 * 0x200) - ((self.x() - x * 16 * 0x200) / 2) + 0x800 + self.hit_bounds().top as i32);
if self.is_player() && !self.cond().hidden() && self.vel_y() < -0x200 {
state.sound_manager.play_sfx(3);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as isize, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as isize, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as i32, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as i32, CaretType::LittleParticles, Direction::Left);
}
if self.vel_y() < 0 {
@ -154,17 +154,17 @@ pub trait PhysicalEntity {
}
// upper left slope (smaller half)
fn judge_hit_triangle_b(&mut self, state: &mut SharedGameState, x: isize, y: isize) {
fn judge_hit_triangle_b(&mut self, state: &mut SharedGameState, x: i32, y: i32) {
if self.x() < (x * 16 + 8) * 0x200
&& self.x() > (x * 16 - 8) * 0x200
&& (self.y() - self.hit_bounds().top as isize) < (y * 16 * 0x200) - (self.x() - x * 16 * 0x200) / 2 - 0x800
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 16 - 8) * 0x200 {
self.set_y((y * 16 * 0x200) - ((self.x() - x * 16 * 0x200) / 2) - 0x800 + self.hit_bounds().top as isize);
&& (self.y() - self.hit_bounds().top as i32) < (y * 16 * 0x200) - (self.x() - x * 16 * 0x200) / 2 - 0x800
&& (self.y() + self.hit_bounds().bottom as i32) > (y * 16 - 8) * 0x200 {
self.set_y((y * 16 * 0x200) - ((self.x() - x * 16 * 0x200) / 2) - 0x800 + self.hit_bounds().top as i32);
if self.is_player() && !self.cond().hidden() && self.vel_y() < -0x200 {
state.sound_manager.play_sfx(3);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as isize, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as isize, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as i32, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as i32, CaretType::LittleParticles, Direction::Left);
}
if self.vel_y() < 0 {
@ -176,17 +176,17 @@ pub trait PhysicalEntity {
}
// upper right slope (smaller half)
fn judge_hit_triangle_c(&mut self, state: &mut SharedGameState, x: isize, y: isize) {
fn judge_hit_triangle_c(&mut self, state: &mut SharedGameState, x: i32, y: i32) {
if self.x() < (x * 16 + 8) * 0x200
&& self.x() > (x * 16 - 8) * 0x200
&& (self.y() - self.hit_bounds().top as isize) < (y * 16 * 0x200) + (self.x() - x * 16 * 0x200) / 2 - 0x800
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 16 - 8) * 0x200 {
self.set_y((y * 16 * 0x200) + ((self.x() - x * 16 * 0x200) / 2) - 0x800 + self.hit_bounds().top as isize);
&& (self.y() - self.hit_bounds().top as i32) < (y * 16 * 0x200) + (self.x() - x * 16 * 0x200) / 2 - 0x800
&& (self.y() + self.hit_bounds().bottom as i32) > (y * 16 - 8) * 0x200 {
self.set_y((y * 16 * 0x200) + ((self.x() - x * 16 * 0x200) / 2) - 0x800 + self.hit_bounds().top as i32);
if self.is_player() && !self.cond().hidden() && self.vel_y() < -0x200 {
state.sound_manager.play_sfx(3);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as isize, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as isize, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as i32, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as i32, CaretType::LittleParticles, Direction::Left);
}
if self.vel_y() < 0 {
@ -198,17 +198,17 @@ pub trait PhysicalEntity {
}
// upper right slope (bigger half)
fn judge_hit_triangle_d(&mut self, state: &mut SharedGameState, x: isize, y: isize) {
fn judge_hit_triangle_d(&mut self, state: &mut SharedGameState, x: i32, y: i32) {
if (self.x() < (x * 16 + 8) * 0x200)
&& (self.x() > (x * 16 - 8) * 0x200)
&& (self.y() - self.hit_bounds().top as isize) < (y * 16 * 0x200) + (self.x() - x * 16 * 0x200) / 2 + 0x800
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 16 - 8) * 0x200 {
self.set_y((y * 16 * 0x200) + ((self.x() - x * 16 * 0x200) / 2) + 0x800 + self.hit_bounds().top as isize);
&& (self.y() - self.hit_bounds().top as i32) < (y * 16 * 0x200) + (self.x() - x * 16 * 0x200) / 2 + 0x800
&& (self.y() + self.hit_bounds().bottom as i32) > (y * 16 - 8) * 0x200 {
self.set_y((y * 16 * 0x200) + ((self.x() - x * 16 * 0x200) / 2) + 0x800 + self.hit_bounds().top as i32);
if self.is_player() && !self.cond().hidden() && self.vel_y() < -0x200 {
state.sound_manager.play_sfx(3);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as isize, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as isize, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as i32, CaretType::LittleParticles, Direction::Left);
state.create_caret(self.x(), self.y() - self.hit_bounds().top as i32, CaretType::LittleParticles, Direction::Left);
}
if self.vel_y() < 0 {
@ -220,14 +220,14 @@ pub trait PhysicalEntity {
}
// lower left half (bigger)
fn judge_hit_triangle_e(&mut self, state: &mut SharedGameState, x: isize, y: isize) {
fn judge_hit_triangle_e(&mut self, state: &mut SharedGameState, x: i32, y: i32) {
self.flags().set_hit_left_bigger_half(true);
if (self.x() < (x * 16 + 8) * 0x200)
&& (self.x() > (x * 16 - 8) * 0x200)
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 16 * 0x200) + (self.x() - x * 16 * 0x200) / 2 - 0x800
&& (self.y() - self.hit_bounds().top as isize) < (y * 16 + 8) * 0x200 {
self.set_y((y * 16 * 0x200) + ((self.x() - x * 16 * 0x200) / 2) - 0x800 - self.hit_bounds().bottom as isize);
&& (self.y() + self.hit_bounds().bottom as i32) > (y * 16 * 0x200) + (self.x() - x * 16 * 0x200) / 2 - 0x800
&& (self.y() - self.hit_bounds().top as i32) < (y * 16 + 8) * 0x200 {
self.set_y((y * 16 * 0x200) + ((self.x() - x * 16 * 0x200) / 2) - 0x800 - self.hit_bounds().bottom as i32);
if self.is_player() && self.vel_y() > 0x400 {
state.sound_manager.play_sfx(23);
@ -243,14 +243,14 @@ pub trait PhysicalEntity {
}
// lower left half (smaller)
fn judge_hit_triangle_f(&mut self, state: &mut SharedGameState, x: isize, y: isize) {
fn judge_hit_triangle_f(&mut self, state: &mut SharedGameState, x: i32, y: i32) {
self.flags().set_hit_left_smaller_half(true);
if (self.x() < (x * 16 + 8) * 0x200)
&& (self.x() > (x * 16 - 8) * 0x200)
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 16 * 0x200) + (self.x() - x * 16 * 0x200) / 2 + 0x800
&& (self.y() - self.hit_bounds().top as isize) < (y * 16 + 8) * 0x200 {
self.set_y((y * 16 * 0x200) + ((self.x() - x * 16 * 0x200) / 2) + 0x800 - self.hit_bounds().bottom as isize);
&& (self.y() + self.hit_bounds().bottom as i32) > (y * 16 * 0x200) + (self.x() - x * 16 * 0x200) / 2 + 0x800
&& (self.y() - self.hit_bounds().top as i32) < (y * 16 + 8) * 0x200 {
self.set_y((y * 16 * 0x200) + ((self.x() - x * 16 * 0x200) / 2) + 0x800 - self.hit_bounds().bottom as i32);
if self.is_player() && self.vel_y() > 0x400 {
state.sound_manager.play_sfx(23);
@ -266,14 +266,14 @@ pub trait PhysicalEntity {
}
// lower right half (smaller)
fn judge_hit_triangle_g(&mut self, state: &mut SharedGameState, x: isize, y: isize) {
fn judge_hit_triangle_g(&mut self, state: &mut SharedGameState, x: i32, y: i32) {
self.flags().set_hit_right_smaller_half(true);
if (self.x() < (x * 16 + 8) * 0x200)
&& (self.x() > (x * 16 - 8) * 0x200)
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 16 * 0x200) - (self.x() - x * 16 * 0x200) / 2 + 0x800
&& (self.y() - self.hit_bounds().top as isize) < (y * 16 + 8) * 0x200 {
self.set_y((y * 16 * 0x200) - ((self.x() - x * 16 * 0x200) / 2) + 0x800 - self.hit_bounds().bottom as isize);
&& (self.y() + self.hit_bounds().bottom as i32) > (y * 16 * 0x200) - (self.x() - x * 16 * 0x200) / 2 + 0x800
&& (self.y() - self.hit_bounds().top as i32) < (y * 16 + 8) * 0x200 {
self.set_y((y * 16 * 0x200) - ((self.x() - x * 16 * 0x200) / 2) + 0x800 - self.hit_bounds().bottom as i32);
if self.is_player() && self.vel_y() > 0x400 {
state.sound_manager.play_sfx(23);
@ -289,14 +289,14 @@ pub trait PhysicalEntity {
}
// lower right half (bigger)
fn judge_hit_triangle_h(&mut self, state: &mut SharedGameState, x: isize, y: isize) {
fn judge_hit_triangle_h(&mut self, state: &mut SharedGameState, x: i32, y: i32) {
self.flags().set_hit_right_bigger_half(true);
if (self.x() < (x * 16 + 8) * 0x200)
&& (self.x() > (x * 16 - 8) * 0x200)
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 16 * 0x200) - (self.x() - x * 16 * 0x200) / 2 - 0x800
&& (self.y() - self.hit_bounds().top as isize) < (y * 16 + 8) * 0x200 {
self.set_y((y * 16 * 0x200) - ((self.x() - x * 16 * 0x200) / 2) - 0x800 - self.hit_bounds().bottom as isize);
&& (self.y() + self.hit_bounds().bottom as i32) > (y * 16 * 0x200) - (self.x() - x * 16 * 0x200) / 2 - 0x800
&& (self.y() - self.hit_bounds().top as i32) < (y * 16 + 8) * 0x200 {
self.set_y((y * 16 * 0x200) - ((self.x() - x * 16 * 0x200) / 2) - 0x800 - self.hit_bounds().bottom as i32);
if self.is_player() && self.vel_y() > 0x400 {
state.sound_manager.play_sfx(23);
@ -311,19 +311,19 @@ pub trait PhysicalEntity {
}
}
fn judge_hit_water(&mut self, x: isize, y: isize) {
fn judge_hit_water(&mut self, x: i32, y: i32) {
let bounds_x = if self.is_player() { 5 } else { 6 };
let bounds_up = if self.is_player() { 5 } else { 6 };
let bounds_down = if self.is_player() { 0 } else { 6 };
if (self.x() - self.hit_bounds().right as isize) < (x * 16 + bounds_x) * 0x200
&& (self.x() + self.hit_bounds().right as isize) > (x * 16 - bounds_x) * 0x200
&& (self.y() - self.hit_bounds().top as isize) < (y * 16 + bounds_up) * 0x200
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 16 - bounds_down) * 0x200 {
if (self.x() - self.hit_bounds().right as i32) < (x * 16 + bounds_x) * 0x200
&& (self.x() + self.hit_bounds().right as i32) > (x * 16 - bounds_x) * 0x200
&& (self.y() - self.hit_bounds().top as i32) < (y * 16 + bounds_up) * 0x200
&& (self.y() + self.hit_bounds().bottom as i32) > (y * 16 - bounds_down) * 0x200 {
self.flags().set_in_water(true);
}
}
fn judge_hit_spike(&mut self, x: isize, y: isize, water: bool) {
fn judge_hit_spike(&mut self, x: i32, y: i32, water: bool) {
if (self.x() - 0x800) < (x * 16 + 4) * 0x200
&& (self.x() + 0x800) > (x * 16 - 4) * 0x200
&& (self.y() - 0x800) < (y * 16 + 3) * 0x200
@ -335,11 +335,11 @@ pub trait PhysicalEntity {
}
}
fn judge_hit_force(&mut self, x: isize, y: isize, direction: Direction, water: bool) {
if (self.x() - self.hit_bounds().left as isize) < (x * 16 + 6) * 0x200
&& (self.x() + self.hit_bounds().right as isize) > (x * 16 - 6) * 0x200
&& (self.y() - self.hit_bounds().top as isize) < (y * 16 + 6) * 0x200
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 16 - 6) * 0x200 {
fn judge_hit_force(&mut self, x: i32, y: i32, direction: Direction, water: bool) {
if (self.x() - self.hit_bounds().left as i32) < (x * 16 + 6) * 0x200
&& (self.x() + self.hit_bounds().right as i32) > (x * 16 - 6) * 0x200
&& (self.y() - self.hit_bounds().top as i32) < (y * 16 + 6) * 0x200
&& (self.y() + self.hit_bounds().bottom as i32) > (y * 16 - 6) * 0x200 {
match direction {
Direction::Left => self.flags().set_force_left(true),
Direction::Up => self.flags().set_force_up(true),

View File

@ -51,14 +51,14 @@ impl TargetPlayer {
#[derive(Clone)]
pub struct Player {
pub x: isize,
pub y: isize,
pub vel_x: isize,
pub vel_y: isize,
pub target_x: isize,
pub target_y: isize,
pub prev_x: isize,
pub prev_y: isize,
pub x: i32,
pub y: i32,
pub vel_x: i32,
pub vel_y: i32,
pub target_x: i32,
pub target_y: i32,
pub prev_x: i32,
pub prev_y: i32,
pub life: u16,
pub max_life: u16,
pub cond: Condition,
@ -69,7 +69,7 @@ pub struct Player {
pub hit_bounds: Rect<usize>,
pub control_mode: ControlMode,
pub question: bool,
pub booster_fuel: usize,
pub booster_fuel: u32,
pub up: bool,
pub down: bool,
pub shock_counter: u8,
@ -81,8 +81,8 @@ pub struct Player {
pub appearance: PlayerAppearance,
pub controller: Box<dyn PlayerController>,
weapon_offset_y: i8,
index_x: isize,
index_y: isize,
index_x: i32,
index_y: i32,
splash: bool,
booster_switch: u8,
bubble: u8,
@ -193,7 +193,7 @@ impl Player {
self.booster_switch = 0;
if state.settings.infinite_booster {
self.booster_fuel = usize::MAX;
self.booster_fuel = u32::MAX;
} else if self.equip.has_booster_0_8() || self.equip.has_booster_2_0() {
self.booster_fuel = state.constants.booster.fuel;
} else {
@ -398,7 +398,7 @@ impl Player {
self.vel_y -= 0x20;
if self.booster_fuel % 3 == 0 {
state.create_caret(self.x, self.y + self.hit_bounds.bottom as isize / 2, CaretType::Exhaust, Direction::Bottom);
state.create_caret(self.x, self.y + self.hit_bounds.bottom as i32 / 2, CaretType::Exhaust, Direction::Bottom);
state.sound_manager.play_sfx(113);
}
@ -448,15 +448,15 @@ impl Player {
droplet.direction = if self.flags.water_splash_facing_right() { Direction::Right } else { Direction::Left };
for _ in 0..7 {
droplet.x = self.x + (state.game_rng.range(-8..8) * 0x200) as isize;
droplet.x = self.x + (state.game_rng.range(-8..8) * 0x200) as i32;
droplet.vel_x = if vertical_splash {
(self.vel_x + state.game_rng.range(-0x200..0x200) as isize) - (self.vel_x / 2)
(self.vel_x + state.game_rng.range(-0x200..0x200) as i32) - (self.vel_x / 2)
} else if horizontal_splash {
self.vel_x + state.game_rng.range(-0x200..0x200) as isize
self.vel_x + state.game_rng.range(-0x200..0x200) as i32
} else {
0 as isize
0 as i32
};
droplet.vel_y = state.game_rng.range(-0x200..0x80) as isize;
droplet.vel_y = state.game_rng.range(-0x200..0x80) as i32;
let _ = npc_list.spawn(0x100, droplet.clone());
}
@ -616,7 +616,7 @@ impl Player {
self.anim_rect.bottom += offset;
}
pub fn damage(&mut self, hp: isize, state: &mut SharedGameState, npc_list: &NPCList) {
pub fn damage(&mut self, hp: i32, state: &mut SharedGameState, npc_list: &NPCList) {
if state.settings.god_mode || self.shock_counter > 0 {
return;
}
@ -648,8 +648,8 @@ impl Player {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
for _ in 0..0x40 {
npc.x = self.x + state.game_rng.range(-10..10) as isize * 0x200;
npc.y = self.y + state.game_rng.range(-10..10) as isize * 0x200;
npc.x = self.x + state.game_rng.range(-10..10) as i32 * 0x200;
npc.y = self.y + state.game_rng.range(-10..10) as i32 * 0x200;
let _ = npc_list.spawn(0x100, npc.clone());
}
@ -694,11 +694,11 @@ impl GameEntity<&NPCList> for Player {
{
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "MyChar")?;
batch.add_rect(
interpolate_fix9_scale(self.prev_x - self.display_bounds.left as isize - frame.prev_x,
self.x - self.display_bounds.left as isize - frame.x,
interpolate_fix9_scale(self.prev_x - self.display_bounds.left as i32 - frame.prev_x,
self.x - self.display_bounds.left as i32 - frame.x,
state.frame_time),
interpolate_fix9_scale(self.prev_y - self.display_bounds.left as isize - frame.prev_y,
self.y - self.display_bounds.left as isize - frame.y,
interpolate_fix9_scale(self.prev_y - self.display_bounds.left as i32 - frame.prev_y,
self.y - self.display_bounds.left as i32 - frame.y,
state.frame_time),
&self.anim_rect,
);
@ -710,22 +710,22 @@ impl GameEntity<&NPCList> for Player {
match self.direction {
Direction::Left => {
batch.add_rect(
interpolate_fix9_scale(self.prev_x - self.display_bounds.left as isize - frame.prev_x,
self.x - self.display_bounds.left as isize - frame.x,
interpolate_fix9_scale(self.prev_x - self.display_bounds.left as i32 - frame.prev_x,
self.x - self.display_bounds.left as i32 - frame.x,
state.frame_time) - 8.0,
interpolate_fix9_scale(self.prev_y - self.display_bounds.left as isize - frame.prev_y,
self.y - self.display_bounds.left as isize - frame.y,
interpolate_fix9_scale(self.prev_y - self.display_bounds.left as i32 - frame.prev_y,
self.y - self.display_bounds.left as i32 - frame.y,
state.frame_time) + self.weapon_offset_y as f32,
&self.weapon_rect,
);
}
Direction::Right => {
batch.add_rect(
interpolate_fix9_scale(self.prev_x - self.display_bounds.left as isize - frame.prev_x,
self.x - self.display_bounds.left as isize - frame.x,
interpolate_fix9_scale(self.prev_x - self.display_bounds.left as i32 - frame.prev_x,
self.x - self.display_bounds.left as i32 - frame.x,
state.frame_time),
interpolate_fix9_scale(self.prev_y - self.display_bounds.left as isize - frame.prev_y,
self.y - self.display_bounds.left as isize - frame.y,
interpolate_fix9_scale(self.prev_y - self.display_bounds.left as i32 - frame.prev_y,
self.y - self.display_bounds.left as i32 - frame.y,
state.frame_time) + self.weapon_offset_y as f32,
&self.weapon_rect,
);

View File

@ -14,22 +14,22 @@ use crate::shared_game_state::SharedGameState;
impl PhysicalEntity for Player {
#[inline(always)]
fn x(&self) -> isize {
fn x(&self) -> i32 {
self.x
}
#[inline(always)]
fn y(&self) -> isize {
fn y(&self) -> i32 {
self.y
}
#[inline(always)]
fn vel_x(&self) -> isize {
fn vel_x(&self) -> i32 {
self.vel_x
}
#[inline(always)]
fn vel_y(&self) -> isize {
fn vel_y(&self) -> i32 {
self.vel_y
}
@ -43,22 +43,22 @@ impl PhysicalEntity for Player {
}
#[inline(always)]
fn set_x(&mut self, x: isize) {
fn set_x(&mut self, x: i32) {
self.x = x;
}
#[inline(always)]
fn set_y(&mut self, y: isize) {
fn set_y(&mut self, y: i32) {
self.y = y;
}
#[inline(always)]
fn set_vel_x(&mut self, vel_x: isize) {
fn set_vel_x(&mut self, vel_x: i32) {
self.vel_x = vel_x;
}
#[inline(always)]
fn set_vel_y(&mut self, vel_y: isize) {
fn set_vel_y(&mut self, vel_y: i32) {
self.vel_y = vel_y;
}
@ -95,10 +95,10 @@ impl Player {
fn judge_hit_npc_solid_soft(&mut self, npc: &NPC) -> Flag {
let mut flags = Flag(0);
if ((self.y - self.hit_bounds.top as isize) < (npc.y + npc.hit_bounds.bottom as isize - 3 * 0x200))
&& ((self.y + self.hit_bounds.top as isize) > (npc.y - npc.hit_bounds.bottom as isize + 3 * 0x200))
&& ((self.x - self.hit_bounds.right as isize) < (npc.x + npc.hit_bounds.right as isize))
&& ((self.x - self.hit_bounds.right as isize) > npc.x) {
if ((self.y - self.hit_bounds.top as i32) < (npc.y + npc.hit_bounds.bottom as i32 - 3 * 0x200))
&& ((self.y + self.hit_bounds.top as i32) > (npc.y - npc.hit_bounds.bottom as i32 + 3 * 0x200))
&& ((self.x - self.hit_bounds.right as i32) < (npc.x + npc.hit_bounds.right as i32))
&& ((self.x - self.hit_bounds.right as i32) > npc.x) {
if self.vel_x < 0x200 {
self.vel_x += 0x200;
}
@ -106,10 +106,10 @@ impl Player {
flags.set_hit_left_wall(true);
}
if ((self.y - self.hit_bounds.top as isize) < (npc.y + npc.hit_bounds.bottom as isize - 3 * 0x200))
&& ((self.y + self.hit_bounds.top as isize) > (npc.y - npc.hit_bounds.bottom as isize + 3 * 0x200))
&& ((self.x + self.hit_bounds.right as isize - 0x200) > (npc.x - npc.hit_bounds.right as isize))
&& ((self.x + self.hit_bounds.right as isize - 0x200) < npc.x) {
if ((self.y - self.hit_bounds.top as i32) < (npc.y + npc.hit_bounds.bottom as i32 - 3 * 0x200))
&& ((self.y + self.hit_bounds.top as i32) > (npc.y - npc.hit_bounds.bottom as i32 + 3 * 0x200))
&& ((self.x + self.hit_bounds.right as i32 - 0x200) > (npc.x - npc.hit_bounds.right as i32))
&& ((self.x + self.hit_bounds.right as i32 - 0x200) < npc.x) {
if self.vel_x > -0x200 {
self.vel_x -= 0x200;
}
@ -118,10 +118,10 @@ impl Player {
}
if ((self.x - self.hit_bounds.right as isize) < (npc.x + npc.hit_bounds.right as isize - 3 * 0x200))
&& ((self.x + self.hit_bounds.right as isize) > (npc.x - npc.hit_bounds.right as isize + 3 * 0x200))
&& ((self.y - self.hit_bounds.top as isize) < (npc.y + npc.hit_bounds.bottom as isize))
&& ((self.y - self.hit_bounds.top as isize) > npc.y) {
if ((self.x - self.hit_bounds.right as i32) < (npc.x + npc.hit_bounds.right as i32 - 3 * 0x200))
&& ((self.x + self.hit_bounds.right as i32) > (npc.x - npc.hit_bounds.right as i32 + 3 * 0x200))
&& ((self.y - self.hit_bounds.top as i32) < (npc.y + npc.hit_bounds.bottom as i32))
&& ((self.y - self.hit_bounds.top as i32) > npc.y) {
if self.vel_y < 0 {
self.vel_y = 0;
}
@ -129,15 +129,15 @@ impl Player {
flags.set_hit_top_wall(true);
}
if ((self.x - self.hit_bounds.right as isize) < (npc.x + npc.hit_bounds.right as isize - 3 * 0x200))
&& ((self.x + self.hit_bounds.right as isize) > (npc.x - npc.hit_bounds.right as isize + 3 * 0x200))
&& ((self.y + self.hit_bounds.bottom as isize - 0x200) > (npc.y - npc.hit_bounds.top as isize))
&& ((self.y + self.hit_bounds.bottom as isize - 0x200) < (npc.y + 3 * 0x200)) {
if ((self.x - self.hit_bounds.right as i32) < (npc.x + npc.hit_bounds.right as i32 - 3 * 0x200))
&& ((self.x + self.hit_bounds.right as i32) > (npc.x - npc.hit_bounds.right as i32 + 3 * 0x200))
&& ((self.y + self.hit_bounds.bottom as i32 - 0x200) > (npc.y - npc.hit_bounds.top as i32))
&& ((self.y + self.hit_bounds.bottom as i32 - 0x200) < (npc.y + 3 * 0x200)) {
if npc.npc_flags.bouncy() {
self.vel_y = npc.vel_y - 0x200;
flags.set_hit_bottom_wall(true);
} else if !self.flags.hit_bottom_wall() && self.vel_y > npc.vel_y {
self.y = npc.y - npc.hit_bounds.top as isize - self.hit_bounds.bottom as isize + 0x200;
self.y = npc.y - npc.hit_bounds.top as i32 - self.hit_bounds.bottom as i32 + 0x200;
self.vel_y = npc.vel_y;
self.x += npc.vel_x;
flags.set_hit_bottom_wall(true);
@ -160,58 +160,58 @@ impl Player {
let fx2 = if fx2 == 0.0 { 1.0 } else { fx2 };
if fy1 / fx1 <= fy2 / fx2 {
if (self.y - self.hit_bounds.top as isize) < (npc.y + npc.hit_bounds.bottom as isize)
&& (self.y + self.hit_bounds.bottom as isize) > (npc.y - npc.hit_bounds.top as isize) {
if (self.x - self.hit_bounds.right as isize) < (npc.x + npc.hit_bounds.right as isize)
&& (self.x - self.hit_bounds.right as isize) > npc.x {
if (self.y - self.hit_bounds.top as i32) < (npc.y + npc.hit_bounds.bottom as i32)
&& (self.y + self.hit_bounds.bottom as i32) > (npc.y - npc.hit_bounds.top as i32) {
if (self.x - self.hit_bounds.right as i32) < (npc.x + npc.hit_bounds.right as i32)
&& (self.x - self.hit_bounds.right as i32) > npc.x {
if self.vel_x < npc.vel_x {
self.vel_x = npc.vel_x;
}
self.x = npc.x + npc.hit_bounds.right as isize + self.hit_bounds.right as isize;
self.x = npc.x + npc.hit_bounds.right as i32 + self.hit_bounds.right as i32;
flags.set_hit_left_wall(true);
}
if (self.x + self.hit_bounds.right as isize) > (npc.x - npc.hit_bounds.right as isize)
&& (self.x + self.hit_bounds.right as isize) < npc.x {
if (self.x + self.hit_bounds.right as i32) > (npc.x - npc.hit_bounds.right as i32)
&& (self.x + self.hit_bounds.right as i32) < npc.x {
if self.vel_x > npc.vel_x {
self.vel_x = npc.vel_x;
}
self.x = npc.x - npc.hit_bounds.right as isize - self.hit_bounds.right as isize;
self.x = npc.x - npc.hit_bounds.right as i32 - self.hit_bounds.right as i32;
flags.set_hit_right_wall(true);
}
}
} else if (self.x - self.hit_bounds.right as isize) < (npc.x + npc.hit_bounds.right as isize)
&& (self.x + self.hit_bounds.right as isize) > (npc.x - npc.hit_bounds.right as isize) {
if (self.y - self.hit_bounds.top as isize) < (npc.y + npc.hit_bounds.bottom as isize)
&& (self.y - self.hit_bounds.top as isize) > npc.y {
} else if (self.x - self.hit_bounds.right as i32) < (npc.x + npc.hit_bounds.right as i32)
&& (self.x + self.hit_bounds.right as i32) > (npc.x - npc.hit_bounds.right as i32) {
if (self.y - self.hit_bounds.top as i32) < (npc.y + npc.hit_bounds.bottom as i32)
&& (self.y - self.hit_bounds.top as i32) > npc.y {
if self.vel_y >= npc.vel_y {
if self.vel_y < 0 {
self.vel_y = 0;
}
} else {
self.y = npc.y + npc.hit_bounds.bottom as isize + self.hit_bounds.top as isize + 0x200;
self.y = npc.y + npc.hit_bounds.bottom as i32 + self.hit_bounds.top as i32 + 0x200;
self.vel_y = npc.vel_y;
}
flags.set_hit_top_wall(true);
}
if (self.y + self.hit_bounds.bottom as isize) > (npc.y - npc.hit_bounds.top as isize)
&& (self.y + self.hit_bounds.bottom as isize) < (npc.y + 3 * 0x200) {
if (self.y + self.hit_bounds.bottom as i32) > (npc.y - npc.hit_bounds.top as i32)
&& (self.y + self.hit_bounds.bottom as i32) < (npc.y + 3 * 0x200) {
if self.vel_y - npc.vel_y > 2 * 0x200 {
state.sound_manager.play_sfx(23);
}
if self.control_mode == ControlMode::IronHead {
self.y = npc.y - npc.hit_bounds.top as isize - self.hit_bounds.bottom as isize + 0x200;
self.y = npc.y - npc.hit_bounds.top as i32 - self.hit_bounds.bottom as i32 + 0x200;
flags.set_hit_bottom_wall(true);
} else if npc.npc_flags.bouncy() {
self.vel_y = npc.vel_y - 0x200;
flags.set_hit_bottom_wall(true);
} else if !self.flags.hit_bottom_wall() && self.vel_y > npc.vel_y {
self.y = npc.y - npc.hit_bounds.top as isize - self.hit_bounds.bottom as isize + 0x200;
self.y = npc.y - npc.hit_bounds.top as i32 - self.hit_bounds.bottom as i32 + 0x200;
self.vel_y = npc.vel_y;
self.x += npc.vel_x;
@ -225,13 +225,13 @@ impl Player {
fn judge_hit_npc_non_solid(&mut self, npc: &NPC) -> Flag {
let mut flags = Flag(0);
let hit_left = if npc.direction == Direction::Left { npc.hit_bounds.left } else { npc.hit_bounds.right } as isize;
let hit_right = if npc.direction == Direction::Left { npc.hit_bounds.right } else { npc.hit_bounds.left } as isize;
let hit_left = if npc.direction == Direction::Left { npc.hit_bounds.left } else { npc.hit_bounds.right } as i32;
let hit_right = if npc.direction == Direction::Left { npc.hit_bounds.right } else { npc.hit_bounds.left } as i32;
if self.x + (2 * 0x200) > npc.x - hit_left
&& self.x - (2 * 0x200) < npc.x + hit_right
&& self.y + (2 * 0x200) > npc.y - npc.hit_bounds.top as isize
&& self.y - (2 * 0x200) < npc.y + npc.hit_bounds.bottom as isize {
&& self.y + (2 * 0x200) > npc.y - npc.hit_bounds.top as i32
&& self.y - (2 * 0x200) < npc.y + npc.hit_bounds.bottom as i32 {
flags.set_hit_left_wall(true);
}
@ -316,10 +316,10 @@ impl Player {
|| flags.hit_right_wall() && npc.vel_x < 0
|| flags.hit_top_wall() && npc.vel_y > 0
|| flags.hit_bottom_wall() && npc.vel_y < 0 {
self.damage(npc.damage as isize, state, npc_list);
self.damage(npc.damage as i32, state, npc_list);
}
} else if flags.0 != 0 && npc.damage != 0 && !state.control_flags.interactions_disabled() {
self.damage(npc.damage as isize, state, npc_list);
self.damage(npc.damage as i32, state, npc_list);
}
}
}

View File

@ -96,8 +96,8 @@ impl GameProfile {
game_scene.player1.equip.0 = self.equipment as u16;
game_scene.player1.x = self.pos_x as isize;
game_scene.player1.y = self.pos_y as isize;
game_scene.player1.x = self.pos_x;
game_scene.player1.y = self.pos_y;
game_scene.player1.control_mode = if self.control_mode == 1 { ControlMode::IronHead } else { ControlMode::Normal };
game_scene.player1.direction = self.direction;

View File

@ -32,7 +32,7 @@ use crate::ui::Components;
use crate::weapon::WeaponType;
pub struct GameScene {
pub tick: usize,
pub tick: u32,
pub stage: Stage,
pub boss_life_bar: BossLifeBar,
pub stage_select: StageSelect,
@ -180,9 +180,9 @@ impl GameScene {
BackgroundType::OutsideWind | BackgroundType::Outside => {
graphics::clear(ctx, Color::from_rgb(0, 0, 0));
let offset = (self.tick % 640) as isize;
let offset = (self.tick % 640) as i32;
for x in (0..(state.canvas_size.0 as isize)).step_by(200) {
for x in (0..(state.canvas_size.0 as i32)).step_by(200) {
batch.add_rect(x as f32, 0.0,
&Rect::new_size(0, 0, 200, 88));
}
@ -190,22 +190,22 @@ impl GameScene {
batch.add_rect(state.canvas_size.0 - 320.0, 0.0,
&Rect::new_size(0, 0, 320, 88));
for x in ((-offset / 2)..(state.canvas_size.0 as isize)).step_by(320) {
for x in ((-offset / 2)..(state.canvas_size.0 as i32)).step_by(320) {
batch.add_rect(x as f32, 88.0,
&Rect::new_size(0, 88, 320, 35));
}
for x in ((-offset % 320)..(state.canvas_size.0 as isize)).step_by(320) {
for x in ((-offset % 320)..(state.canvas_size.0 as i32)).step_by(320) {
batch.add_rect(x as f32, 123.0,
&Rect::new_size(0, 123, 320, 23));
}
for x in ((-offset * 2)..(state.canvas_size.0 as isize)).step_by(320) {
for x in ((-offset * 2)..(state.canvas_size.0 as i32)).step_by(320) {
batch.add_rect(x as f32, 146.0,
&Rect::new_size(0, 146, 320, 30));
}
for x in ((-offset * 4)..(state.canvas_size.0 as isize)).step_by(320) {
for x in ((-offset * 4)..(state.canvas_size.0 as i32)).step_by(320) {
batch.add_rect(x as f32, 176.0,
&Rect::new_size(0, 176, 320, 64));
}
@ -219,36 +219,36 @@ impl GameScene {
fn draw_bullets(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "Bullet")?;
let mut x: isize;
let mut y: isize;
let mut prev_x: isize;
let mut prev_y: isize;
let mut x: i32;
let mut y: i32;
let mut prev_x: i32;
let mut prev_y: i32;
for bullet in self.bullet_manager.bullets.iter() {
match bullet.direction {
Direction::Left => {
x = bullet.x - bullet.display_bounds.left as isize;
y = bullet.y - bullet.display_bounds.top as isize;
prev_x = bullet.prev_x - bullet.display_bounds.left as isize;
prev_y = bullet.prev_y - bullet.display_bounds.top as isize;
x = bullet.x - bullet.display_bounds.left as i32;
y = bullet.y - bullet.display_bounds.top as i32;
prev_x = bullet.prev_x - bullet.display_bounds.left as i32;
prev_y = bullet.prev_y - bullet.display_bounds.top as i32;
}
Direction::Up => {
x = bullet.x - bullet.display_bounds.top as isize;
y = bullet.y - bullet.display_bounds.left as isize;
prev_x = bullet.prev_x - bullet.display_bounds.top as isize;
prev_y = bullet.prev_y - bullet.display_bounds.left as isize;
x = bullet.x - bullet.display_bounds.top as i32;
y = bullet.y - bullet.display_bounds.left as i32;
prev_x = bullet.prev_x - bullet.display_bounds.top as i32;
prev_y = bullet.prev_y - bullet.display_bounds.left as i32;
}
Direction::Right => {
x = bullet.x - bullet.display_bounds.right as isize;
y = bullet.y - bullet.display_bounds.top as isize;
prev_x = bullet.prev_x - bullet.display_bounds.right as isize;
prev_y = bullet.prev_y - bullet.display_bounds.top as isize;
x = bullet.x - bullet.display_bounds.right as i32;
y = bullet.y - bullet.display_bounds.top as i32;
prev_x = bullet.prev_x - bullet.display_bounds.right as i32;
prev_y = bullet.prev_y - bullet.display_bounds.top as i32;
}
Direction::Bottom => {
x = bullet.x - bullet.display_bounds.top as isize;
y = bullet.y - bullet.display_bounds.right as isize;
prev_x = bullet.prev_x - bullet.display_bounds.top as isize;
prev_y = bullet.prev_y - bullet.display_bounds.right as isize;
x = bullet.x - bullet.display_bounds.top as i32;
y = bullet.y - bullet.display_bounds.right as i32;
prev_x = bullet.prev_x - bullet.display_bounds.top as i32;
prev_y = bullet.prev_y - bullet.display_bounds.right as i32;
}
Direction::FacingPlayer => unreachable!(),
}
@ -297,14 +297,14 @@ impl GameScene {
FadeDirection::Left | FadeDirection::Right => {
let mut frame = tick;
for x in (0..(state.canvas_size.0 as isize + 16)).step_by(16) {
for x in (0..(state.canvas_size.0 as i32 + 16)).step_by(16) {
if frame > 15 { frame = 15; } else { frame += 1; }
if frame >= 0 {
rect.left = frame as u16 * 16;
rect.right = rect.left + 16;
for y in (0..(state.canvas_size.1 as isize + 16)).step_by(16) {
for y in (0..(state.canvas_size.1 as i32 + 16)).step_by(16) {
if direction == FadeDirection::Left {
batch.add_rect(state.canvas_size.0 - x as f32, y as f32, &rect);
} else {
@ -317,14 +317,14 @@ impl GameScene {
FadeDirection::Up | FadeDirection::Down => {
let mut frame = tick;
for y in (0..(state.canvas_size.1 as isize + 16)).step_by(16) {
for y in (0..(state.canvas_size.1 as i32 + 16)).step_by(16) {
if frame > 15 { frame = 15; } else { frame += 1; }
if frame >= 0 {
rect.left = frame as u16 * 16;
rect.right = rect.left + 16;
for x in (0..(state.canvas_size.0 as isize + 16)).step_by(16) {
for x in (0..(state.canvas_size.0 as i32 + 16)).step_by(16) {
if direction == FadeDirection::Down {
batch.add_rect(x as f32, y as f32, &rect);
} else {
@ -335,8 +335,8 @@ impl GameScene {
}
}
FadeDirection::Center => {
let center_x = (state.canvas_size.0 / 2.0 - 8.0) as isize;
let center_y = (state.canvas_size.1 / 2.0 - 8.0) as isize;
let center_x = (state.canvas_size.0 / 2.0 - 8.0) as i32;
let center_y = (state.canvas_size.1 / 2.0 - 8.0) as i32;
let mut start_frame = tick;
for x in (0..(center_x + 16)).step_by(16) {
@ -534,10 +534,10 @@ impl GameScene {
}
for npc in self.npc_list.iter_alive() {
if npc.cond.hidden() || (npc.x < (self.frame.x - 128 * 0x200 - npc.display_bounds.width() as isize * 0x200)
|| npc.x > (self.frame.x + 128 * 0x200 + (state.canvas_size.0 as isize + npc.display_bounds.width() as isize) * 0x200)
&& npc.y < (self.frame.y - 128 * 0x200 - npc.display_bounds.height() as isize * 0x200)
|| npc.y > (self.frame.y + 128 * 0x200 + (state.canvas_size.1 as isize + npc.display_bounds.height() as isize) * 0x200)) {
if npc.cond.hidden() || (npc.x < (self.frame.x - 128 * 0x200 - npc.display_bounds.width() as i32 * 0x200)
|| npc.x > (self.frame.x + 128 * 0x200 + (state.canvas_size.0 as i32 + npc.display_bounds.width() as i32) * 0x200)
&& npc.y < (self.frame.y - 128 * 0x200 - npc.display_bounds.height() as i32 * 0x200)
|| npc.y > (self.frame.y + 128 * 0x200 + (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200)) {
continue;
}
@ -670,10 +670,10 @@ impl GameScene {
// cheap, clones a reference underneath
let mut tmp_batch = SpriteBatch::new(state.tmp_canvas.image().clone());
let tile_start_x = clamp(self.frame.x / 0x200 / 16, 0, self.stage.map.width as isize) as usize;
let tile_start_y = clamp(self.frame.y / 0x200 / 16, 0, self.stage.map.height as isize) as usize;
let tile_end_x = clamp((self.frame.x / 0x200 + 8 + state.canvas_size.0 as isize) / 16 + 1, 0, self.stage.map.width as isize) as usize;
let tile_end_y = clamp((self.frame.y / 0x200 + 8 + state.canvas_size.1 as isize) / 16 + 1, 0, self.stage.map.height as isize) as usize;
let tile_start_x = clamp(self.frame.x / 0x200 / 16, 0, self.stage.map.width as i32) as usize;
let tile_start_y = clamp(self.frame.y / 0x200 / 16, 0, self.stage.map.height as i32) as usize;
let tile_end_x = clamp((self.frame.x / 0x200 + 8 + state.canvas_size.0 as i32) / 16 + 1, 0, self.stage.map.width as i32) as usize;
let tile_end_y = clamp((self.frame.y / 0x200 + 8 + state.canvas_size.1 as i32) / 16 + 1, 0, self.stage.map.height as i32) as usize;
let mut rect = Rect {
left: 0.0,
top: 0.0,
@ -683,12 +683,14 @@ impl GameScene {
for y in tile_start_y..tile_end_y {
for x in tile_start_x..tile_end_x {
let tile = self.stage.map.attrib[*self.stage.map.tiles
.get((y * self.stage.map.width) + x)
.unwrap() as usize];
let tile_above = self.stage.map.attrib[*self.stage.map.tiles
.get((y.saturating_sub(1) * self.stage.map.width) + x)
.unwrap() as usize];
let tile = unsafe {
self.stage.map.attrib[*self.stage.map.tiles
.get_unchecked((y * self.stage.map.width as usize) + x) as usize]
};
let tile_above = unsafe {
self.stage.map.attrib[*self.stage.map.tiles
.get_unchecked((y.saturating_sub(1) * self.stage.map.width as usize) + x) as usize]
};
if !self.is_water(tile) {
continue;
@ -733,10 +735,10 @@ impl GameScene {
let mut rect = Rect::new(0, 0, 16, 16);
let (frame_x, frame_y) = self.frame.xy_interpolated(state.frame_time, state.scale);
let tile_start_x = clamp(self.frame.x / 0x200 / 16, 0, self.stage.map.width as isize) as usize;
let tile_start_y = clamp(self.frame.y / 0x200 / 16, 0, self.stage.map.height as isize) as usize;
let tile_end_x = clamp((self.frame.x / 0x200 + 8 + state.canvas_size.0 as isize) / 16 + 1, 0, self.stage.map.width as isize) as usize;
let tile_end_y = clamp((self.frame.y / 0x200 + 8 + state.canvas_size.1 as isize) / 16 + 1, 0, self.stage.map.height as isize) as usize;
let tile_start_x = clamp(frame_x as i32 / 16, 0, self.stage.map.width as i32) as usize;
let tile_start_y = clamp(frame_y as i32 / 16, 0, self.stage.map.height as i32) as usize;
let tile_end_x = clamp((frame_x as i32 + 8 + state.canvas_size.0 as i32) / 16 + 1, 0, self.stage.map.width as i32) as usize;
let tile_end_y = clamp((frame_y as i32 + 8 + state.canvas_size.1 as i32) / 16 + 1, 0, self.stage.map.height as i32) as usize;
if layer == TileLayer::Snack {
rect = state.constants.world.snack_rect;
@ -745,7 +747,7 @@ impl GameScene {
for y in tile_start_y..tile_end_y {
for x in tile_start_x..tile_end_x {
let tile = *self.stage.map.tiles
.get((y * self.stage.map.width) + x)
.get((y * self.stage.map.width as usize) + x)
.unwrap();
match layer {
@ -874,16 +876,16 @@ impl GameScene {
let hit = (
npc.npc_flags.shootable()
&& (npc.x - npc.hit_bounds.right as isize) < (bullet.x + bullet.enemy_hit_width as isize)
&& (npc.x + npc.hit_bounds.right as isize) > (bullet.x - bullet.enemy_hit_width as isize)
&& (npc.y - npc.hit_bounds.top as isize) < (bullet.y + bullet.enemy_hit_height as isize)
&& (npc.y + npc.hit_bounds.bottom as isize) > (bullet.y - bullet.enemy_hit_height as isize)
&& (npc.x - npc.hit_bounds.right as i32) < (bullet.x + bullet.enemy_hit_width as i32)
&& (npc.x + npc.hit_bounds.right as i32) > (bullet.x - bullet.enemy_hit_width as i32)
&& (npc.y - npc.hit_bounds.top as i32) < (bullet.y + bullet.enemy_hit_height as i32)
&& (npc.y + npc.hit_bounds.bottom as i32) > (bullet.y - bullet.enemy_hit_height as i32)
) || (
npc.npc_flags.invulnerable()
&& (npc.x - npc.hit_bounds.right as isize) < (bullet.x + bullet.hit_bounds.right as isize)
&& (npc.x + npc.hit_bounds.right as isize) > (bullet.x - bullet.hit_bounds.left as isize)
&& (npc.y - npc.hit_bounds.top as isize) < (bullet.y + bullet.hit_bounds.bottom as isize)
&& (npc.y + npc.hit_bounds.bottom as isize) > (bullet.y - bullet.hit_bounds.top as isize)
&& (npc.x - npc.hit_bounds.right as i32) < (bullet.x + bullet.hit_bounds.right as i32)
&& (npc.x + npc.hit_bounds.right as i32) > (bullet.x - bullet.hit_bounds.left as i32)
&& (npc.y - npc.hit_bounds.top as i32) < (bullet.y + bullet.hit_bounds.bottom as i32)
&& (npc.y + npc.hit_bounds.bottom as i32) > (bullet.y - bullet.hit_bounds.top as i32)
);
if !hit {
@ -1070,10 +1072,10 @@ impl GameScene {
}
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)
&& npc.y < (self.frame.y - 128 - npc.display_bounds.height() as isize * 0x200)
|| npc.y > (self.frame.y + 128 + (state.canvas_size.1 as isize + npc.display_bounds.height() as isize) * 0x200) {
if npc.x < (self.frame.x - 128 - npc.display_bounds.width() as i32 * 0x200)
|| npc.x > (self.frame.x + 128 + (state.canvas_size.0 as i32 + npc.display_bounds.width() as i32) * 0x200)
&& npc.y < (self.frame.y - 128 - npc.display_bounds.height() as i32 * 0x200)
|| npc.y > (self.frame.y + 128 + (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200) {
return Ok(());
}
@ -1231,6 +1233,10 @@ impl Scene for GameScene {
}
TextScriptVM::run(state, self, ctx)?;
#[cfg(feature = "scripting")]
state.lua.scene_tick(self);
self.tick = self.tick.wrapping_add(1);
Ok(())
}
@ -1286,10 +1292,10 @@ impl Scene for GameScene {
self.boss.draw(state, ctx, &self.frame)?;
for npc in self.npc_list.iter_alive() {
if npc.x < (self.frame.x - 128 * 0x200 - npc.display_bounds.width() as isize * 0x200)
|| npc.x > (self.frame.x + 128 * 0x200 + (state.canvas_size.0 as isize + npc.display_bounds.width() as isize) * 0x200)
&& npc.y < (self.frame.y - 128 * 0x200 - npc.display_bounds.height() as isize * 0x200)
|| npc.y > (self.frame.y + 128 * 0x200 + (state.canvas_size.1 as isize + npc.display_bounds.height() as isize) * 0x200) {
if npc.x < (self.frame.x - 128 * 0x200 - npc.display_bounds.width() as i32 * 0x200)
|| npc.x > (self.frame.x + 128 * 0x200 + (state.canvas_size.0 as i32 + npc.display_bounds.width() as i32) * 0x200)
&& npc.y < (self.frame.y - 128 * 0x200 - npc.display_bounds.height() as i32 * 0x200)
|| npc.y > (self.frame.y + 128 * 0x200 + (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200) {
continue;
}
@ -1333,7 +1339,7 @@ impl Scene for GameScene {
state.font.draw_colored_text(P2_LEFT_TEXT.chars(),
8.0, y,
(96, 96, 255, 255), &state.constants, &mut state.texture_set, ctx)?;
} else if self.player2.x - 8 * 0x200 > self.frame.x + state.canvas_size.0 as isize * 0x200 {
} else if self.player2.x - 8 * 0x200 > self.frame.x + state.canvas_size.0 as i32 * 0x200 {
let width = state.font.text_width(P2_RIGHT_TEXT.chars(), &state.constants);
state.font.draw_colored_text(P2_RIGHT_TEXT.chars(),

View File

@ -13,6 +13,8 @@ use crate::input::touch_controls::TouchControlType;
enum CurrentMenu {
MainMenu,
OptionMenu,
SaveSelectMenu,
ChallengesMenu,
StartGame,
LoadGame,
}
@ -23,6 +25,7 @@ pub struct TitleScene {
current_menu: CurrentMenu,
main_menu: Menu,
option_menu: Menu,
save_select_menu: Menu,
}
impl TitleScene {
@ -31,8 +34,9 @@ impl TitleScene {
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),
main_menu: Menu::new(0, 0, 100, 0),
option_menu: Menu::new(0, 0, 180, 0),
save_select_menu: Menu::new(0, 0, 200, 0),
}
}
@ -93,9 +97,12 @@ impl Scene for TitleScene {
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()));
if cfg!(feature = "editor") {
self.main_menu.push_entry(MenuEntry::Active("Editor".to_string()));
} else {
self.main_menu.push_entry(MenuEntry::Hidden);
}
self.main_menu.push_entry(MenuEntry::Active("Quit".to_string()));
self.main_menu.height = self.main_menu.entries.len() as u16 * 14 + 6;
self.option_menu.push_entry(MenuEntry::Toggle("Original timing (50TPS)".to_string(), state.timing_mode == TimingMode::_50Hz));
self.option_menu.push_entry(MenuEntry::Toggle("Lighting effects".to_string(), state.settings.shader_effects));
@ -113,7 +120,12 @@ impl Scene for TitleScene {
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.save_select_menu.push_entry(MenuEntry::NewSave);
self.save_select_menu.push_entry(MenuEntry::NewSave);
self.save_select_menu.push_entry(MenuEntry::NewSave);
self.save_select_menu.push_entry(MenuEntry::Active("Delete a save".to_string()));
self.save_select_menu.push_entry(MenuEntry::Active("Back".to_string()));
self.controller.update(state, ctx)?;
self.controller.update_trigger();
@ -126,9 +138,11 @@ impl Scene for TitleScene {
self.controller.update(state, ctx)?;
self.controller.update_trigger();
self.main_menu.update_height();
self.main_menu.x = ((state.canvas_size.0 - self.main_menu.width as f32) / 2.0).floor() as isize;
self.main_menu.y = ((state.canvas_size.1 + 70.0 - self.main_menu.height as f32) / 2.0).floor() as isize;
self.option_menu.update_height();
self.option_menu.x = ((state.canvas_size.0 - self.option_menu.width as f32) / 2.0).floor() as isize;
self.option_menu.y = ((state.canvas_size.1 + 70.0 - self.option_menu.height as f32) / 2.0).floor() as isize;
@ -212,6 +226,7 @@ impl Scene for TitleScene {
state.load_or_start_game(ctx)?;
}
}
_ => {}
}
self.tick += 1;

90
src/scripting/boot.lua Normal file
View File

@ -0,0 +1,90 @@
doukutsu = {}
doukutsu._registered = {
tick = {},
}
doukutsu._handlers = setmetatable({
tick = function(scene)
for _, h in pairs(doukutsu._registered.tick) do
pcall(h, scene)
end
end,
}, {
__index = function(self, event)
error("Unknown event: " .. event)
end,
})
doukutsu._initialize_script = function(script)
-- for compatibility with Lua 5.2+, copy-pasted from Lua mailing list
-- http://lua-users.org/lists/lua-l/2010-06/msg00313.html
local _setfenv = setfenv or function(f, t)
f = (type(f) == 'function' and f or debug.getinfo(f + 1, 'f').func)
local name
local up = 0
repeat
up = up + 1
name = debug.getupvalue(f, up)
until name == '_ENV' or name == nil
if name then
debug.upvaluejoin(f, up, function()
return name
end, 1)
debug.setupvalue(f, up, t)
end
end
global_copy = {}
for k, v in pairs(_G) do
global_copy[k] = v
end
_setfenv(script, global_copy)
script()
end
doukutsu.play_sfx = function(id)
__doukutsu:play_sfx(id)
end
doukutsu.play_song = function(id)
__doukutsu:play_song(id)
end
doukutsu.on = function(event, handler)
assert(type(event) == "string", "event type must be a string.")
assert(type(handler) == "function", "event handler must be a function.")
if doukutsu._registered[event] == nil then
error("Unknown event: " .. event)
end
table.insert(doukutsu._registered[event], handler)
return handler
end
doukutsu.remove_handler = function(event, handler)
assert(type(event) == "string", "event type must be a string.")
assert(type(handler) == "function", "event handler must be a function.")
if doukutsu._registered[event] == nil then
error("Unknown event: " .. event)
end
local index = -1
for i, h in pairs(doukutsu._registered[event]) do
if handler == h then
index = i
break
end
end
if index ~= -1 then
table.remove(doukutsu._registered[event], index)
end
return handler
end

64
src/scripting/doukutsu.rs Normal file
View File

@ -0,0 +1,64 @@
use lua_ffi::ffi::luaL_Reg;
use lua_ffi::{LuaObject, State, c_int};
use crate::scene::game_scene::GameScene;
use crate::scripting::LuaScriptingState;
use crate::shared_game_state::SharedGameState;
pub struct Doukutsu {
pub ptr: *mut LuaScriptingState,
}
impl Doukutsu {
pub fn new(ptr: *mut LuaScriptingState) -> Doukutsu {
Doukutsu {
ptr,
}
}
unsafe fn lua_play_sfx(&self, state: &mut State) -> c_int {
if let Some(index) = state.to_int(2) {
let game_state = &mut (*(*self.ptr).state_ptr);
game_state.sound_manager.play_sfx(index as u8);
}
0
}
unsafe fn lua_play_song(&self, state: &mut State) -> c_int {
if let Some(index) = state.to_int(2) {
let game_state = &mut (*(*self.ptr).state_ptr);
let ctx = &mut (*(*self.ptr).ctx_ptr);
game_state.sound_manager.play_song(index as usize, &game_state.constants, ctx);
}
0
}
unsafe fn lua_flag(&self, state: &mut State) -> c_int {
if let Some(index) = state.to_int(2) {
let game_state = &mut (*(*self.ptr).state_ptr);
state.push(*game_state.game_flags.get(index.max(0) as usize).unwrap_or(&false));
} else {
state.push_nil();
}
1
}
}
impl LuaObject for Doukutsu {
fn name() -> *const i8 {
c_str!("Doukutsu")
}
fn lua_fns() -> Vec<luaL_Reg> {
vec![
lua_method!("play_sfx", Doukutsu, Doukutsu::lua_play_sfx),
lua_method!("play_song", Doukutsu, Doukutsu::lua_play_song),
]
}
}

142
src/scripting/mod.rs Normal file
View File

@ -0,0 +1,142 @@
use std::io::{Read, Seek};
use std::ptr::null_mut;
use ggez::{Context, filesystem, GameError, GameResult};
use ggez::filesystem::File;
use lua_ffi::{c_int, LuaFunction, LuaObject, State, ThreadStatus};
use lua_ffi::ffi::lua_pushcfunction;
use crate::scene::game_scene::GameScene;
use crate::scripting::doukutsu::Doukutsu;
use crate::shared_game_state::SharedGameState;
mod doukutsu;
mod player;
mod scene;
pub struct LuaScriptingState {
state: Option<State>,
state_ptr: *mut SharedGameState,
ctx_ptr: *mut Context,
game_scene: *mut GameScene,
}
pub static REF_ERROR: &str = "Reference went out of scope. DO NOT store/use references to game objects outside the event.";
static BOOT_SCRIPT: &str = include_str!("boot.lua");
fn check_status(status: ThreadStatus, state: &mut State) -> GameResult {
match status {
ThreadStatus::Ok | ThreadStatus::Yield => { return Ok(()); }
_ => {}
}
let error = state.to_str(-1).unwrap_or("???");
match status {
ThreadStatus::RuntimeError => Err(GameError::EventLoopError(format!("Lua Runtime Error: {}", error))),
ThreadStatus::SyntaxError => Err(GameError::EventLoopError(format!("Lua Syntax Error: {}", error))),
ThreadStatus::MemoryError => Err(GameError::EventLoopError(format!("Lua Memory Error: {}", error))),
ThreadStatus::MsgHandlerError => Err(GameError::EventLoopError(format!("Lua Message Handler Error: {}", error))),
ThreadStatus::FileError => Err(GameError::EventLoopError(format!("Lua File Error: {}", error))),
_ => Ok(())
}
}
fn print(state: &mut State) -> c_int {
if let Some(msg) = state.to_str(1) {
log::info!("[Lua] {}", msg);
}
0
}
impl LuaScriptingState {
pub fn new() -> LuaScriptingState {
LuaScriptingState {
state: None,
state_ptr: null_mut(),
ctx_ptr: null_mut(),
game_scene: null_mut(),
}
}
pub fn update_refs(&mut self, state: *mut SharedGameState, ctx: *mut Context) {
self.state_ptr = state;
self.ctx_ptr = ctx;
}
fn load_script(mut state: &mut State, path: &str, mut script: File) -> bool {
let mut buf = Vec::with_capacity(1024);
let res = script.read_to_end(&mut buf);
if let Err(err) = res {
log::warn!("Error reading script {}: {}", path, err);
return false;
}
let name = format!("@{}", path);
let res = state.load_buffer(&buf, &name);
let res = check_status(res, &mut state);
if let Err(err) = res {
log::warn!("Error loading script {}: {}", path, err);
return false;
}
state.get_global("doukutsu");
state.get_field(-1, "_initialize_script");
state.push_value(-3);
let res = state.pcall(1, 0, 0);
if let Err((_, err)) = res {
log::warn!("Error evaluating script {}: {}", path, err);
return false;
}
log::info!("Successfully loaded Lua script: {}", path);
true
}
pub fn reload_scripts(&mut self, ctx: &mut Context) -> GameResult {
let mut state = State::new();
state.open_libs();
state.push(lua_fn!(print));
state.set_global("print");
state.push(Doukutsu { ptr: self as *mut LuaScriptingState });
state.set_global("__doukutsu");
let res = state.do_string(BOOT_SCRIPT);
check_status(res, &mut state)?;
if filesystem::exists(ctx, "/scripts/") {
let mut script_count = 0;
let mut files = filesystem::read_dir(ctx, "/scripts/")?
.filter(|f| f.to_string_lossy().to_lowercase().ends_with(".lua"));
for file in files {
let path = file.clone();
match filesystem::open(ctx, file) {
Ok(script) => {
if LuaScriptingState::load_script(&mut state, path.to_string_lossy().as_ref(), script) {
script_count += 1;
}
}
Err(err) => {
log::warn!("Error opening script {:?}: {}", path, err);
}
}
}
if script_count > 0 {
log::info!("{} Lua scripts have been loaded.", script_count);
}
}
self.state = Some(state);
Ok(())
}
}

134
src/scripting/player.rs Normal file
View File

@ -0,0 +1,134 @@
use lua_ffi::{c_int, LuaObject, State};
use lua_ffi::ffi::luaL_Reg;
use crate::inventory::Inventory;
use crate::player::Player;
use crate::scripting::REF_ERROR;
use crate::weapon::WeaponType;
pub struct LuaPlayer {
valid_reference: bool,
plr_ptr: *mut Player,
inv_ptr: *mut Inventory,
}
impl LuaPlayer {
fn check_ref(&self, state: &mut State) -> bool {
if !self.valid_reference {
state.error(REF_ERROR);
return true;
}
false
}
fn lua_get_x(&self, state: &mut State) -> c_int {
if self.check_ref(state) { return 0; }
unsafe {
state.push((*self.plr_ptr).x);
}
1
}
fn lua_get_y(&self, state: &mut State) -> c_int {
if self.check_ref(state) { return 0; }
unsafe {
state.push((*self.plr_ptr).y);
}
1
}
fn lua_get_vel_x(&self, state: &mut State) -> c_int {
if self.check_ref(state) { return 0; }
unsafe {
state.push((*self.plr_ptr).vel_x);
}
1
}
fn lua_get_vel_y(&self, state: &mut State) -> c_int {
if self.check_ref(state) { return 0; }
unsafe {
state.push((*self.plr_ptr).vel_y);
}
1
}
fn lua_set_vel_x(&self, state: &mut State) -> c_int {
if self.check_ref(state) { return 0; }
unsafe {
if let Some(vel_x) = state.to_int(2) {
(*self.plr_ptr).vel_x = vel_x;
}
}
0
}
fn lua_set_vel_y(&self, state: &mut State) -> c_int {
if self.check_ref(state) { return 0; }
unsafe {
if let Some(vel_y) = state.to_int(2) {
(*self.plr_ptr).vel_y = vel_y;
}
}
0
}
fn lua_get_weapon_ammo(&self, state: &mut State) -> c_int {
if self.check_ref(state) { return 0; }
if let Some(index) = state.to_int(2) {} else {
state.error("Weapon type must be a number");
return 0;
}
unsafe {
if let Some(weap) = (*self.inv_ptr).get_weapon_by_type_mut(WeaponType::PolarStar) {}
}
1
}
pub(crate) fn new(plr_ptr: *mut Player, inv_ptr: *mut Inventory) -> LuaPlayer {
LuaPlayer {
valid_reference: true,
plr_ptr,
inv_ptr,
}
}
}
impl Drop for LuaPlayer {
fn drop(&mut self) {
self.valid_reference = false;
}
}
impl LuaObject for LuaPlayer {
fn name() -> *const i8 {
c_str!("Player")
}
fn lua_fns() -> Vec<luaL_Reg> {
vec![
lua_method!("x", LuaPlayer, LuaPlayer::lua_get_x),
lua_method!("y", LuaPlayer, LuaPlayer::lua_get_y),
lua_method!("vel_x", LuaPlayer, LuaPlayer::lua_get_vel_x),
lua_method!("vel_y", LuaPlayer, LuaPlayer::lua_get_vel_y),
lua_method!("set_vel_x", LuaPlayer, LuaPlayer::lua_set_vel_x),
lua_method!("set_vel_y", LuaPlayer, LuaPlayer::lua_set_vel_y),
]
}
}

87
src/scripting/scene.rs Normal file
View File

@ -0,0 +1,87 @@
use lua_ffi::{c_int, LuaObject, State};
use lua_ffi::ffi::luaL_Reg;
use crate::inventory::Inventory;
use crate::player::Player;
use crate::scene::game_scene::GameScene;
use crate::scripting::LuaScriptingState;
use crate::scripting::player::LuaPlayer;
pub struct LuaGameScene {
valid_reference: bool,
ptr: *mut GameScene,
}
impl LuaGameScene {
unsafe fn lua_get_tick(&self, state: &mut State) -> c_int {
state.push((*self.ptr).tick as u32);
1
}
unsafe fn lua_get_player(&self, state: &mut State) -> c_int {
if let Some(index) = state.to_int(2) {
let (player_ref, inv_ref) = match index {
0 => (&mut (*self.ptr).player1, &mut (*self.ptr).inventory_player1),
1 => (&mut (*self.ptr).player2, &mut (*self.ptr).inventory_player2),
_ => {
state.error("Player index out of range!");
return 0;
}
};
state.push(LuaPlayer::new(player_ref as *mut Player, inv_ref as *mut Inventory));
1
} else {
state.error("Player index must be a number.");
0
}
}
pub(crate) fn new(ptr: *mut GameScene) -> LuaGameScene {
LuaGameScene {
valid_reference: true,
ptr,
}
}
}
impl Drop for LuaGameScene {
fn drop(&mut self) {
self.valid_reference = false;
}
}
impl LuaObject for LuaGameScene {
fn name() -> *const i8 {
c_str!("GameScene")
}
fn lua_fns() -> Vec<luaL_Reg> {
vec![
lua_method!("tick", LuaGameScene, LuaGameScene::lua_get_tick),
lua_method!("player", LuaGameScene, LuaGameScene::lua_get_player),
]
}
}
impl LuaScriptingState {
pub fn scene_tick(&mut self, game_scene: &mut GameScene) {
self.game_scene = game_scene as *mut GameScene;
if let Some(state) = self.state.as_mut() {
let val = LuaGameScene::new(self.game_scene);
state.get_global("doukutsu");
state.get_field(-1, "_handlers");
state.get_field(-1, "tick");
state.push(val);
if let Err((_, err)) = state.pcall(1, 0, 0) {
println!("scene_tick error: {}", err);
}
state.pop(2);
}
}
}

View File

@ -18,6 +18,8 @@ use crate::profile::GameProfile;
use crate::rng::XorShift;
use crate::scene::game_scene::GameScene;
use crate::scene::Scene;
#[cfg(feature = "scripting")]
use crate::scripting::LuaScriptingState;
use crate::settings::Settings;
use crate::shaders::Shaders;
use crate::sound::SoundManager;
@ -96,7 +98,7 @@ pub struct SharedGameState {
pub touch_controls: TouchControls,
pub base_path: String,
pub npc_table: NPCTable,
pub npc_super_pos: (isize, isize),
pub npc_super_pos: (i32, i32),
pub stages: Vec<StageData>,
pub frame_time: f64,
pub scale: f32,
@ -112,6 +114,8 @@ pub struct SharedGameState {
pub constants: EngineConstants,
pub font: BMFontRenderer,
pub texture_set: TextureSet,
#[cfg(feature = "scripting")]
pub lua: LuaScriptingState,
pub sound_manager: SoundManager,
pub settings: Settings,
pub shutdown: bool,
@ -184,6 +188,8 @@ impl SharedGameState {
constants,
font,
texture_set,
#[cfg(feature = "scripting")]
lua: LuaScriptingState::new(),
sound_manager: SoundManager::new(ctx)?,
settings,
shutdown: false,
@ -208,6 +214,9 @@ impl SharedGameState {
self.fade_state = FadeState::Hidden;
self.textscript_vm.state = TextScriptExecutionState::Running(200, 0);
#[cfg(feature = "scripting")]
self.lua.reload_scripts(ctx)?;
self.next_scene = Some(Box::new(next_scene));
Ok(())
@ -222,6 +231,9 @@ impl SharedGameState {
self.fade_state = FadeState::Hidden;
self.textscript_vm.state = TextScriptExecutionState::Running(100, 0);
#[cfg(feature = "scripting")]
self.lua.reload_scripts(ctx)?;
self.next_scene = Some(Box::new(next_scene));
Ok(())
@ -247,6 +259,9 @@ impl SharedGameState {
profile.apply(self, &mut next_scene, ctx);
#[cfg(feature = "scripting")]
self.lua.reload_scripts(ctx)?;
self.next_scene = Some(Box::new(next_scene));
return Ok(());
}
@ -292,7 +307,7 @@ impl SharedGameState {
self.carets.retain(|c| !c.is_dead());
}
pub fn create_caret(&mut self, x: isize, y: isize, ctype: CaretType, direct: Direction) {
pub fn create_caret(&mut self, x: i32, y: i32, ctype: CaretType, direct: Direction) {
self.carets.push(Caret::new(x, y, ctype, direct, &self.constants));
}

View File

@ -459,7 +459,7 @@ impl Stage {
}
pub fn tile_at(&self, x: usize, y: usize) -> u8 {
if let Some(&tile) = self.map.tiles.get(y * self.map.width + x) {
if let Some(&tile) = self.map.tiles.get(y * self.map.width as usize + x) {
tile
} else {
0
@ -468,7 +468,7 @@ impl Stage {
/// Changes map tile. Returns true if smoke should be emitted
pub fn change_tile(&mut self, x: usize, y: usize, tile_type: u8) -> bool {
if let Some(ptr) = self.map.tiles.get_mut(y * self.map.width + x) {
if let Some(ptr) = self.map.tiles.get_mut(y * self.map.width as usize + x) {
if *ptr != tile_type {
*ptr = tile_type;
return true;

View File

@ -949,8 +949,8 @@ impl TextScriptVM {
if game_scene.stage.change_tile(pos_x, pos_y, tile_type) {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
npc.x = pos_x as isize * 16 * 0x200;
npc.y = pos_y as isize * 16 * 0x200;
npc.x = pos_x as i32 * 16 * 0x200;
npc.y = pos_y as i32 * 16 * 0x200;
game_scene.npc_list.spawn(0x100, npc.clone())?;
game_scene.npc_list.spawn(0x100, npc)?;
@ -1031,8 +1031,8 @@ impl TextScriptVM {
OpCode::TRA => {
let map_id = read_cur_varint(&mut cursor)? as usize;
let event_num = read_cur_varint(&mut cursor)? as u16;
let pos_x = read_cur_varint(&mut cursor)? as isize * 16 * 0x200;
let pos_y = read_cur_varint(&mut cursor)? as isize * 16 * 0x200;
let pos_x = read_cur_varint(&mut cursor)? as i32 * 16 * 0x200;
let pos_y = read_cur_varint(&mut cursor)? as i32 * 16 * 0x200;
let mut new_scene = GameScene::new(state, ctx, map_id)?;
new_scene.intro_mode = game_scene.intro_mode;
@ -1064,8 +1064,8 @@ impl TextScriptVM {
exec_state = TextScriptExecutionState::Running(event_num, 0);
}
OpCode::MOV => {
let pos_x = read_cur_varint(&mut cursor)? as isize * 16 * 0x200;
let pos_y = read_cur_varint(&mut cursor)? as isize * 16 * 0x200;
let pos_x = read_cur_varint(&mut cursor)? as i32 * 16 * 0x200;
let pos_y = read_cur_varint(&mut cursor)? as i32 * 16 * 0x200;
for player in [&mut game_scene.player1, &mut game_scene.player2].iter_mut() {
player.vel_x = 0;
@ -1177,7 +1177,7 @@ impl TextScriptVM {
}
OpCode::FOB => {
let part_id = read_cur_varint(&mut cursor)? as u16;
let ticks = read_cur_varint(&mut cursor)? as isize;
let ticks = read_cur_varint(&mut cursor)? as i32;
game_scene.frame.wait = ticks;
game_scene.frame.update_target = UpdateTarget::Boss(part_id);
@ -1185,7 +1185,7 @@ impl TextScriptVM {
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
OpCode::FOM => {
let ticks = read_cur_varint(&mut cursor)? as isize;
let ticks = read_cur_varint(&mut cursor)? as i32;
game_scene.frame.wait = ticks;
game_scene.frame.update_target = UpdateTarget::Player;
@ -1193,7 +1193,7 @@ impl TextScriptVM {
}
OpCode::FON => {
let event_num = read_cur_varint(&mut cursor)? as u16;
let ticks = read_cur_varint(&mut cursor)? as isize;
let ticks = read_cur_varint(&mut cursor)? as i32;
game_scene.frame.wait = ticks;
for npc in game_scene.npc_list.iter() {
@ -1312,8 +1312,8 @@ impl TextScriptVM {
}
OpCode::MNP => {
let event_num = read_cur_varint(&mut cursor)? as u16;
let x = read_cur_varint(&mut cursor)? as isize;
let y = read_cur_varint(&mut cursor)? as isize;
let x = read_cur_varint(&mut cursor)? as i32;
let y = read_cur_varint(&mut cursor)? as i32;
let tsc_direction = read_cur_varint(&mut cursor)? as usize;
let direction = Direction::from_int_facing(tsc_direction).unwrap_or(Direction::Left);
@ -1341,8 +1341,8 @@ impl TextScriptVM {
}
OpCode::SNP => {
let npc_type = read_cur_varint(&mut cursor)? as u16;
let x = read_cur_varint(&mut cursor)? as isize;
let y = read_cur_varint(&mut cursor)? as isize;
let x = read_cur_varint(&mut cursor)? as i32;
let y = read_cur_varint(&mut cursor)? as i32;
let tsc_direction = read_cur_varint(&mut cursor)? as usize;
let direction = Direction::from_int_facing(tsc_direction).unwrap_or(Direction::Left);