mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Added dungeon bosses, boss loot, boss arenas
This commit is contained in:
parent
71dd520cd6
commit
aac28d04d5
@ -73,6 +73,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Added tab completion in chat for player names and chat commands
|
||||
- Added server persistence for character stats
|
||||
- Added a popup when setting your character's waypoint
|
||||
- Added dungeon arenas
|
||||
- Added dungeon bosses and rare boss loot
|
||||
|
||||
### Changed
|
||||
|
||||
|
12
assets/common/items/boss_drops/lantern.ron
Normal file
12
assets/common/items/boss_drops/lantern.ron
Normal file
@ -0,0 +1,12 @@
|
||||
Item(
|
||||
name: "Magic Lantern",
|
||||
description: "Illuminates even the darkest dungeon\nA great monster was slain for this item",
|
||||
kind: Lantern(
|
||||
(
|
||||
kind: Blue0,
|
||||
color: (r: 220, g: 220, b: 255),
|
||||
strength_thousandths: 6500,
|
||||
flicker_thousandths: 300,
|
||||
),
|
||||
),
|
||||
)
|
12
assets/common/items/boss_drops/potions.ron
Normal file
12
assets/common/items/boss_drops/potions.ron
Normal file
@ -0,0 +1,12 @@
|
||||
Item(
|
||||
name: "Powerful Potion",
|
||||
description: "Restores 100 Health\nA great monster was slain for this item\n\n<Right-Click to use>",
|
||||
kind: Consumable(
|
||||
kind: Potion,
|
||||
effect: Health((
|
||||
amount: 100,
|
||||
cause: Item,
|
||||
)),
|
||||
amount: 15,
|
||||
),
|
||||
)
|
8
assets/common/items/boss_drops/xp_potion.ron
Normal file
8
assets/common/items/boss_drops/xp_potion.ron
Normal file
@ -0,0 +1,8 @@
|
||||
Item(
|
||||
name: "Potion of Skill",
|
||||
description: "Provides 250 XP to the drinker\n\n<Right-Click to use>",
|
||||
kind: Consumable(
|
||||
kind: Potion,
|
||||
effect: Xp(250),
|
||||
),
|
||||
)
|
@ -3,7 +3,7 @@ Item(
|
||||
description: "Two-Hand Staff\n\nPower: 2-10\n\nWalking stick with a sharpened end\n\n<Right-Click to use>",
|
||||
kind: Tool(
|
||||
(
|
||||
kind: Staff(BasicStaff),
|
||||
kind: Staff(BasicStaff),
|
||||
equip_time_millis: 200,
|
||||
)
|
||||
),
|
||||
|
@ -123,6 +123,8 @@ impl Item {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_from_asset(asset: &str) -> Self { (*assets::load_expect::<Self>(asset)).clone() }
|
||||
|
||||
pub fn set_amount(&mut self, give_amount: u32) -> Result<(), assets::Error> {
|
||||
use ItemKind::*;
|
||||
match self.kind {
|
||||
@ -235,3 +237,10 @@ impl Item {
|
||||
impl Component for Item {
|
||||
type Storage = FlaggedStorage<Self, IDVStorage<Self>>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct ItemDrop(pub Item);
|
||||
|
||||
impl Component for ItemDrop {
|
||||
type Storage = FlaggedStorage<Self, IDVStorage<Self>>;
|
||||
}
|
||||
|
@ -31,7 +31,9 @@ pub use controller::{
|
||||
pub use energy::{Energy, EnergySource};
|
||||
pub use inputs::CanBuild;
|
||||
pub use inventory::{
|
||||
item, item::Item, slot, Inventory, InventoryUpdate, InventoryUpdateEvent, MAX_PICKUP_RANGE_SQR,
|
||||
item,
|
||||
item::{Item, ItemDrop},
|
||||
slot, Inventory, InventoryUpdate, InventoryUpdateEvent, MAX_PICKUP_RANGE_SQR,
|
||||
};
|
||||
pub use last::Last;
|
||||
pub use location::{Waypoint, WaypointArea};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{comp, sync::Uid, util::Dir};
|
||||
use comp::{item::ToolKind, InventoryUpdateEvent};
|
||||
use comp::{item::ToolKind, InventoryUpdateEvent, Item};
|
||||
use parking_lot::Mutex;
|
||||
use serde::Deserialize;
|
||||
use specs::Entity as EcsEntity;
|
||||
@ -107,6 +107,7 @@ pub enum ServerEvent {
|
||||
agent: comp::Agent,
|
||||
alignment: comp::Alignment,
|
||||
scale: comp::Scale,
|
||||
drop_item: Option<Item>,
|
||||
},
|
||||
CreateWaypoint(Vec3<f32>),
|
||||
ClientDisconnect(EcsEntity),
|
||||
|
@ -16,6 +16,9 @@ pub struct EntityInfo {
|
||||
pub body: Body,
|
||||
pub name: Option<String>,
|
||||
pub main_tool: Option<Item>,
|
||||
pub scale: f32,
|
||||
pub level: Option<u32>,
|
||||
pub loot_drop: Option<Item>,
|
||||
}
|
||||
|
||||
impl EntityInfo {
|
||||
@ -28,6 +31,9 @@ impl EntityInfo {
|
||||
body: Body::Humanoid(humanoid::Body::random()),
|
||||
name: None,
|
||||
main_tool: Some(Item::empty()),
|
||||
scale: 1.0,
|
||||
level: None,
|
||||
loot_drop: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,6 +74,21 @@ impl EntityInfo {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_loot_drop(mut self, loot_drop: Item) -> Self {
|
||||
self.loot_drop = Some(loot_drop);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_scale(mut self, scale: f32) -> Self {
|
||||
self.scale = scale;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_level(mut self, level: u32) -> Self {
|
||||
self.level = Some(level);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_automatic_name(mut self) -> Self {
|
||||
self.name = match &self.body {
|
||||
Body::Humanoid(body) => Some(get_npc_name(&NPC_NAMES.humanoid, body.race)),
|
||||
|
@ -153,6 +153,7 @@ impl State {
|
||||
ecs.register::<comp::Waypoint>();
|
||||
ecs.register::<comp::Projectile>();
|
||||
ecs.register::<comp::Attacking>();
|
||||
ecs.register::<comp::ItemDrop>();
|
||||
|
||||
// Register synced resources used by the ECS.
|
||||
ecs.insert(TimeOfDay(0.0));
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::{sys, Server, StateExt};
|
||||
use common::{
|
||||
comp::{
|
||||
self, Agent, Alignment, Body, Gravity, LightEmitter, Loadout, Pos, Projectile, Scale,
|
||||
Stats, Vel, WaypointArea,
|
||||
self, Agent, Alignment, Body, Gravity, Item, ItemDrop, LightEmitter, Loadout, Pos,
|
||||
Projectile, Scale, Stats, Vel, WaypointArea,
|
||||
},
|
||||
util::Dir,
|
||||
};
|
||||
@ -32,14 +32,22 @@ pub fn handle_create_npc(
|
||||
agent: Agent,
|
||||
alignment: Alignment,
|
||||
scale: Scale,
|
||||
drop_item: Option<Item>,
|
||||
) {
|
||||
server
|
||||
let entity = server
|
||||
.state
|
||||
.create_npc(pos, stats, loadout, body)
|
||||
.with(agent)
|
||||
.with(scale)
|
||||
.with(alignment)
|
||||
.build();
|
||||
.with(alignment);
|
||||
|
||||
let entity = if let Some(drop_item) = drop_item {
|
||||
entity.with(ItemDrop(drop_item))
|
||||
} else {
|
||||
entity
|
||||
};
|
||||
|
||||
entity.build();
|
||||
}
|
||||
|
||||
pub fn handle_shoot(
|
||||
|
@ -106,10 +106,17 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
||||
.ecs()
|
||||
.write_storage()
|
||||
.insert(entity, Body::Object(object::Body::Pouch));
|
||||
let _ = state.ecs().write_storage().insert(
|
||||
entity,
|
||||
assets::load_expect_cloned::<Item>("common.items.cheese"),
|
||||
);
|
||||
|
||||
let mut item_drops = state.ecs().write_storage::<comp::ItemDrop>();
|
||||
let item = if let Some(item_drop) = item_drops.get(entity).cloned() {
|
||||
item_drops.remove(entity);
|
||||
item_drop.0
|
||||
} else {
|
||||
assets::load_expect_cloned::<Item>("common.items.cheese")
|
||||
};
|
||||
|
||||
let _ = state.ecs().write_storage().insert(entity, item);
|
||||
|
||||
state.ecs().write_storage::<comp::Stats>().remove(entity);
|
||||
state.ecs().write_storage::<comp::Agent>().remove(entity);
|
||||
state
|
||||
|
@ -84,7 +84,10 @@ impl Server {
|
||||
agent,
|
||||
alignment,
|
||||
scale,
|
||||
} => handle_create_npc(self, pos, stats, loadout, body, agent, alignment, scale),
|
||||
drop_item,
|
||||
} => handle_create_npc(
|
||||
self, pos, stats, loadout, body, agent, alignment, scale, drop_item,
|
||||
),
|
||||
ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos),
|
||||
ServerEvent::ClientDisconnect(entity) => {
|
||||
frontend_events.push(handle_client_disconnect(self, entity))
|
||||
|
@ -245,11 +245,15 @@ impl<'a> System<'a> for Sys {
|
||||
},
|
||||
};
|
||||
|
||||
let mut scale = 1.0;
|
||||
let mut scale = entity.scale;
|
||||
|
||||
// TODO: Remove this and implement scaling or level depending on stuff like
|
||||
// species instead
|
||||
stats.level.set_level(rand::thread_rng().gen_range(1, 9));
|
||||
stats.level.set_level(
|
||||
entity.level.unwrap_or_else(|| {
|
||||
(rand::thread_rng().gen_range(1, 9) as f32 * scale) as u32
|
||||
}),
|
||||
);
|
||||
|
||||
// Replace stuff if it's a boss
|
||||
if entity.is_giant {
|
||||
@ -336,6 +340,7 @@ impl<'a> System<'a> for Sys {
|
||||
alignment,
|
||||
agent: comp::Agent::default().with_patrol_origin(entity.pos),
|
||||
scale: comp::Scale(scale),
|
||||
drop_item: entity.loot_drop,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
37
world/examples/namegen.rs
Normal file
37
world/examples/namegen.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use rand::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let cons = vec![
|
||||
"d", "f", "ph", "r", "st", "t", "s", "p", "sh", "th", "br", "tr", "m", "k", "st", "w", "y",
|
||||
];
|
||||
let mut start = cons.clone();
|
||||
start.extend(vec![
|
||||
"cr", "thr", "str", "br", "ivy", "est", "ost", "ing", "kr", "in", "on", "tr", "tw", "wh",
|
||||
"eld", "ar", "or", "ear", "ir",
|
||||
]);
|
||||
let mut middle = cons.clone();
|
||||
middle.extend(vec!["tt"]);
|
||||
let vowel = vec!["o", "e", "a", "i", "u", "au", "ee", "ow", "ay", "ey", "oe"];
|
||||
let end = vec![
|
||||
"et", "ige", "age", "ist", "en", "on", "og", "end", "ind", "ock", "een", "edge", "ist",
|
||||
"ed", "est", "eed", "ast", "olt", "ey", "ean", "ead", "onk", "ink", "eon", "er", "ow",
|
||||
"cot", "in", "on",
|
||||
];
|
||||
|
||||
let gen_name = || {
|
||||
let mut name = String::new();
|
||||
|
||||
name += start.choose(&mut thread_rng()).unwrap();
|
||||
if thread_rng().gen() {
|
||||
name += vowel.choose(&mut thread_rng()).unwrap();
|
||||
name += middle.choose(&mut thread_rng()).unwrap();
|
||||
}
|
||||
name += end.choose(&mut thread_rng()).unwrap();
|
||||
|
||||
name
|
||||
};
|
||||
|
||||
for _ in 0..20 {
|
||||
println!("{}", gen_name());
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ use common::{
|
||||
astar::Astar,
|
||||
comp,
|
||||
generation::{ChunkSupplement, EntityInfo},
|
||||
npc,
|
||||
store::{Id, Store},
|
||||
terrain::{Block, BlockKind, Structure, TerrainChunkSize},
|
||||
vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol},
|
||||
@ -49,6 +50,8 @@ pub struct GenCtx<'a, R: Rng> {
|
||||
|
||||
const ALT_OFFSET: i32 = -2;
|
||||
|
||||
const LEVELS: usize = 5;
|
||||
|
||||
impl Dungeon {
|
||||
pub fn generate(wpos: Vec2<i32>, sim: Option<&WorldSim>, rng: &mut impl Rng) -> Self {
|
||||
let mut ctx = GenCtx { sim, rng };
|
||||
@ -61,9 +64,9 @@ impl Dungeon {
|
||||
+ 6,
|
||||
seed: ctx.rng.gen(),
|
||||
noise: RandomField::new(ctx.rng.gen()),
|
||||
floors: (0..6)
|
||||
floors: (0..LEVELS)
|
||||
.scan(Vec2::zero(), |stair_tile, level| {
|
||||
let (floor, st) = Floor::generate(&mut ctx, *stair_tile, level);
|
||||
let (floor, st) = Floor::generate(&mut ctx, *stair_tile, level as i32);
|
||||
*stair_tile = st;
|
||||
Some(floor)
|
||||
})
|
||||
@ -184,7 +187,7 @@ const TILE_SIZE: i32 = 13;
|
||||
#[derive(Clone)]
|
||||
pub enum Tile {
|
||||
UpStair,
|
||||
DownStair,
|
||||
DownStair(Id<Room>),
|
||||
Room(Id<Room>),
|
||||
Tunnel,
|
||||
Solid,
|
||||
@ -194,7 +197,7 @@ impl Tile {
|
||||
fn is_passable(&self) -> bool {
|
||||
match self {
|
||||
Tile::UpStair => true,
|
||||
Tile::DownStair => true,
|
||||
Tile::DownStair(_) => true,
|
||||
Tile::Room(_) => true,
|
||||
Tile::Tunnel => true,
|
||||
_ => false,
|
||||
@ -206,7 +209,10 @@ pub struct Room {
|
||||
seed: u32,
|
||||
loot_density: f32,
|
||||
enemy_density: Option<f32>,
|
||||
boss: bool,
|
||||
area: Rect<i32, i32>,
|
||||
height: i32,
|
||||
pillars: Option<i32>, // Pillars with the given separation
|
||||
}
|
||||
|
||||
pub struct Floor {
|
||||
@ -227,40 +233,69 @@ impl Floor {
|
||||
stair_tile: Vec2<i32>,
|
||||
level: i32,
|
||||
) -> (Self, Vec2<i32>) {
|
||||
let new_stair_tile = std::iter::from_fn(|| {
|
||||
Some(FLOOR_SIZE.map(|sz| ctx.rng.gen_range(-sz / 2 + 2, sz / 2 - 1)))
|
||||
})
|
||||
.filter(|pos| *pos != stair_tile)
|
||||
.take(8)
|
||||
.max_by_key(|pos| (*pos - stair_tile).map(|e| e.abs()).sum())
|
||||
.unwrap();
|
||||
let final_level = level == LEVELS as i32 - 1;
|
||||
|
||||
let new_stair_tile = if final_level {
|
||||
Vec2::zero()
|
||||
} else {
|
||||
std::iter::from_fn(|| {
|
||||
Some(FLOOR_SIZE.map(|sz| ctx.rng.gen_range(-sz / 2 + 2, sz / 2 - 1)))
|
||||
})
|
||||
.filter(|pos| *pos != stair_tile)
|
||||
.take(8)
|
||||
.max_by_key(|pos| (*pos - stair_tile).map(|e| e.abs()).sum())
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let tile_offset = -FLOOR_SIZE / 2;
|
||||
let mut this = Floor {
|
||||
tile_offset,
|
||||
tiles: Grid::new(FLOOR_SIZE, Tile::Solid),
|
||||
rooms: Store::default(),
|
||||
solid_depth: if level == 0 { 80 } else { 13 * 2 },
|
||||
hollow_depth: 13,
|
||||
solid_depth: if level == 0 { 80 } else { 32 },
|
||||
hollow_depth: 30,
|
||||
stair_tile: new_stair_tile - tile_offset,
|
||||
};
|
||||
|
||||
const STAIR_ROOM_HEIGHT: i32 = 13;
|
||||
// Create rooms for entrance and exit
|
||||
this.create_room(Room {
|
||||
seed: ctx.rng.gen(),
|
||||
loot_density: 0.0,
|
||||
enemy_density: None,
|
||||
boss: false,
|
||||
area: Rect::from((stair_tile - tile_offset - 1, Extent2::broadcast(3))),
|
||||
height: STAIR_ROOM_HEIGHT,
|
||||
pillars: None,
|
||||
});
|
||||
this.tiles.set(stair_tile - tile_offset, Tile::UpStair);
|
||||
this.create_room(Room {
|
||||
seed: ctx.rng.gen(),
|
||||
loot_density: 0.0,
|
||||
enemy_density: None,
|
||||
area: Rect::from((new_stair_tile - tile_offset - 1, Extent2::broadcast(3))),
|
||||
});
|
||||
this.tiles
|
||||
.set(new_stair_tile - tile_offset, Tile::DownStair);
|
||||
if final_level {
|
||||
// Boss room
|
||||
this.create_room(Room {
|
||||
seed: ctx.rng.gen(),
|
||||
loot_density: 0.0,
|
||||
enemy_density: Some(0.001), // Minions!
|
||||
boss: true,
|
||||
area: Rect::from((new_stair_tile - tile_offset - 4, Extent2::broadcast(9))),
|
||||
height: 30,
|
||||
pillars: Some(2),
|
||||
});
|
||||
} else {
|
||||
// Create downstairs room
|
||||
let downstair_room = this.create_room(Room {
|
||||
seed: ctx.rng.gen(),
|
||||
loot_density: 0.0,
|
||||
enemy_density: None,
|
||||
boss: false,
|
||||
area: Rect::from((new_stair_tile - tile_offset - 1, Extent2::broadcast(3))),
|
||||
height: STAIR_ROOM_HEIGHT,
|
||||
pillars: None,
|
||||
});
|
||||
this.tiles.set(
|
||||
new_stair_tile - tile_offset,
|
||||
Tile::DownStair(downstair_room),
|
||||
);
|
||||
}
|
||||
|
||||
this.create_rooms(ctx, level, 7);
|
||||
// Create routes between all rooms
|
||||
@ -316,8 +351,11 @@ impl Floor {
|
||||
self.create_room(Room {
|
||||
seed: ctx.rng.gen(),
|
||||
loot_density: 0.000025 + level as f32 * 0.00015,
|
||||
enemy_density: Some(0.001 + level as f32 * 0.00004),
|
||||
enemy_density: Some(0.001 + level as f32 * 0.00006),
|
||||
boss: false,
|
||||
area,
|
||||
height: ctx.rng.gen_range(10, 15),
|
||||
pillars: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -334,7 +372,7 @@ impl Floor {
|
||||
let transition = |_a: &Vec2<i32>, b: &Vec2<i32>| match self.tiles.get(*b) {
|
||||
Some(Tile::Room(_)) | Some(Tile::Tunnel) => 1.0,
|
||||
Some(Tile::Solid) => 25.0,
|
||||
Some(Tile::UpStair) | Some(Tile::DownStair) => 0.0,
|
||||
Some(Tile::UpStair) | Some(Tile::DownStair(_)) => 0.0,
|
||||
_ => 100000.0,
|
||||
};
|
||||
let satisfied = |l: &Vec2<i32>| *l == b;
|
||||
@ -367,6 +405,7 @@ impl Floor {
|
||||
for x in area.min.x..area.max.x {
|
||||
for y in area.min.y..area.max.y {
|
||||
let tile_pos = Vec2::new(x, y).map(|e| e.div_euclid(TILE_SIZE)) - self.tile_offset;
|
||||
let wpos2d = origin.xy() + Vec2::new(x, y);
|
||||
if let Some(Tile::Room(room)) = self.tiles.get(tile_pos) {
|
||||
let room = &self.rooms[*room];
|
||||
|
||||
@ -376,10 +415,20 @@ impl Floor {
|
||||
.map(|e| e.div_euclid(TILE_SIZE) * TILE_SIZE + TILE_SIZE / 2),
|
||||
);
|
||||
|
||||
let tile_is_pillar = room
|
||||
.pillars
|
||||
.map(|pillar_space| {
|
||||
tile_pos
|
||||
.map(|e| e.rem_euclid(pillar_space) == 0)
|
||||
.reduce_and()
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
if room
|
||||
.enemy_density
|
||||
.map(|density| rng.gen_range(0, density.recip() as usize) == 0)
|
||||
.unwrap_or(false)
|
||||
&& !tile_is_pillar
|
||||
{
|
||||
// Bad
|
||||
let entity = EntityInfo::at(
|
||||
@ -389,7 +438,7 @@ impl Floor {
|
||||
.map(|e| (RandomField::new(room.seed.wrapping_add(10 + e)).get(Vec3::from(tile_pos)) % 32) as i32 - 16)
|
||||
.map(|e| e as f32 / 16.0),
|
||||
)
|
||||
.do_if(RandomField::new(room.seed.wrapping_add(1)).chance(Vec3::from(tile_pos), 0.2), |e| e.into_giant())
|
||||
.do_if(RandomField::new(room.seed.wrapping_add(1)).chance(Vec3::from(tile_pos), 0.2) && !room.boss, |e| e.into_giant())
|
||||
.with_alignment(comp::Alignment::Enemy)
|
||||
.with_body(comp::Body::Humanoid(comp::humanoid::Body::random()))
|
||||
.with_automatic_name()
|
||||
@ -404,6 +453,46 @@ impl Floor {
|
||||
|
||||
supplement.add_entity(entity);
|
||||
}
|
||||
|
||||
if room.boss {
|
||||
let boss_spawn_tile = room.area.center();
|
||||
// Don't spawn the boss in a pillar
|
||||
let boss_spawn_tile = boss_spawn_tile + if tile_is_pillar { 1 } else { 0 };
|
||||
|
||||
if tile_pos == boss_spawn_tile && tile_wcenter.xy() == wpos2d {
|
||||
let entity = EntityInfo::at(tile_wcenter.map(|e| e as f32))
|
||||
.with_scale(4.0)
|
||||
.with_level(rng.gen_range(50, 70))
|
||||
.with_alignment(comp::Alignment::Enemy)
|
||||
.with_body(comp::Body::Humanoid(comp::humanoid::Body::random()))
|
||||
.with_name(format!(
|
||||
"{}, Destroyer of Worlds",
|
||||
npc::get_npc_name(npc::NpcKind::Humanoid)
|
||||
))
|
||||
.with_main_tool(assets::load_expect_cloned(
|
||||
match rng.gen_range(0, 5) {
|
||||
0 => "common.items.weapons.sword.starter_sword",
|
||||
1 => "common.items.weapons.sword.short_sword_0",
|
||||
2 => "common.items.weapons.sword.wood_sword",
|
||||
3 => "common.items.weapons.sword.zweihander_sword_0",
|
||||
_ => "common.items.weapons.hammer.hammer_1",
|
||||
},
|
||||
))
|
||||
.with_loot_drop(match rng.gen_range(0, 3) {
|
||||
0 => comp::Item::expect_from_asset(
|
||||
"common.items.boss_drops.lantern",
|
||||
),
|
||||
1 => comp::Item::expect_from_asset(
|
||||
"common.items.boss_drops.potions",
|
||||
),
|
||||
_ => comp::Item::expect_from_asset(
|
||||
"common.items.boss_drops.xp_potion",
|
||||
),
|
||||
});
|
||||
|
||||
supplement.add_entity(entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -477,9 +566,20 @@ impl Floor {
|
||||
BlockMask::nothing()
|
||||
}
|
||||
},
|
||||
Some(Tile::Room(_)) | Some(Tile::DownStair)
|
||||
Some(Tile::Room(room)) | Some(Tile::DownStair(room))
|
||||
if dist_to_wall < wall_thickness
|
||||
|| z as f32 >= self.hollow_depth as f32 - 13.0 * tunnel_dist.powf(4.0) =>
|
||||
|| z as f32
|
||||
>= self.rooms[*room].height as f32 * (1.0 - tunnel_dist.powf(4.0))
|
||||
|| self.rooms[*room]
|
||||
.pillars
|
||||
.map(|pillar_space| {
|
||||
tile_pos
|
||||
.map(|e| e.rem_euclid(pillar_space) == 0)
|
||||
.reduce_and()
|
||||
&& rtile_pos.map(|e| e as f32).magnitude_squared()
|
||||
< 3.5f32.powf(2.0)
|
||||
})
|
||||
.unwrap_or(false) =>
|
||||
{
|
||||
BlockMask::nothing()
|
||||
},
|
||||
@ -492,7 +592,7 @@ impl Floor {
|
||||
empty
|
||||
}
|
||||
},
|
||||
Some(Tile::DownStair) => {
|
||||
Some(Tile::DownStair(_)) => {
|
||||
make_staircase(Vec3::new(rtile_pos.x, rtile_pos.y, z), 0.0, 0.5, 9.0)
|
||||
.resolve_with(empty)
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user