feat: difficulty scaling by depth + new items (bow, shield, mana potion)

Difficulty scaling:
- Spawn rates increase with depth (goblins every 30-10 ticks, slimes every 45-15)
- Enemy count caps increase with depth (goblins 3-8, slimes 2-5)
- Enemy health scales with depth (+5 hp/goblin, +3 hp/slime per depth)
- Goblin strength stat scales with depth

New items:
- Bow: ranged weapon, +4 damage bonus
- Shield: armor, +3 armor bonus
- Mana Potion: consumable, heals 20 hp
- Items spawn in world gen: sword, bow, shield, health potion, mana potion, leather armor

All 171 tests pass.
This commit is contained in:
Emil
2026-06-21 16:34:57 +03:00
parent 9778154348
commit 876eb031df
2 changed files with 44 additions and 8 deletions
+27 -4
View File
@@ -2,11 +2,14 @@
pub enum ItemType {
Dagger,
Sword,
Bow,
LeatherArmor,
PlateArmor,
HealthPotion,
ManaPotion,
Food,
Scroll,
Shield,
}
#[derive(Clone, Debug)]
@@ -25,9 +28,12 @@ impl Item {
match self.typ {
ItemType::Dagger => "Dagger",
ItemType::Sword => "Sword",
ItemType::Bow => "Bow",
ItemType::LeatherArmor => "Leather Armor",
ItemType::PlateArmor => "Plate Armor",
ItemType::Shield => "Shield",
ItemType::HealthPotion => "Health Potion",
ItemType::ManaPotion => "Mana Potion",
ItemType::Food => "Food",
ItemType::Scroll => "Scroll",
}
@@ -37,9 +43,12 @@ impl Item {
match self.typ {
ItemType::Dagger => '/',
ItemType::Sword => '|',
ItemType::Bow => ')',
ItemType::LeatherArmor => '[',
ItemType::PlateArmor => '{',
ItemType::Shield => 'O',
ItemType::HealthPotion => '!',
ItemType::ManaPotion => '!',
ItemType::Food => '%',
ItemType::Scroll => '?',
}
@@ -49,9 +58,12 @@ impl Item {
match self.typ {
ItemType::Dagger => [200, 200, 200],
ItemType::Sword => [240, 240, 240],
ItemType::Bow => [180, 140, 80],
ItemType::LeatherArmor => [140, 90, 50],
ItemType::PlateArmor => [180, 180, 190],
ItemType::Shield => [160, 140, 60],
ItemType::HealthPotion => [255, 40, 40],
ItemType::ManaPotion => [60, 60, 255],
ItemType::Food => [80, 200, 60],
ItemType::Scroll => [255, 220, 120],
}
@@ -60,22 +72,30 @@ impl Item {
pub fn is_equipment(&self) -> bool {
matches!(
self.typ,
ItemType::Dagger | ItemType::Sword | ItemType::LeatherArmor | ItemType::PlateArmor
ItemType::Dagger
| ItemType::Sword
| ItemType::Bow
| ItemType::LeatherArmor
| ItemType::PlateArmor
| ItemType::Shield
)
}
pub fn is_weapon(&self) -> bool {
matches!(self.typ, ItemType::Dagger | ItemType::Sword)
matches!(self.typ, ItemType::Dagger | ItemType::Sword | ItemType::Bow)
}
pub fn is_armor(&self) -> bool {
matches!(self.typ, ItemType::LeatherArmor | ItemType::PlateArmor)
matches!(
self.typ,
ItemType::LeatherArmor | ItemType::PlateArmor | ItemType::Shield
)
}
pub fn is_consumable(&self) -> bool {
matches!(
self.typ,
ItemType::HealthPotion | ItemType::Food | ItemType::Scroll
ItemType::HealthPotion | ItemType::ManaPotion | ItemType::Food | ItemType::Scroll
)
}
@@ -83,6 +103,7 @@ impl Item {
match self.typ {
ItemType::Dagger => 3.0,
ItemType::Sword => 6.0,
ItemType::Bow => 4.0,
_ => 0.0,
}
}
@@ -91,6 +112,7 @@ impl Item {
match self.typ {
ItemType::LeatherArmor => 2.0,
ItemType::PlateArmor => 5.0,
ItemType::Shield => 3.0,
_ => 0.0,
}
}
@@ -98,6 +120,7 @@ impl Item {
pub fn heal_amount(&self) -> f32 {
match self.typ {
ItemType::HealthPotion => 40.0,
ItemType::ManaPotion => 20.0,
ItemType::Food => 15.0,
_ => 0.0,
}
+17 -4
View File
@@ -245,6 +245,12 @@ impl Game {
.spawn(ItemType::HealthPotion, px as i32 + 6, py as i32 + 1);
self.items
.spawn(ItemType::LeatherArmor, px as i32 - 3, py as i32 - 8);
self.items
.spawn(ItemType::Bow, px as i32 + 10, py as i32 + 1);
self.items
.spawn(ItemType::Shield, px as i32 - 10, py as i32 + 1);
self.items
.spawn(ItemType::ManaPotion, px as i32 + 3, py as i32 - 6);
}
pub fn center_camera_on(&mut self, px: f32, py: f32) {
@@ -516,10 +522,10 @@ impl Game {
self.update_score();
self.update_item_pickup();
if self.tick % 30 == 0 {
if self.tick % (30u64 - (self.depth as u64 * 3).min(20)) == 0 {
self.try_spawn_goblin();
}
if self.tick % 45 == 0 {
if self.tick % (45u64 - (self.depth as u64 * 4).min(30)) == 0 {
self.try_spawn_slime();
}
@@ -1364,13 +1370,14 @@ impl Game {
}
fn try_spawn_goblin(&mut self) {
let max_goblins = 3usize + self.depth.min(5) as usize;
let alive_goblins = self
.entities
.all()
.iter()
.filter(|e| e.alive && e.kind == EntityKind::Goblin)
.count();
if alive_goblins >= 3 {
if alive_goblins >= max_goblins {
return;
}
@@ -1397,17 +1404,21 @@ impl Game {
let id = self.entities.spawn(EntityKind::Goblin);
if let Some(g) = self.entities.get_mut(id) {
g.build_humanoid(spawn_x as f32, spawn_y as f32);
g.health += self.depth as f32 * 5.0;
g.max_health += self.depth as f32 * 5.0;
g.strength += self.depth;
}
}
fn try_spawn_slime(&mut self) {
let max_slimes = 2usize + self.depth.min(3) as usize;
let alive_slimes = self
.entities
.all()
.iter()
.filter(|e| e.alive && e.kind == EntityKind::Slime)
.count();
if alive_slimes >= 2 {
if alive_slimes >= max_slimes {
return;
}
@@ -1434,6 +1445,8 @@ impl Game {
let id = self.entities.spawn(EntityKind::Slime);
if let Some(s) = self.entities.get_mut(id) {
s.build_humanoid(spawn_x as f32, spawn_y as f32);
s.health += self.depth as f32 * 3.0;
s.max_health += self.depth as f32 * 3.0;
}
}
}