diff --git a/common/src/comp/body.rs b/common/src/comp/body.rs index c61081864b..470b747242 100644 --- a/common/src/comp/body.rs +++ b/common/src/comp/body.rs @@ -880,7 +880,7 @@ impl Body { Body::BirdLarge(_) => 50.0, Body::BirdMedium(_) => 40.0, Body::Dragon(_) => 60.0, - Body::Ship(ship) if ship.can_fly() => 60.0, + Body::Ship(ship) => ship.flying_height(), _ => 0.0, } } diff --git a/common/src/comp/body/ship.rs b/common/src/comp/body/ship.rs index 84bedf43aa..a1618d7958 100644 --- a/common/src/comp/body/ship.rs +++ b/common/src/comp/body/ship.rs @@ -110,6 +110,8 @@ impl Body { matches!(self, Body::DefaultAirship | Body::AirBalloon | Body::Volume) } + pub fn flying_height(&self) -> f32 { if self.can_fly() { 200.0 } else { 0.0 } } + pub fn has_water_thrust(&self) -> bool { !self.can_fly() // TODO: Differentiate this more carefully } diff --git a/common/src/event.rs b/common/src/event.rs index 89c708b5e0..6f5b2f15be 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -227,6 +227,7 @@ pub enum ServerEvent { }, CreateShip { pos: Pos, + ori: Ori, ship: comp::ship::Body, rtsim_entity: Option, driver: Option, diff --git a/common/src/path.rs b/common/src/path.rs index a6dc55a5c4..4f5237a01b 100644 --- a/common/src/path.rs +++ b/common/src/path.rs @@ -466,7 +466,9 @@ impl Chaser { /*if traversal_cfg.can_fly { Some(((tgt - pos) , 1.0)) } else */ - if !walking_towards_edge || traversal_cfg.can_fly { + if traversal_cfg.can_fly { + Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.5), 1.0)) + } else if !walking_towards_edge { Some(((tgt - pos) * Vec3::new(1.0, 1.0, 0.0), 1.0)) } else { // This is unfortunately where an NPC will stare blankly diff --git a/rtsim/src/data/npc.rs b/rtsim/src/data/npc.rs index 8f2897f8cd..61f0cc7167 100644 --- a/rtsim/src/data/npc.rs +++ b/rtsim/src/data/npc.rs @@ -262,6 +262,7 @@ pub enum VehicleKind { #[derive(Clone, Serialize, Deserialize)] pub struct Vehicle { pub wpos: Vec3, + pub dir: Vec2, pub body: comp::ship::Body, @@ -283,6 +284,7 @@ impl Vehicle { pub fn new(wpos: Vec3, body: comp::ship::Body) -> Self { Self { wpos, + dir: Vec2::unit_y(), body, chunk_pos: None, driver: None, @@ -295,10 +297,10 @@ impl Vehicle { /// Max speed in block/s pub fn get_speed(&self) -> f32 { match self.body { - comp::ship::Body::DefaultAirship => 15.0, - comp::ship::Body::AirBalloon => 16.0, - comp::ship::Body::SailBoat => 12.0, - comp::ship::Body::Galleon => 13.0, + comp::ship::Body::DefaultAirship => 7.0, + comp::ship::Body::AirBalloon => 8.0, + comp::ship::Body::SailBoat => 5.0, + comp::ship::Body::Galleon => 6.0, _ => 10.0, } } diff --git a/rtsim/src/rule/npc_ai.rs b/rtsim/src/rule/npc_ai.rs index 47af93136c..cf24019378 100644 --- a/rtsim/src/rule/npc_ai.rs +++ b/rtsim/src/rule/npc_ai.rs @@ -61,7 +61,7 @@ fn path_in_site(start: Vec2, end: Vec2, site: &site2::Site) -> PathRes TileKind::Empty => 3.0, TileKind::Hazard(_) => 50.0, TileKind::Field => 8.0, - TileKind::Plaza | TileKind::Road { .. } | TileKind::Path => 1.0, + TileKind::Plaza | TileKind::Road { .. } | TileKind::Path | TileKind::Bridge => 1.0, TileKind::Building | TileKind::Castle @@ -95,13 +95,14 @@ fn path_in_site(start: Vec2, end: Vec2, site: &site2::Site) -> PathRes distance * terrain + building }; - astar.poll( - 1000, - heuristic, - |&tile| CARDINALS.iter().map(move |c| tile + *c), - transition, - |tile| *tile == end || site.tiles.get_known(*tile).is_none(), - ) + let neighbors = |tile: &Vec2| { + let tile = *tile; + CARDINALS.iter().map(move |c| tile + *c) + }; + + astar.poll(1000, heuristic, neighbors, transition, |tile| { + *tile == end || site.tiles.get_known(*tile).is_none() + }) } fn path_between_sites( @@ -136,23 +137,23 @@ fn path_between_sites( let neighbors = |site: &Id| world.civs().neighbors(*site); - let track_between = |a: Id, b: Id| { + let transition = |a: &Id, b: &Id| { world .civs() - .tracks - .get(world.civs().track_between(a, b).unwrap().0) + .track_between(*a, *b) + .map(|(id, _)| world.civs().tracks.get(id).cost) + .unwrap_or(f32::INFINITY) }; - let transition = |a: &Id, b: &Id| track_between(*a, *b).cost; - let path = astar.poll(250, heuristic, neighbors, transition, |site| *site == end); path.map(|path| { let path = path .into_iter() .tuple_windows::<(_, _)>() - .map(|(a, b)| world.civs().track_between(a, b).unwrap()) - .collect_vec(); + // Since we get a, b from neighbors, track_between shouldn't return None. + .filter_map(|(a, b)| world.civs().track_between(a, b)) + .collect(); Path { nodes: path } }) } @@ -279,13 +280,9 @@ fn idle() -> impl Action { just(|ctx| ctx.controller.do_idle()).debug(|| "idle") fn goto(wpos: Vec3, speed_factor: f32, goal_dist: f32) -> impl Action { const STEP_DIST: f32 = 24.0; const WAYPOINT_DIST: f32 = 12.0; - let mut waypoint = None; just(move |ctx| { - let rpos = wpos - ctx.npc.wpos; - let len = rpos.magnitude(); - // If we're close to the next waypoint, complete it if waypoint.map_or(false, |waypoint: Vec3| { ctx.npc.wpos.xy().distance_squared(waypoint.xy()) < WAYPOINT_DIST.powi(2) @@ -295,6 +292,8 @@ fn goto(wpos: Vec3, speed_factor: f32, goal_dist: f32) -> impl Action { // Get the next waypoint on the route toward the goal let waypoint = waypoint.get_or_insert_with(|| { + let rpos = wpos - ctx.npc.wpos; + let len = rpos.magnitude(); let wpos = ctx.npc.wpos + (rpos / len) * len.min(STEP_DIST); wpos.with_z( @@ -313,6 +312,49 @@ fn goto(wpos: Vec3, speed_factor: f32, goal_dist: f32) -> impl Action { .map(|_| {}) } +/// Try to walk fly a 3D position following the terrain altitude at an offset +/// without caring for obstacles. +fn goto_flying( + wpos: Vec3, + speed_factor: f32, + goal_dist: f32, + step_dist: f32, + waypoint_dist: f32, + height_offset: f32, +) -> impl Action { + let mut waypoint = None; + + just(move |ctx| { + // If we're close to the next waypoint, complete it + if waypoint.map_or(false, |waypoint: Vec3| { + ctx.npc.wpos.distance_squared(waypoint) < waypoint_dist.powi(2) + }) { + waypoint = None; + } + + // Get the next waypoint on the route toward the goal + let waypoint = waypoint.get_or_insert_with(|| { + let rpos = wpos - ctx.npc.wpos; + let len = rpos.magnitude(); + let wpos = ctx.npc.wpos + (rpos / len) * len.min(step_dist); + + wpos.with_z( + ctx.world + .sim() + .get_surface_alt_approx(wpos.xy().as_()) + .map(|alt| alt + height_offset) + .unwrap_or(wpos.z), + ) + }); + + ctx.controller.do_goto(*waypoint, speed_factor); + }) + .repeat() + .stop_if(move |ctx| ctx.npc.wpos.xy().distance_squared(wpos.xy()) < goal_dist.powi(2)) + .debug(move || format!("goto {}, {}, {}", wpos.x, wpos.y, wpos.z)) + .map(|_| {}) +} + /// Try to walk toward a 2D position on the terrain without caring for /// obstacles. fn goto_2d(wpos2d: Vec2, speed_factor: f32, goal_dist: f32) -> impl Action { @@ -322,6 +364,30 @@ fn goto_2d(wpos2d: Vec2, speed_factor: f32, goal_dist: f32) -> impl Action }) } +/// Try to fly toward a 2D position following the terrain altitude at an offset +/// without caring for obstacles. +fn goto_2d_flying( + wpos2d: Vec2, + speed_factor: f32, + goal_dist: f32, + step_dist: f32, + waypoint_dist: f32, + height_offset: f32, +) -> impl Action { + now(move |ctx| { + let wpos = wpos2d + .with_z(ctx.world.sim().get_alt_approx(wpos2d.as_()).unwrap_or(0.0) + height_offset); + goto_flying( + wpos, + speed_factor, + goal_dist, + step_dist, + waypoint_dist, + height_offset, + ) + }) +} + fn traverse_points(mut next_point: F, speed_factor: f32) -> impl Action where F: FnMut(&mut NpcCtx) -> Option> + Send + Sync + 'static, @@ -345,7 +411,7 @@ where if let Some(path) = path_site(wpos, site_exit, site, ctx.index) { Some(Either::Left( - seq(path.into_iter().map(|wpos| goto_2d(wpos, 1.0, 8.0))).then(goto_2d( + seq(path.into_iter().map(move |wpos| goto_2d(wpos, 1.0, 8.0))).then(goto_2d( site_exit, speed_factor, 8.0, @@ -821,53 +887,9 @@ fn follow(npc: NpcId, distance: f32) -> impl Action { } */ -fn chunk_path( - from: Vec2, - to: Vec2, - chunk_height: impl Fn(Vec2) -> Option, -) -> Box { - let heuristics = - |(p, _): &(Vec2, i32), _: &(Vec2, i32)| p.distance_squared(to) as f32; - let start = (from, chunk_height(from).unwrap()); - let mut astar = Astar::new(1000, start, BuildHasherDefault::::default()); - - let path = astar.poll( - 1000, - heuristics, - |&(p, _)| { - NEIGHBORS - .into_iter() - .map(move |n| p + n) - .filter_map(|p| Some((p, chunk_height(p)?))) - }, - |(p0, h0), (p1, h1)| { - let diff = (p0 - p1).as_().cpos_to_wpos().with_z((h0 - h1) as f32); - - diff.magnitude_squared() - }, - |(e, _)| *e == to, - ); - let path = match path { - PathResult::Exhausted(p) | PathResult::Path(p) => p, - _ => return finish().boxed(), - }; - let len = path.len(); - seq(path - .into_iter() - .enumerate() - .map(move |(i, (chunk_pos, height))| { - let wpos = TerrainChunkSize::center_wpos(chunk_pos) - .with_z(height) - .as_(); - goto(wpos, 1.0, 5.0) - .debug(move || format!("chunk path {i}/{len} chunk: {chunk_pos}, height: {height}")) - })) - .boxed() -} - -fn pilot() -> impl Action { +fn pilot(ship: common::comp::ship::Body) -> impl Action { // Travel between different towns in a straight line - now(|ctx| { + now(move |ctx| { let data = &*ctx.state.data(); let site = data .sites @@ -880,16 +902,19 @@ fn pilot() -> impl Action { }) .choose(&mut ctx.rng); if let Some((_id, site)) = site { - let start_chunk = ctx.npc.wpos.xy().as_().wpos_to_cpos(); - let end_chunk = site.wpos.wpos_to_cpos(); - chunk_path(start_chunk, end_chunk, |chunk| { - ctx.world - .sim() - .get_alt_approx(TerrainChunkSize::center_wpos(chunk)) - .map(|f| (f + 150.0) as i32) - }) + Either::Right( + goto_2d_flying( + site.wpos.as_(), + 1.0, + 50.0, + 150.0, + 110.0, + ship.flying_height(), + ) + .then(goto_2d_flying(site.wpos.as_(), 1.0, 10.0, 32.0, 16.0, 10.0)), + ) } else { - finish().boxed() + Either::Left(finish()) } }) .repeat() @@ -990,7 +1015,7 @@ fn humanoid() -> impl Action { if let Some(vehicle) = ctx.state.data().npcs.vehicles.get(riding.vehicle) { match vehicle.body { common::comp::ship::Body::DefaultAirship - | common::comp::ship::Body::AirBalloon => important(pilot()), + | common::comp::ship::Body::AirBalloon => important(pilot(vehicle.body)), common::comp::ship::Body::SailBoat | common::comp::ship::Body::Galleon => { important(captain()) }, @@ -1024,6 +1049,16 @@ fn bird_large() -> impl Action { let data = ctx.state.data(); if let Some(home) = ctx.npc.home { let is_home = ctx.npc.current_site.map_or(false, |site| home == site); + let goto = |wpos| { + casual(goto_2d_flying( + wpos, + 1.0, + 20.0, + 32.0, + 22.0, + ctx.npc.body.flying_height(), + )) + }; if is_home { if let Some((_, site)) = data .sites @@ -1036,32 +1071,12 @@ fn bird_large() -> impl Action { }) .choose(&mut ctx.rng) { - casual(goto( - site.wpos.as_::().with_z( - ctx.world - .sim() - .get_surface_alt_approx(site.wpos) - .unwrap_or(0.0) - + ctx.npc.body.flying_height(), - ), - 1.0, - 20.0, - )) + goto(site.wpos.as_::()) } else { casual(idle()) } } else if let Some(site) = data.sites.get(home) { - casual(goto( - site.wpos.as_::().with_z( - ctx.world - .sim() - .get_surface_alt_approx(site.wpos) - .unwrap_or(0.0) - + ctx.npc.body.flying_height(), - ), - 1.0, - 20.0, - )) + goto(site.wpos.as_::()) } else { casual(idle()) } diff --git a/rtsim/src/rule/simulate_npcs.rs b/rtsim/src/rule/simulate_npcs.rs index f319b67985..08b60004c7 100644 --- a/rtsim/src/rule/simulate_npcs.rs +++ b/rtsim/src/rule/simulate_npcs.rs @@ -11,6 +11,7 @@ use common::{ use rand::prelude::*; use rand_chacha::ChaChaRng; use tracing::{error, warn}; +use vek::Vec2; use world::site::SiteKind; pub struct SimulateNpcs; @@ -166,12 +167,11 @@ fn on_tick(ctx: EventCtx) { let dist2 = diff.magnitude_squared(); if dist2 > 0.5f32.powi(2) { - let mut wpos = vehicle.wpos + let wpos = vehicle.wpos + (diff * (vehicle.get_speed() * speed_factor * ctx.event.dt / dist2.sqrt()) - .min(1.0)) - .with_z(0.0); + .min(1.0)); let is_valid = match vehicle.body { common::comp::ship::Body::DefaultAirship @@ -188,33 +188,11 @@ fn on_tick(ctx: EventCtx) { }; if is_valid { - match vehicle.body { - common::comp::ship::Body::DefaultAirship - | common::comp::ship::Body::AirBalloon => { - if let Some(alt) = ctx - .world - .sim() - .get_alt_approx(wpos.xy().as_()) - .filter(|alt| wpos.z < *alt) - { - wpos.z = alt; - } - }, - common::comp::ship::Body::SailBoat - | common::comp::ship::Body::Galleon => { - wpos.z = ctx - .world - .sim() - .get_interpolated( - wpos.xy().map(|e| e as i32), - |chunk| chunk.water_alt, - ) - .unwrap_or(0.0); - }, - _ => {}, - } vehicle.wpos = wpos; } + vehicle.dir = (target.xy() - vehicle.wpos.xy()) + .try_normalized() + .unwrap_or(Vec2::unit_y()); } }, // When riding, other actions are disabled @@ -264,7 +242,6 @@ fn on_tick(ctx: EventCtx) { NpcAction::Attack(_) => {}, // TODO: Implement simulated combat } } - // Make sure NPCs remain on the surface npc.wpos.z = ctx .world diff --git a/server/agent/src/action_nodes.rs b/server/agent/src/action_nodes.rs index af815f59ad..55377a5cf2 100644 --- a/server/agent/src/action_nodes.rs +++ b/server/agent/src/action_nodes.rs @@ -247,7 +247,7 @@ impl<'a> AgentData<'a> { self.vel.0, chase_tgt, TraversalConfig { - min_tgt_dist: 1.25, + min_tgt_dist: self.traversal_config.min_tgt_dist * 1.25, ..self.traversal_config }, ) { diff --git a/server/src/cmd.rs b/server/src/cmd.rs index cb64723a05..91c4807dd8 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -1572,19 +1572,14 @@ fn handle_spawn_airship( pos.0.z += 50.0; const DESTINATION_RADIUS: f32 = 2000.0; let angle = angle.map(|a| a * std::f32::consts::PI / 180.0); - let destination = angle.map(|a| { - pos.0 - + Vec3::new( - DESTINATION_RADIUS * a.cos(), - DESTINATION_RADIUS * a.sin(), - 200.0, - ) - }); + let dir = angle.map(|a| Vec3::new(a.cos(), a.sin(), 0.0)); + let destination = dir.map(|dir| pos.0 + dir * DESTINATION_RADIUS + Vec3::new(0.0, 0.0, 200.0)); let mut rng = thread_rng(); let ship = comp::ship::Body::random_airship_with(&mut rng); + let ori = comp::Ori::from(common::util::Dir::new(dir.unwrap_or(Vec3::unit_y()))); let mut builder = server .state - .create_ship(pos, ship, |ship| ship.make_collider()) + .create_ship(pos, ori, ship, |ship| ship.make_collider()) .with(LightEmitter { col: Rgb::new(1.0, 0.65, 0.2), strength: 2.0, @@ -1621,19 +1616,14 @@ fn handle_spawn_ship( pos.0.z += 50.0; const DESTINATION_RADIUS: f32 = 2000.0; let angle = angle.map(|a| a * std::f32::consts::PI / 180.0); - let destination = angle.map(|a| { - pos.0 - + Vec3::new( - DESTINATION_RADIUS * a.cos(), - DESTINATION_RADIUS * a.sin(), - 200.0, - ) - }); + let dir = angle.map(|a| Vec3::new(a.cos(), a.sin(), 0.0)); + let destination = dir.map(|dir| pos.0 + dir * DESTINATION_RADIUS + Vec3::new(0.0, 0.0, 200.0)); let mut rng = thread_rng(); let ship = comp::ship::Body::random_ship_with(&mut rng); + let ori = comp::Ori::from(common::util::Dir::new(dir.unwrap_or(Vec3::unit_y()))); let mut builder = server .state - .create_ship(pos, ship, |ship| ship.make_collider()) + .create_ship(pos, ori, ship, |ship| ship.make_collider()) .with(LightEmitter { col: Rgb::new(1.0, 0.65, 0.2), strength: 2.0, @@ -1683,9 +1673,12 @@ fn handle_make_volume( }; server .state - .create_ship(comp::Pos(pos.0 + Vec3::unit_z() * 50.0), ship, move |_| { - collider - }) + .create_ship( + comp::Pos(pos.0 + Vec3::unit_z() * 50.0), + comp::Ori::default(), + ship, + move |_| collider, + ) .build(); server.notify_client( diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 61901ca597..76067e4a78 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -194,6 +194,7 @@ pub fn handle_create_npc(server: &mut Server, pos: Pos, mut npc: NpcBuilder) -> pub fn handle_create_ship( server: &mut Server, pos: Pos, + ori: Ori, ship: comp::ship::Body, rtsim_vehicle: Option, driver: Option, @@ -201,7 +202,7 @@ pub fn handle_create_ship( ) { let mut entity = server .state - .create_ship(pos, ship, |ship| ship.make_collider()); + .create_ship(pos, ori, ship, |ship| ship.make_collider()); /* if let Some(mut agent) = agent { let (kp, ki, kd) = pid_coefficients(&Body::Ship(ship)); diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index a6c4882257..6e2a28e3d7 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -193,10 +193,11 @@ impl Server { }, ServerEvent::CreateShip { pos, + ori, ship, rtsim_entity, driver, - } => handle_create_ship(self, pos, ship, rtsim_entity, driver, Vec::new()), + } => handle_create_ship(self, pos, ori, ship, rtsim_entity, driver, Vec::new()), ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos), 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 31956ca1c6..7f2c72cd6d 100644 --- a/server/src/rtsim/tick.rs +++ b/server/src/rtsim/tick.rs @@ -11,6 +11,7 @@ use common::{ slowjob::SlowJobPool, terrain::CoordinateConversions, trade::{Good, SiteInformation}, + util::Dir, LoadoutBuilder, }; use common_ecs::{Job, Origin, Phase, System}; @@ -315,6 +316,7 @@ impl<'a> System<'a> for Sys { emitter.emit(ServerEvent::CreateShip { pos: comp::Pos(vehicle.wpos), + ori: comp::Ori::from(Dir::new(vehicle.dir.with_z(0.0))), ship: vehicle.body, rtsim_entity: Some(RtSimVehicle(vehicle_id)), driver: vehicle.driver.and_then(&mut actor_info), @@ -330,6 +332,8 @@ impl<'a> System<'a> for Sys { // loaded if matches!(npc.mode, SimulationMode::Simulated) && chunk_states.0.get(chunk).map_or(false, |c| c.is_some()) + // Riding npcs will be spawned by the vehicle. + && npc.riding.is_none() { npc.mode = SimulationMode::Loaded; let entity_info = get_npc_entity_info(npc, &data.sites, index.as_index_ref()); diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index 850a2b19e7..bbf5a7d280 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -64,6 +64,7 @@ pub trait StateExt { fn create_ship comp::Collider>( &mut self, pos: comp::Pos, + ori: comp::Ori, ship: comp::ship::Body, make_collider: F, ) -> EcsEntityBuilder; @@ -338,6 +339,7 @@ impl StateExt for State { fn create_ship comp::Collider>( &mut self, pos: comp::Pos, + ori: comp::Ori, ship: comp::ship::Body, make_collider: F, ) -> EcsEntityBuilder { @@ -347,7 +349,7 @@ impl StateExt for State { .create_entity_synced() .with(pos) .with(comp::Vel(Vec3::zero())) - .with(comp::Ori::default()) + .with(ori) .with(body.mass()) .with(body.density()) .with(make_collider(ship)) diff --git a/server/src/sys/agent.rs b/server/src/sys/agent.rs index 5151fbea23..b7ccafc870 100644 --- a/server/src/sys/agent.rs +++ b/server/src/sys/agent.rs @@ -107,6 +107,10 @@ impl<'a> System<'a> for Sys { .unwrap_or(entity); let moving_body = read_data.bodies.get(moving_entity); + let physics_state = read_data + .physics_states + .get(moving_entity) + .unwrap_or(physics_state); // Hack, replace with better system when groups are more sophisticated // Override alignment if in a group unless entity is owned already @@ -136,7 +140,10 @@ impl<'a> System<'a> for Sys { controller.inputs.look_dir = ori.look_dir(); } - let scale = read_data.scales.get(entity).map_or(1.0, |Scale(s)| *s); + let scale = read_data + .scales + .get(moving_entity) + .map_or(1.0, |Scale(s)| *s); let glider_equipped = inventory .equipped(EquipSlot::Glider) @@ -180,7 +187,7 @@ impl<'a> System<'a> for Sys { slow_factor, on_ground: physics_state.on_ground.is_some(), in_liquid: physics_state.in_liquid().is_some(), - min_tgt_dist: 1.0, + min_tgt_dist: scale * moving_body.map_or(1.0, |body| body.max_radius()), can_climb: moving_body.map_or(false, Body::can_climb), can_fly: moving_body.map_or(false, |b| b.fly_thrust().is_some()), }; diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index d7a9cd1f00..e8e507756f 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -1067,7 +1067,7 @@ impl Site { }); site.blit_aabr(aabr, Tile { - kind: TileKind::Building, + kind: TileKind::Bridge, plot: Some(plot), hard_alt: None, }); diff --git a/world/src/site2/tile.rs b/world/src/site2/tile.rs index 137038e9dd..4de6d01db8 100644 --- a/world/src/site2/tile.rs +++ b/world/src/site2/tile.rs @@ -191,6 +191,7 @@ pub enum TileKind { Keep(KeepKind), Gate, GnarlingFortification, + Bridge, } #[derive(Clone, PartialEq)]