From 73a79b32a1c118423c611a0ae9b7addaa82e6617 Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Sun, 18 Jun 2023 17:49:53 +0200 Subject: [PATCH 01/22] no dungeons = no dungeons with caves! --- world/src/civ/mod.rs | 16 +++++++++++++++- world/src/layer/cave.rs | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index c8935d6b17..e59b483dc7 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -4,6 +4,7 @@ mod econ; use crate::{ config::CONFIG, + layer::cave, sim::WorldSim, site::{namegen::NameGen, Castle, Settlement, Site as WorldSite, Tree}, site2, @@ -1879,7 +1880,20 @@ impl SiteKind { } true }, - SiteKind::Dungeon => on_land(), + SiteKind::Dungeon => { + on_land() && { + let land = Land::from_sim(sim); + let loc = loc.map(|v| TerrainChunkSize::RECT_SIZE.y as i32 * v); + + // Get shallow caves under the dungeon + let mut tunnels = + cave::tunnels_at(loc /* very surely wrong */, 1, &land) + .chain(cave::tunnels_at(loc, 2, &land)); + + // Make sure there are no shallow caves near the dungeon + tunnels.next().is_none() + } + }, SiteKind::Refactor | SiteKind::Settlement => suitable_for_town(), SiteKind::Bridge(_, _) => true, } diff --git a/world/src/layer/cave.rs b/world/src/layer/cave.rs index ec9f3fbdff..4ee40b67a7 100644 --- a/world/src/layer/cave.rs +++ b/world/src/layer/cave.rs @@ -217,7 +217,7 @@ impl Tunnel { } } -fn tunnels_at<'a>( +pub(crate) fn tunnels_at<'a>( wpos: Vec2, level: u32, land: &'a Land, From 2d616352a2dde63c167f73cbc3630e0b2a7aa88c Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Wed, 5 Jul 2023 01:51:45 +0200 Subject: [PATCH 02/22] grey walled dungeons --- world/src/civ/mod.rs | 2 +- world/src/site2/plot/dungeon.rs | 41 +++++++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index e59b483dc7..b18dbaac0e 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -1891,7 +1891,7 @@ impl SiteKind { .chain(cave::tunnels_at(loc, 2, &land)); // Make sure there are no shallow caves near the dungeon - tunnels.next().is_none() + tunnels.next().is_none() || true } }, SiteKind::Refactor | SiteKind::Settlement => suitable_for_town(), diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 638e12ef77..8d6f3ffc74 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -976,6 +976,7 @@ impl Floor { let lava = Block::new(BlockKind::Lava, noisy_color(Rgb::new(184, 39, 0), 8)); let stone = Block::new(BlockKind::Rock, Rgb::new(150, 150, 175)); let stone_purple = Block::new(BlockKind::GlowingRock, Rgb::new(96, 0, 128)); + let stone_wall = Block::new(BlockKind::Rock, Rgb::new(120, 120, 135)); // Sprites are randomly positioned and have random kinds, this primitive // produces a box of dots that will later get truncated to just the @@ -1015,6 +1016,20 @@ impl Floor { let tiles = Arc::clone(&tiles); make_wall_contours(tiles, floor_corner, floor_z, wall_thickness, tunnel_height) })); + let walls_fat = painter.prim(Primitive::sampling(floor_prim, { + let tiles = Arc::clone(&tiles); + make_wall_contours( + tiles, + floor_corner, + floor_z, + wall_thickness - 1., + tunnel_height + 1., + ) + })); + + // Fill Walls + let walls_only = painter.prim(Primitive::without(wall_contours, walls_fat)); + painter.fill(walls_only, Fill::Block(stone_wall)); // The surface 1 unit thicker than the walls is used to place the torches onto let wall_contour_surface = painter.prim(Primitive::sampling(floor_prim, { @@ -1085,6 +1100,7 @@ impl Floor { // after handling all the local information per-tile let mut stairs_bb = Vec::new(); let mut stairs = Vec::new(); + let mut stair_walls = Vec::new(); let mut pillars = Vec::new(); let mut boss_room_center = None; let mut sprites = Vec::new(); @@ -1097,6 +1113,10 @@ impl Floor { min: tile_corner, max: tile_corner + Vec2::broadcast(TILE_SIZE), }; + let outer_tile_aabr = Aabr { + min: tile_corner - Vec2::broadcast(2), + max: tile_corner + Vec2::broadcast(TILE_SIZE + 2), + }; let tile_center = tile_corner + Vec2::broadcast(TILE_SIZE / 2); let (mut height, room) = match tile { Tile::UpStair(room, _) => (self.hollow_depth, Some(room)), @@ -1106,6 +1126,9 @@ impl Floor { Tile::Solid => continue, }; + let tile_floor_fill = painter.prim(Primitive::Aabb(aabr_with_z(tile_aabr, floor_z - 1..floor_z))); + painter.fill(tile_floor_fill, Fill::Block(stone_wall)); + // Sprites are contained to the level above the floor, and not within walls let sprite_layer = painter.prim(Primitive::Aabb(aabr_with_z( tile_aabr, @@ -1131,10 +1154,18 @@ impl Floor { let center = tile_center.with_z(floor_z); let radius = TILE_SIZE as f32 / 2.0; let aabb = aabr_with_z(tile_aabr, floor_z..floor_z + self.total_depth()); + let outer_aabb = aabr_with_z( + outer_tile_aabr, + floor_z + tunnel_height as i32..floor_z + self.total_depth() - 1, + ); let bb = painter.prim(match kind { StairsKind::Spiral => Primitive::Cylinder(aabb), StairsKind::WallSpiral => Primitive::Aabb(aabb), }); + let outer_bb = painter.prim(match kind { + StairsKind::WallSpiral => Primitive::Aabb(outer_aabb), + StairsKind::Spiral => Primitive::Cylinder(outer_aabb), + }); let stair = painter.prim(Primitive::sampling(bb, match kind { StairsKind::Spiral => spiral_staircase(center, radius, 0.5, 9.0), StairsKind::WallSpiral => wall_staircase(center, radius, 27.0), @@ -1159,6 +1190,7 @@ impl Floor { } lights = painter.prim(Primitive::intersect(lights, lighting_mask)); stairs_bb.push(bb); + stair_walls.push(outer_bb); stairs.push((stair, lights)); } if matches!(tile, Tile::Room(_) | Tile::DownStair(_)) { @@ -1274,7 +1306,7 @@ impl Floor { // Carve out the room's air inside the walls let tile_air = painter.prim(Primitive::Aabb(aabr_with_z( tile_aabr, - floor_z..floor_z + height, + floor_z..floor_z + tunnel_height as i32, ))); let tile_air = painter.prim(Primitive::without(tile_air, wall_contours)); painter.fill(tile_air, Fill::Block(vacant)); @@ -1319,8 +1351,13 @@ impl Floor { painter.fill(*lights, sconces_inward.clone()); painter.fill(*pillar, Fill::Block(stone)); } + // Carve out space for the stairs - for stair_bb in stairs_bb.iter() { + for (stair_bb, outer_stairs_bb) in stairs_bb.iter().zip(stair_walls.iter()) { + painter.fill( + painter.prim(Primitive::without(*outer_stairs_bb, *stair_bb)), + Fill::Block(stone_wall), + ); painter.fill(*stair_bb, Fill::Block(vacant)); // Prevent sprites from floating above the stairs let stair_bb_up = painter.prim(Primitive::translate(*stair_bb, Vec3::unit_z())); From ed8fe545f859af9f0e6034d3978300d603bfe7c0 Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Sun, 9 Jul 2023 20:18:48 +0200 Subject: [PATCH 03/22] Fixed temperate dungeon entrance height and wall artifacts around dungeons --- .../dungeon/temperate_entrance/ruins_4.vox | 4 +-- world/src/site2/plot/dungeon.rs | 30 +++++++++++++------ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/assets/world/structure/dungeon/temperate_entrance/ruins_4.vox b/assets/world/structure/dungeon/temperate_entrance/ruins_4.vox index 9a78312a9a..bc5c9fd3ab 100644 --- a/assets/world/structure/dungeon/temperate_entrance/ruins_4.vox +++ b/assets/world/structure/dungeon/temperate_entrance/ruins_4.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3bf9a8a38d5e42b1cf731df40cda238104dd9f17b261d0ce48f89da8f683a376 -size 63972 +oid sha256:c4a1e96fd77bf5c630674666ab1db1dc92a039ed61529f21ff5c505a6fa127da +size 93851 diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 8d6f3ffc74..7ee9320a51 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -1027,10 +1027,6 @@ impl Floor { ) })); - // Fill Walls - let walls_only = painter.prim(Primitive::without(wall_contours, walls_fat)); - painter.fill(walls_only, Fill::Block(stone_wall)); - // The surface 1 unit thicker than the walls is used to place the torches onto let wall_contour_surface = painter.prim(Primitive::sampling(floor_prim, { let tiles = Arc::clone(&tiles); @@ -1096,6 +1092,8 @@ impl Floor { .without(lighting_mask_x.intersect(lighting_mask_y)) }; + let walls_only = painter.prim(Primitive::without(wall_contours, walls_fat)); + // Declare collections of various disjoint primitives that need postprocessing // after handling all the local information per-tile let mut stairs_bb = Vec::new(); @@ -1113,9 +1111,9 @@ impl Floor { min: tile_corner, max: tile_corner + Vec2::broadcast(TILE_SIZE), }; - let outer_tile_aabr = Aabr { - min: tile_corner - Vec2::broadcast(2), - max: tile_corner + Vec2::broadcast(TILE_SIZE + 2), + let outer_tile_aabr = |dist| Aabr { + min: tile_corner - Vec2::broadcast(dist), + max: tile_corner + Vec2::broadcast(TILE_SIZE + dist), }; let tile_center = tile_corner + Vec2::broadcast(TILE_SIZE / 2); let (mut height, room) = match tile { @@ -1126,7 +1124,21 @@ impl Floor { Tile::Solid => continue, }; - let tile_floor_fill = painter.prim(Primitive::Aabb(aabr_with_z(tile_aabr, floor_z - 1..floor_z))); + // TODO: Get outer tile aabb + + let outer_tile_aabb = painter.prim(Primitive::Aabb(aabr_with_z( + outer_tile_aabr(0), + floor_z - 2..(floor_z + tunnel_height as i32) + 2, + ))); + let walls_in_tile = painter.prim(Primitive::intersect(outer_tile_aabb, walls_only)); + + // Fill Walls + painter.fill(walls_in_tile, Fill::Block(stone_wall)); + + let tile_floor_fill = painter.prim(Primitive::Aabb(aabr_with_z( + tile_aabr, + floor_z - 1..floor_z, + ))); painter.fill(tile_floor_fill, Fill::Block(stone_wall)); // Sprites are contained to the level above the floor, and not within walls @@ -1155,7 +1167,7 @@ impl Floor { let radius = TILE_SIZE as f32 / 2.0; let aabb = aabr_with_z(tile_aabr, floor_z..floor_z + self.total_depth()); let outer_aabb = aabr_with_z( - outer_tile_aabr, + outer_tile_aabr(2), floor_z + tunnel_height as i32..floor_z + self.total_depth() - 1, ); let bb = painter.prim(match kind { From 730f96536a366f339bd465645596203eac727a15 Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Wed, 12 Jul 2023 17:26:18 +0200 Subject: [PATCH 04/22] remove staircases --- world/src/site2/plot/dungeon.rs | 306 +++++++++++++++++--------------- 1 file changed, 159 insertions(+), 147 deletions(-) diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 7ee9320a51..8d24f169b1 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -1157,161 +1157,173 @@ impl Floor { let mut chests = None; - if let Some(room) = room.map(|i| self.rooms.get(*i)) { - height = height.min(room.height); - if let Tile::UpStair(_, kind) = tile { - // Construct the staircase that connects this tile to the matching DownStair - // tile on the floor above (or to the surface if this is the top floor), and a - // hollow bounding box to place air in - let center = tile_center.with_z(floor_z); - let radius = TILE_SIZE as f32 / 2.0; - let aabb = aabr_with_z(tile_aabr, floor_z..floor_z + self.total_depth()); - let outer_aabb = aabr_with_z( - outer_tile_aabr(2), - floor_z + tunnel_height as i32..floor_z + self.total_depth() - 1, - ); - let bb = painter.prim(match kind { - StairsKind::Spiral => Primitive::Cylinder(aabb), - StairsKind::WallSpiral => Primitive::Aabb(aabb), - }); - let outer_bb = painter.prim(match kind { - StairsKind::WallSpiral => Primitive::Aabb(outer_aabb), - StairsKind::Spiral => Primitive::Cylinder(outer_aabb), - }); - let stair = painter.prim(Primitive::sampling(bb, match kind { - StairsKind::Spiral => spiral_staircase(center, radius, 0.5, 9.0), - StairsKind::WallSpiral => wall_staircase(center, radius, 27.0), - })); - // Construct the lights that go inside the staircase, starting above the - // ceiling to avoid placing them floating in mid-air - let mut lights = painter.prim(Primitive::Empty); - for i in height..self.total_depth() { - if i % 9 == 0 { - let mut light = painter.prim(Primitive::Aabb(Aabb { - min: aabb.min.with_z(floor_z + i), - max: aabb.max.with_z(floor_z + i + 1), - })); - let inner = painter.prim(Primitive::Aabb(Aabb { - min: (aabb.min + Vec3::new(1, 1, 0)).with_z(floor_z + i), - max: (aabb.max - Vec3::new(1, 1, 0)).with_z(floor_z + i + 1), - })); - - light = painter.prim(Primitive::without(light, inner)); - lights = painter.prim(Primitive::union(light, lights)); + 'room: { + if let Some(room) = room.map(|i| self.rooms.get(*i)) { + height = height.min(room.height); + if let Tile::UpStair(_, kind) = tile { + if self.final_level { + break 'room; } - } - lights = painter.prim(Primitive::intersect(lights, lighting_mask)); - stairs_bb.push(bb); - stair_walls.push(outer_bb); - stairs.push((stair, lights)); - } - if matches!(tile, Tile::Room(_) | Tile::DownStair(_)) { - let seed = room.seed; - let loot_density = room.loot_density; - let difficulty = room.difficulty; - // Place chests with a random distribution based on the - // room's loot density in valid sprite locations, - // filled based on the room's difficulty - let chest_sprite = painter.prim(Primitive::sampling( - sprite_layer, - Box::new(move |pos| RandomField::new(seed).chance(pos, loot_density * 0.5)), - )); - let chest_sprite_fill = Fill::Block(Block::air(match difficulty { - 2 => SpriteKind::DungeonChest2, - 3 => SpriteKind::DungeonChest3, - 4 => SpriteKind::DungeonChest4, - 5 => SpriteKind::DungeonChest5, - _ => SpriteKind::Chest, - })); - chests = Some((chest_sprite, chest_sprite_fill)); - - // If a room has pits, place them - if room.pits.is_some() { - // Make an air pit - let tile_pit = painter.prim(Primitive::Aabb(aabr_with_z( - tile_aabr, - floor_z - 7..floor_z, - ))); - let tile_pit = painter.prim(Primitive::without(tile_pit, wall_contours)); - painter.fill(tile_pit, Fill::Block(vacant)); - - // Fill with lava - let tile_lava = painter.prim(Primitive::Aabb(aabr_with_z( - tile_aabr, - floor_z - 7..floor_z - 5, - ))); - let tile_lava = painter.prim(Primitive::without(tile_lava, wall_contours)); - //pits.push(tile_pit); - //pits.push(tile_lava); - painter.fill(tile_lava, Fill::Block(lava)); - } - if room - .pits - .map(|pit_space| { - tile_pos.map(|e| e.rem_euclid(pit_space) == 0).reduce_and() - }) - .unwrap_or(false) - { - let platform = painter.prim(Primitive::Aabb(Aabb { - min: (tile_center - Vec2::broadcast(pillar_thickness - 1)) - .with_z(floor_z - 7), - max: (tile_center + Vec2::broadcast(pillar_thickness)).with_z(floor_z), + // Construct the staircase that connects this tile to the matching DownStair + // tile on the floor above (or to the surface if this is the top floor), and + // a hollow bounding box to place air in + let center = tile_center.with_z(floor_z); + let radius = TILE_SIZE as f32 / 2.0; + let aabb = aabr_with_z(tile_aabr, floor_z..floor_z + self.total_depth()); + let outer_aabb = aabr_with_z( + outer_tile_aabr(2), + floor_z + tunnel_height as i32..floor_z + self.total_depth() - 1, + ); + let bb = painter.prim(match kind { + StairsKind::Spiral => Primitive::Cylinder(aabb), + StairsKind::WallSpiral => Primitive::Aabb(aabb), + }); + let outer_bb = painter.prim(match kind { + StairsKind::WallSpiral => Primitive::Aabb(outer_aabb), + StairsKind::Spiral => Primitive::Cylinder(outer_aabb), + }); + let stair = painter.prim(Primitive::sampling(bb, match kind { + StairsKind::Spiral => spiral_staircase(center, radius, 0.5, 9.0), + StairsKind::WallSpiral => wall_staircase(center, radius, 27.0), })); - painter.fill(platform, Fill::Block(stone)); - } + // Construct the lights that go inside the staircase, starting above the + // ceiling to avoid placing them floating in mid-air + let mut lights = painter.prim(Primitive::Empty); + for i in height..self.total_depth() { + if i % 9 == 0 { + let mut light = painter.prim(Primitive::Aabb(Aabb { + min: aabb.min.with_z(floor_z + i), + max: aabb.max.with_z(floor_z + i + 1), + })); + let inner = painter.prim(Primitive::Aabb(Aabb { + min: (aabb.min + Vec3::new(1, 1, 0)).with_z(floor_z + i), + max: (aabb.max - Vec3::new(1, 1, 0)).with_z(floor_z + i + 1), + })); - // If a room has pillars, the current tile aligns with the pillar spacing, and - // we're not too close to a wall (i.e. the adjacent tiles are rooms and not - // hallways/solid), place a pillar - if room - .pillars - .map(|pillar_space| { - tile_pos - .map(|e| e.rem_euclid(pillar_space) == 0) - .reduce_and() - }) - .unwrap_or(false) - && DIRS - .iter() - .map(|dir| tile_pos + *dir) - .all(|other_tile_pos| { - matches!(self.tiles.get(other_tile_pos), Some(Tile::Room(_))) + light = painter.prim(Primitive::without(light, inner)); + lights = painter.prim(Primitive::union(light, lights)); + } + } + lights = painter.prim(Primitive::intersect(lights, lighting_mask)); + stairs_bb.push(bb); + stair_walls.push(outer_bb); + stairs.push((stair, lights)); + } + if matches!(tile, Tile::Room(_) | Tile::DownStair(_)) { + let seed = room.seed; + let loot_density = room.loot_density; + let difficulty = room.difficulty; + // Place chests with a random distribution based on the + // room's loot density in valid sprite locations, + // filled based on the room's difficulty + let chest_sprite = painter.prim(Primitive::sampling( + sprite_layer, + Box::new(move |pos| { + RandomField::new(seed).chance(pos, loot_density * 0.5) + }), + )); + let chest_sprite_fill = Fill::Block(Block::air(match difficulty { + 2 => SpriteKind::DungeonChest2, + 3 => SpriteKind::DungeonChest3, + 4 => SpriteKind::DungeonChest4, + 5 => SpriteKind::DungeonChest5, + _ => SpriteKind::Chest, + })); + chests = Some((chest_sprite, chest_sprite_fill)); + + // If a room has pits, place them + if room.pits.is_some() { + // Make an air pit + let tile_pit = painter.prim(Primitive::Aabb(aabr_with_z( + tile_aabr, + floor_z - 7..floor_z, + ))); + let tile_pit = + painter.prim(Primitive::without(tile_pit, wall_contours)); + painter.fill(tile_pit, Fill::Block(vacant)); + + // Fill with lava + let tile_lava = painter.prim(Primitive::Aabb(aabr_with_z( + tile_aabr, + floor_z - 7..floor_z - 5, + ))); + let tile_lava = + painter.prim(Primitive::without(tile_lava, wall_contours)); + //pits.push(tile_pit); + //pits.push(tile_lava); + painter.fill(tile_lava, Fill::Block(lava)); + } + if room + .pits + .map(|pit_space| { + tile_pos.map(|e| e.rem_euclid(pit_space) == 0).reduce_and() }) - { - let mut pillar = painter.prim(Primitive::Cylinder(Aabb { - min: (tile_center - Vec2::broadcast(pillar_thickness - 1)) - .with_z(floor_z), - max: (tile_center + Vec2::broadcast(pillar_thickness)) - .with_z(floor_z + height), - })); - let base = painter.prim(Primitive::Cylinder(Aabb { - min: (tile_center - Vec2::broadcast(1 + pillar_thickness - 1)) - .with_z(floor_z), - max: (tile_center + Vec2::broadcast(1 + pillar_thickness)) - .with_z(floor_z + 1), - })); - - let scale = (pillar_thickness + 2) as f32 / pillar_thickness as f32; - let mut lights = painter - .prim(Primitive::scale(pillar, Vec2::broadcast(scale).with_z(1.0))); - lights = painter.prim(Primitive::intersect(lighting_plane, lights)); - // Only add the base (and shift the lights up) - // for boss-rooms pillars - if room.kind == RoomKind::Boss { - lights = painter.prim(Primitive::translate(lights, 3 * Vec3::unit_z())); - pillar = painter.prim(Primitive::union(pillar, base)); + .unwrap_or(false) + { + let platform = painter.prim(Primitive::Aabb(Aabb { + min: (tile_center - Vec2::broadcast(pillar_thickness - 1)) + .with_z(floor_z - 7), + max: (tile_center + Vec2::broadcast(pillar_thickness)) + .with_z(floor_z), + })); + painter.fill(platform, Fill::Block(stone)); } - // Specifically don't include pillars in Minotaur arena - if !(room.kind == RoomKind::Boss && self.difficulty == 4) { - pillars.push((tile_center, pillar, lights)); + + // If a room has pillars, the current tile aligns with the pillar spacing, + // and we're not too close to a wall (i.e. the + // adjacent tiles are rooms and not hallways/solid), + // place a pillar + if room + .pillars + .map(|pillar_space| { + tile_pos + .map(|e| e.rem_euclid(pillar_space) == 0) + .reduce_and() + }) + .unwrap_or(false) + && DIRS + .iter() + .map(|dir| tile_pos + *dir) + .all(|other_tile_pos| { + matches!(self.tiles.get(other_tile_pos), Some(Tile::Room(_))) + }) + { + let mut pillar = painter.prim(Primitive::Cylinder(Aabb { + min: (tile_center - Vec2::broadcast(pillar_thickness - 1)) + .with_z(floor_z), + max: (tile_center + Vec2::broadcast(pillar_thickness)) + .with_z(floor_z + height), + })); + let base = painter.prim(Primitive::Cylinder(Aabb { + min: (tile_center - Vec2::broadcast(1 + pillar_thickness - 1)) + .with_z(floor_z), + max: (tile_center + Vec2::broadcast(1 + pillar_thickness)) + .with_z(floor_z + 1), + })); + + let scale = (pillar_thickness + 2) as f32 / pillar_thickness as f32; + let mut lights = painter + .prim(Primitive::scale(pillar, Vec2::broadcast(scale).with_z(1.0))); + lights = painter.prim(Primitive::intersect(lighting_plane, lights)); + // Only add the base (and shift the lights up) + // for boss-rooms pillars + if room.kind == RoomKind::Boss { + lights = + painter.prim(Primitive::translate(lights, 3 * Vec3::unit_z())); + pillar = painter.prim(Primitive::union(pillar, base)); + } + // Specifically don't include pillars in Minotaur arena + if !(room.kind == RoomKind::Boss && self.difficulty == 4) { + pillars.push((tile_center, pillar, lights)); + } } } - } - // Keep track of the boss room to be able to add decorations later - if room.kind == RoomKind::Boss { - boss_room_center = - Some(floor_corner + TILE_SIZE * room.area.center() + TILE_SIZE / 2); + // Keep track of the boss room to be able to add decorations later + if room.kind == RoomKind::Boss { + boss_room_center = + Some(floor_corner + TILE_SIZE * room.area.center() + TILE_SIZE / 2); + } } } From 0f2db6849810d11a6e3cd0f9835540fdb5a1eb40 Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Sat, 15 Jul 2023 22:40:22 +0200 Subject: [PATCH 05/22] added portals --- assets/voxygen/voxel/object/portal.vox | 3 + assets/voxygen/voxel/object_manifest.ron | 10 +++ common/net/src/synced_components.rs | 6 ++ common/src/cmd.rs | 12 +++ common/src/comp/body/object.rs | 6 +- common/src/comp/misc.rs | 11 +++ common/src/comp/mod.rs | 2 +- common/src/event.rs | 3 +- common/src/generation.rs | 18 ++-- common/state/src/state.rs | 1 + server/src/cmd.rs | 35 ++++++++ server/src/events/entity_creation.rs | 9 +- server/src/events/mod.rs | 8 +- server/src/rtsim/tick.rs | 2 + server/src/state_ext.rs | 19 ++++ server/src/sys/mod.rs | 2 + server/src/sys/teleporter.rs | 87 +++++++++++++++++++ server/src/sys/terrain.rs | 17 ++-- world/src/lib.rs | 5 +- world/src/site2/plot/cliff_tower.rs | 7 +- world/src/site2/plot/coastal_workshop.rs | 4 +- world/src/site2/plot/desert_city_multiplot.rs | 7 +- world/src/site2/plot/dungeon.rs | 32 ++++++- world/src/site2/plot/dwarven_mine.rs | 5 +- world/src/site2/plot/savannah_pit.rs | 6 +- world/src/site2/plot/savannah_workshop.rs | 8 +- world/src/site2/plot/workshop.rs | 4 +- 27 files changed, 296 insertions(+), 33 deletions(-) create mode 100644 assets/voxygen/voxel/object/portal.vox create mode 100644 server/src/sys/teleporter.rs diff --git a/assets/voxygen/voxel/object/portal.vox b/assets/voxygen/voxel/object/portal.vox new file mode 100644 index 0000000000..f30dc90ddf --- /dev/null +++ b/assets/voxygen/voxel/object/portal.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:af311d5461a03bec05799d20b3298b5d98f07a108603300b0514376965aa02e2 +size 28971 diff --git a/assets/voxygen/voxel/object_manifest.ron b/assets/voxygen/voxel/object_manifest.ron index 6b220d517d..9592647da3 100644 --- a/assets/voxygen/voxel/object_manifest.ron +++ b/assets/voxygen/voxel/object_manifest.ron @@ -979,4 +979,14 @@ central: ("armor.empty"), ) ), + Portal: ( + bone0: ( + offset: (-11.0, -1.5, 0.0), + central: ("object.portal"), + ), + bone1: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ) }) diff --git a/common/net/src/synced_components.rs b/common/net/src/synced_components.rs index d8d5bf858b..bf8cc26a19 100755 --- a/common/net/src/synced_components.rs +++ b/common/net/src/synced_components.rs @@ -102,6 +102,8 @@ synced_components!(reexport_comps); // === NetSync implementations === // =============================== +use common::comp::Teleporter; + use crate::sync::{NetSync, SyncFrom}; impl NetSync for Body { @@ -234,6 +236,10 @@ impl NetSync for Stance { const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity; } +impl NetSync for Teleporter { + const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity; +} + // These are synced only from the client's own entity. impl NetSync for Admin { diff --git a/common/src/cmd.rs b/common/src/cmd.rs index a420e7224b..9c201563e7 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -310,6 +310,7 @@ pub enum ServerChatCommand { Object, PermitBuild, Players, + Portal, Region, ReloadChunks, RemoveLights, @@ -618,6 +619,16 @@ impl ServerChatCommand { Some(Admin), ), ServerChatCommand::Players => cmd(vec![], "Lists players currently online", None), + ServerChatCommand::Portal => cmd( + vec![ + Float("x", 0., Required), + Float("y", 0., Required), + Float("z", 0., Required), + Boolean("requires_no_aggro", "true".to_string(), Optional), + ], + "Spawns a portal", + Some(Moderator), + ), ServerChatCommand::ReloadChunks => cmd( vec![], "Reloads all chunks loaded on the server", @@ -890,6 +901,7 @@ impl ServerChatCommand { ServerChatCommand::Object => "object", ServerChatCommand::PermitBuild => "permit_build", ServerChatCommand::Players => "players", + ServerChatCommand::Portal => "portal", ServerChatCommand::Region => "region", ServerChatCommand::ReloadChunks => "reload_chunks", ServerChatCommand::RemoveLights => "remove_lights", diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index 44735011e1..261c4b6cd5 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -110,6 +110,7 @@ make_case_elim!( Mine = 95, LightningBolt = 96, SpearIcicle = 97, + Portal = 98, } ); @@ -120,7 +121,7 @@ impl Body { } } -pub const ALL_OBJECTS: [Body; 98] = [ +pub const ALL_OBJECTS: [Body; 99] = [ Body::Arrow, Body::Bomb, Body::Scarecrow, @@ -219,6 +220,7 @@ pub const ALL_OBJECTS: [Body; 98] = [ Body::Mine, Body::LightningBolt, Body::SpearIcicle, + Body::Portal, ]; impl From for super::Body { @@ -326,6 +328,7 @@ impl Body { Body::Mine => "mine", Body::LightningBolt => "lightning_bolt", Body::SpearIcicle => "spear_icicle", + Body::Portal => "portal", } } @@ -450,6 +453,7 @@ impl Body { Body::AdletTrap => 10.0, Body::Mine => 100.0, Body::LightningBolt | Body::SpearIcicle => 20000.0, + Body::Portal => 10., // I dont know really }; Mass(m) diff --git a/common/src/comp/misc.rs b/common/src/comp/misc.rs index c803ec64e9..f6002140f1 100644 --- a/common/src/comp/misc.rs +++ b/common/src/comp/misc.rs @@ -3,6 +3,7 @@ use crate::{resources::Time, uid::Uid}; use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage}; use std::time::Duration; +use vek::Vec3; #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Object { @@ -22,3 +23,13 @@ pub enum Object { impl Component for Object { type Storage = DerefFlaggedStorage>; } + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Teleporter { + pub target: Vec3, + pub requires_no_aggro: bool, +} + +impl Component for Teleporter { + type Storage = DerefFlaggedStorage>; +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 198bf4402d..5fb3d86e07 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -86,7 +86,7 @@ pub use self::{ location::{MapMarker, MapMarkerChange, MapMarkerUpdate, Waypoint, WaypointArea}, loot_owner::LootOwner, melee::{Melee, MeleeConstructor, MeleeConstructorKind}, - misc::Object, + misc::{Object, Teleporter}, ori::Ori, pet::Pet, phys::{ diff --git a/common/src/event.rs b/common/src/event.rs index 006607afd5..eef17c1035 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -6,7 +6,7 @@ use crate::{ agent::Sound, dialogue::Subject, invite::{InviteKind, InviteResponse}, - DisconnectReason, Ori, Pos, + DisconnectReason, Ori, Pos, Teleporter, }, lottery::LootSpec, mounting::VolumePos, @@ -239,6 +239,7 @@ pub enum ServerEvent { driver: Option, }, CreateWaypoint(Vec3), + CreateTeleporter(Vec3, Teleporter), ClientDisconnect(EcsEntity, DisconnectReason), ClientDisconnectWithoutPersistence(EcsEntity), Command(EcsEntity, String, Vec), diff --git a/common/src/generation.rs b/common/src/generation.rs index 404833a148..4818ff5554 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -3,7 +3,7 @@ use crate::{ comp::{ self, agent, humanoid, inventory::loadout_builder::{LoadoutBuilder, LoadoutSpec}, - Alignment, Body, Item, + Alignment, Body, Item, Teleporter, }, lottery::LootSpec, npc::{self, NPC_NAMES}, @@ -163,10 +163,15 @@ pub fn try_all_entity_configs() -> Result, Error> { Ok(configs.ids().map(|id| id.to_string()).collect()) } +#[derive(Clone)] +pub enum SpecialEntity { + Waypoint, + Teleporter(Teleporter), +} + #[derive(Clone)] pub struct EntityInfo { pub pos: Vec3, - pub is_waypoint: bool, // Edge case, overrides everything else pub alignment: Alignment, /// Parameterises agent behaviour pub has_agency: bool, @@ -194,13 +199,15 @@ pub struct EntityInfo { // we can't use DHashMap, do we want to move that into common? pub trading_information: Option, //Option>, /* price and available amount */ + + // Edge cases, override everything else + pub special_entity: Option, // Campfire } impl EntityInfo { pub fn at(pos: Vec3) -> Self { Self { pos, - is_waypoint: false, alignment: Alignment::Wild, has_agency: true, @@ -219,6 +226,7 @@ impl EntityInfo { skillset_asset: None, pet: None, trading_information: None, + special_entity: None, } } @@ -377,8 +385,8 @@ impl EntityInfo { } #[must_use] - pub fn into_waypoint(mut self) -> Self { - self.is_waypoint = true; + pub fn into_special(mut self, special: SpecialEntity) -> Self { + self.special_entity = Some(special); self } diff --git a/common/state/src/state.rs b/common/state/src/state.rs index 7771d54f00..84341cf56e 100644 --- a/common/state/src/state.rs +++ b/common/state/src/state.rs @@ -219,6 +219,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); // Register components send from clients -> server ecs.register::(); diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 5b1a4fbba7..fe2bf29404 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -167,6 +167,7 @@ fn do_command( ServerChatCommand::Object => handle_object, ServerChatCommand::PermitBuild => handle_permit_build, ServerChatCommand::Players => handle_players, + ServerChatCommand::Portal => handle_spawn_portal, ServerChatCommand::Region => handle_region, ServerChatCommand::ReloadChunks => handle_reload_chunks, ServerChatCommand::RemoveLights => handle_remove_lights, @@ -694,6 +695,9 @@ fn handle_make_npc( NpcData::Waypoint(_) => { return Err("Waypoint spawning is not implemented".to_owned()); }, + NpcData::Teleporter(_, _) => { + return Err("Teleporter spawning is not implemented".to_owned()); + }, NpcData::Data { inventory, pos, @@ -2038,6 +2042,37 @@ fn handle_players( Ok(()) } +fn handle_spawn_portal( + server: &mut Server, + client: EcsEntity, + target: EcsEntity, + args: Vec, + _action: &ServerChatCommand, +) -> CmdResult<()> { + let pos = position(server, target, "target")?; + + if let (Some(x), Some(y), Some(z), requires_no_aggro) = + parse_cmd_args!(args, f32, f32, f32, bool) + { + let requires_no_aggro = requires_no_aggro.unwrap_or(false); + server + .state + .create_teleporter(pos, comp::Teleporter { + target: Vec3::new(x, y, z), + requires_no_aggro, + }) + .build(); + + server.notify_client( + client, + ServerGeneral::server_msg(ChatType::CommandInfo, "Spawned portal"), + ); + Ok(()) + } else { + Err("Invalid arguments".to_string()) + } +} + fn handle_build( server: &mut Server, client: EcsEntity, diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 1362722ccc..7d36516d02 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -11,7 +11,7 @@ use common::{ buff::{BuffCategory, BuffData, BuffKind, BuffSource}, ship::figuredata::VOXEL_COLLIDER_MANIFEST, shockwave, Alignment, BehaviorCapability, Body, ItemDrops, LightEmitter, Object, Ori, Pos, - Projectile, TradingBehavior, Vel, WaypointArea, + Projectile, Teleporter, TradingBehavior, Vel, WaypointArea, }, event::{EventBus, NpcBuilder, UpdateCharacterMetadata}, mounting::{Mounting, Volume, VolumeMounting, VolumePos}, @@ -418,3 +418,10 @@ pub fn handle_create_waypoint(server: &mut Server, pos: Vec3) { ])) .build(); } + +pub fn handle_create_teleporter(server: &mut Server, pos: Vec3, teleporter: Teleporter) { + server + .state + .create_teleporter(comp::Pos(pos), teleporter) + .build(); +} diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 1173f9267b..24e519b8a2 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -1,5 +1,8 @@ use crate::{ - events::interaction::{handle_mount_volume, handle_tame_pet}, + events::{ + entity_creation::handle_create_teleporter, + interaction::{handle_mount_volume, handle_tame_pet}, + }, persistence::PersistedComponents, state_ext::StateExt, Server, @@ -209,6 +212,9 @@ impl Server { driver, } => handle_create_ship(self, pos, ori, ship, rtsim_entity, driver, Vec::new()), ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos), + ServerEvent::CreateTeleporter(pos, teleporter) => { + handle_create_teleporter(self, pos, teleporter) + }, ServerEvent::ClientDisconnect(entity, reason) => { frontend_events.push(handle_client_disconnect(self, entity, reason, false)) }, diff --git a/server/src/rtsim/tick.rs b/server/src/rtsim/tick.rs index e9edcb9000..af6938be4d 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -338,6 +338,7 @@ impl<'a> System<'a> for Sys { // as of now, and if someone will try to spawn // rtsim waypoint it is definitely error. NpcData::Waypoint(_) => unimplemented!(), + NpcData::Teleporter(_, _) => unimplemented!(), }) } else { error!("Npc is loaded but vehicle is unloaded"); @@ -401,6 +402,7 @@ impl<'a> System<'a> for Sys { // as of now, and if someone will try to spawn // rtsim waypoint it is definitely error. NpcData::Waypoint(_) => unimplemented!(), + NpcData::Teleporter(_, _) => unimplemented!(), }); } } diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index ae77bd5d53..3602e3f64f 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -18,6 +18,7 @@ use common::{ comp::{ self, item::{ItemKind, MaterialStatManifest}, + object, skills::{GeneralSkill, Skill}, ChatType, Group, Inventory, Item, LootOwner, Object, Player, Poise, Presence, PresenceKind, }, @@ -114,6 +115,14 @@ pub trait StateExt { world: &std::sync::Arc, index: &world::IndexOwned, ) -> EcsEntityBuilder; + /// Creates a teleporter entity, which allows players to teleport to the + /// `target` position. You might want to require the teleporting entity + /// to not have agro for teleporting. + fn create_teleporter( + &mut self, + pos: comp::Pos, + teleporter: comp::Teleporter, + ) -> EcsEntityBuilder; /// Insert common/default components for a new character joining the server fn initialize_character_data( &mut self, @@ -607,6 +616,16 @@ impl StateExt for State { )) } + fn create_teleporter( + &mut self, + pos: comp::Pos, + teleporter: comp::Teleporter, + ) -> EcsEntityBuilder { + self.create_object(pos, object::Body::Portal) + .with(comp::Immovable) + .with(teleporter) + } + fn initialize_character_data( &mut self, entity: EcsEntity, diff --git a/server/src/sys/mod.rs b/server/src/sys/mod.rs index 59e2b446f6..9e71553634 100644 --- a/server/src/sys/mod.rs +++ b/server/src/sys/mod.rs @@ -11,6 +11,7 @@ pub mod persistence; pub mod pets; pub mod sentinel; pub mod subscription; +pub mod teleporter; pub mod terrain; pub mod terrain_sync; pub mod waypoint; @@ -32,6 +33,7 @@ pub fn add_server_systems(dispatch_builder: &mut DispatcherBuilder) { dispatch::(dispatch_builder, &[]); dispatch::(dispatch_builder, &[&msg::terrain::Sys::sys_name()]); dispatch::(dispatch_builder, &[]); + dispatch::(dispatch_builder, &[]); dispatch::(dispatch_builder, &[]); dispatch::(dispatch_builder, &[]); dispatch::(dispatch_builder, &[]); diff --git a/server/src/sys/teleporter.rs b/server/src/sys/teleporter.rs new file mode 100644 index 0000000000..442dae21de --- /dev/null +++ b/server/src/sys/teleporter.rs @@ -0,0 +1,87 @@ +use common::{ + comp::{Agent, ForceUpdate, Player, Pos, Teleporter}, + CachedSpatialGrid, +}; +use common_ecs::{Origin, Phase, System}; +use specs::{Entities, Join, Read, ReadStorage, WriteStorage}; + +const TELEPORT_RADIUS: f32 = 1.; +const MAX_AGGRO_DIST: f32 = 200.; // If an entity further than this is aggroed at a player, the portal will still work + +#[derive(Default)] +pub struct Sys; + +impl<'a> System<'a> for Sys { + type SystemData = ( + Entities<'a>, + WriteStorage<'a, Pos>, + ReadStorage<'a, Player>, + ReadStorage<'a, Teleporter>, + ReadStorage<'a, Agent>, + WriteStorage<'a, ForceUpdate>, + Read<'a, CachedSpatialGrid>, + ); + + const NAME: &'static str = "teleporter"; + const ORIGIN: Origin = Origin::Server; + const PHASE: Phase = Phase::Create; + + fn run( + _job: &mut common_ecs::Job, + ( + entities, + mut positions, + players, + teleporters, + agent, + mut forced_update, + spatial_grid, + ): Self::SystemData, + ) { + let mut attempt_teleport = vec![]; + let mut player_data = (&entities, &positions, &players).join(); + + for (_entity, teleporter_pos, teleporter) in (&entities, &positions, &teleporters).join() { + let nearby_entities = spatial_grid + .0 + .in_circle_aabr(teleporter_pos.0.xy(), TELEPORT_RADIUS); + + for (entity, position, _) in nearby_entities.filter_map(|entity| { + player_data + .get(entity, &entities) + .filter(|(_, player_pos, _)| { + player_pos.0.distance_squared(teleporter_pos.0) <= (TELEPORT_RADIUS).powi(2) + }) + }) { + // TODO: Check for aggro + attempt_teleport.push((entity, position.0, teleporter)) + } + } + + for (entity, origin_pos, teleporter) in attempt_teleport { + if teleporter.requires_no_aggro { + // FIXME: How does this go with performance? + let is_aggroed = spatial_grid + .0 + .in_circle_aabr(origin_pos.xy(), MAX_AGGRO_DIST) + .any(|agent_entity| { + agent.get(agent_entity).map_or(false, |agent| { + agent.target.map_or(false, |agent_target| { + agent_target.target == entity && agent_target.aggro_on + }) + }) + }); + + if is_aggroed { + continue; + } + } + positions + .get_mut(entity) + .map(|position| position.0 = teleporter.target); + forced_update + .get_mut(entity) + .map(|forced_update| forced_update.update()); + } + } +} diff --git a/server/src/sys/terrain.rs b/server/src/sys/terrain.rs index 185848174c..5ce339ed22 100644 --- a/server/src/sys/terrain.rs +++ b/server/src/sys/terrain.rs @@ -13,10 +13,10 @@ use common::{ calendar::Calendar, comp::{ self, agent, biped_small, bird_medium, skillset::skills, BehaviorCapability, ForceUpdate, - Pos, Presence, Waypoint, + Pos, Presence, Teleporter, Waypoint, }, event::{EventBus, NpcBuilder, ServerEvent}, - generation::EntityInfo, + generation::{EntityInfo, SpecialEntity}, lottery::LootSpec, resources::{Time, TimeOfDay}, slowjob::SlowJobPool, @@ -222,6 +222,9 @@ impl<'a> System<'a> for Sys { .with_loot(loot), }); }, + NpcData::Teleporter(pos, teleporter) => { + server_emitter.emit(ServerEvent::CreateTeleporter(pos, teleporter)); + }, } } } @@ -415,13 +418,14 @@ pub enum NpcData { loot: LootSpec, }, Waypoint(Vec3), + Teleporter(Vec3, Teleporter), } impl NpcData { pub fn from_entity_info(entity: EntityInfo) -> Self { let EntityInfo { // flags - is_waypoint, + special_entity, has_agency, agent_mark, alignment, @@ -444,8 +448,11 @@ impl NpcData { pet: _, // TODO: I had no idea we have this. } = entity; - if is_waypoint { - return Self::Waypoint(pos); + if let Some(special) = special_entity { + return match special { + SpecialEntity::Waypoint => Self::Waypoint(pos), + SpecialEntity::Teleporter(teleporter) => Self::Teleporter(pos, teleporter), + }; } let name = name.unwrap_or_else(|| "Unnamed".to_string()); diff --git a/world/src/lib.rs b/world/src/lib.rs index 4a2ebbfb28..4cc675b468 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -46,7 +46,7 @@ use crate::{ use common::{ assets, calendar::Calendar, - generation::{ChunkSupplement, EntityInfo}, + generation::{ChunkSupplement, EntityInfo, SpecialEntity}, lod, resources::TimeOfDay, rtsim::ChunkResource, @@ -487,7 +487,8 @@ impl World { .fold(SpawnRules::default(), |a, b| a.combine(b)) .waypoints { - supplement.add_entity(EntityInfo::at(waypoint_pos).into_waypoint()); + supplement + .add_entity(EntityInfo::at(waypoint_pos).into_special(SpecialEntity::Waypoint)); } } diff --git a/world/src/site2/plot/cliff_tower.rs b/world/src/site2/plot/cliff_tower.rs index b0015977c0..0babab8929 100644 --- a/world/src/site2/plot/cliff_tower.rs +++ b/world/src/site2/plot/cliff_tower.rs @@ -4,7 +4,7 @@ use crate::{ Land, }; use common::{ - generation::EntityInfo, + generation::{EntityInfo, SpecialEntity}, terrain::{BlockKind, SpriteKind}, }; use rand::prelude::*; @@ -752,7 +752,10 @@ impl Structure for CliffTower { // spawn campfire next to some clifftowers if self.campfire { let campfire_pos = (plot_center - 20).with_z(self.alt + 18); - painter.spawn(EntityInfo::at(campfire_pos.map(|e| e as f32)).into_waypoint()); + painter.spawn( + EntityInfo::at(campfire_pos.map(|e| e as f32)) + .into_special(SpecialEntity::Waypoint), + ); } } } diff --git a/world/src/site2/plot/coastal_workshop.rs b/world/src/site2/plot/coastal_workshop.rs index 129119eb41..c81931a150 100644 --- a/world/src/site2/plot/coastal_workshop.rs +++ b/world/src/site2/plot/coastal_workshop.rs @@ -3,7 +3,7 @@ use crate::{ util::{RandomField, Sampler, CARDINALS}, Land, }; -use common::terrain::{BlockKind, SpriteKind}; +use common::{terrain::{BlockKind, SpriteKind}, generation::SpecialEntity}; use rand::prelude::*; use std::sync::Arc; use vek::*; @@ -326,7 +326,7 @@ impl Structure for CoastalWorkshop { } painter.spawn( - EntityInfo::at((center - 2).with_z(base - 2).map(|e| e as f32 + 0.5)).into_waypoint(), + EntityInfo::at((center - 2).with_z(base - 2).map(|e| e as f32 + 0.5)).into_special(SpecialEntity::Waypoint), ); } } diff --git a/world/src/site2/plot/desert_city_multiplot.rs b/world/src/site2/plot/desert_city_multiplot.rs index b9a10af893..d7cadc4eff 100644 --- a/world/src/site2/plot/desert_city_multiplot.rs +++ b/world/src/site2/plot/desert_city_multiplot.rs @@ -6,7 +6,7 @@ use crate::{ Land, }; use common::{ - generation::EntityInfo, + generation::{EntityInfo, SpecialEntity}, terrain::{Block, BlockKind, SpriteKind, Structure as PrefabStructure, StructuresGroup}, }; use lazy_static::lazy_static; @@ -2432,7 +2432,10 @@ impl Structure for DesertCityMultiPlot { // spawn campfire in some multiplots that are not markethall let campfire_pos = (center).with_z(base + 1); if self.campfire { - painter.spawn(EntityInfo::at(campfire_pos.map(|e| e as f32)).into_waypoint()) + painter.spawn( + EntityInfo::at(campfire_pos.map(|e| e as f32)) + .into_special(SpecialEntity::Waypoint), + ) } }, } diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 8d24f169b1..cd0d9cbf98 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -9,7 +9,8 @@ use crate::{ use common::{ assets::{self, AssetExt, AssetHandle}, astar::Astar, - generation::{ChunkSupplement, EntityInfo}, + comp::Teleporter, + generation::{ChunkSupplement, EntityInfo, SpecialEntity}, store::{Id, Store}, terrain::{ BiomeKind, Block, BlockKind, SpriteKind, Structure, StructuresGroup, TerrainChunkSize, @@ -146,7 +147,7 @@ impl Dungeon { if area.contains_point(pos - self.origin) { supplement.add_entity( EntityInfo::at(Vec3::new(pos.x as f32, pos.y as f32, self.alt as f32) + 5.0) - .into_waypoint(), + .into_special(SpecialEntity::Waypoint), ); } @@ -615,6 +616,14 @@ impl Floor { 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); + let is_boss_tile = self.tiles.get(tile_pos).filter(|_| self.final_level); + + let tile_wcenter = origin + + Vec3::from( + Vec2::new(x, y) + .map(|e| e.div_euclid(TILE_SIZE) * TILE_SIZE + TILE_SIZE / 2), + ); + if let Some(Tile::Room(room)) = self.tiles.get(tile_pos) { let room = &self.rooms[*room]; @@ -648,6 +657,25 @@ impl Floor { ), RoomKind::Peaceful | RoomKind::LavaPlatforming => {}, } + } else if let Some(Tile::UpStair(_, _)) = is_boss_tile && tile_wcenter.xy() == wpos2d { + // Create one teleporter at the top of the "stairs" and one at the botton + let bottom_pos = tile_wcenter.map(|v| v as f32) ; + let top_pos = + (tile_wcenter + Vec3::unit_z() * self.total_depth()).map(|v| v as f32); + + // Move both a bit to the side to prevent teleportation loop, ideally we'd have the portals at another location + supplement.add_entity(EntityInfo::at(top_pos).into_special( + SpecialEntity::Teleporter(Teleporter { + target: bottom_pos+ Vec3::unit_x() * 5., + requires_no_aggro: false, + }), + )); + supplement.add_entity(EntityInfo::at(bottom_pos).into_special( + SpecialEntity::Teleporter(Teleporter { + target: top_pos+ Vec3::unit_x() * 5., + requires_no_aggro: true, + }), + )); } } } diff --git a/world/src/site2/plot/dwarven_mine.rs b/world/src/site2/plot/dwarven_mine.rs index 4e5a64e80a..bf09aa574a 100644 --- a/world/src/site2/plot/dwarven_mine.rs +++ b/world/src/site2/plot/dwarven_mine.rs @@ -13,7 +13,7 @@ ChatGPT: Improving Code Quality, Speeding up finding Syntax Errors use super::*; use crate::{site2::gen::PrimitiveTransform, Land}; -use common::terrain::Structure as PrefabStructure; +use common::{generation::SpecialEntity, terrain::Structure as PrefabStructure}; use rand::prelude::*; use vek::*; @@ -166,7 +166,8 @@ impl Structure for DwarvenMine { let entrance_offset: Vec3 = (20.0, -20.0, 45.0).into(); // Spawn waypoint let waypoint_pos = (entrance_pos + Vec3::new(1, -1, 4)).map(|x| x as f32) + entrance_offset; - painter.spawn(EntityInfo::at(waypoint_pos.map(|e| e)).into_waypoint()); + painter + .spawn(EntityInfo::at(waypoint_pos.map(|e| e)).into_special(SpecialEntity::Waypoint)); let miner_pos: Vec<(Vec3, &str)> = vec![ // Entrance diff --git a/world/src/site2/plot/savannah_pit.rs b/world/src/site2/plot/savannah_pit.rs index b626d2da97..a614f001d4 100644 --- a/world/src/site2/plot/savannah_pit.rs +++ b/world/src/site2/plot/savannah_pit.rs @@ -4,7 +4,7 @@ use crate::{ Land, }; use common::{ - generation::EntityInfo, + generation::{EntityInfo, SpecialEntity}, terrain::{BlockKind, SpriteKind}, }; use rand::prelude::*; @@ -942,7 +942,9 @@ impl Structure for SavannahPit { // campfire let campfire_pos = (center - 10).with_z(base); - painter.spawn(EntityInfo::at(campfire_pos.map(|e| e as f32)).into_waypoint()); + painter.spawn( + EntityInfo::at(campfire_pos.map(|e| e as f32)).into_special(SpecialEntity::Waypoint), + ); // underground market hall let stairs_pos = Vec2::new(center.x, center.y - (2 * length) + 1); diff --git a/world/src/site2/plot/savannah_workshop.rs b/world/src/site2/plot/savannah_workshop.rs index 8cffcf37c8..7d2fcef6f7 100644 --- a/world/src/site2/plot/savannah_workshop.rs +++ b/world/src/site2/plot/savannah_workshop.rs @@ -3,7 +3,10 @@ use crate::{ util::{RandomField, Sampler, CARDINALS, DIAGONALS}, Land, }; -use common::terrain::{BlockKind, SpriteKind}; +use common::{ + generation::SpecialEntity, + terrain::{BlockKind, SpriteKind}, +}; use rand::prelude::*; use std::{f32::consts::TAU, sync::Arc}; use vek::*; @@ -302,7 +305,8 @@ impl Structure for SavannahWorkshop { } painter.spawn( - EntityInfo::at((center).with_z(base - 2).map(|e| e as f32 + 0.5)).into_waypoint(), + EntityInfo::at((center).with_z(base - 2).map(|e| e as f32 + 0.5)) + .into_special(SpecialEntity::Waypoint), ); } } diff --git a/world/src/site2/plot/workshop.rs b/world/src/site2/plot/workshop.rs index dc06b9601d..805feba992 100644 --- a/world/src/site2/plot/workshop.rs +++ b/world/src/site2/plot/workshop.rs @@ -4,7 +4,7 @@ use crate::{ Land, }; use common::{ - generation::EntityInfo, + generation::{EntityInfo, SpecialEntity}, terrain::{Block, BlockKind, SpriteKind}, }; use rand::prelude::*; @@ -145,7 +145,7 @@ impl Structure for Workshop { painter.spawn( EntityInfo::at(self.bounds.center().with_z(base).map(|e| e as f32 + 0.5)) - .into_waypoint(), + .into_special(SpecialEntity::Waypoint), ); } } From 14141ecc80f2648596ca94eb94123034654185a4 Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Mon, 17 Jul 2023 21:21:18 +0200 Subject: [PATCH 06/22] failed attempt at making dungeons avoid caves --- CHANGELOG.md | 2 ++ world/src/civ/mod.rs | 17 +++++++++++++---- world/src/layer/cave.rs | 4 ++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea621f30eb..b42e196162 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Recipe for a new leather pack - Keybinds for zooming the camera (Defaults: ']' for zooming in and '[' for zooming out) - Added the ability to make pets sit, they wont follow nor defend you in this state +- Portals that spawn in place of the last staircase at old style dungeons to prevent stair cheesing ### Changed @@ -53,6 +54,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed wild roaming cyclop loot table to not drop the quarry key +- Dungeons now have an outer wall, preventing them from intersecting with caves or leaving holes in sides of mountains. ## [0.15.0] - 2023-07-01 diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index b18dbaac0e..d97726b476 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -1884,14 +1884,23 @@ impl SiteKind { on_land() && { let land = Land::from_sim(sim); let loc = loc.map(|v| TerrainChunkSize::RECT_SIZE.y as i32 * v); + let dungeon_aabr = Aabr { + min: loc - Vec2::broadcast(200), + max: loc + Vec2::broadcast(200), + }; // Get shallow caves under the dungeon - let mut tunnels = - cave::tunnels_at(loc /* very surely wrong */, 1, &land) - .chain(cave::tunnels_at(loc, 2, &land)); + let mut tunnels = cave::tunnels_at(loc, 1, &land) + .chain(cave::tunnels_at(loc, 2, &land)) + .filter(|tunnel| { + dungeon_aabr.collides_with_aabr(Aabr { + min: tunnel.a.wpos, + max: tunnel.b.wpos, + }) + }); // Make sure there are no shallow caves near the dungeon - tunnels.next().is_none() || true + tunnels.next().is_none() } }, SiteKind::Refactor | SiteKind::Settlement => suitable_for_town(), diff --git a/world/src/layer/cave.rs b/world/src/layer/cave.rs index 4ee40b67a7..a95e183bd7 100644 --- a/world/src/layer/cave.rs +++ b/world/src/layer/cave.rs @@ -76,8 +76,8 @@ pub fn surface_entrances<'a>(land: &'a Land) -> impl Iterator> } pub struct Tunnel { - a: Node, - b: Node, + pub a: Node, + pub b: Node, curve: f32, } From 25fcb04ad363e86a7594865c8d2ddc5ba5c263d0 Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Tue, 18 Jul 2023 00:47:19 +0200 Subject: [PATCH 07/22] buildup times and portal model update --- assets/voxygen/voxel/object/portal.vox | 4 +- assets/voxygen/voxel/object/portal_active.vox | 3 + assets/voxygen/voxel/object_manifest.ron | 14 ++- common/src/cmd.rs | 1 + common/src/comp/body/object.rs | 7 +- common/src/comp/misc.rs | 11 -- common/src/comp/mod.rs | 4 +- common/src/comp/teleport.rs | 27 ++++ common/state/src/state.rs | 1 + server/src/cmd.rs | 6 +- server/src/sys/teleporter.rs | 115 ++++++++++++++---- world/src/site2/plot/coastal_workshop.rs | 8 +- world/src/site2/plot/dungeon.rs | 3 + 13 files changed, 156 insertions(+), 48 deletions(-) create mode 100644 assets/voxygen/voxel/object/portal_active.vox create mode 100644 common/src/comp/teleport.rs diff --git a/assets/voxygen/voxel/object/portal.vox b/assets/voxygen/voxel/object/portal.vox index f30dc90ddf..e8358bf549 100644 --- a/assets/voxygen/voxel/object/portal.vox +++ b/assets/voxygen/voxel/object/portal.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:af311d5461a03bec05799d20b3298b5d98f07a108603300b0514376965aa02e2 -size 28971 +oid sha256:a7908a4ec9f15eebaa3360598de0f44fdbb317bdebdf94538015bef363aa5a17 +size 64414 diff --git a/assets/voxygen/voxel/object/portal_active.vox b/assets/voxygen/voxel/object/portal_active.vox new file mode 100644 index 0000000000..c8a3df3720 --- /dev/null +++ b/assets/voxygen/voxel/object/portal_active.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98cfc67080935c5a832939c857dbe76912a93e233b93387a338f413c151f3929 +size 64414 diff --git a/assets/voxygen/voxel/object_manifest.ron b/assets/voxygen/voxel/object_manifest.ron index 9592647da3..c66b0ecaa9 100644 --- a/assets/voxygen/voxel/object_manifest.ron +++ b/assets/voxygen/voxel/object_manifest.ron @@ -981,12 +981,22 @@ ), Portal: ( bone0: ( - offset: (-11.0, -1.5, 0.0), + offset: (-33.0, -33.0, 0.0), central: ("object.portal"), ), bone1: ( offset: (0.0, 0.0, 0.0), central: ("armor.empty"), ) - ) + ), + PortalActive: ( + bone0: ( + offset: (-33.0, -33.0, 0.0), + central: ("object.portal_active"), + ), + bone1: ( + offset: (0.0, 0.0, 0.0), + central: ("armor.empty"), + ) + ), }) diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 9c201563e7..1a20c5132d 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -625,6 +625,7 @@ impl ServerChatCommand { Float("y", 0., Required), Float("z", 0., Required), Boolean("requires_no_aggro", "true".to_string(), Optional), + Float("buildup_time", 5., Optional), ], "Spawns a portal", Some(Moderator), diff --git a/common/src/comp/body/object.rs b/common/src/comp/body/object.rs index 261c4b6cd5..8119470f95 100644 --- a/common/src/comp/body/object.rs +++ b/common/src/comp/body/object.rs @@ -111,6 +111,7 @@ make_case_elim!( LightningBolt = 96, SpearIcicle = 97, Portal = 98, + PortalActive = 99, } ); @@ -121,7 +122,7 @@ impl Body { } } -pub const ALL_OBJECTS: [Body; 99] = [ +pub const ALL_OBJECTS: [Body; 100] = [ Body::Arrow, Body::Bomb, Body::Scarecrow, @@ -221,6 +222,7 @@ pub const ALL_OBJECTS: [Body; 99] = [ Body::LightningBolt, Body::SpearIcicle, Body::Portal, + Body::PortalActive, ]; impl From for super::Body { @@ -329,6 +331,7 @@ impl Body { Body::LightningBolt => "lightning_bolt", Body::SpearIcicle => "spear_icicle", Body::Portal => "portal", + Self::PortalActive => "portal_active", } } @@ -453,7 +456,7 @@ impl Body { Body::AdletTrap => 10.0, Body::Mine => 100.0, Body::LightningBolt | Body::SpearIcicle => 20000.0, - Body::Portal => 10., // I dont know really + Body::Portal | Body::PortalActive => 10., // I dont know really }; Mass(m) diff --git a/common/src/comp/misc.rs b/common/src/comp/misc.rs index f6002140f1..c803ec64e9 100644 --- a/common/src/comp/misc.rs +++ b/common/src/comp/misc.rs @@ -3,7 +3,6 @@ use crate::{resources::Time, uid::Uid}; use serde::{Deserialize, Serialize}; use specs::{Component, DerefFlaggedStorage}; use std::time::Duration; -use vek::Vec3; #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Object { @@ -23,13 +22,3 @@ pub enum Object { impl Component for Object { type Storage = DerefFlaggedStorage>; } - -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Teleporter { - pub target: Vec3, - pub requires_no_aggro: bool, -} - -impl Component for Teleporter { - type Storage = DerefFlaggedStorage>; -} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 5fb3d86e07..e64dcce8da 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -34,6 +34,7 @@ pub mod projectile; pub mod shockwave; pub mod skillset; mod stats; +mod teleport; pub mod visual; // Reexports @@ -86,7 +87,7 @@ pub use self::{ location::{MapMarker, MapMarkerChange, MapMarkerUpdate, Waypoint, WaypointArea}, loot_owner::LootOwner, melee::{Melee, MeleeConstructor, MeleeConstructorKind}, - misc::{Object, Teleporter}, + misc::Object, ori::Ori, pet::Pet, phys::{ @@ -103,6 +104,7 @@ pub use self::{ SkillGroup, SkillGroupKind, SkillSet, }, stats::{Stats, StatsModifier}, + teleport::{Teleporter, Teleporting}, visual::{LightAnimation, LightEmitter}, }; diff --git a/common/src/comp/teleport.rs b/common/src/comp/teleport.rs new file mode 100644 index 0000000000..63fbd4c2aa --- /dev/null +++ b/common/src/comp/teleport.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; +use specs::{Component, DerefFlaggedStorage, Entity}; +use vek::Vec3; + +use crate::resources::{Secs, Time}; + +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Teleporter { + pub target: Vec3, + pub requires_no_aggro: bool, + pub buildup_time: Secs, +} + +impl Component for Teleporter { + type Storage = DerefFlaggedStorage>; +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Teleporting { + pub teleport_start: Time, + pub portal: Entity, + pub end_time: Time, +} + +impl Component for Teleporting { + type Storage = DerefFlaggedStorage>; +} diff --git a/common/state/src/state.rs b/common/state/src/state.rs index 84341cf56e..47cbd990c6 100644 --- a/common/state/src/state.rs +++ b/common/state/src/state.rs @@ -220,6 +220,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); // Register components send from clients -> server ecs.register::(); diff --git a/server/src/cmd.rs b/server/src/cmd.rs index fe2bf29404..97b8c9e742 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -2051,15 +2051,17 @@ fn handle_spawn_portal( ) -> CmdResult<()> { let pos = position(server, target, "target")?; - if let (Some(x), Some(y), Some(z), requires_no_aggro) = - parse_cmd_args!(args, f32, f32, f32, bool) + if let (Some(x), Some(y), Some(z), requires_no_aggro, buildup_time) = + parse_cmd_args!(args, f32, f32, f32, bool, f64) { let requires_no_aggro = requires_no_aggro.unwrap_or(false); + let buildup_time = Secs(buildup_time.unwrap_or(0.)); server .state .create_teleporter(pos, comp::Teleporter { target: Vec3::new(x, y, z), requires_no_aggro, + buildup_time, }) .build(); diff --git a/server/src/sys/teleporter.rs b/server/src/sys/teleporter.rs index 442dae21de..f4a1c3a809 100644 --- a/server/src/sys/teleporter.rs +++ b/server/src/sys/teleporter.rs @@ -1,16 +1,22 @@ use common::{ - comp::{Agent, ForceUpdate, Player, Pos, Teleporter}, + comp::{object, Agent, Body, ForceUpdate, Player, Pos, Teleporter, Teleporting}, + resources::Time, CachedSpatialGrid, }; use common_ecs::{Origin, Phase, System}; use specs::{Entities, Join, Read, ReadStorage, WriteStorage}; +use vek::Vec3; -const TELEPORT_RADIUS: f32 = 1.; +const TELEPORT_RADIUS: f32 = 3.; const MAX_AGGRO_DIST: f32 = 200.; // If an entity further than this is aggroed at a player, the portal will still work #[derive(Default)] pub struct Sys; +fn in_portal_range(player_pos: Vec3, portal_pos: Vec3) -> bool { + player_pos.distance_squared(portal_pos) <= (TELEPORT_RADIUS).powi(2) +} + impl<'a> System<'a> for Sys { type SystemData = ( Entities<'a>, @@ -19,7 +25,10 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Teleporter>, ReadStorage<'a, Agent>, WriteStorage<'a, ForceUpdate>, + WriteStorage<'a, Teleporting>, + WriteStorage<'a, Body>, Read<'a, CachedSpatialGrid>, + Read<'a, Time>, ); const NAME: &'static str = "teleporter"; @@ -35,47 +44,101 @@ impl<'a> System<'a> for Sys { teleporters, agent, mut forced_update, + mut teleporting, + mut bodies, spatial_grid, + time, ): Self::SystemData, ) { let mut attempt_teleport = vec![]; - let mut player_data = (&entities, &positions, &players).join(); + let mut cancel_teleport = vec![]; + let mut start_teleporting = vec![]; - for (_entity, teleporter_pos, teleporter) in (&entities, &positions, &teleporters).join() { + let mut player_data = (&entities, &positions, &players, teleporting.maybe()).join(); + + let check_aggro = |entity, pos: Vec3| { + spatial_grid + .0 + .in_circle_aabr(pos.xy(), MAX_AGGRO_DIST) + .any(|agent_entity| { + agent.get(agent_entity).map_or(false, |agent| { + agent.target.map_or(false, |agent_target| { + agent_target.target == entity && agent_target.aggro_on + }) + }) + }) + }; + + for (portal_entity, teleporter_pos, mut body, teleporter) in + (&entities, &positions, &mut bodies, &teleporters).join() + { let nearby_entities = spatial_grid .0 .in_circle_aabr(teleporter_pos.0.xy(), TELEPORT_RADIUS); - for (entity, position, _) in nearby_entities.filter_map(|entity| { + let mut is_active = false; + + for (entity, pos, _, teleporting) in nearby_entities.filter_map(|entity| { player_data .get(entity, &entities) - .filter(|(_, player_pos, _)| { - player_pos.0.distance_squared(teleporter_pos.0) <= (TELEPORT_RADIUS).powi(2) - }) + .filter(|(_, player_pos, _, _)| in_portal_range(player_pos.0, teleporter_pos.0)) }) { - // TODO: Check for aggro - attempt_teleport.push((entity, position.0, teleporter)) + if teleporter.requires_no_aggro && check_aggro(entity, pos.0) { + if teleporting.is_some() { + cancel_teleport.push(entity) + }; + + continue; + } + + if teleporting.is_none() { + start_teleporting.push((entity, Teleporting { + teleport_start: *time, + portal: portal_entity, + end_time: Time(time.0 + teleporter.buildup_time.0), + })); + } + + is_active = true; + } + + if (*body == Body::Object(object::Body::PortalActive)) != is_active { + *body = Body::Object(if is_active { + object::Body::PortalActive + } else { + object::Body::Portal + }); } } - for (entity, origin_pos, teleporter) in attempt_teleport { - if teleporter.requires_no_aggro { - // FIXME: How does this go with performance? - let is_aggroed = spatial_grid - .0 - .in_circle_aabr(origin_pos.xy(), MAX_AGGRO_DIST) - .any(|agent_entity| { - agent.get(agent_entity).map_or(false, |agent| { - agent.target.map_or(false, |agent_target| { - agent_target.target == entity && agent_target.aggro_on - }) - }) - }); + for (entity, teleporting_data) in start_teleporting { + let _ = teleporting.insert(entity, teleporting_data); + } - if is_aggroed { - continue; - } + for (entity, position, _, teleporting) in + (&entities, &positions, &players, &teleporting).join() + { + let portal_pos = positions.get(teleporting.portal); + let Some(teleporter) = teleporters.get(teleporting.portal) else { + cancel_teleport.push(entity); + continue + }; + + if portal_pos.map_or(true, |portal_pos| { + !in_portal_range(position.0, portal_pos.0) + }) { + cancel_teleport.push(entity); + } else if teleporting.end_time.0 <= time.0 { + attempt_teleport.push((entity, *teleporter)); + cancel_teleport.push(entity); } + } + + for entity in cancel_teleport { + teleporting.remove(entity); + } + + for (entity, teleporter) in attempt_teleport { positions .get_mut(entity) .map(|position| position.0 = teleporter.target); diff --git a/world/src/site2/plot/coastal_workshop.rs b/world/src/site2/plot/coastal_workshop.rs index c81931a150..03f57c92b1 100644 --- a/world/src/site2/plot/coastal_workshop.rs +++ b/world/src/site2/plot/coastal_workshop.rs @@ -3,7 +3,10 @@ use crate::{ util::{RandomField, Sampler, CARDINALS}, Land, }; -use common::{terrain::{BlockKind, SpriteKind}, generation::SpecialEntity}; +use common::{ + generation::SpecialEntity, + terrain::{BlockKind, SpriteKind}, +}; use rand::prelude::*; use std::sync::Arc; use vek::*; @@ -326,7 +329,8 @@ impl Structure for CoastalWorkshop { } painter.spawn( - EntityInfo::at((center - 2).with_z(base - 2).map(|e| e as f32 + 0.5)).into_special(SpecialEntity::Waypoint), + EntityInfo::at((center - 2).with_z(base - 2).map(|e| e as f32 + 0.5)) + .into_special(SpecialEntity::Waypoint), ); } } diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index cd0d9cbf98..70c1f7a743 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -11,6 +11,7 @@ use common::{ astar::Astar, comp::Teleporter, generation::{ChunkSupplement, EntityInfo, SpecialEntity}, + resources::Secs, store::{Id, Store}, terrain::{ BiomeKind, Block, BlockKind, SpriteKind, Structure, StructuresGroup, TerrainChunkSize, @@ -668,12 +669,14 @@ impl Floor { SpecialEntity::Teleporter(Teleporter { target: bottom_pos+ Vec3::unit_x() * 5., requires_no_aggro: false, + buildup_time: Secs(1.), }), )); supplement.add_entity(EntityInfo::at(bottom_pos).into_special( SpecialEntity::Teleporter(Teleporter { target: top_pos+ Vec3::unit_x() * 5., requires_no_aggro: true, + buildup_time: Secs(3.), }), )); } From 3343ddf4cc5d0059ca8f9825b93bffb1c5c39cae Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Tue, 18 Jul 2023 12:11:19 +0200 Subject: [PATCH 08/22] go into blink state at half buildup time --- assets/voxygen/voxel/object/portal.vox | 4 +- assets/voxygen/voxel/object/portal_active.vox | 4 +- server/src/sys/teleporter.rs | 59 ++++++++++++++++--- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/assets/voxygen/voxel/object/portal.vox b/assets/voxygen/voxel/object/portal.vox index e8358bf549..bfb531545b 100644 --- a/assets/voxygen/voxel/object/portal.vox +++ b/assets/voxygen/voxel/object/portal.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7908a4ec9f15eebaa3360598de0f44fdbb317bdebdf94538015bef363aa5a17 -size 64414 +oid sha256:14b9db19d7ba5320d3fd72d062d45c1efb0baddedaf1bf5cae79fc2909541856 +size 50670 diff --git a/assets/voxygen/voxel/object/portal_active.vox b/assets/voxygen/voxel/object/portal_active.vox index c8a3df3720..c8481918a4 100644 --- a/assets/voxygen/voxel/object/portal_active.vox +++ b/assets/voxygen/voxel/object/portal_active.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:98cfc67080935c5a832939c857dbe76912a93e233b93387a338f413c151f3929 -size 64414 +oid sha256:fa3dbaa31ec9cc6fa91dc86c13ab6a0cf6dfb576ef3d2692b91777aa51334679 +size 50670 diff --git a/server/src/sys/teleporter.rs b/server/src/sys/teleporter.rs index f4a1c3a809..0b30dc131a 100644 --- a/server/src/sys/teleporter.rs +++ b/server/src/sys/teleporter.rs @@ -1,6 +1,15 @@ +use std::time::Duration; + use common::{ - comp::{object, Agent, Body, ForceUpdate, Player, Pos, Teleporter, Teleporting}, + comp::{ + ability::AbilityMeta, object, Agent, Body, CharacterState, ForceUpdate, Player, Pos, + Teleporter, Teleporting, + }, resources::Time, + states::{ + blink, + utils::{AbilityInfo, StageSection}, + }, CachedSpatialGrid, }; use common_ecs::{Origin, Phase, System}; @@ -27,6 +36,7 @@ impl<'a> System<'a> for Sys { WriteStorage<'a, ForceUpdate>, WriteStorage<'a, Teleporting>, WriteStorage<'a, Body>, + WriteStorage<'a, CharacterState>, Read<'a, CachedSpatialGrid>, Read<'a, Time>, ); @@ -46,6 +56,7 @@ impl<'a> System<'a> for Sys { mut forced_update, mut teleporting, mut bodies, + mut character_states, spatial_grid, time, ): Self::SystemData, @@ -54,7 +65,14 @@ impl<'a> System<'a> for Sys { let mut cancel_teleport = vec![]; let mut start_teleporting = vec![]; - let mut player_data = (&entities, &positions, &players, teleporting.maybe()).join(); + let mut player_data = ( + &entities, + &positions, + &players, + &mut character_states, + teleporting.maybe(), + ) + .join(); let check_aggro = |entity, pos: Vec3| { spatial_grid @@ -78,11 +96,15 @@ impl<'a> System<'a> for Sys { let mut is_active = false; - for (entity, pos, _, teleporting) in nearby_entities.filter_map(|entity| { - player_data - .get(entity, &entities) - .filter(|(_, player_pos, _, _)| in_portal_range(player_pos.0, teleporter_pos.0)) - }) { + for (entity, pos, _, mut character_state, teleporting) in + nearby_entities.filter_map(|entity| { + player_data + .get(entity, &entities) + .filter(|(_, player_pos, _, _, _)| { + in_portal_range(player_pos.0, teleporter_pos.0) + }) + }) + { if teleporter.requires_no_aggro && check_aggro(entity, pos.0) { if teleporting.is_some() { cancel_teleport.push(entity) @@ -97,6 +119,29 @@ impl<'a> System<'a> for Sys { portal: portal_entity, end_time: Time(time.0 + teleporter.buildup_time.0), })); + } else if let Some(remaining) = teleporting.and_then(|teleporting| { + ((time.0 - teleporting.teleport_start.0) >= teleporter.buildup_time.0 / 2. + && !matches!(*character_state, CharacterState::Blink(_))) + .then_some(teleporter.buildup_time.0 - (time.0 - teleporting.teleport_start.0)) + }) { + // Move into blink character state at half buildup time + *character_state = CharacterState::Blink(blink::Data { + timer: Duration::default(), + stage_section: StageSection::Buildup, + static_data: blink::StaticData { + buildup_duration: Duration::from_millis((remaining * 1000.) as u64), + recover_duration: Duration::default(), + max_range: 0., + ability_info: AbilityInfo { + tool: None, + hand: None, + input: common::comp::InputKind::Primary, + ability_meta: AbilityMeta::default(), + ability: None, + input_attr: None, + }, + }, + }); } is_active = true; From 09f7087ac6b0c6962088989b6266ff87bff4dc01 Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Tue, 18 Jul 2023 12:56:44 +0200 Subject: [PATCH 09/22] buildup time adjustments --- assets/voxygen/voxel/object/portal.vox | 2 +- assets/voxygen/voxel/object/portal_active.vox | 2 +- server/src/sys/teleporter.rs | 7 +- world/src/site2/plot/dungeon.rs | 312 +++++++++--------- 4 files changed, 159 insertions(+), 164 deletions(-) diff --git a/assets/voxygen/voxel/object/portal.vox b/assets/voxygen/voxel/object/portal.vox index bfb531545b..1cf5786208 100644 --- a/assets/voxygen/voxel/object/portal.vox +++ b/assets/voxygen/voxel/object/portal.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:14b9db19d7ba5320d3fd72d062d45c1efb0baddedaf1bf5cae79fc2909541856 +oid sha256:441d3720809b51ea6a9885346f470efaa134e2f507052d95e1986100f23a3ace size 50670 diff --git a/assets/voxygen/voxel/object/portal_active.vox b/assets/voxygen/voxel/object/portal_active.vox index c8481918a4..8136fbf713 100644 --- a/assets/voxygen/voxel/object/portal_active.vox +++ b/assets/voxygen/voxel/object/portal_active.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa3dbaa31ec9cc6fa91dc86c13ab6a0cf6dfb576ef3d2692b91777aa51334679 +oid sha256:f4ad6e5f6bc14a9bdf497159d885a3bf38d3b474c8e305f6deac57ab631765fb size 50670 diff --git a/server/src/sys/teleporter.rs b/server/src/sys/teleporter.rs index 0b30dc131a..3413f13936 100644 --- a/server/src/sys/teleporter.rs +++ b/server/src/sys/teleporter.rs @@ -120,7 +120,7 @@ impl<'a> System<'a> for Sys { end_time: Time(time.0 + teleporter.buildup_time.0), })); } else if let Some(remaining) = teleporting.and_then(|teleporting| { - ((time.0 - teleporting.teleport_start.0) >= teleporter.buildup_time.0 / 2. + ((time.0 - teleporting.teleport_start.0) >= teleporter.buildup_time.0 / 3. && !matches!(*character_state, CharacterState::Blink(_))) .then_some(teleporter.buildup_time.0 - (time.0 - teleporting.teleport_start.0)) }) { @@ -181,6 +181,11 @@ impl<'a> System<'a> for Sys { for entity in cancel_teleport { teleporting.remove(entity); + character_states.get_mut(entity).map(|mut state| { + if let CharacterState::Blink(data) = &mut *state { + data.stage_section = StageSection::Recover; + } + }); } for (entity, teleporter) in attempt_teleport { diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 70c1f7a743..fa44e4709c 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -667,16 +667,16 @@ impl Floor { // Move both a bit to the side to prevent teleportation loop, ideally we'd have the portals at another location supplement.add_entity(EntityInfo::at(top_pos).into_special( SpecialEntity::Teleporter(Teleporter { - target: bottom_pos+ Vec3::unit_x() * 5., + target: bottom_pos + Vec3::unit_x() * 5., requires_no_aggro: false, buildup_time: Secs(1.), }), )); supplement.add_entity(EntityInfo::at(bottom_pos).into_special( SpecialEntity::Teleporter(Teleporter { - target: top_pos+ Vec3::unit_x() * 5., + target: top_pos + Vec3::unit_x() * 5., requires_no_aggro: true, - buildup_time: Secs(3.), + buildup_time: Secs(5.), }), )); } @@ -1188,173 +1188,163 @@ impl Floor { let mut chests = None; - 'room: { - if let Some(room) = room.map(|i| self.rooms.get(*i)) { - height = height.min(room.height); - if let Tile::UpStair(_, kind) = tile { - if self.final_level { - break 'room; + if let Some(room) = room.map(|i| self.rooms.get(*i)) { + height = height.min(room.height); + if let Tile::UpStair(_, kind) = tile && !self.final_level { + // Construct the staircase that connects this tile to the matching DownStair + // tile on the floor above (or to the surface if this is the top floor), and + // a hollow bounding box to place air in + let center = tile_center.with_z(floor_z); + let radius = TILE_SIZE as f32 / 2.0; + let aabb = aabr_with_z(tile_aabr, floor_z..floor_z + self.total_depth()); + let outer_aabb = aabr_with_z( + outer_tile_aabr(2), + floor_z + tunnel_height as i32..floor_z + self.total_depth() - 1, + ); + let bb = painter.prim(match kind { + StairsKind::Spiral => Primitive::Cylinder(aabb), + StairsKind::WallSpiral => Primitive::Aabb(aabb), + }); + let outer_bb = painter.prim(match kind { + StairsKind::WallSpiral => Primitive::Aabb(outer_aabb), + StairsKind::Spiral => Primitive::Cylinder(outer_aabb), + }); + let stair = painter.prim(Primitive::sampling(bb, match kind { + StairsKind::Spiral => spiral_staircase(center, radius, 0.5, 9.0), + StairsKind::WallSpiral => wall_staircase(center, radius, 27.0), + })); + // Construct the lights that go inside the staircase, starting above the + // ceiling to avoid placing them floating in mid-air + let mut lights = painter.prim(Primitive::Empty); + for i in height..self.total_depth() { + if i % 9 == 0 { + let mut light = painter.prim(Primitive::Aabb(Aabb { + min: aabb.min.with_z(floor_z + i), + max: aabb.max.with_z(floor_z + i + 1), + })); + let inner = painter.prim(Primitive::Aabb(Aabb { + min: (aabb.min + Vec3::new(1, 1, 0)).with_z(floor_z + i), + max: (aabb.max - Vec3::new(1, 1, 0)).with_z(floor_z + i + 1), + })); + + light = painter.prim(Primitive::without(light, inner)); + lights = painter.prim(Primitive::union(light, lights)); } - // Construct the staircase that connects this tile to the matching DownStair - // tile on the floor above (or to the surface if this is the top floor), and - // a hollow bounding box to place air in - let center = tile_center.with_z(floor_z); - let radius = TILE_SIZE as f32 / 2.0; - let aabb = aabr_with_z(tile_aabr, floor_z..floor_z + self.total_depth()); - let outer_aabb = aabr_with_z( - outer_tile_aabr(2), - floor_z + tunnel_height as i32..floor_z + self.total_depth() - 1, - ); - let bb = painter.prim(match kind { - StairsKind::Spiral => Primitive::Cylinder(aabb), - StairsKind::WallSpiral => Primitive::Aabb(aabb), - }); - let outer_bb = painter.prim(match kind { - StairsKind::WallSpiral => Primitive::Aabb(outer_aabb), - StairsKind::Spiral => Primitive::Cylinder(outer_aabb), - }); - let stair = painter.prim(Primitive::sampling(bb, match kind { - StairsKind::Spiral => spiral_staircase(center, radius, 0.5, 9.0), - StairsKind::WallSpiral => wall_staircase(center, radius, 27.0), + } + lights = painter.prim(Primitive::intersect(lights, lighting_mask)); + stairs_bb.push(bb); + stair_walls.push(outer_bb); + stairs.push((stair, lights)); + } + + if matches!(tile, Tile::Room(_) | Tile::DownStair(_)) { + let seed = room.seed; + let loot_density = room.loot_density; + let difficulty = room.difficulty; + // Place chests with a random distribution based on the + // room's loot density in valid sprite locations, + // filled based on the room's difficulty + let chest_sprite = painter.prim(Primitive::sampling( + sprite_layer, + Box::new(move |pos| RandomField::new(seed).chance(pos, loot_density * 0.5)), + )); + let chest_sprite_fill = Fill::Block(Block::air(match difficulty { + 2 => SpriteKind::DungeonChest2, + 3 => SpriteKind::DungeonChest3, + 4 => SpriteKind::DungeonChest4, + 5 => SpriteKind::DungeonChest5, + _ => SpriteKind::Chest, + })); + chests = Some((chest_sprite, chest_sprite_fill)); + + // If a room has pits, place them + if room.pits.is_some() { + // Make an air pit + let tile_pit = painter.prim(Primitive::Aabb(aabr_with_z( + tile_aabr, + floor_z - 7..floor_z, + ))); + let tile_pit = painter.prim(Primitive::without(tile_pit, wall_contours)); + painter.fill(tile_pit, Fill::Block(vacant)); + + // Fill with lava + let tile_lava = painter.prim(Primitive::Aabb(aabr_with_z( + tile_aabr, + floor_z - 7..floor_z - 5, + ))); + let tile_lava = painter.prim(Primitive::without(tile_lava, wall_contours)); + //pits.push(tile_pit); + //pits.push(tile_lava); + painter.fill(tile_lava, Fill::Block(lava)); + } + if room + .pits + .map(|pit_space| { + tile_pos.map(|e| e.rem_euclid(pit_space) == 0).reduce_and() + }) + .unwrap_or(false) + { + let platform = painter.prim(Primitive::Aabb(Aabb { + min: (tile_center - Vec2::broadcast(pillar_thickness - 1)) + .with_z(floor_z - 7), + max: (tile_center + Vec2::broadcast(pillar_thickness)).with_z(floor_z), })); - // Construct the lights that go inside the staircase, starting above the - // ceiling to avoid placing them floating in mid-air - let mut lights = painter.prim(Primitive::Empty); - for i in height..self.total_depth() { - if i % 9 == 0 { - let mut light = painter.prim(Primitive::Aabb(Aabb { - min: aabb.min.with_z(floor_z + i), - max: aabb.max.with_z(floor_z + i + 1), - })); - let inner = painter.prim(Primitive::Aabb(Aabb { - min: (aabb.min + Vec3::new(1, 1, 0)).with_z(floor_z + i), - max: (aabb.max - Vec3::new(1, 1, 0)).with_z(floor_z + i + 1), - })); - - light = painter.prim(Primitive::without(light, inner)); - lights = painter.prim(Primitive::union(light, lights)); - } - } - lights = painter.prim(Primitive::intersect(lights, lighting_mask)); - stairs_bb.push(bb); - stair_walls.push(outer_bb); - stairs.push((stair, lights)); + painter.fill(platform, Fill::Block(stone)); } - if matches!(tile, Tile::Room(_) | Tile::DownStair(_)) { - let seed = room.seed; - let loot_density = room.loot_density; - let difficulty = room.difficulty; - // Place chests with a random distribution based on the - // room's loot density in valid sprite locations, - // filled based on the room's difficulty - let chest_sprite = painter.prim(Primitive::sampling( - sprite_layer, - Box::new(move |pos| { - RandomField::new(seed).chance(pos, loot_density * 0.5) - }), - )); - let chest_sprite_fill = Fill::Block(Block::air(match difficulty { - 2 => SpriteKind::DungeonChest2, - 3 => SpriteKind::DungeonChest3, - 4 => SpriteKind::DungeonChest4, - 5 => SpriteKind::DungeonChest5, - _ => SpriteKind::Chest, + + // If a room has pillars, the current tile aligns with the pillar spacing, + // and we're not too close to a wall (i.e. the + // adjacent tiles are rooms and not hallways/solid), + // place a pillar + if room + .pillars + .map(|pillar_space| { + tile_pos + .map(|e| e.rem_euclid(pillar_space) == 0) + .reduce_and() + }) + .unwrap_or(false) + && DIRS + .iter() + .map(|dir| tile_pos + *dir) + .all(|other_tile_pos| { + matches!(self.tiles.get(other_tile_pos), Some(Tile::Room(_))) + }) + { + let mut pillar = painter.prim(Primitive::Cylinder(Aabb { + min: (tile_center - Vec2::broadcast(pillar_thickness - 1)) + .with_z(floor_z), + max: (tile_center + Vec2::broadcast(pillar_thickness)) + .with_z(floor_z + height), + })); + let base = painter.prim(Primitive::Cylinder(Aabb { + min: (tile_center - Vec2::broadcast(1 + pillar_thickness - 1)) + .with_z(floor_z), + max: (tile_center + Vec2::broadcast(1 + pillar_thickness)) + .with_z(floor_z + 1), })); - chests = Some((chest_sprite, chest_sprite_fill)); - // If a room has pits, place them - if room.pits.is_some() { - // Make an air pit - let tile_pit = painter.prim(Primitive::Aabb(aabr_with_z( - tile_aabr, - floor_z - 7..floor_z, - ))); - let tile_pit = - painter.prim(Primitive::without(tile_pit, wall_contours)); - painter.fill(tile_pit, Fill::Block(vacant)); - - // Fill with lava - let tile_lava = painter.prim(Primitive::Aabb(aabr_with_z( - tile_aabr, - floor_z - 7..floor_z - 5, - ))); - let tile_lava = - painter.prim(Primitive::without(tile_lava, wall_contours)); - //pits.push(tile_pit); - //pits.push(tile_lava); - painter.fill(tile_lava, Fill::Block(lava)); + let scale = (pillar_thickness + 2) as f32 / pillar_thickness as f32; + let mut lights = painter + .prim(Primitive::scale(pillar, Vec2::broadcast(scale).with_z(1.0))); + lights = painter.prim(Primitive::intersect(lighting_plane, lights)); + // Only add the base (and shift the lights up) + // for boss-rooms pillars + if room.kind == RoomKind::Boss { + lights = painter.prim(Primitive::translate(lights, 3 * Vec3::unit_z())); + pillar = painter.prim(Primitive::union(pillar, base)); } - if room - .pits - .map(|pit_space| { - tile_pos.map(|e| e.rem_euclid(pit_space) == 0).reduce_and() - }) - .unwrap_or(false) - { - let platform = painter.prim(Primitive::Aabb(Aabb { - min: (tile_center - Vec2::broadcast(pillar_thickness - 1)) - .with_z(floor_z - 7), - max: (tile_center + Vec2::broadcast(pillar_thickness)) - .with_z(floor_z), - })); - painter.fill(platform, Fill::Block(stone)); - } - - // If a room has pillars, the current tile aligns with the pillar spacing, - // and we're not too close to a wall (i.e. the - // adjacent tiles are rooms and not hallways/solid), - // place a pillar - if room - .pillars - .map(|pillar_space| { - tile_pos - .map(|e| e.rem_euclid(pillar_space) == 0) - .reduce_and() - }) - .unwrap_or(false) - && DIRS - .iter() - .map(|dir| tile_pos + *dir) - .all(|other_tile_pos| { - matches!(self.tiles.get(other_tile_pos), Some(Tile::Room(_))) - }) - { - let mut pillar = painter.prim(Primitive::Cylinder(Aabb { - min: (tile_center - Vec2::broadcast(pillar_thickness - 1)) - .with_z(floor_z), - max: (tile_center + Vec2::broadcast(pillar_thickness)) - .with_z(floor_z + height), - })); - let base = painter.prim(Primitive::Cylinder(Aabb { - min: (tile_center - Vec2::broadcast(1 + pillar_thickness - 1)) - .with_z(floor_z), - max: (tile_center + Vec2::broadcast(1 + pillar_thickness)) - .with_z(floor_z + 1), - })); - - let scale = (pillar_thickness + 2) as f32 / pillar_thickness as f32; - let mut lights = painter - .prim(Primitive::scale(pillar, Vec2::broadcast(scale).with_z(1.0))); - lights = painter.prim(Primitive::intersect(lighting_plane, lights)); - // Only add the base (and shift the lights up) - // for boss-rooms pillars - if room.kind == RoomKind::Boss { - lights = - painter.prim(Primitive::translate(lights, 3 * Vec3::unit_z())); - pillar = painter.prim(Primitive::union(pillar, base)); - } - // Specifically don't include pillars in Minotaur arena - if !(room.kind == RoomKind::Boss && self.difficulty == 4) { - pillars.push((tile_center, pillar, lights)); - } + // Specifically don't include pillars in Minotaur arena + if !(room.kind == RoomKind::Boss && self.difficulty == 4) { + pillars.push((tile_center, pillar, lights)); } } + } - // Keep track of the boss room to be able to add decorations later - if room.kind == RoomKind::Boss { - boss_room_center = - Some(floor_corner + TILE_SIZE * room.area.center() + TILE_SIZE / 2); - } + // Keep track of the boss room to be able to add decorations later + if room.kind == RoomKind::Boss { + boss_room_center = + Some(floor_corner + TILE_SIZE * room.area.center() + TILE_SIZE / 2); } } From bb2034ca9283858ba53d1c9660110a198c25fed3 Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Wed, 19 Jul 2023 02:21:24 +0200 Subject: [PATCH 10/22] portal particles --- assets/voxygen/shaders/particle-vert.glsl | 11 +++++ common/src/generation.rs | 2 +- server/src/sys/teleporter.rs | 2 +- voxygen/src/render/pipelines/particle.rs | 1 + voxygen/src/scene/particle.rs | 56 +++++++++++++++++++++++ 5 files changed, 70 insertions(+), 2 deletions(-) diff --git a/assets/voxygen/shaders/particle-vert.glsl b/assets/voxygen/shaders/particle-vert.glsl index 6352fde4b9..3fab3114a0 100644 --- a/assets/voxygen/shaders/particle-vert.glsl +++ b/assets/voxygen/shaders/particle-vert.glsl @@ -83,6 +83,7 @@ const int GIGA_SNOW = 42; const int CYCLOPS_CHARGE = 43; const int PORTAL_FIZZ = 45; const int INK = 46; +const int UPWARD_PORTAL_FIZZ = 47; // meters per second squared (acceleration) const float earth_gravity = 9.807; @@ -692,6 +693,7 @@ void main() { spin_in_axis(vec3(rand6, rand7, rand8), rand9 * 3 + lifetime * 5) ); break; +<<<<<<< HEAD case INK: f_reflect = 0.0; // Magic water doesn't reflect light, it emits it float black_color = 0.3 + 0.2 * rand3 + 0.3 * max(floor(rand4 + 0.3), 0.0); @@ -701,6 +703,15 @@ void main() { vec3(ink_size), vec4(0.5 * black_color, 0.75 * black_color, black_color, 1), spin_in_axis(vec3(rand6, rand7, rand8), percent() * 10 + 3 * rand9) +======= + case UPWARD_PORTAL_FIZZ: + f_reflect = 0.0; + attr = Attr( + inst_dir * percent(), + vec3(max(1.0, 0.05 * length(start_pos + inst_dir * percent()))), + vec4(vec3(1.23, 1.41, 1.44), 1.0),// * (1.0 - length(inst_dir) * 0.1), + identity()//spin_in_axis(perp_axis, asin(inst_dir.z / length(inst_dir)) + PI / 2.0) +>>>>>>> 70e6945e87 (portal particles) ); break; default: diff --git a/common/src/generation.rs b/common/src/generation.rs index 4818ff5554..a13021612a 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -201,7 +201,7 @@ pub struct EntityInfo { //Option>, /* price and available amount */ // Edge cases, override everything else - pub special_entity: Option, // Campfire + pub special_entity: Option, } impl EntityInfo { diff --git a/server/src/sys/teleporter.rs b/server/src/sys/teleporter.rs index 3413f13936..97de8a41e4 100644 --- a/server/src/sys/teleporter.rs +++ b/server/src/sys/teleporter.rs @@ -129,7 +129,7 @@ impl<'a> System<'a> for Sys { timer: Duration::default(), stage_section: StageSection::Buildup, static_data: blink::StaticData { - buildup_duration: Duration::from_millis((remaining * 1000.) as u64), + buildup_duration: Duration::from_secs_f64(remaining), recover_duration: Duration::default(), max_range: 0., ability_info: AbilityInfo { diff --git a/voxygen/src/render/pipelines/particle.rs b/voxygen/src/render/pipelines/particle.rs index d5c8d6d50a..9441400978 100644 --- a/voxygen/src/render/pipelines/particle.rs +++ b/voxygen/src/render/pipelines/particle.rs @@ -98,6 +98,7 @@ pub enum ParticleMode { SnowStorm = 44, PortalFizz = 45, Ink = 46, + UpwardPortalFizz = 47, } impl ParticleMode { diff --git a/voxygen/src/scene/particle.rs b/voxygen/src/scene/particle.rs index edf60557cc..dec7612e95 100644 --- a/voxygen/src/scene/particle.rs +++ b/voxygen/src/scene/particle.rs @@ -499,6 +499,12 @@ impl ParticleMgr { | object::Body::FireworkWhite | object::Body::FireworkYellow, ) => self.maintain_bomb_particles(scene_data, interpolated.pos, vel), + Body::Object(object::Body::PortalActive) => { + self.maintain_active_portal_particles(scene_data, interpolated.pos) + }, + Body::Object(object::Body::Portal) => { + self.maintain_portal_particles(scene_data, interpolated.pos) + }, _ => {}, } } @@ -723,6 +729,56 @@ impl ParticleMgr { } } + fn maintain_active_portal_particles(&mut self, scene_data: &SceneData, pos: Vec3) { + span!( + _guard, + "active_portal_particles", + "ParticleMgr::maintain_active_portal_particles" + ); + + let time = scene_data.state.get_time(); + let mut rng = thread_rng(); + + for _ in 0..self.scheduler.heartbeats(Duration::from_millis(5)) { + let outer_pos = + pos + (Vec2::unit_x().rotated_z(rng.gen_range((0.)..PI * 2.)) * 2.7).with_z(0.); + + self.particles.push(Particle::new_directed( + Duration::from_secs_f32(rng.gen_range(0.2..0.5)), + time, + ParticleMode::UpwardPortalFizz, + outer_pos, + outer_pos + Vec3::unit_z() * rng.gen_range(5.0..7.0), + )); + } + } + + fn maintain_portal_particles(&mut self, scene_data: &SceneData, pos: Vec3) { + span!( + _guard, + "portal_particles", + "ParticleMgr::maintain_portal_particles" + ); + + let time = scene_data.state.get_time(); + let mut rng = thread_rng(); + + for _ in 0..self.scheduler.heartbeats(Duration::from_millis(150)) { + let outer_pos = pos + + (Vec2::unit_x().rotated_z(rng.gen_range((0.)..PI * 2.)) + * rng.gen_range(1.0..2.9)) + .with_z(0.); + + self.particles.push(Particle::new_directed( + Duration::from_secs_f32(rng.gen_range(0.5..3.0)), + time, + ParticleMode::UpwardPortalFizz, + outer_pos, + outer_pos + Vec3::unit_z() * rng.gen_range(3.0..4.0), + )); + } + } + fn maintain_mine_particles(&mut self, scene_data: &SceneData, pos: Vec3) { span!( _guard, From e5fe64ccf6dbc5097b4be1d7fa45a33957431f74 Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Fri, 21 Jul 2023 22:44:47 +0200 Subject: [PATCH 11/22] pre-review cleanups --- common/net/src/synced_components.rs | 3 +-- server/src/cmd.rs | 2 +- server/src/sys/teleporter.rs | 26 ++++++++++++-------------- world/src/civ/mod.rs | 4 ++-- world/src/layer/cave.rs | 6 ++++-- world/src/site2/plot/dungeon.rs | 13 +++++-------- 6 files changed, 25 insertions(+), 29 deletions(-) diff --git a/common/net/src/synced_components.rs b/common/net/src/synced_components.rs index bf8cc26a19..89bd7a17c0 100755 --- a/common/net/src/synced_components.rs +++ b/common/net/src/synced_components.rs @@ -46,6 +46,7 @@ macro_rules! synced_components { beam_segment: BeamSegment, alignment: Alignment, stance: Stance, + teleporter: Teleporter, // TODO: change this to `SyncFrom::ClientEntity` and sync the bare minimum // from other entities (e.g. just keys needed to show appearance // based on their loadout). Also, it looks like this actually has @@ -102,8 +103,6 @@ synced_components!(reexport_comps); // === NetSync implementations === // =============================== -use common::comp::Teleporter; - use crate::sync::{NetSync, SyncFrom}; impl NetSync for Body { diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 97b8c9e742..42ae04aef4 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -2055,7 +2055,7 @@ fn handle_spawn_portal( parse_cmd_args!(args, f32, f32, f32, bool, f64) { let requires_no_aggro = requires_no_aggro.unwrap_or(false); - let buildup_time = Secs(buildup_time.unwrap_or(0.)); + let buildup_time = Secs(buildup_time.unwrap_or(7.)); server .state .create_teleporter(pos, comp::Teleporter { diff --git a/server/src/sys/teleporter.rs b/server/src/sys/teleporter.rs index 97de8a41e4..0ec6345071 100644 --- a/server/src/sys/teleporter.rs +++ b/server/src/sys/teleporter.rs @@ -13,7 +13,7 @@ use common::{ CachedSpatialGrid, }; use common_ecs::{Origin, Phase, System}; -use specs::{Entities, Join, Read, ReadStorage, WriteStorage}; +use specs::{storage::StorageEntry, Entities, Join, Read, ReadStorage, WriteStorage}; use vek::Vec3; const TELEPORT_RADIUS: f32 = 3.; @@ -63,14 +63,13 @@ impl<'a> System<'a> for Sys { ) { let mut attempt_teleport = vec![]; let mut cancel_teleport = vec![]; - let mut start_teleporting = vec![]; let mut player_data = ( &entities, &positions, &players, &mut character_states, - teleporting.maybe(), + teleporting.entries(), ) .join(); @@ -106,24 +105,27 @@ impl<'a> System<'a> for Sys { }) { if teleporter.requires_no_aggro && check_aggro(entity, pos.0) { - if teleporting.is_some() { - cancel_teleport.push(entity) + if let StorageEntry::Occupied(entry) = teleporting { + entry.remove(); }; continue; } - if teleporting.is_none() { - start_teleporting.push((entity, Teleporting { + if let StorageEntry::Vacant(entry) = teleporting { + entry.insert(Teleporting { teleport_start: *time, portal: portal_entity, end_time: Time(time.0 + teleporter.buildup_time.0), - })); - } else if let Some(remaining) = teleporting.and_then(|teleporting| { + }); + } else if let Some(remaining) = if let StorageEntry::Occupied(entry) = teleporting { + let teleporting = entry.get(); ((time.0 - teleporting.teleport_start.0) >= teleporter.buildup_time.0 / 3. && !matches!(*character_state, CharacterState::Blink(_))) .then_some(teleporter.buildup_time.0 - (time.0 - teleporting.teleport_start.0)) - }) { + } else { + None + } { // Move into blink character state at half buildup time *character_state = CharacterState::Blink(blink::Data { timer: Duration::default(), @@ -156,10 +158,6 @@ impl<'a> System<'a> for Sys { } } - for (entity, teleporting_data) in start_teleporting { - let _ = teleporting.insert(entity, teleporting_data); - } - for (entity, position, _, teleporting) in (&entities, &positions, &players, &teleporting).join() { diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index d97726b476..b8939bd5b1 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -1894,8 +1894,8 @@ impl SiteKind { .chain(cave::tunnels_at(loc, 2, &land)) .filter(|tunnel| { dungeon_aabr.collides_with_aabr(Aabr { - min: tunnel.a.wpos, - max: tunnel.b.wpos, + min: tunnel.nodes().0.wpos, + max: tunnel.nodes().1.wpos, }) }); diff --git a/world/src/layer/cave.rs b/world/src/layer/cave.rs index a95e183bd7..7a1f40bbfa 100644 --- a/world/src/layer/cave.rs +++ b/world/src/layer/cave.rs @@ -76,8 +76,8 @@ pub fn surface_entrances<'a>(land: &'a Land) -> impl Iterator> } pub struct Tunnel { - pub a: Node, - pub b: Node, + a: Node, + b: Node, curve: f32, } @@ -215,6 +215,8 @@ impl Tunnel { depth, } } + + pub fn nodes(&self) -> (&Node, &Node) { (&self.a, &self.b) } } pub(crate) fn tunnels_at<'a>( diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index fa44e4709c..9b6d83f3e7 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -1155,8 +1155,6 @@ impl Floor { Tile::Solid => continue, }; - // TODO: Get outer tile aabb - let outer_tile_aabb = painter.prim(Primitive::Aabb(aabr_with_z( outer_tile_aabr(0), floor_z - 2..(floor_z + tunnel_height as i32) + 2, @@ -1192,8 +1190,8 @@ impl Floor { height = height.min(room.height); if let Tile::UpStair(_, kind) = tile && !self.final_level { // Construct the staircase that connects this tile to the matching DownStair - // tile on the floor above (or to the surface if this is the top floor), and - // a hollow bounding box to place air in + // tile on the floor above (or to the surface if this is the top floor), and a + // hollow bounding box to place air in let center = tile_center.with_z(floor_z); let radius = TILE_SIZE as f32 / 2.0; let aabb = aabr_with_z(tile_aabr, floor_z..floor_z + self.total_depth()); @@ -1292,10 +1290,9 @@ impl Floor { painter.fill(platform, Fill::Block(stone)); } - // If a room has pillars, the current tile aligns with the pillar spacing, - // and we're not too close to a wall (i.e. the - // adjacent tiles are rooms and not hallways/solid), - // place a pillar + // If a room has pillars, the current tile aligns with the pillar spacing, and + // we're not too close to a wall (i.e. the adjacent tiles are rooms and not + // hallways/solid), place a pillar if room .pillars .map(|pillar_space| { From 8fe5f2e708c5eb7749d4cbea59097d7d2ef1b949 Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Mon, 24 Jul 2023 09:58:32 +0200 Subject: [PATCH 12/22] addressed review comments --- common/src/comp/mod.rs | 2 +- common/src/comp/teleport.rs | 5 + common/src/event.rs | 1 + server/src/events/entity_manipulation.rs | 30 ++++++ server/src/events/mod.rs | 2 + server/src/sys/teleporter.rs | 119 +++++++---------------- world/src/site2/plot/dungeon.rs | 33 ++----- 7 files changed, 84 insertions(+), 108 deletions(-) diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index e64dcce8da..c6eef4cf7b 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -34,7 +34,7 @@ pub mod projectile; pub mod shockwave; pub mod skillset; mod stats; -mod teleport; +pub mod teleport; pub mod visual; // Reexports diff --git a/common/src/comp/teleport.rs b/common/src/comp/teleport.rs index 63fbd4c2aa..ccecb7116f 100644 --- a/common/src/comp/teleport.rs +++ b/common/src/comp/teleport.rs @@ -4,6 +4,11 @@ use vek::Vec3; use crate::resources::{Secs, Time}; +pub enum TeleporterEvent { + PortalTeleport { entity: Entity, target: Vec3 }, + SetPortalActive { portal: Entity, active: bool }, +} + #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Teleporter { pub target: Vec3, diff --git a/common/src/event.rs b/common/src/event.rs index eef17c1035..34ebf8ae74 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -332,6 +332,7 @@ pub enum ServerEvent { RemoveLightEmitter { entity: EcsEntity, }, + PortalEvent(comp::teleport::TeleporterEvent), } pub struct EventBus { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 2e2664ead7..165d63b8bb 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -21,6 +21,8 @@ use common::{ inventory::item::{AbilityMap, MaterialStatManifest}, item::flatten_counted_items, loot_owner::LootOwnerKind, + object, + teleport::TeleporterEvent, Alignment, Auras, Body, CharacterState, Energy, Group, Health, HealthChange, Inventory, Player, Poise, Pos, SkillSet, Stats, }, @@ -1656,3 +1658,31 @@ pub fn handle_remove_light_emitter(server: &mut Server, entity: EcsEntity) { .write_storage::() .remove(entity); } + +pub fn handle_portal_event(server: &mut Server, event: TeleporterEvent) { + let ecs = server.state.ecs(); + + match event { + TeleporterEvent::PortalTeleport { entity, target } => { + ecs.write_storage::() + .get_mut(entity) + .map(|old_position| { + old_position.0 = target; + }); + ecs.write_storage::() + .get_mut(entity) + .map(|forced_update| forced_update.update()); + }, + TeleporterEvent::SetPortalActive { portal, active } => { + ecs.write_storage::() + .get_mut(portal) + .map(|mut body| { + *body = comp::body::Body::Object(if active { + object::Body::PortalActive + } else { + object::Body::Portal + }); + }); + }, + } +} diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 24e519b8a2..2f4a77eda6 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -1,6 +1,7 @@ use crate::{ events::{ entity_creation::handle_create_teleporter, + entity_manipulation::handle_portal_event, interaction::{handle_mount_volume, handle_tame_pet}, }, persistence::PersistedComponents, @@ -299,6 +300,7 @@ impl Server { ServerEvent::RemoveLightEmitter { entity } => { handle_remove_light_emitter(self, entity) }, + ServerEvent::PortalEvent(event) => handle_portal_event(self, event), } } diff --git a/server/src/sys/teleporter.rs b/server/src/sys/teleporter.rs index 0ec6345071..f2b637deb5 100644 --- a/server/src/sys/teleporter.rs +++ b/server/src/sys/teleporter.rs @@ -1,15 +1,10 @@ -use std::time::Duration; - use common::{ comp::{ - ability::AbilityMeta, object, Agent, Body, CharacterState, ForceUpdate, Player, Pos, - Teleporter, Teleporting, + object, teleport::TeleporterEvent, Agent, Body, CharacterState, Player, Pos, Teleporter, + Teleporting, }, + event::{EventBus, ServerEvent}, resources::Time, - states::{ - blink, - utils::{AbilityInfo, StageSection}, - }, CachedSpatialGrid, }; use common_ecs::{Origin, Phase, System}; @@ -29,16 +24,16 @@ fn in_portal_range(player_pos: Vec3, portal_pos: Vec3) -> bool { impl<'a> System<'a> for Sys { type SystemData = ( Entities<'a>, - WriteStorage<'a, Pos>, + ReadStorage<'a, Pos>, ReadStorage<'a, Player>, ReadStorage<'a, Teleporter>, ReadStorage<'a, Agent>, - WriteStorage<'a, ForceUpdate>, WriteStorage<'a, Teleporting>, - WriteStorage<'a, Body>, - WriteStorage<'a, CharacterState>, + ReadStorage<'a, Body>, + ReadStorage<'a, CharacterState>, Read<'a, CachedSpatialGrid>, Read<'a, Time>, + Read<'a, EventBus>, ); const NAME: &'static str = "teleporter"; @@ -49,26 +44,23 @@ impl<'a> System<'a> for Sys { _job: &mut common_ecs::Job, ( entities, - mut positions, + positions, players, teleporters, agent, - mut forced_update, mut teleporting, - mut bodies, - mut character_states, + bodies, + character_states, spatial_grid, time, + event_bus, ): Self::SystemData, ) { - let mut attempt_teleport = vec![]; - let mut cancel_teleport = vec![]; - let mut player_data = ( &entities, &positions, &players, - &mut character_states, + &character_states, teleporting.entries(), ) .join(); @@ -86,8 +78,8 @@ impl<'a> System<'a> for Sys { }) }; - for (portal_entity, teleporter_pos, mut body, teleporter) in - (&entities, &positions, &mut bodies, &teleporters).join() + for (portal_entity, teleporter_pos, body, teleporter) in + (&entities, &positions, &bodies, &teleporters).join() { let nearby_entities = spatial_grid .0 @@ -95,7 +87,7 @@ impl<'a> System<'a> for Sys { let mut is_active = false; - for (entity, pos, _, mut character_state, teleporting) in + for (entity, pos, _, character_state, teleporting) in nearby_entities.filter_map(|entity| { player_data .get(entity, &entities) @@ -104,7 +96,11 @@ impl<'a> System<'a> for Sys { }) }) { - if teleporter.requires_no_aggro && check_aggro(entity, pos.0) { + if !matches!( + character_state, + CharacterState::Idle(_) | CharacterState::Wielding(_) + ) || (teleporter.requires_no_aggro && check_aggro(entity, pos.0)) + { if let StorageEntry::Occupied(entry) = teleporting { entry.remove(); }; @@ -118,81 +114,40 @@ impl<'a> System<'a> for Sys { portal: portal_entity, end_time: Time(time.0 + teleporter.buildup_time.0), }); - } else if let Some(remaining) = if let StorageEntry::Occupied(entry) = teleporting { - let teleporting = entry.get(); - ((time.0 - teleporting.teleport_start.0) >= teleporter.buildup_time.0 / 3. - && !matches!(*character_state, CharacterState::Blink(_))) - .then_some(teleporter.buildup_time.0 - (time.0 - teleporting.teleport_start.0)) - } else { - None - } { - // Move into blink character state at half buildup time - *character_state = CharacterState::Blink(blink::Data { - timer: Duration::default(), - stage_section: StageSection::Buildup, - static_data: blink::StaticData { - buildup_duration: Duration::from_secs_f64(remaining), - recover_duration: Duration::default(), - max_range: 0., - ability_info: AbilityInfo { - tool: None, - hand: None, - input: common::comp::InputKind::Primary, - ability_meta: AbilityMeta::default(), - ability: None, - input_attr: None, - }, - }, - }); } is_active = true; } if (*body == Body::Object(object::Body::PortalActive)) != is_active { - *body = Body::Object(if is_active { - object::Body::PortalActive - } else { - object::Body::Portal - }); + event_bus.emit_now(ServerEvent::PortalEvent(TeleporterEvent::SetPortalActive { + portal: portal_entity, + active: is_active, + })); } } - for (entity, position, _, teleporting) in - (&entities, &positions, &players, &teleporting).join() + for (entity, position, _, teleporting_entry) in + (&entities, &positions, &players, teleporting.entries()).join() { - let portal_pos = positions.get(teleporting.portal); - let Some(teleporter) = teleporters.get(teleporting.portal) else { - cancel_teleport.push(entity); + let StorageEntry::Occupied(teleporting) = teleporting_entry else { continue }; + let portal_pos = positions.get(teleporting.get().portal); + let Some(teleporter) = teleporters.get(teleporting.get().portal) else { + teleporting.remove(); continue }; if portal_pos.map_or(true, |portal_pos| { !in_portal_range(position.0, portal_pos.0) }) { - cancel_teleport.push(entity); - } else if teleporting.end_time.0 <= time.0 { - attempt_teleport.push((entity, *teleporter)); - cancel_teleport.push(entity); + teleporting.remove(); + } else if teleporting.get().end_time.0 <= time.0 { + teleporting.remove(); + event_bus.emit_now(ServerEvent::PortalEvent(TeleporterEvent::PortalTeleport { + entity, + target: teleporter.target, + })); } } - - for entity in cancel_teleport { - teleporting.remove(entity); - character_states.get_mut(entity).map(|mut state| { - if let CharacterState::Blink(data) = &mut *state { - data.stage_section = StageSection::Recover; - } - }); - } - - for (entity, teleporter) in attempt_teleport { - positions - .get_mut(entity) - .map(|position| position.0 = teleporter.target); - forced_update - .get_mut(entity) - .map(|forced_update| forced_update.update()); - } } } diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 9b6d83f3e7..03ce3931d6 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -1047,16 +1047,6 @@ impl Floor { let tiles = Arc::clone(&tiles); make_wall_contours(tiles, floor_corner, floor_z, wall_thickness, tunnel_height) })); - let walls_fat = painter.prim(Primitive::sampling(floor_prim, { - let tiles = Arc::clone(&tiles); - make_wall_contours( - tiles, - floor_corner, - floor_z, - wall_thickness - 1., - tunnel_height + 1., - ) - })); // The surface 1 unit thicker than the walls is used to place the torches onto let wall_contour_surface = painter.prim(Primitive::sampling(floor_prim, { @@ -1123,13 +1113,10 @@ impl Floor { .without(lighting_mask_x.intersect(lighting_mask_y)) }; - let walls_only = painter.prim(Primitive::without(wall_contours, walls_fat)); - // Declare collections of various disjoint primitives that need postprocessing // after handling all the local information per-tile let mut stairs_bb = Vec::new(); let mut stairs = Vec::new(); - let mut stair_walls = Vec::new(); let mut pillars = Vec::new(); let mut boss_room_center = None; let mut sprites = Vec::new(); @@ -1159,10 +1146,9 @@ impl Floor { outer_tile_aabr(0), floor_z - 2..(floor_z + tunnel_height as i32) + 2, ))); - let walls_in_tile = painter.prim(Primitive::intersect(outer_tile_aabb, walls_only)); // Fill Walls - painter.fill(walls_in_tile, Fill::Block(stone_wall)); + painter.fill(outer_tile_aabb, Fill::Block(stone_wall)); let tile_floor_fill = painter.prim(Primitive::Aabb(aabr_with_z( tile_aabr, @@ -1203,10 +1189,12 @@ impl Floor { StairsKind::Spiral => Primitive::Cylinder(aabb), StairsKind::WallSpiral => Primitive::Aabb(aabb), }); - let outer_bb = painter.prim(match kind { + + painter.fill(painter.prim(match kind { StairsKind::WallSpiral => Primitive::Aabb(outer_aabb), StairsKind::Spiral => Primitive::Cylinder(outer_aabb), - }); + }), Fill::Block(stone_wall)); + let stair = painter.prim(Primitive::sampling(bb, match kind { StairsKind::Spiral => spiral_staircase(center, radius, 0.5, 9.0), StairsKind::WallSpiral => wall_staircase(center, radius, 27.0), @@ -1231,7 +1219,6 @@ impl Floor { } lights = painter.prim(Primitive::intersect(lights, lighting_mask)); stairs_bb.push(bb); - stair_walls.push(outer_bb); stairs.push((stair, lights)); } @@ -1395,14 +1382,10 @@ impl Floor { } // Carve out space for the stairs - for (stair_bb, outer_stairs_bb) in stairs_bb.iter().zip(stair_walls.iter()) { - painter.fill( - painter.prim(Primitive::without(*outer_stairs_bb, *stair_bb)), - Fill::Block(stone_wall), - ); - painter.fill(*stair_bb, Fill::Block(vacant)); + for stair_bb in stairs_bb { + painter.fill(stair_bb, Fill::Block(vacant)); // Prevent sprites from floating above the stairs - let stair_bb_up = painter.prim(Primitive::translate(*stair_bb, Vec3::unit_z())); + let stair_bb_up = painter.prim(Primitive::translate(stair_bb, Vec3::unit_z())); for (sprite, _) in sprites.iter_mut() { *sprite = painter.prim(Primitive::without(*sprite, stair_bb_up)); } From 26ada89bb98cc06d45a87199f419c79069c6dde9 Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Thu, 27 Jul 2023 00:11:41 +0200 Subject: [PATCH 13/22] portal event -> change body and teleport to --- common/src/comp/teleport.rs | 5 --- common/src/event.rs | 5 ++- server/src/events/entity_manipulation.rs | 39 ++++++++-------------- server/src/events/mod.rs | 6 ++-- server/src/sys/teleporter.rs | 41 ++++++++++++------------ 5 files changed, 42 insertions(+), 54 deletions(-) diff --git a/common/src/comp/teleport.rs b/common/src/comp/teleport.rs index ccecb7116f..63fbd4c2aa 100644 --- a/common/src/comp/teleport.rs +++ b/common/src/comp/teleport.rs @@ -4,11 +4,6 @@ use vek::Vec3; use crate::resources::{Secs, Time}; -pub enum TeleporterEvent { - PortalTeleport { entity: Entity, target: Vec3 }, - SetPortalActive { portal: Entity, active: bool }, -} - #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Teleporter { pub target: Vec3, diff --git a/common/src/event.rs b/common/src/event.rs index 34ebf8ae74..7b23bcc33e 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -332,7 +332,10 @@ pub enum ServerEvent { RemoveLightEmitter { entity: EcsEntity, }, - PortalEvent(comp::teleport::TeleporterEvent), + TeleportToPosition { + entity: EcsEntity, + position: Vec3, + }, } pub struct EventBus { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 165d63b8bb..533d0b0d86 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -21,8 +21,6 @@ use common::{ inventory::item::{AbilityMap, MaterialStatManifest}, item::flatten_counted_items, loot_owner::LootOwnerKind, - object, - teleport::TeleporterEvent, Alignment, Auras, Body, CharacterState, Energy, Group, Health, HealthChange, Inventory, Player, Poise, Pos, SkillSet, Stats, }, @@ -1659,30 +1657,19 @@ pub fn handle_remove_light_emitter(server: &mut Server, entity: EcsEntity) { .remove(entity); } -pub fn handle_portal_event(server: &mut Server, event: TeleporterEvent) { +pub fn handle_teleport_to_position_event( + server: &mut Server, + entity: EcsEntity, + position: Vec3, +) { let ecs = server.state.ecs(); - match event { - TeleporterEvent::PortalTeleport { entity, target } => { - ecs.write_storage::() - .get_mut(entity) - .map(|old_position| { - old_position.0 = target; - }); - ecs.write_storage::() - .get_mut(entity) - .map(|forced_update| forced_update.update()); - }, - TeleporterEvent::SetPortalActive { portal, active } => { - ecs.write_storage::() - .get_mut(portal) - .map(|mut body| { - *body = comp::body::Body::Object(if active { - object::Body::PortalActive - } else { - object::Body::Portal - }); - }); - }, - } + ecs.write_storage::() + .get_mut(entity) + .map(|old_position| { + old_position.0 = position; + }); + ecs.write_storage::() + .get_mut(entity) + .map(|forced_update| forced_update.update()); } diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 2f4a77eda6..260b59fadb 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -1,7 +1,7 @@ use crate::{ events::{ entity_creation::handle_create_teleporter, - entity_manipulation::handle_portal_event, + entity_manipulation::handle_teleport_to_position_event, interaction::{handle_mount_volume, handle_tame_pet}, }, persistence::PersistedComponents, @@ -300,7 +300,9 @@ impl Server { ServerEvent::RemoveLightEmitter { entity } => { handle_remove_light_emitter(self, entity) }, - ServerEvent::PortalEvent(event) => handle_portal_event(self, event), + ServerEvent::TeleportToPosition { entity, position } => { + handle_teleport_to_position_event(self, entity, position) + }, } } diff --git a/server/src/sys/teleporter.rs b/server/src/sys/teleporter.rs index f2b637deb5..2181922f6d 100644 --- a/server/src/sys/teleporter.rs +++ b/server/src/sys/teleporter.rs @@ -1,8 +1,5 @@ use common::{ - comp::{ - object, teleport::TeleporterEvent, Agent, Body, CharacterState, Player, Pos, Teleporter, - Teleporting, - }, + comp::{object, Agent, Body, CharacterState, Player, Pos, Teleporter, Teleporting}, event::{EventBus, ServerEvent}, resources::Time, CachedSpatialGrid, @@ -56,15 +53,6 @@ impl<'a> System<'a> for Sys { event_bus, ): Self::SystemData, ) { - let mut player_data = ( - &entities, - &positions, - &players, - &character_states, - teleporting.entries(), - ) - .join(); - let check_aggro = |entity, pos: Vec3| { spatial_grid .0 @@ -87,6 +75,15 @@ impl<'a> System<'a> for Sys { let mut is_active = false; + let mut player_data = ( + &entities, + &positions, + &players, + &character_states, + teleporting.entries(), + ) + .join(); + for (entity, pos, _, character_state, teleporting) in nearby_entities.filter_map(|entity| { player_data @@ -120,10 +117,14 @@ impl<'a> System<'a> for Sys { } if (*body == Body::Object(object::Body::PortalActive)) != is_active { - event_bus.emit_now(ServerEvent::PortalEvent(TeleporterEvent::SetPortalActive { - portal: portal_entity, - active: is_active, - })); + event_bus.emit_now(ServerEvent::ChangeBody { + entity: portal_entity, + new_body: Body::Object(if is_active { + object::Body::PortalActive + } else { + object::Body::Portal + }), + }); } } @@ -143,10 +144,10 @@ impl<'a> System<'a> for Sys { teleporting.remove(); } else if teleporting.get().end_time.0 <= time.0 { teleporting.remove(); - event_bus.emit_now(ServerEvent::PortalEvent(TeleporterEvent::PortalTeleport { + event_bus.emit_now(ServerEvent::TeleportToPosition { entity, - target: teleporter.target, - })); + position: teleporter.target, + }); } } } From ac68f0af7b111cf46441dc8c9b492c15eabc061d Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Sun, 30 Jul 2023 14:50:16 +0200 Subject: [PATCH 14/22] addressed review comments and added hud interaction --- assets/voxygen/i18n/en/hud/misc.ftl | 1 + common/src/comp/body.rs | 7 +++ server/src/sys/teleporter.rs | 91 ++++++++++++++++------------- voxygen/src/hud/mod.rs | 12 ++-- voxygen/src/session/interactable.rs | 2 +- 5 files changed, 68 insertions(+), 45 deletions(-) diff --git a/assets/voxygen/i18n/en/hud/misc.ftl b/assets/voxygen/i18n/en/hud/misc.ftl index 1115e37759..b8644d3436 100644 --- a/assets/voxygen/i18n/en/hud/misc.ftl +++ b/assets/voxygen/i18n/en/hud/misc.ftl @@ -58,3 +58,4 @@ hud-follow = Follow hud-stay= Stay hud-sit = Sit hud-steer = Steer +hud-portal = Portal diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index 6cf0118a78..a3fad51819 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -261,6 +261,13 @@ impl Body { pub fn is_campfire(&self) -> bool { matches!(self, Body::Object(object::Body::CampfireLit)) } + pub fn is_portal(&self) -> bool { + matches!( + self, + Body::Object(object::Body::Portal | object::Body::PortalActive) + ) + } + pub fn bleeds(&self) -> bool { !matches!( self, diff --git a/server/src/sys/teleporter.rs b/server/src/sys/teleporter.rs index 2181922f6d..7da21028be 100644 --- a/server/src/sys/teleporter.rs +++ b/server/src/sys/teleporter.rs @@ -1,11 +1,11 @@ use common::{ - comp::{object, Agent, Body, CharacterState, Player, Pos, Teleporter, Teleporting}, + comp::{object, Agent, Body, CharacterState, Pos, Teleporter, Teleporting}, event::{EventBus, ServerEvent}, resources::Time, CachedSpatialGrid, }; use common_ecs::{Origin, Phase, System}; -use specs::{storage::StorageEntry, Entities, Join, Read, ReadStorage, WriteStorage}; +use specs::{Entities, Entity, Join, Read, ReadStorage, WriteStorage}; use vek::Vec3; const TELEPORT_RADIUS: f32 = 3.; @@ -22,7 +22,6 @@ impl<'a> System<'a> for Sys { type SystemData = ( Entities<'a>, ReadStorage<'a, Pos>, - ReadStorage<'a, Player>, ReadStorage<'a, Teleporter>, ReadStorage<'a, Agent>, WriteStorage<'a, Teleporting>, @@ -42,7 +41,6 @@ impl<'a> System<'a> for Sys { ( entities, positions, - players, teleporters, agent, mut teleporting, @@ -66,6 +64,8 @@ impl<'a> System<'a> for Sys { }) }; + let mut teleporting_updates = Vec::new(); + for (portal_entity, teleporter_pos, body, teleporter) in (&entities, &positions, &bodies, &teleporters).join() { @@ -75,42 +75,38 @@ impl<'a> System<'a> for Sys { let mut is_active = false; - let mut player_data = ( - &entities, - &positions, - &players, - &character_states, - teleporting.entries(), - ) - .join(); - - for (entity, pos, _, character_state, teleporting) in + for (entity, pos, character_state, teleporting) in nearby_entities.filter_map(|entity| { - player_data + ( + &entities, + &positions, + &character_states, + teleporting.maybe(), + ) + .join() .get(entity, &entities) - .filter(|(_, player_pos, _, _, _)| { - in_portal_range(player_pos.0, teleporter_pos.0) - }) + .filter(|(_, pos, _, _)| in_portal_range(pos.0, teleporter_pos.0)) }) { - if !matches!( - character_state, - CharacterState::Idle(_) | CharacterState::Wielding(_) - ) || (teleporter.requires_no_aggro && check_aggro(entity, pos.0)) + if !matches!(character_state, CharacterState::Sit) + || (teleporter.requires_no_aggro && check_aggro(entity, pos.0)) { - if let StorageEntry::Occupied(entry) = teleporting { - entry.remove(); + if teleporting.is_some() { + teleporting_updates.push((entity, None)); }; continue; } - if let StorageEntry::Vacant(entry) = teleporting { - entry.insert(Teleporting { - teleport_start: *time, - portal: portal_entity, - end_time: Time(time.0 + teleporter.buildup_time.0), - }); + if teleporting.is_none() { + teleporting_updates.push(( + entity, + Some(Teleporting { + teleport_start: *time, + portal: portal_entity, + end_time: Time(time.0 + teleporter.buildup_time.0), + }), + )); } is_active = true; @@ -128,27 +124,42 @@ impl<'a> System<'a> for Sys { } } - for (entity, position, _, teleporting_entry) in - (&entities, &positions, &players, teleporting.entries()).join() - { - let StorageEntry::Occupied(teleporting) = teleporting_entry else { continue }; - let portal_pos = positions.get(teleporting.get().portal); - let Some(teleporter) = teleporters.get(teleporting.get().portal) else { - teleporting.remove(); + update_teleporting(&mut teleporting_updates, &mut teleporting); + + for (entity, position, teleporting) in (&entities, &positions, &teleporting).join() { + let mut remove = || teleporting_updates.push((entity, None)); + let portal_pos = positions.get(teleporting.portal); + let Some(teleporter) = teleporters.get(teleporting.portal) else { + remove(); continue }; if portal_pos.map_or(true, |portal_pos| { !in_portal_range(position.0, portal_pos.0) }) { - teleporting.remove(); - } else if teleporting.get().end_time.0 <= time.0 { - teleporting.remove(); + remove(); + } else if teleporting.end_time.0 <= time.0 { + remove(); event_bus.emit_now(ServerEvent::TeleportToPosition { entity, position: teleporter.target, }); } } + + update_teleporting(&mut teleporting_updates, &mut teleporting); + } +} + +fn update_teleporting( + updates: &mut Vec<(Entity, Option)>, + teleporting: &mut WriteStorage<'_, Teleporting>, +) { + for (entity, update) in updates.drain(..) { + if let Some(add) = update { + let _ = teleporting.insert(entity, add); + } else { + let _ = teleporting.remove(entity); + } } } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 30277e58c3..1d5f052c6e 100755 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -2208,13 +2208,13 @@ impl Hud { .set(overitem_id, ui_widgets); } } else if let Some(Interactable::Entity(entity)) = interactable { - // show hud for campfire - if client + // show hud for campfires and portals + if let Some(body) = client .state() .ecs() .read_storage::() .get(*entity) - .map_or(false, |b| b.is_campfire()) + .filter(|b| b.is_campfire() || b.is_portal()) { let overitem_id = overitem_walker.next( &mut self.ids.overitems, @@ -2234,7 +2234,11 @@ impl Hud { let over_pos = pos + Vec3::unit_z() * 1.5; overitem::Overitem::new( - i18n.get_msg("hud-crafting-campfire"), + i18n.get_msg(if body.is_campfire() { + "hud-crafting-campfire" + } else { + "hud-portal" + }), overitem::TEXT_COLOR, pos.distance_squared(player_pos), &self.fonts, diff --git a/voxygen/src/session/interactable.rs b/voxygen/src/session/interactable.rs index 12443b7528..5946c5b42a 100644 --- a/voxygen/src/session/interactable.rs +++ b/voxygen/src/session/interactable.rs @@ -247,7 +247,7 @@ pub(super) fn select_interactable( // * Are not riding the player let not_riding_player = is_rider .map_or(true, |is_rider| Some(&is_rider.mount) != uids.get(viewpoint_entity)); - let is_interactable = (b.is_campfire() || has_stats_or_item.is_some()) && not_riding_player; + let is_interactable = (b.is_campfire() || (b.is_portal() && (p.0.distance_squared(player_pos) <= 3f32.powi(2))) || has_stats_or_item.is_some()) && not_riding_player; if !is_interactable { return None; }; From 6a4a6bab9fd6ba752bc6fa5d4e5d63354714d55d Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Tue, 1 Aug 2023 11:15:34 +0200 Subject: [PATCH 15/22] dance dance --- server/src/sys/teleporter.rs | 2 +- voxygen/src/hud/mod.rs | 5 +++-- voxygen/src/session/mod.rs | 28 +++++++++++++++++++--------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/server/src/sys/teleporter.rs b/server/src/sys/teleporter.rs index 7da21028be..441f75ec49 100644 --- a/server/src/sys/teleporter.rs +++ b/server/src/sys/teleporter.rs @@ -88,7 +88,7 @@ impl<'a> System<'a> for Sys { .filter(|(_, pos, _, _)| in_portal_range(pos.0, teleporter_pos.0)) }) { - if !matches!(character_state, CharacterState::Sit) + if !matches!(character_state, CharacterState::Dance) || (teleporter.requires_no_aggro && check_aggro(entity, pos.0)) { if teleporting.is_some() { diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 1d5f052c6e..2b2086e4fd 100755 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -2233,8 +2233,9 @@ impl Hud { .map_or(Vec3::zero(), |e| e.0); let over_pos = pos + Vec3::unit_z() * 1.5; + let is_campfire = body.is_campfire(); overitem::Overitem::new( - i18n.get_msg(if body.is_campfire() { + i18n.get_msg(if is_campfire { "hud-crafting-campfire" } else { "hud-portal" @@ -2249,7 +2250,7 @@ impl Hud { &global_state.window.key_layout, vec![( Some(GameInput::Interact), - i18n.get_msg("hud-sit").to_string(), + i18n.get_msg(if is_campfire { "hud-sit" } else { "gameinput-dance" }).to_string(), )], ) .x_y(0.0, 100.0) diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 9a4ea9dc34..3b13acf367 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -1035,15 +1035,25 @@ impl PlayState for SessionState { .is_some() { client.pick_up(*entity); - } else if client - .state() - .ecs() - .read_storage::() - .get(*entity) - .map_or(false, |b| b.is_campfire()) - { - // TODO: maybe start crafting instead? - client.toggle_sit(); + } else if let Some(is_campfire) = { + let bodies = client + .state() + .ecs() + .read_storage::(); + + bodies + .get(*entity) + .filter(|body| { + body.is_campfire() || body.is_portal() + }) + .map(|body| body.is_campfire()) + } { + if is_campfire { + // TODO: maybe start crafting instead? + client.toggle_sit(); + } else { + client.toggle_dance(); + } } else { client.npc_interact(*entity, Subject::Regular); } From 0929a3c76cb2a5d5838a8bedbe5953623937bb4a Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Tue, 1 Aug 2023 13:16:49 +0200 Subject: [PATCH 16/22] use same timings for both portals --- common/src/cmd.rs | 2 +- world/src/site2/plot/dungeon.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/cmd.rs b/common/src/cmd.rs index 1a20c5132d..78cc5955ee 100644 --- a/common/src/cmd.rs +++ b/common/src/cmd.rs @@ -628,7 +628,7 @@ impl ServerChatCommand { Float("buildup_time", 5., Optional), ], "Spawns a portal", - Some(Moderator), + Some(Admin), ), ServerChatCommand::ReloadChunks => cmd( vec![], diff --git a/world/src/site2/plot/dungeon.rs b/world/src/site2/plot/dungeon.rs index 03ce3931d6..fdc16366c3 100644 --- a/world/src/site2/plot/dungeon.rs +++ b/world/src/site2/plot/dungeon.rs @@ -668,8 +668,8 @@ impl Floor { supplement.add_entity(EntityInfo::at(top_pos).into_special( SpecialEntity::Teleporter(Teleporter { target: bottom_pos + Vec3::unit_x() * 5., - requires_no_aggro: false, - buildup_time: Secs(1.), + requires_no_aggro: true, + buildup_time: Secs(5.), }), )); supplement.add_entity(EntityInfo::at(bottom_pos).into_special( From 92ab8dab9ac8f9463efc131c827aac2c51df14cc Mon Sep 17 00:00:00 2001 From: Maxicarlos08 Date: Tue, 1 Aug 2023 20:56:55 +0200 Subject: [PATCH 17/22] require interaction to start teleportation --- assets/voxygen/i18n/en/hud/misc.ftl | 1 + client/src/lib.rs | 6 ++++ common/src/comp/controller.rs | 1 + common/src/comp/teleport.rs | 1 - common/src/event.rs | 4 +++ common/systems/src/controller.rs | 8 +++++ server/src/events/entity_manipulation.rs | 39 ++++++++++++++++++---- server/src/events/mod.rs | 7 ++-- server/src/sys/teleporter.rs | 21 ++++-------- voxygen/src/hud/mod.rs | 2 +- voxygen/src/session/mod.rs | 41 +++++++++++++----------- 11 files changed, 87 insertions(+), 44 deletions(-) diff --git a/assets/voxygen/i18n/en/hud/misc.ftl b/assets/voxygen/i18n/en/hud/misc.ftl index b8644d3436..cc99b03fe2 100644 --- a/assets/voxygen/i18n/en/hud/misc.ftl +++ b/assets/voxygen/i18n/en/hud/misc.ftl @@ -39,6 +39,7 @@ hud-auto_walk_indicator = Auto walk/swim active hud-zoom_lock_indicator-remind = Zoom locked hud-zoom_lock_indicator-enable = Camera zoom locked hud-zoom_lock_indicator-disable = Camera zoom unlocked +hud-activate = Activate hud-collect = Collect hud-pick_up = Pick up hud-open = Open diff --git a/client/src/lib.rs b/client/src/lib.rs index cd07e42870..122ec37837 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -1625,6 +1625,12 @@ impl Client { } } + pub fn activate_portal(&mut self, portal: Uid) { + self.send_msg(ClientGeneral::ControlEvent(ControlEvent::ActivatePortal( + portal, + ))); + } + fn control_action(&mut self, control_action: ControlAction) { if let Some(controller) = self .state diff --git a/common/src/comp/controller.rs b/common/src/comp/controller.rs index c0e1e82159..fe84c1f642 100644 --- a/common/src/comp/controller.rs +++ b/common/src/comp/controller.rs @@ -158,6 +158,7 @@ pub enum ControlEvent { auxiliary_key: ability::AuxiliaryKey, new_ability: ability::AuxiliaryAbility, }, + ActivatePortal(Uid), } #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] diff --git a/common/src/comp/teleport.rs b/common/src/comp/teleport.rs index 63fbd4c2aa..816aabb1e1 100644 --- a/common/src/comp/teleport.rs +++ b/common/src/comp/teleport.rs @@ -17,7 +17,6 @@ impl Component for Teleporter { #[derive(Copy, Clone, Debug, PartialEq)] pub struct Teleporting { - pub teleport_start: Time, pub portal: Entity, pub end_time: Time, } diff --git a/common/src/event.rs b/common/src/event.rs index 7b23bcc33e..0090f7d9a8 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -336,6 +336,10 @@ pub enum ServerEvent { entity: EcsEntity, position: Vec3, }, + StartTeleporting { + entity: EcsEntity, + portal: EcsEntity, + } } pub struct EventBus { diff --git a/common/systems/src/controller.rs b/common/systems/src/controller.rs index 9da0652d13..7d713ca4cc 100644 --- a/common/systems/src/controller.rs +++ b/common/systems/src/controller.rs @@ -138,6 +138,14 @@ impl<'a> System<'a> for Sys { stance: Stance::None, }); }, + ControlEvent::ActivatePortal(portal_uid) => { + if let Some(portal) = read_data.id_maps.uid_entity(portal_uid) { + server_emitter.emit(ServerEvent::StartTeleporting { + entity, + portal, + }); + } + } } } } diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 533d0b0d86..3d6672135e 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -8,7 +8,7 @@ use crate::{ BuffKind, BuffSource, PhysicsState, }, rtsim, - sys::terrain::SAFE_ZONE_RADIUS, + sys::{teleporter::TELEPORT_RADIUS, terrain::SAFE_ZONE_RADIUS}, Server, SpawnPoint, StateExt, }; use authc::Uuid; @@ -1657,11 +1657,7 @@ pub fn handle_remove_light_emitter(server: &mut Server, entity: EcsEntity) { .remove(entity); } -pub fn handle_teleport_to_position_event( - server: &mut Server, - entity: EcsEntity, - position: Vec3, -) { +pub fn handle_teleport_to_position(server: &mut Server, entity: EcsEntity, position: Vec3) { let ecs = server.state.ecs(); ecs.write_storage::() @@ -1673,3 +1669,34 @@ pub fn handle_teleport_to_position_event( .get_mut(entity) .map(|forced_update| forced_update.update()); } + +pub fn handle_start_teleporting(server: &mut Server, entity: EcsEntity, portal: EcsEntity) { + let ecs = server.state.ecs(); + let positions = ecs.read_storage::(); + let mut teleportings = ecs.write_storage::(); + let now = ecs.read_resource::