1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-11-25 13:45:46 +00:00

more entities and stuff

This commit is contained in:
Alula 2020-09-05 04:09:52 +02:00
parent f137d84a46
commit da9bcd3249
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
8 changed files with 212 additions and 27 deletions

View file

@ -78,7 +78,20 @@ pub struct WorldConsts {
pub struct NPCConsts {
pub n016_save_point: [Rect<usize>; 8],
pub n017_health_refill: [Rect<usize>; 2],
pub n018_door_rects: [Rect<usize>; 2],
pub n018_door: [Rect<usize>; 2],
pub n020_computer: [Rect<usize>; 4],
pub n021_chest_open: Rect<usize>,
pub n022_teleporter: [Rect<usize>; 2],
pub n023_teleporter_lights: [Rect<usize>; 8],
pub n027_death_trap: Rect<usize>,
pub n029_cthulhu: [Rect<usize>; 4],
pub n030_hermit_gunsmith: [Rect<usize>; 3],
pub n032_life_capsule: [Rect<usize>; 2],
pub n034_bed: [Rect<usize>; 2],
pub n035_mannan: [Rect<usize>; 8],
pub n037_sign: [Rect<usize>; 2],
pub n038_fireplace: [Rect<usize>; 4],
pub n039_save_sign: [Rect<usize>; 2],
}
#[derive(Debug, Copy, Clone)]
@ -256,9 +269,74 @@ impl EngineConstants {
Rect { left: 288, top: 0, right: 304, bottom: 16 },
Rect { left: 304, top: 0, right: 320, bottom: 16 },
],
n018_door_rects: [
n018_door: [
Rect { left: 224, top: 16, right: 240, bottom: 40 },
Rect { left: 192, top: 112, right: 208, bottom: 136 },
],
n020_computer: [
Rect { left: 288, top: 16, right: 320, bottom: 40 }, // left
Rect { left: 288, top: 40, right: 320, bottom: 64 }, // right
Rect { left: 288, top: 40, right: 320, bottom: 64 },
Rect { left: 288, top: 64, right: 320, bottom: 88 },
],
n021_chest_open: Rect { left: 224, top: 40, right: 240, bottom: 48 },
n022_teleporter: [
Rect { left: 240, top: 16, right: 264, bottom: 48 },
Rect { left: 248, top: 152, right: 272, bottom: 184 },
],
n023_teleporter_lights: [
Rect { left: 264, top: 16, right: 288, bottom: 20 },
Rect { left: 264, top: 20, right: 288, bottom: 24 },
Rect { left: 264, top: 24, right: 288, bottom: 28 },
Rect { left: 264, top: 28, right: 288, bottom: 32 },
Rect { left: 264, top: 32, right: 288, bottom: 36 },
Rect { left: 264, top: 36, right: 288, bottom: 40 },
Rect { left: 264, top: 40, right: 288, bottom: 44 },
Rect { left: 264, top: 44, right: 288, bottom: 48 },
],
n027_death_trap: Rect { left: 96, top: 64, right: 128, bottom: 88 },
n029_cthulhu: [
Rect { left: 0, top: 192, right: 16, bottom: 216 }, // left
Rect { left: 16, top: 192, right: 32, bottom: 216 },
Rect { left: 0, top: 216, right: 16, bottom: 240 }, // right
Rect { left: 16, top: 216, right: 32, bottom: 240 },
],
n030_hermit_gunsmith: [
Rect { left: 48, top: 0, right: 64, bottom: 16 },
Rect { left: 48, top: 16, right: 64, bottom: 32 },
Rect { left: 0, top: 32, right: 16, bottom: 48 },
],
n032_life_capsule: [
Rect { left: 32, top: 96, right: 48, bottom: 112 },
Rect { left: 48, top: 96, right: 64, bottom: 112 },
],
n034_bed: [
Rect { left: 192, top: 48, right: 224, bottom: 64 },
Rect { left: 192, top: 184, right: 224, bottom: 200 },
],
n035_mannan: [
Rect { left: 96, top: 64, right: 120, bottom: 96 }, // left
Rect { left: 120, top: 64, right: 144, bottom: 96 },
Rect { left: 144, top: 64, right: 168, bottom: 96 },
Rect { left: 168, top: 64, right: 192, bottom: 96 },
Rect { left: 96, top: 96, right: 120, bottom: 128 }, // right
Rect { left: 120, top: 96, right: 144, bottom: 128 },
Rect { left: 144, top: 96, right: 168, bottom: 128 },
Rect { left: 168, top: 96, right: 192, bottom: 128 },
],
n037_sign: [
Rect { left: 192, top: 64, right: 208, bottom: 80 },
Rect { left: 208, top: 64, right: 224, bottom: 80 },
],
n038_fireplace: [
Rect { left: 128, top: 64, right: 144, bottom: 80 },
Rect { left: 144, top: 64, right: 160, bottom: 80 },
Rect { left: 160, top: 64, right: 176, bottom: 80 },
Rect { left: 176, top: 64, right: 192, bottom: 80 },
],
n039_save_sign: [
Rect { left: 224, top: 64, right: 240, bottom: 80 },
Rect { left: 240, top: 64, right: 256, bottom: 80 },
]
},
tex_sizes: hashmap! {

View file

@ -3,8 +3,8 @@ use crate::ggez::{Context, GameResult};
use crate::frame::Frame;
use crate::SharedGameState;
pub trait GameEntity {
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult;
pub trait GameEntity<C> {
fn tick(&mut self, state: &mut SharedGameState, custom: C) -> GameResult;
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, frame: &Frame) -> GameResult;
}

View file

@ -60,7 +60,7 @@ pub struct NPCData {
pub id: u16,
pub x: i16,
pub y: i16,
pub flag_id: u16,
pub flag_num: u16,
pub event_num: u16,
pub npc_type: u16,
pub flags: u16,
@ -88,7 +88,7 @@ impl NPCData {
for i in 0..count {
let x = data.read_i16::<LE>()?;
let y = data.read_i16::<LE>()?;
let flag_id = data.read_u16::<LE>()?;
let flag_num = data.read_u16::<LE>()?;
let event_num = data.read_u16::<LE>()?;
let npc_type = data.read_u16::<LE>()?;
let flags = data.read_u16::<LE>()?;
@ -100,7 +100,7 @@ impl NPCData {
id: 170 + i as u16,
x,
y,
flag_id,
flag_num,
event_num,
npc_type,
flags,

View file

@ -8,8 +8,24 @@ impl NPC {
Ok(())
}
pub(crate) fn tick_n016_save_point(&mut self, state: &mut SharedGameState) -> GameResult {
if self.action_num == 0 {
self.action_num = 1;
if self.direction == Direction::Right {
self.npc_flags.set_interactable(false);
self.vel_y = -0x200;
}
}
if self.flags.hit_bottom_wall() {
self.npc_flags.set_interactable(true);
}
self.anim_counter = (self.anim_counter + 1) % 24;
self.anim_num = self.anim_counter / 3;
self.anim_rect = state.constants.npc.n016_save_point[self.anim_num as usize];
Ok(())
}
@ -76,8 +92,8 @@ impl NPC {
match self.action_num {
0 => {
match self.direction {
Direction::Left => { self.anim_rect = state.constants.npc.n018_door_rects[0] }
Direction::Right => { self.anim_rect = state.constants.npc.n018_door_rects[1] }
Direction::Left => { self.anim_rect = state.constants.npc.n018_door[0] }
Direction::Right => { self.anim_rect = state.constants.npc.n018_door[1] }
_ => {}
}
}
@ -90,4 +106,68 @@ impl NPC {
Ok(())
}
pub(crate) fn tick_n020_computer(&mut self, state: &mut SharedGameState) -> GameResult {
Ok(())
}
pub(crate) fn tick_n027_death_trap(&mut self, state: &mut SharedGameState) -> GameResult {
if self.action_num == 0 {
self.action_num = 1;
self.anim_rect = state.constants.npc.n027_death_trap;
}
Ok(())
}
pub(crate) fn tick_n032_life_capsule(&mut self, state: &mut SharedGameState) -> GameResult {
self.anim_counter = (self.anim_counter + 1) % 4;
self.anim_num = self.anim_counter / 2;
self.anim_rect = state.constants.npc.n032_life_capsule[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n034_bed(&mut self, state: &mut SharedGameState) -> GameResult {
match self.direction {
Direction::Left => { self.anim_rect = state.constants.npc.n034_bed[0] }
Direction::Right => { self.anim_rect = state.constants.npc.n034_bed[1] }
_ => {}
}
Ok(())
}
pub(crate) fn tick_n037_sign(&mut self, state: &mut SharedGameState) -> GameResult {
self.anim_counter = (self.anim_counter + 1) % 4;
self.anim_num = self.anim_counter / 2;
self.anim_rect = state.constants.npc.n037_sign[self.anim_num as usize];
Ok(())
}
pub(crate) fn tick_n038_fireplace(&mut self, state: &mut SharedGameState) -> GameResult {
match self.action_num {
0 => {
self.anim_counter = (self.anim_counter + 1) % 16;
self.anim_num = self.anim_counter / 4;
self.anim_rect = state.constants.npc.n038_fireplace[self.anim_num as usize];
}
10 => {}
11 => {}
_ => {}
}
Ok(())
}
pub(crate) fn tick_n039_save_sign(&mut self, state: &mut SharedGameState) -> GameResult {
match self.direction {
Direction::Left => { self.anim_rect = state.constants.npc.n039_save_sign[0] }
Direction::Right => { self.anim_rect = state.constants.npc.n039_save_sign[1] }
_ => {}
}
Ok(())
}
}

View file

@ -2,6 +2,7 @@ use std::collections::{BTreeSet, HashMap};
use std::io;
use std::io::Cursor;
use bitvec::vec::BitVec;
use byteorder::{LE, ReadBytesExt};
use crate::{bitfield, SharedGameState};
@ -12,6 +13,7 @@ use crate::entity::GameEntity;
use crate::frame::Frame;
use crate::ggez::{Context, GameResult};
use crate::map::NPCData;
use crate::player::Player;
use crate::str;
pub mod misc;
@ -58,20 +60,28 @@ pub struct NPC {
pub hit_bounds: Rect<usize>,
pub action_num: u16,
pub anim_num: u16,
pub flag_num: u16,
pub event_num: u16,
pub action_counter: u16,
pub anim_counter: u16,
pub anim_rect: Rect<usize>,
}
impl GameEntity for NPC {
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
impl GameEntity<&mut Player> for NPC {
fn tick(&mut self, state: &mut SharedGameState, scene: &mut Player) -> GameResult {
// maybe use macros?
match self.npc_type {
0 => { NPC::tick_n000_null(self, state) }
16 => { NPC::tick_n016_save_point(self, state) }
17 => { NPC::tick_n017_health_refill(self, state) }
18 => { NPC::tick_n018_door(self, state) }
20 => { NPC::tick_n020_computer(self, state) }
27 => { NPC::tick_n027_death_trap(self, state) }
32 => { NPC::tick_n032_life_capsule(self, state) }
34 => { NPC::tick_n034_bed(self, state) }
37 => { NPC::tick_n037_sign(self, state) }
38 => { NPC::tick_n038_fireplace(self, state) }
39 => { NPC::tick_n039_save_sign(self, state) }
_ => { Ok(()) }
}
}
@ -132,10 +142,11 @@ impl NPCMap {
target_y: 0,
action_num: 0,
anim_num: 0,
flag_num: data.flag_num,
event_num: data.event_num,
life: table.get_life(data.npc_type),
cond: Condition(0x00),
flags: Flag(data.flag_id as u32),
flags: Flag(data.flag_num as u32),
npc_flags: NPCFlag(data.flags),
direction: Direction::Left,
display_bounds: table.get_display_bounds(data.npc_type),
@ -151,6 +162,15 @@ impl NPCMap {
self.npcs.get_mut(&data.id).unwrap()
}
pub fn remove_by_event(&mut self, event_num: u16, game_flags: &mut BitVec) {
for npc in self.npcs.values_mut() {
if npc.event_num == event_num {
npc.cond.set_alive(false);
game_flags.set(npc.flag_num as usize, true);
}
}
}
}
pub struct NPCTableEntry {
@ -275,7 +295,6 @@ impl NPCTable {
}
}
pub fn get_hit_bounds(&self, npc_type: u16) -> Rect<usize> {
if let Some(npc) = self.entries.get(npc_type as usize) {
Rect {

View file

@ -501,8 +501,8 @@ impl Player {
}
}
impl GameEntity for Player {
fn tick(&mut self, state: &mut SharedGameState, _ctx: &mut Context) -> GameResult {
impl GameEntity<()> for Player {
fn tick(&mut self, state: &mut SharedGameState, _cust: ()) -> GameResult {
if !self.cond.alive() {
return Ok(());
}

View file

@ -417,11 +417,11 @@ impl Scene for GameScene {
for npc_data in npcs.iter() {
let npc = self.npc_map.create_npc_from_data(&state.npc_table, npc_data);
if npc.npc_flags.appear_when_flag_set() {
if let Some(true) = state.game_flags.get(npc_data.flag_id as usize) {
if let Some(true) = state.game_flags.get(npc_data.flag_num as usize) {
npc.cond.set_alive(true);
}
} else if npc.npc_flags.hide_unless_flag_set() {
if let Some(false) = state.game_flags.get(npc_data.flag_id as usize) {
if let Some(false) = state.game_flags.get(npc_data.flag_num as usize) {
npc.cond.set_alive(true);
}
} else {
@ -461,20 +461,20 @@ impl Scene for GameScene {
}
if state.control_flags.flag_x01() {
self.player.tick(state, ctx)?;
self.player.tick(state, ())?;
self.player.flags.0 = 0;
state.tick_carets();
self.player.tick_map_collisions(state, &self.stage);
self.player.tick_npc_collisions(state, &mut self.npc_map);
self.frame.update(state, &self.player, &self.stage);
}
for npc_id in self.npc_map.npc_ids.iter() {
if let Some(npc) = self.npc_map.npcs.get_mut(npc_id) {
npc.tick(state, ctx)?;
for npc_id in self.npc_map.npc_ids.iter() {
if let Some(npc) = self.npc_map.npcs.get_mut(npc_id) {
npc.tick(state, &mut self.player)?;
}
}
self.frame.update(state, &self.player, &self.stage);
}
if state.control_flags.control_enabled() {
@ -518,6 +518,7 @@ impl Scene for GameScene {
self.draw_hud(state, ctx)?;
}
self.draw_fade(state, ctx)?;
if self.map_name_counter > 0 {
let width = state.font.text_width(self.stage.data.name.chars(), &state.constants);
state.font.draw_text(self.stage.data.name.chars(),
@ -525,7 +526,6 @@ impl Scene for GameScene {
&state.constants, &mut state.texture_set, ctx)?;
}
self.draw_fade(state, ctx)?;
self.draw_text_boxes(state, ctx)?;
self.draw_number(state.canvas_size.0 - 8.0, 8.0, timer::fps(ctx) as usize, Alignment::Right, state, ctx)?;

View file

@ -552,6 +552,7 @@ impl TextScriptVM {
}
OpCode::MLp => {
let life = read_cur_varint(&mut cursor)? as usize;
game_scene.player.life += life;
game_scene.player.max_life += life;
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
@ -664,6 +665,13 @@ impl TextScriptVM {
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
OpCode::DNP => {
let event_num = read_cur_varint(&mut cursor)? as u16;
game_scene.npc_map.remove_by_event(event_num, &mut state.game_flags);
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
// unimplemented opcodes
// Zero operands
OpCode::AEp | OpCode::CAT | OpCode::CIL | OpCode::CPS |
@ -677,7 +685,7 @@ impl TextScriptVM {
}
// One operand codes
OpCode::BOA | OpCode::BSL | OpCode::FOB | OpCode::FOM | OpCode::UNI |
OpCode::MYB | OpCode::GIT | OpCode::NUM | OpCode::DNA | OpCode::DNP |
OpCode::MYB | OpCode::GIT | OpCode::NUM | OpCode::DNA |
OpCode::MPp | OpCode::SKm | OpCode::SKp | OpCode::EQp | OpCode::EQm |
OpCode::ITp | OpCode::ITm | OpCode::AMm | OpCode::UNJ | OpCode::MPJ | OpCode::YNJ |
OpCode::XX1 | OpCode::SIL | OpCode::LIp | OpCode::SOU |