1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-03-23 10:29:18 +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 bullet_table: Vec<BulletData>,
pub bullet_rects: BulletRects,
pub level_table: [[u16; 3]; 14],
}
impl Clone for WeaponConsts {
@ -126,6 +127,7 @@ impl Clone for WeaponConsts {
WeaponConsts {
bullet_table: self.bullet_table.clone(),
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 },
],
},
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! {
"ArmsImage" => (256, 16),

View file

@ -1,3 +1,5 @@
use crate::engine_constants::EngineConstants;
use crate::SharedGameState;
use crate::weapon::{Weapon, WeaponLevel, WeaponType};
#[derive(Clone, Copy)]
@ -11,6 +13,13 @@ pub struct Inventory {
weapons: Vec<Weapon>,
}
#[derive(Clone, Copy)]
pub enum AddExperienceResult {
None,
LevelUp,
AddStar,
}
impl Inventory {
#[allow(clippy::new_without_default)]
pub fn new() -> Inventory {
@ -56,6 +65,10 @@ impl Inventory {
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> {
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) {
if let Some(weapon) = self.weapons.get(self.current_weapon as usize) {
(weapon.ammo, weapon.max_ammo)

View file

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

View file

@ -8,6 +8,7 @@ use crate::player::Player;
use crate::SharedGameState;
use crate::stage::Stage;
use std::borrow::Borrow;
use crate::inventory::{Inventory, AddExperienceResult};
impl PhysicalEntity for Player {
#[inline(always)]
@ -152,10 +153,10 @@ impl Player {
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() {
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; }
let mut flags = Flag(0);
@ -169,6 +170,23 @@ impl Player {
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() {
state.textscript_vm.start_script(npc.event_num);
self.cond.set_interacted(false);

View file

@ -103,6 +103,7 @@ impl GameScene {
// none
let weap_x = self.weapon_x_pos as f32;
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")?;
if max_ammo == 0 {
@ -113,17 +114,28 @@ impl GameScene {
}
// 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));
// lv
batch.add_rect(16.0, 32.0,
batch.add_rect(weap_x, 32.0,
&Rect::<usize>::new_size(80, 80, 16, 8));
// 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));
// experience
//batch.add_rect(40.0, 32.0,
// &Rect::<usize>::new_size(0, 80, (40), 8)); // todo
if max_xp > 0 {
if xp == max_xp {
// 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 {
// 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, 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)?;
Ok(())
@ -486,7 +498,6 @@ impl GameScene {
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() {
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.frame.immediate_update(state, &self.player, &self.stage);
self.inventory.add_weapon(WeaponType::PolarStar, 0);
self.player.equip.set_booster_2_0(true);
//self.inventory.add_weapon(WeaponType::PolarStar, 0);
//self.player.equip.set_booster_2_0(true);
Ok(())
}
@ -706,7 +717,7 @@ impl Scene for GameScene {
self.bullet_manager.tick_bullets(state, &self.player, &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() {
if let Some(npc_cell) = self.npc_map.npcs.get_mut(npc_id) {

View file

@ -22,15 +22,6 @@ pub enum WeaponType {
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)]
#[repr(u8)]
pub enum WeaponLevel {
@ -40,6 +31,26 @@ pub enum WeaponLevel {
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 {
pub fn new(wtype: WeaponType, level: WeaponLevel, experience: u16, ammo: u16, max_ammo: u16) -> Weapon {
Weapon {