1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-12-02 09:30:20 +00:00

teleporter slot menu

This commit is contained in:
Alula 2020-09-29 22:19:47 +02:00
parent 69331f6925
commit ebe070b77e
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
4 changed files with 262 additions and 104 deletions

View file

@ -212,6 +212,8 @@ pub struct TextScriptConsts {
pub get_item_top_right: Rect<usize>,
pub get_item_right: Rect<usize>,
pub get_item_bottom_right: Rect<usize>,
pub stage_select_text: Rect<usize>,
pub cursor: [Rect<usize>; 2],
}
@ -1177,6 +1179,11 @@ impl EngineConstants {
get_item_top_right: Rect { left: 240, top: 0, right: 244, bottom: 8 },
get_item_right: Rect { left: 240, top: 8, right: 244, bottom: 16 },
get_item_bottom_right: Rect { left: 240, top: 16, right: 244, bottom: 24 },
stage_select_text: Rect { left: 80, top: 64, right: 144, bottom: 72 },
cursor: [
Rect { left: 80, top: 88, right: 112, bottom: 104 },
Rect { left: 80, top: 104, right: 112, bottom: 120 },
],
},
title: TitleConsts {
logo_rect: Rect { left: 0, top: 0, right: 144, bottom: 40 },

View file

@ -12,13 +12,14 @@ use crate::inventory::{Inventory, TakeExperienceResult};
use crate::npc::NPCMap;
use crate::physics::PhysicalEntity;
use crate::player::Player;
use crate::rng::RNG;
use crate::scene::Scene;
use crate::shared_game_state::SharedGameState;
use crate::stage::{BackgroundType, Stage};
use crate::text_script::{ConfirmSelection, TextScriptExecutionState, TextScriptVM};
use crate::text_script::{ConfirmSelection, ScriptMode, TextScriptExecutionState, TextScriptVM};
use crate::texture_set::SizedBatch;
use crate::ui::Components;
use crate::weapon::WeaponType;
use itertools::Itertools;
pub struct GameScene {
pub tick: usize,
@ -29,11 +30,13 @@ pub struct GameScene {
pub stage_id: usize,
pub npc_map: NPCMap,
pub bullet_manager: BulletManager,
pub current_teleport_slot: u8,
tex_background_name: String,
tex_tileset_name: String,
life_bar: u16,
life_bar_counter: u16,
map_name_counter: u16,
stage_select_text_y_pos: usize,
weapon_x_pos: isize,
}
@ -81,6 +84,8 @@ impl GameScene {
life_bar: 0,
life_bar_counter: 0,
map_name_counter: 0,
stage_select_text_y_pos: 54,
current_teleport_slot: 0,
weapon_x_pos: 16,
})
}
@ -740,7 +745,7 @@ impl GameScene {
Ok(())
}
pub fn tick_npc_bullet_collissions(&mut self, state: &mut SharedGameState) {
fn tick_npc_bullet_collissions(&mut self, state: &mut SharedGameState) {
let mut dead_npcs = Vec::new();
for npc_id in self.npc_map.npc_ids.iter() {
@ -830,49 +835,11 @@ impl GameScene {
self.npc_map.garbage_collect();
}
}
}
impl Scene for GameScene {
fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
state.textscript_vm.set_scene_script(self.stage.load_text_script(&state.base_path, &state.constants, ctx)?);
state.textscript_vm.suspend = false;
fn tick_world(&mut self, state: &mut SharedGameState) -> GameResult {
self.stage_select_text_y_pos = 54;
self.current_teleport_slot = 0;
let npcs = self.stage.load_npcs(&state.base_path, ctx)?;
for npc_data in npcs.iter() {
log::info!("creating npc: {:?}", npc_data);
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_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_num as usize) {
npc.cond.set_alive(true);
}
} else {
npc.cond.set_alive(true);
}
}
state.npc_table.tileset_name = self.tex_tileset_name.to_owned();
state.npc_table.tex_npc1_name = ["Npc/", &self.stage.data.npc1.filename()].join("");
state.npc_table.tex_npc2_name = ["Npc/", &self.stage.data.npc2.filename()].join("");
self.player.target_x = self.player.x;
self.player.target_y = self.player.y;
self.frame.immediate_update(state, &self.player, &self.stage);
// self.inventory.add_weapon(WeaponType::PolarStar, 0);
// self.inventory.add_xp(120, state);
// self.player.equip.set_booster_2_0(true);
Ok(())
}
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
state.update_key_trigger();
if state.control_flags.tick_world() {
self.player.current_weapon = {
if let Some(weapon) = self.inventory.get_current_weapon_mut() {
weapon.wtype as u8
@ -929,7 +896,6 @@ impl Scene for GameScene {
self.bullet_manager.tick_bullets(state, &self.player, &mut self.stage);
self.frame.update(state, &self.player, &self.stage);
}
if state.control_flags.control_enabled() {
if let Some(weapon) = self.inventory.get_current_weapon_mut() {
@ -963,6 +929,144 @@ impl Scene for GameScene {
}
}
Ok(())
}
fn tick_stage_select(&mut self, state: &mut SharedGameState) -> GameResult {
let slot_count = state.teleporter_slots.iter()
.filter(|&&(index, _event_num)| index != 0)
.count();
if self.stage_select_text_y_pos > 46 {
self.stage_select_text_y_pos -= 1;
}
if state.key_trigger.left() {
if self.current_teleport_slot == 0 {
self.current_teleport_slot = slot_count.saturating_sub(1) as u8;
} else {
self.current_teleport_slot -= 1;
}
} else if state.key_trigger.right() {
if self.current_teleport_slot == slot_count.saturating_sub(1) as u8 {
self.current_teleport_slot = 0;
} else {
self.current_teleport_slot += 1;
}
}
if state.key_trigger.left() || state.key_trigger.right() {
state.sound_manager.play_sfx(1);
if let Some(&(index, _event_num)) = state.teleporter_slots.get(self.current_teleport_slot as usize) {
state.textscript_vm.start_script(1000 + index);
} else {
state.textscript_vm.start_script(1000);
}
}
if state.key_trigger.jump() | state.key_trigger.fire() {
state.textscript_vm.set_mode(ScriptMode::Map);
state.control_flags.set_tick_world(true);
state.control_flags.set_control_enabled(true);
state.control_flags.set_interactions_disabled(false);
if state.key_trigger.jump() {
if let Some(&(_index, event_num)) = state.teleporter_slots.get(self.current_teleport_slot as usize) {
state.textscript_vm.start_script(event_num);
}
}
}
Ok(())
}
fn draw_stage_select(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "StageImage")?;
let slot_count = state.teleporter_slots.iter()
.filter(|&&(index, _event_num)| index != 0)
.count();
let slot_offset = ((state.canvas_size.0 - 40.0 * slot_count as f32) / 2.0).floor();
let mut slot_rect = Rect::new(0,0,0,0);
for i in 0..slot_count {
let index = state.teleporter_slots[i].0;
slot_rect.left = 32 * (index as usize % 8);
slot_rect.top = 16 * (index as usize / 8);
slot_rect.right = slot_rect.left + 32;
slot_rect.bottom = slot_rect.top + 16;
batch.add_rect(slot_offset + i as f32 * 40.0, 64.0, &slot_rect);
}
batch.draw(ctx)?;
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;
batch.add_rect(128.0, self.stage_select_text_y_pos as f32, &state.constants.textscript.stage_select_text);
if slot_count > 0 {
batch.add_rect(slot_offset + self.current_teleport_slot as f32 * 40.0, 64.0, &state.constants.textscript.cursor[self.tick / 2 % 2]);
}
batch.draw(ctx)?;
Ok(())
}
}
impl Scene for GameScene {
fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
let seed = (self.player.max_life as i32)
.wrapping_add(self.player.x as i32)
.wrapping_add(self.player.y as i32)
.wrapping_add(self.stage_id as i32)
.wrapping_mul(7);
state.game_rng = RNG::new(seed);
state.textscript_vm.set_scene_script(self.stage.load_text_script(&state.base_path, &state.constants, ctx)?);
state.textscript_vm.suspend = false;
let npcs = self.stage.load_npcs(&state.base_path, ctx)?;
for npc_data in npcs.iter() {
log::info!("creating npc: {:?}", npc_data);
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_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_num as usize) {
npc.cond.set_alive(true);
}
} else {
npc.cond.set_alive(true);
}
}
state.npc_table.tileset_name = self.tex_tileset_name.to_owned();
state.npc_table.tex_npc1_name = ["Npc/", &self.stage.data.npc1.filename()].join("");
state.npc_table.tex_npc2_name = ["Npc/", &self.stage.data.npc2.filename()].join("");
self.player.target_x = self.player.x;
self.player.target_y = self.player.y;
self.frame.immediate_update(state, &self.player, &self.stage);
// self.inventory.add_weapon(WeaponType::PolarStar, 0);
// self.inventory.add_xp(120, state);
// self.player.equip.set_booster_2_0(true);
Ok(())
}
fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
state.update_key_trigger();
match state.textscript_vm.mode {
ScriptMode::Map if state.control_flags.tick_world() => self.tick_world(state)?,
ScriptMode::StageSelect => self.tick_stage_select(state)?,
_ => {}
}
if self.map_name_counter > 0 {
self.map_name_counter -= 1;
}
@ -1017,6 +1121,10 @@ impl Scene for GameScene {
self.draw_hud(state, ctx)?;
}
if state.textscript_vm.mode == ScriptMode::StageSelect {
self.draw_stage_select(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);

View file

@ -16,7 +16,7 @@ use crate::scene::Scene;
use crate::sound::SoundManager;
use crate::stage::StageData;
use crate::str;
use crate::text_script::{TextScriptExecutionState, TextScriptVM};
use crate::text_script::{TextScriptExecutionState, TextScriptVM, ScriptMode};
use crate::texture_set::TextureSet;
use crate::ggez::graphics::Canvas;
@ -52,6 +52,7 @@ pub struct SharedGameState {
pub game_rng: RNG,
pub effect_rng: RNG,
pub quake_counter: u16,
pub teleporter_slots: Vec<(u16, u16)>,
pub carets: Vec<Caret>,
pub key_state: KeyState,
pub key_trigger: KeyState,
@ -109,6 +110,7 @@ impl SharedGameState {
game_rng: RNG::new(0),
effect_rng: RNG::new(Instant::now().elapsed().as_nanos() as i32),
quake_counter: 0,
teleporter_slots: Vec::with_capacity(8),
carets: Vec::with_capacity(32),
key_state: KeyState(0),
key_trigger: KeyState(0),
@ -177,13 +179,14 @@ impl SharedGameState {
self.game_flags = bitvec::bitvec![0; 8000];
self.fade_state = FadeState::Hidden;
self.game_rng = RNG::new(0);
self.teleporter_slots.clear();
self.quake_counter = 0;
self.carets.clear();
self.key_state.0 = 0;
self.key_trigger.0 = 0;
self.key_old = 0;
self.new_npcs.clear();
self.textscript_vm.reset();
self.textscript_vm.set_mode(ScriptMode::Map);
self.textscript_vm.suspend = true;
}

View file

@ -390,12 +390,26 @@ pub struct Scripts {
}
impl Scripts {
pub fn find_script(&self, event_num: u16) -> Option<&Vec<u8>> {
pub fn find_script(&self, mode: ScriptMode, event_num: u16) -> Option<&Vec<u8>> {
match mode {
ScriptMode::Map => {
if let Some(tsc) = self.scene_script.event_map.get(&event_num) {
return Some(tsc);
} else if let Some(tsc) = self.global_script.event_map.get(&event_num) {
return Some(tsc);
}
}
ScriptMode::Inventory => {
if let Some(tsc) = self.inventory_script.event_map.get(&event_num) {
return Some(tsc);
}
}
ScriptMode::StageSelect => {
if let Some(tsc) = self.stage_select_script.event_map.get(&event_num) {
return Some(tsc);
}
}
}
None
}
@ -473,7 +487,10 @@ impl TextScriptVM {
self.line_3.clear();
}
pub fn set_mode(&mut self, mode: ScriptMode) {}
pub fn set_mode(&mut self, mode: ScriptMode) {
self.reset();
self.mode = mode;
}
pub fn start_script(&mut self, event_num: u16) {
self.reset();
@ -505,7 +522,7 @@ impl TextScriptVM {
break;
}
if let Some(bytecode) = state.textscript_vm.scripts.find_script(event) {
if let Some(bytecode) = state.textscript_vm.scripts.find_script(state.textscript_vm.mode, event) {
let mut cursor = Cursor::new(bytecode);
cursor.seek(SeekFrom::Start(ip as u64))?;
@ -613,7 +630,7 @@ impl TextScriptVM {
let mut exec_state = state.textscript_vm.state;
let mut tick_npc = 0u16;
if let Some(bytecode) = state.textscript_vm.scripts.find_script(event) {
if let Some(bytecode) = state.textscript_vm.scripts.find_script(state.textscript_vm.mode, event) {
let mut cursor = Cursor::new(bytecode);
cursor.seek(SeekFrom::Start(ip as u64))?;
@ -656,6 +673,29 @@ impl TextScriptVM {
exec_state = TextScriptExecutionState::Ended;
}
OpCode::SLP => {
state.textscript_vm.set_mode(ScriptMode::StageSelect);
let event_num = if let Some(slot) = state.teleporter_slots.get(game_scene.current_teleport_slot as usize) {
1000 + slot.0
} else {
1000
};
exec_state = TextScriptExecutionState::Running(event_num, 0);
}
OpCode::PSp => {
let index = read_cur_varint(&mut cursor)? as u16;
let event_num = read_cur_varint(&mut cursor)? as u16;
if let Some(slot) = state.teleporter_slots.iter_mut().find(|s| s.0 == index) {
slot.1 = event_num;
} else {
state.teleporter_slots.push((index, event_num));
}
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
OpCode::PRI => {
state.control_flags.set_tick_world(false);
state.control_flags.set_control_enabled(false);
@ -1250,7 +1290,7 @@ impl TextScriptVM {
// Zero operands
OpCode::CAT | OpCode::CIL | OpCode::CPS | OpCode::KE2 |
OpCode::CRE | OpCode::CSS | OpCode::FLA | OpCode::MLP |
OpCode::SAT | OpCode::SLP | OpCode::SPS | OpCode::FR2 |
OpCode::SAT | OpCode::SPS | OpCode::FR2 |
OpCode::STC | OpCode::SVP | OpCode::TUR | OpCode::HM2 => {
log::warn!("unimplemented opcode: {:?}", op);
@ -1268,7 +1308,7 @@ impl TextScriptVM {
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
// Two operand codes
OpCode::SKJ | OpCode::SMP | OpCode::PSp => {
OpCode::SKJ | OpCode::SMP => {
let par_a = read_cur_varint(&mut cursor)?;
let par_b = read_cur_varint(&mut cursor)?;