1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-12-01 17:00:43 +00:00

complete the xp and weapon leveling system

This commit is contained in:
Alula 2020-09-13 01:45:36 +02:00
parent 84ac596d50
commit aee221107e
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
6 changed files with 144 additions and 29 deletions

View file

@ -119,6 +119,7 @@ pub struct BulletRects {
pub struct WeaponConsts { pub struct WeaponConsts {
pub bullet_table: Vec<BulletData>, pub bullet_table: Vec<BulletData>,
pub bullet_rects: BulletRects, pub bullet_rects: BulletRects,
pub level_table: [[u16; 3]; 14],
} }
impl Clone for WeaponConsts { impl Clone for WeaponConsts {
@ -126,6 +127,7 @@ impl Clone for WeaponConsts {
WeaponConsts { WeaponConsts {
bullet_table: self.bullet_table.clone(), bullet_table: self.bullet_table.clone(),
bullet_rects: self.bullet_rects, bullet_rects: self.bullet_rects,
level_table: self.level_table,
} }
} }
} }
@ -935,6 +937,22 @@ impl EngineConstants {
Rect { left: 256, top: 32, right: 264, bottom: 40 }, Rect { left: 256, top: 32, right: 264, bottom: 40 },
], ],
}, },
level_table: [
[0, 0, 100],
[30, 40, 16],
[10, 20, 10],
[10, 20, 20],
[30, 40, 10],
[10, 20, 10],
[10, 20, 30],
[10, 20, 5],
[10, 20, 100],
[30, 60, 0],
[30, 60, 10],
[10, 20, 100],
[1, 1, 1],
[40, 60, 200],
],
}, },
tex_sizes: case_insensitive_hashmap! { tex_sizes: case_insensitive_hashmap! {
"ArmsImage" => (256, 16), "ArmsImage" => (256, 16),

View file

@ -1,3 +1,5 @@
use crate::engine_constants::EngineConstants;
use crate::SharedGameState;
use crate::weapon::{Weapon, WeaponLevel, WeaponType}; use crate::weapon::{Weapon, WeaponLevel, WeaponType};
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -11,6 +13,13 @@ pub struct Inventory {
weapons: Vec<Weapon>, weapons: Vec<Weapon>,
} }
#[derive(Clone, Copy)]
pub enum AddExperienceResult {
None,
LevelUp,
AddStar,
}
impl Inventory { impl Inventory {
#[allow(clippy::new_without_default)] #[allow(clippy::new_without_default)]
pub fn new() -> Inventory { pub fn new() -> Inventory {
@ -56,6 +65,10 @@ impl Inventory {
self.weapons.get(idx) self.weapons.get(idx)
} }
pub fn get_current_weapon(&self) -> Option<&Weapon> {
self.weapons.get(self.current_weapon as usize)
}
pub fn get_current_weapon_mut(&mut self) -> Option<&mut Weapon> { pub fn get_current_weapon_mut(&mut self) -> Option<&mut Weapon> {
self.weapons.get_mut(self.current_weapon as usize) self.weapons.get_mut(self.current_weapon as usize)
} }
@ -73,6 +86,50 @@ impl Inventory {
} }
} }
pub fn add_xp(&mut self, exp: u16, state: &mut SharedGameState) -> AddExperienceResult {
let mut result = AddExperienceResult::None;
if let Some(weapon) = self.get_current_weapon_mut() {
let curr_level_idx = weapon.level as usize - 1;
let lvl_table = state.constants.weapon.level_table[weapon.wtype as usize];
weapon.experience += exp;
if weapon.level == WeaponLevel::Level3 {
if weapon.experience > lvl_table[2] {
weapon.experience = lvl_table[2];
result = AddExperienceResult::AddStar;
}
} else {
if weapon.experience > lvl_table[curr_level_idx] {
weapon.level = weapon.level.next();
weapon.experience = 0;
if weapon.wtype != WeaponType::Spur {
result = AddExperienceResult::LevelUp;
}
}
}
}
result
}
pub fn get_current_max_exp(&self, constants: &EngineConstants) -> (u16, u16) {
if let Some(weapon) = self.weapons.get(self.current_weapon as usize) {
if weapon.level == WeaponLevel::None {
return (0, 0);
}
let level_idx = weapon.level as usize - 1;
let max_exp = constants.weapon.level_table[weapon.wtype as usize][level_idx];
(weapon.experience, max_exp)
} else {
(0, 0)
}
}
pub fn get_current_ammo(&self) -> (u16, u16) { pub fn get_current_ammo(&self) -> (u16, u16) {
if let Some(weapon) = self.weapons.get(self.current_weapon as usize) { if let Some(weapon) = self.weapons.get(self.current_weapon as usize) {
(weapon.ammo, weapon.max_ammo) (weapon.ammo, weapon.max_ammo)

View file

@ -43,12 +43,12 @@ pub struct Player {
pub shock_counter: u8, pub shock_counter: u8,
pub current_weapon: u8, pub current_weapon: u8,
pub update_target: bool, pub update_target: bool,
pub stars: u8,
weapon_offset_y: i8, weapon_offset_y: i8,
index_x: isize, index_x: isize,
index_y: isize, index_y: isize,
sprash: bool, splash: bool,
booster_switch: u8, booster_switch: u8,
star: u8,
bubble: u8, bubble: u8,
exp_wait: isize, exp_wait: isize,
exp_count: isize, exp_count: isize,
@ -82,7 +82,7 @@ impl Player {
booster_fuel: 0, booster_fuel: 0,
index_x: 0, index_x: 0,
index_y: 0, index_y: 0,
sprash: false, splash: false,
update_target: true, update_target: true,
up: false, up: false,
down: false, down: false,
@ -90,7 +90,7 @@ impl Player {
weapon_offset_y: 0, weapon_offset_y: 0,
shock_counter: 0, shock_counter: 0,
booster_switch: 0, booster_switch: 0,
star: 0, stars: 0,
bubble: 0, bubble: 0,
exp_wait: 0, exp_wait: 0,
exp_count: 0, exp_count: 0,
@ -357,7 +357,7 @@ impl Player {
// todo: water splashing // todo: water splashing
if !self.flags.in_water() { if !self.flags.in_water() {
self.sprash = false; self.splash = false;
} }
// spike damage // spike damage
@ -528,8 +528,8 @@ impl Player {
self.life = if hp >= self.life as isize { 0 } else { (self.life as isize - hp) as usize }; self.life = if hp >= self.life as isize { 0 } else { (self.life as isize - hp) as usize };
if self.equip.has_whimsical_star() && self.star > 0 { if self.equip.has_whimsical_star() && self.stars > 0 {
self.star -= 1; self.stars -= 1;
} }
if self.life == 0 { if self.life == 0 {

View file

@ -8,6 +8,7 @@ use crate::player::Player;
use crate::SharedGameState; use crate::SharedGameState;
use crate::stage::Stage; use crate::stage::Stage;
use std::borrow::Borrow; use std::borrow::Borrow;
use crate::inventory::{Inventory, AddExperienceResult};
impl PhysicalEntity for Player { impl PhysicalEntity for Player {
#[inline(always)] #[inline(always)]
@ -152,10 +153,10 @@ impl Player {
flags flags
} }
pub fn tick_npc_collisions(&mut self, state: &mut SharedGameState, npc_map: &mut NPCMap) { pub fn tick_npc_collisions(&mut self, state: &mut SharedGameState, npc_map: &mut NPCMap, inventory: &mut Inventory) {
for npc_id in npc_map.npc_ids.iter() { for npc_id in npc_map.npc_ids.iter() {
if let Some(npc_cell) = npc_map.npcs.get(npc_id) { if let Some(npc_cell) = npc_map.npcs.get(npc_id) {
let npc = npc_cell.borrow_mut(); let mut npc = npc_cell.borrow_mut();
if !npc.cond.alive() { continue; } if !npc.cond.alive() { continue; }
let mut flags = Flag(0); let mut flags = Flag(0);
@ -169,6 +170,23 @@ impl Player {
flags = self.judge_hit_npc_non_solid(npc.borrow()); flags = self.judge_hit_npc_non_solid(npc.borrow());
} }
// xp pickup
if flags.0 != 0 && npc.npc_type == 1 {
match inventory.add_xp(npc.exp, state) {
AddExperienceResult::None => {},
AddExperienceResult::LevelUp => {
// todo play sound 27
state.create_caret(self.x, self.y, CaretType::LevelUp, Direction::Left);
},
AddExperienceResult::AddStar => {
if self.equip.has_whimsical_star() && self.stars < 3 {
self.stars += 1;
}
},
}
npc.cond.set_alive(false);
}
if npc.npc_flags.interactable() && !state.control_flags.interactions_disabled() && flags.0 != 0 && self.cond.interacted() { if npc.npc_flags.interactable() && !state.control_flags.interactions_disabled() && flags.0 != 0 && self.cond.interacted() {
state.textscript_vm.start_script(npc.event_num); state.textscript_vm.start_script(npc.event_num);
self.cond.set_interacted(false); self.cond.set_interacted(false);

View file

@ -103,6 +103,7 @@ impl GameScene {
// none // none
let weap_x = self.weapon_x_pos as f32; let weap_x = self.weapon_x_pos as f32;
let (ammo, max_ammo) = self.inventory.get_current_ammo(); let (ammo, max_ammo) = self.inventory.get_current_ammo();
let (xp, max_xp) = self.inventory.get_current_max_exp(&state.constants);
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?; let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;
if max_ammo == 0 { if max_ammo == 0 {
@ -113,17 +114,28 @@ impl GameScene {
} }
// per // per
batch.add_rect(16.0 + 32.0, 24.0, batch.add_rect(weap_x + 32.0, 24.0,
&Rect::<usize>::new_size(72, 48, 8, 8)); &Rect::<usize>::new_size(72, 48, 8, 8));
// lv // lv
batch.add_rect(16.0, 32.0, batch.add_rect(weap_x, 32.0,
&Rect::<usize>::new_size(80, 80, 16, 8)); &Rect::<usize>::new_size(80, 80, 16, 8));
// xp box // xp box
batch.add_rect(40.0, 32.0, batch.add_rect(weap_x + 24.0, 32.0,
&Rect::<usize>::new_size(0, 72, 40, 8)); &Rect::<usize>::new_size(0, 72, 40, 8));
// experience if max_xp > 0 {
//batch.add_rect(40.0, 32.0, if xp == max_xp {
// &Rect::<usize>::new_size(0, 80, (40), 8)); // todo // max level bar
batch.add_rect(weap_x + 24.0, 32.0,
&Rect::<usize>::new_size(40, 72, 40, 8));
} else {
// xp bar
let bar_width = (xp as f32 / max_xp as f32 * 40.0) as usize;
batch.add_rect(weap_x + 24.0, 32.0,
&Rect::<usize>::new_size(0, 80, bar_width, 8));
}
}
if self.player.max_life != 0 { if self.player.max_life != 0 {
// life box // life box
@ -174,7 +186,7 @@ impl GameScene {
self.draw_number(weap_x + 64.0, 16.0, ammo as usize, Alignment::Right, state, ctx)?; self.draw_number(weap_x + 64.0, 16.0, ammo as usize, Alignment::Right, state, ctx)?;
self.draw_number(weap_x + 64.0, 24.0, max_ammo as usize, Alignment::Right, state, ctx)?; self.draw_number(weap_x + 64.0, 24.0, max_ammo as usize, Alignment::Right, state, ctx)?;
} }
self.draw_number(40.0, 32.0, self.inventory.get_current_level() as usize, Alignment::Right, state, ctx)?; self.draw_number(weap_x + 24.0, 32.0, self.inventory.get_current_level() as usize, Alignment::Right, state, ctx)?;
self.draw_number(40.0, 40.0, self.life_bar as usize, Alignment::Right, state, ctx)?; self.draw_number(40.0, 40.0, self.life_bar as usize, Alignment::Right, state, ctx)?;
Ok(()) Ok(())
@ -486,7 +498,6 @@ impl GameScene {
let text_offset = if state.textscript_vm.face == 0 { 0.0 } else { 56.0 }; let text_offset = if state.textscript_vm.face == 0 { 0.0 } else { 56.0 };
// todo: proper text rendering
if !state.textscript_vm.line_1.is_empty() { if !state.textscript_vm.line_1.is_empty() {
state.font.draw_text(state.textscript_vm.line_1.iter().copied(), left_pos + text_offset + 14.0, top_pos + 10.0, &state.constants, &mut state.texture_set, ctx)?; state.font.draw_text(state.textscript_vm.line_1.iter().copied(), left_pos + text_offset + 14.0, top_pos + 10.0, &state.constants, &mut state.texture_set, ctx)?;
} }
@ -683,8 +694,8 @@ impl Scene for GameScene {
self.player.target_y = self.player.y; self.player.target_y = self.player.y;
self.frame.immediate_update(state, &self.player, &self.stage); self.frame.immediate_update(state, &self.player, &self.stage);
self.inventory.add_weapon(WeaponType::PolarStar, 0); //self.inventory.add_weapon(WeaponType::PolarStar, 0);
self.player.equip.set_booster_2_0(true); //self.player.equip.set_booster_2_0(true);
Ok(()) Ok(())
} }
@ -706,7 +717,7 @@ impl Scene for GameScene {
self.bullet_manager.tick_bullets(state, &self.player, &mut self.stage); self.bullet_manager.tick_bullets(state, &self.player, &mut self.stage);
self.player.tick_map_collisions(state, &mut self.stage); self.player.tick_map_collisions(state, &mut self.stage);
self.player.tick_npc_collisions(state, &mut self.npc_map); self.player.tick_npc_collisions(state, &mut self.npc_map, &mut self.inventory);
for npc_id in self.npc_map.npc_ids.iter() { for npc_id in self.npc_map.npc_ids.iter() {
if let Some(npc_cell) = self.npc_map.npcs.get_mut(npc_id) { if let Some(npc_cell) = self.npc_map.npcs.get_mut(npc_id) {

View file

@ -22,15 +22,6 @@ pub enum WeaponType {
Spur = 13, Spur = 13,
} }
#[derive(Clone)]
pub struct Weapon {
pub wtype: WeaponType,
pub level: WeaponLevel,
pub experience: u16,
pub ammo: u16,
pub max_ammo: u16,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)] #[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[repr(u8)] #[repr(u8)]
pub enum WeaponLevel { pub enum WeaponLevel {
@ -40,6 +31,26 @@ pub enum WeaponLevel {
Level3 = 3, Level3 = 3,
} }
impl WeaponLevel {
pub fn next(self) -> WeaponLevel {
match self {
WeaponLevel::None => { WeaponLevel::Level1 }
WeaponLevel::Level1 => { WeaponLevel::Level2 }
WeaponLevel::Level2 => { WeaponLevel::Level3 }
WeaponLevel::Level3 => { WeaponLevel::Level3 }
}
}
}
#[derive(Clone)]
pub struct Weapon {
pub wtype: WeaponType,
pub level: WeaponLevel,
pub experience: u16,
pub ammo: u16,
pub max_ammo: u16,
}
impl Weapon { impl Weapon {
pub fn new(wtype: WeaponType, level: WeaponLevel, experience: u16, ammo: u16, max_ammo: u16) -> Weapon { pub fn new(wtype: WeaponType, level: WeaponLevel, experience: u16, ammo: u16, max_ammo: u16) -> Weapon {
Weapon { Weapon {