Beginning of light mechanic implementation
This commit is contained in:
parent
760598e3a1
commit
28654a5130
@ -73,6 +73,12 @@ pub struct LocationStats {
|
|||||||
pub total_weight: u64,
|
pub total_weight: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct ObjectRef {
|
||||||
|
pub item_type: String,
|
||||||
|
pub item_code: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, allow(dead_code))]
|
#[cfg_attr(test, allow(dead_code))]
|
||||||
impl DBPool {
|
impl DBPool {
|
||||||
pub async fn record_listener_ping(self: &DBPool, listener: Uuid) -> DResult<()> {
|
pub async fn record_listener_ping(self: &DBPool, listener: Uuid) -> DResult<()> {
|
||||||
@ -401,6 +407,7 @@ impl DBTrans {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deny(dead_code)]
|
||||||
pub async fn find_by_username<'a>(self: &'a Self, username: &'a str) -> DResult<Option<User>> {
|
pub async fn find_by_username<'a>(self: &'a Self, username: &'a str) -> DResult<Option<User>> {
|
||||||
if let Some(details_json) = self
|
if let Some(details_json) = self
|
||||||
.pg_trans()?
|
.pg_trans()?
|
||||||
@ -1906,6 +1913,68 @@ impl DBTrans {
|
|||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn is_illuminated(
|
||||||
|
&self,
|
||||||
|
player: &Item,
|
||||||
|
location: &Item,
|
||||||
|
consider_adjacent: &Vec<ObjectRef>,
|
||||||
|
) -> DResult<bool> {
|
||||||
|
if !location.flags.contains(&ItemFlag::DarkPlace) {
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let player_ref = player.refstr();
|
||||||
|
let loc_ref = location.refstr();
|
||||||
|
let adjacent_json = serde_json::to_value(consider_adjacent)?;
|
||||||
|
Ok(self
|
||||||
|
.pg_trans()?
|
||||||
|
.query_one(
|
||||||
|
&format!("WITH
|
||||||
|
player_lights AS (
|
||||||
|
SELECT 1 FROM items WHERE details->>'location' = $1 AND
|
||||||
|
details->'flags' @> '\"{}\"'
|
||||||
|
),
|
||||||
|
location_direct_lights AS (
|
||||||
|
SELECT 1 FROM items WHERE details->>'location' = $2 AND
|
||||||
|
details->'flags' @> '\"IlluminatesRoom\"'
|
||||||
|
),
|
||||||
|
location_players AS (
|
||||||
|
SELECT * FROM items WHERE details->>'location' = $2 AND details->>'item_type' = 'player'
|
||||||
|
),
|
||||||
|
location_player_lights AS (
|
||||||
|
SELECT 1 FROM items i JOIN location_players p ON i.details->>'location' = CONCAT(p.details->>'item_type', '/', p.details->>'item_code') WHERE i.details->'flags' @> '\"IlluminatesRoom\"'
|
||||||
|
),
|
||||||
|
adjacent AS (
|
||||||
|
SELECT value->>'item_type' AS item_type, value->>'item_code' AS item_code FROM jsonb_array_elements($3 :: JSONB)
|
||||||
|
),
|
||||||
|
adjacent_direct_lights AS (
|
||||||
|
SELECT 1 FROM items i JOIN adjacent a ON i.details->>'item_type' = a.item_type AND i.details->>'item_code' = a.item_code
|
||||||
|
WHERE i.details->'flags' @> '\"IlluminatesAdjacentRoom\"'
|
||||||
|
),
|
||||||
|
adjacent_players AS (
|
||||||
|
SELECT i.* FROM items i JOIN adjacent a ON i.details->>'location' = CONCAT(a.item_type, '/', a.item_code)
|
||||||
|
),
|
||||||
|
adjacent_player_lights AS (
|
||||||
|
SELECT 1 FROM items i JOIN adjacent_players p ON i.details->>'location' = CONCAT(p.details->>'item_type', '/', p.details->>'item_code')
|
||||||
|
WHERE i.details->'flags' @> '\"IlluminatesAdjacentRoom\"'
|
||||||
|
),
|
||||||
|
relevant_lights AS (
|
||||||
|
SELECT * FROM player_lights UNION ALL
|
||||||
|
SELECT * FROM location_direct_lights UNION ALL
|
||||||
|
SELECT * FROM location_player_lights UNION ALL
|
||||||
|
SELECT * FROM adjacent_direct_lights UNION ALL
|
||||||
|
SELECT * FROM adjacent_player_lights
|
||||||
|
)
|
||||||
|
SELECT EXISTS(SELECT 1 FROM relevant_lights);",
|
||||||
|
if player.location == loc_ref { "IlluminatesRoomForPlayer" } else { "IlluminatesAdjacentRoomForPlayer"}
|
||||||
|
),
|
||||||
|
&[&player_ref,
|
||||||
|
&loc_ref,
|
||||||
|
&adjacent_json],
|
||||||
|
)
|
||||||
|
.await?.get(0))
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn commit(mut self: Self) -> DResult<()> {
|
pub async fn commit(mut self: Self) -> DResult<()> {
|
||||||
let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None));
|
let trans_opt = self.with_trans_mut(|t| std::mem::replace(t, None));
|
||||||
if let Some(trans) = trans_opt {
|
if let Some(trans) = trans_opt {
|
||||||
|
@ -7,7 +7,7 @@ use super::{
|
|||||||
#[double]
|
#[double]
|
||||||
use crate::db::DBTrans;
|
use crate::db::DBTrans;
|
||||||
use crate::{
|
use crate::{
|
||||||
db::ItemSearchParams,
|
db::{ItemSearchParams, ObjectRef},
|
||||||
language,
|
language,
|
||||||
models::{
|
models::{
|
||||||
effect::EffectType,
|
effect::EffectType,
|
||||||
@ -376,8 +376,31 @@ fn exits_for_dyn(dynroom: &dynzone::Dynroom) -> String {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn is_room_illuminated(
|
||||||
|
ctx: &VerbContext<'_>,
|
||||||
|
player_item: &Item,
|
||||||
|
location: &Item,
|
||||||
|
room: &room::Room,
|
||||||
|
) -> UResult<bool> {
|
||||||
|
let consider_exits: Vec<ObjectRef> = room
|
||||||
|
.exits
|
||||||
|
.iter()
|
||||||
|
.filter_map(|ex| room::resolve_exit(room, ex))
|
||||||
|
// Do we want to consider skipping exits behind closed doors?
|
||||||
|
.map(|r| ObjectRef {
|
||||||
|
item_type: "room".to_owned(),
|
||||||
|
item_code: r.code.to_owned(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Ok(ctx
|
||||||
|
.trans
|
||||||
|
.is_illuminated(player_item, location, &consider_exits)
|
||||||
|
.await?)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn describe_room(
|
pub async fn describe_room(
|
||||||
ctx: &VerbContext<'_>,
|
ctx: &VerbContext<'_>,
|
||||||
|
player_item: &Item,
|
||||||
item: &Item,
|
item: &Item,
|
||||||
room: &room::Room,
|
room: &room::Room,
|
||||||
contents: &str,
|
contents: &str,
|
||||||
@ -386,30 +409,36 @@ pub async fn describe_room(
|
|||||||
.get(room.zone.as_str())
|
.get(room.zone.as_str())
|
||||||
.map(|z| z.display)
|
.map(|z| z.display)
|
||||||
.unwrap_or("Outside of time");
|
.unwrap_or("Outside of time");
|
||||||
|
let illum = is_room_illuminated(ctx, player_item, item, room).await?;
|
||||||
|
let desc = flow_around(
|
||||||
|
&render_map(room, 5, 5),
|
||||||
|
10,
|
||||||
|
ansi!("<reset> "),
|
||||||
|
&word_wrap(
|
||||||
|
&format!(
|
||||||
|
ansi!("<yellow>{}<reset> (<blue>{}<reset>)\n{}{}.{}\n{}\n"),
|
||||||
|
item.display_for_sentence(1, false),
|
||||||
|
zone,
|
||||||
|
&item.details.as_ref().map(|d| d.as_str()).unwrap_or(""),
|
||||||
|
item.details_dyn_suffix
|
||||||
|
.as_ref()
|
||||||
|
.map(|d| d.as_str())
|
||||||
|
.unwrap_or(""),
|
||||||
|
contents,
|
||||||
|
exits_for(room)
|
||||||
|
),
|
||||||
|
|row| if row >= 5 { 80 } else { 68 },
|
||||||
|
),
|
||||||
|
68,
|
||||||
|
);
|
||||||
ctx.trans
|
ctx.trans
|
||||||
.queue_for_session(
|
.queue_for_session(
|
||||||
ctx.session,
|
ctx.session,
|
||||||
Some(&flow_around(
|
Some(if illum {
|
||||||
&render_map(room, 5, 5),
|
&desc
|
||||||
10,
|
} else {
|
||||||
ansi!("<reset> "),
|
"It's too dark to see much.\n"
|
||||||
&word_wrap(
|
}),
|
||||||
&format!(
|
|
||||||
ansi!("<yellow>{}<reset> (<blue>{}<reset>)\n{}{}.{}\n{}\n"),
|
|
||||||
item.display_for_sentence(1, false),
|
|
||||||
zone,
|
|
||||||
&item.details.as_ref().map(|d| d.as_str()).unwrap_or(""),
|
|
||||||
item.details_dyn_suffix
|
|
||||||
.as_ref()
|
|
||||||
.map(|d| d.as_str())
|
|
||||||
.unwrap_or(""),
|
|
||||||
contents,
|
|
||||||
exits_for(room)
|
|
||||||
),
|
|
||||||
|row| if row >= 5 { 80 } else { 68 },
|
|
||||||
),
|
|
||||||
68,
|
|
||||||
)),
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -768,7 +797,14 @@ impl UserVerb for Verb {
|
|||||||
let room = room::room_map_by_code()
|
let room = room::room_map_by_code()
|
||||||
.get(item.item_code.as_str())
|
.get(item.item_code.as_str())
|
||||||
.ok_or_else(|| UserError("Sorry, that room no longer exists".to_owned()))?;
|
.ok_or_else(|| UserError("Sorry, that room no longer exists".to_owned()))?;
|
||||||
describe_room(ctx, &item, &room, &list_room_contents(ctx, &item).await?).await?;
|
describe_room(
|
||||||
|
ctx,
|
||||||
|
&player_item,
|
||||||
|
&item,
|
||||||
|
&room,
|
||||||
|
&list_room_contents(ctx, &item).await?,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
} else if item.item_type == "dynroom" {
|
} else if item.item_type == "dynroom" {
|
||||||
let (dynzone, dynroom) = match &item.special_data {
|
let (dynzone, dynroom) = match &item.special_data {
|
||||||
Some(ItemSpecialData::DynroomData {
|
Some(ItemSpecialData::DynroomData {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use super::{
|
use super::{
|
||||||
get_player_item_or_fail, user_error, UResult, UserError, UserVerb, UserVerbRef, VerbContext,
|
get_player_item_or_fail, look::is_room_illuminated, user_error, UResult, UserError, UserVerb,
|
||||||
|
UserVerbRef, VerbContext,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
models::item::{Item, ItemSpecialData},
|
models::item::{Item, ItemSpecialData},
|
||||||
@ -541,6 +542,9 @@ impl UserVerb for Verb {
|
|||||||
let room = room::room_map_by_code()
|
let room = room::room_map_by_code()
|
||||||
.get(room_item.item_code.as_str())
|
.get(room_item.item_code.as_str())
|
||||||
.ok_or_else(|| UserError("Sorry, that room no longer exists".to_owned()))?;
|
.ok_or_else(|| UserError("Sorry, that room no longer exists".to_owned()))?;
|
||||||
|
if !is_room_illuminated(ctx, &player_item, &room_item, room).await? {
|
||||||
|
user_error("It's too dark here to make out the map.".to_owned())?;
|
||||||
|
}
|
||||||
map_type.map_room(ctx, &room).await?;
|
map_type.map_room(ctx, &room).await?;
|
||||||
} else if room_item.item_type == "dynroom" {
|
} else if room_item.item_type == "dynroom" {
|
||||||
let (dynzone, dynroom) = match &room_item.special_data {
|
let (dynzone, dynroom) = match &room_item.special_data {
|
||||||
|
@ -325,6 +325,11 @@ pub enum ItemFlag {
|
|||||||
NoSeeContents,
|
NoSeeContents,
|
||||||
DroppedItemsDontExpire,
|
DroppedItemsDontExpire,
|
||||||
PrivatePlace,
|
PrivatePlace,
|
||||||
|
DarkPlace,
|
||||||
|
IlluminatesRoom,
|
||||||
|
IlluminatesRoomForPlayer,
|
||||||
|
IlluminatesAdjacentRoom,
|
||||||
|
IlluminatesAdjacentRoomForPlayer,
|
||||||
Hireable,
|
Hireable,
|
||||||
NPCsDontAttack,
|
NPCsDontAttack,
|
||||||
CanLoad,
|
CanLoad,
|
||||||
|
@ -26,6 +26,7 @@ mod cok_murl;
|
|||||||
pub mod computer_museum;
|
pub mod computer_museum;
|
||||||
pub mod general_hospital;
|
pub mod general_hospital;
|
||||||
pub mod melbs;
|
pub mod melbs;
|
||||||
|
pub mod melbs_sewers;
|
||||||
mod repro_xv;
|
mod repro_xv;
|
||||||
mod special;
|
mod special;
|
||||||
|
|
||||||
@ -74,6 +75,11 @@ pub fn zone_details() -> &'static BTreeMap<&'static str, Zone> {
|
|||||||
display: "General Hospital",
|
display: "General Hospital",
|
||||||
outdoors: false,
|
outdoors: false,
|
||||||
},
|
},
|
||||||
|
Zone {
|
||||||
|
code: "melbs_sewers",
|
||||||
|
display: "Melbs Sewers",
|
||||||
|
outdoors: false,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|x| (x.code, x))
|
.map(|x| (x.code, x))
|
||||||
@ -528,6 +534,7 @@ pub fn room_list() -> &'static Vec<Room> {
|
|||||||
rooms.append(&mut special::room_list());
|
rooms.append(&mut special::room_list());
|
||||||
rooms.append(&mut computer_museum::room_list());
|
rooms.append(&mut computer_museum::room_list());
|
||||||
rooms.append(&mut general_hospital::room_list());
|
rooms.append(&mut general_hospital::room_list());
|
||||||
|
rooms.append(&mut melbs_sewers::room_list());
|
||||||
rooms.into_iter().collect()
|
rooms.into_iter().collect()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -769,4 +776,21 @@ mod test {
|
|||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
assert_eq!(bad_scav_rooms, Vec::<String>::new());
|
assert_eq!(bad_scav_rooms, Vec::<String>::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exits_resolve() {
|
||||||
|
let rl = room_list();
|
||||||
|
let unresolved_exits: Vec<String> = rl
|
||||||
|
.iter()
|
||||||
|
.flat_map(|r: &'static Room| {
|
||||||
|
let r2: &'static Room = r;
|
||||||
|
r.exits.iter().map(move |ex| (r2, ex))
|
||||||
|
})
|
||||||
|
.filter_map(|(r, ex)| match resolve_exit(r, ex) {
|
||||||
|
Some(_) => None,
|
||||||
|
None => Some(format!("{} {}", r.code, &ex.direction.describe())),
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
assert_eq!(unresolved_exits, Vec::<String>::new());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,6 @@
|
|||||||
description: "A sleek reception that could have been the bridge of a 2000s era sci-fi spaceship. Linished metal plates are lit up by ambient blue LEDs, while stone tiles cover the floor. You see a white android, complete with elegant rounded corners and glowing blue eyes.\n\n\"Welcome to Murlison Suites - the best a business can rent\", purs the bot pleasantly. \"Just say <bold>in<reset> corpname\" and I'll guide you to the suite for that corp before you can blink! Or if you hold a corp and would like to rent the best suite money can buy for it, just say <bold>rent deluxe for<reset> corpname, and I'll set you up\""
|
description: "A sleek reception that could have been the bridge of a 2000s era sci-fi spaceship. Linished metal plates are lit up by ambient blue LEDs, while stone tiles cover the floor. You see a white android, complete with elegant rounded corners and glowing blue eyes.\n\n\"Welcome to Murlison Suites - the best a business can rent\", purs the bot pleasantly. \"Just say <bold>in<reset> corpname\" and I'll guide you to the suite for that corp before you can blink! Or if you hold a corp and would like to rent the best suite money can buy for it, just say <bold>rent deluxe for<reset> corpname, and I'll set you up\""
|
||||||
exits:
|
exits:
|
||||||
- direction: west
|
- direction: west
|
||||||
- direction: south
|
|
||||||
rentable_dynzone:
|
rentable_dynzone:
|
||||||
- rent_what: deluxe
|
- rent_what: deluxe
|
||||||
suite_type: Commercial
|
suite_type: Commercial
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
exits:
|
exits:
|
||||||
- direction: south
|
- direction: south
|
||||||
- direction: east
|
- direction: east
|
||||||
|
- direction: down
|
||||||
|
target: !Custom room/melbs_sewers_1a
|
||||||
should_caption: false
|
should_caption: false
|
||||||
scavtable: CityStreet
|
scavtable: CityStreet
|
||||||
- zone: melbs
|
- zone: melbs
|
||||||
@ -808,7 +810,6 @@
|
|||||||
z: 0
|
z: 0
|
||||||
description: A road that looks to have been a major tram thoroughfare before the collapse. Cracks line the filthy concrete footpaths and rusted tram tracks, and weeds poke out from holes in the concrete
|
description: A road that looks to have been a major tram thoroughfare before the collapse. Cracks line the filthy concrete footpaths and rusted tram tracks, and weeds poke out from holes in the concrete
|
||||||
exits:
|
exits:
|
||||||
- direction: west
|
|
||||||
- direction: south
|
- direction: south
|
||||||
should_caption: false
|
should_caption: false
|
||||||
scavtable: CityStreet
|
scavtable: CityStreet
|
||||||
|
24
blastmud_game/src/static_content/room/melbs_sewers.rs
Normal file
24
blastmud_game/src/static_content/room/melbs_sewers.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use super::{Room, SimpleRoom};
|
||||||
|
use crate::{
|
||||||
|
models::item::Scavtype,
|
||||||
|
static_content::{possession_type::PossessionType, scavtable::Scavinfo},
|
||||||
|
};
|
||||||
|
use serde_yaml::from_str as from_yaml_str;
|
||||||
|
|
||||||
|
pub fn sewer_scavtable() -> Vec<Scavinfo> {
|
||||||
|
vec![Scavinfo {
|
||||||
|
possession_type: PossessionType::RustySpike,
|
||||||
|
p_present: 1.0,
|
||||||
|
difficulty_mean: 7.0,
|
||||||
|
difficulty_stdev: 1.0,
|
||||||
|
scavtype: Scavtype::Scavenge,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn room_list() -> Vec<Room> {
|
||||||
|
from_yaml_str::<Vec<SimpleRoom<()>>>(include_str!("melbs_sewers.yaml"))
|
||||||
|
.unwrap()
|
||||||
|
.into_iter()
|
||||||
|
.map(|r| r.into())
|
||||||
|
.collect()
|
||||||
|
}
|
16
blastmud_game/src/static_content/room/melbs_sewers.yaml
Normal file
16
blastmud_game/src/static_content/room/melbs_sewers.yaml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
- zone: melbs_sewers
|
||||||
|
code: melbs_sewers_1a
|
||||||
|
name: Dank narrow sewerpipe
|
||||||
|
short: <yellow>==<reset>
|
||||||
|
grid_coords:
|
||||||
|
x: 1
|
||||||
|
y: -5
|
||||||
|
z: -1
|
||||||
|
description: A relatively narrow sewer pipe, featuring a ladder up to an access hole in a street. It smells like sewage in here. Foul smelling gloop, presumably sewage, flows along the bottom of the pipe. This sewer is a veritable ecosystem of its own; every now and then, you feel something living brush against you
|
||||||
|
exits:
|
||||||
|
- direction: up
|
||||||
|
target: !Custom room/melbs_kingst_latrobest
|
||||||
|
should_caption: false
|
||||||
|
scavtable: CitySewer
|
||||||
|
item_flags:
|
||||||
|
- !DarkPlace
|
@ -1,4 +1,4 @@
|
|||||||
use crate::models::item::Scavtype;
|
use crate::{models::item::Scavtype, static_content::room::melbs_sewers::sewer_scavtable};
|
||||||
|
|
||||||
use super::{possession_type::PossessionType, room::melbs::street_scavtable};
|
use super::{possession_type::PossessionType, room::melbs::street_scavtable};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
@ -18,6 +18,7 @@ pub struct Scavinfo {
|
|||||||
pub enum ScavtableType {
|
pub enum ScavtableType {
|
||||||
Nothing,
|
Nothing,
|
||||||
CityStreet,
|
CityStreet,
|
||||||
|
CitySewer,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scavtable_map() -> &'static BTreeMap<ScavtableType, Vec<Scavinfo>> {
|
pub fn scavtable_map() -> &'static BTreeMap<ScavtableType, Vec<Scavinfo>> {
|
||||||
@ -26,6 +27,7 @@ pub fn scavtable_map() -> &'static BTreeMap<ScavtableType, Vec<Scavinfo>> {
|
|||||||
vec![
|
vec![
|
||||||
(ScavtableType::Nothing, vec![]),
|
(ScavtableType::Nothing, vec![]),
|
||||||
(ScavtableType::CityStreet, street_scavtable()),
|
(ScavtableType::CityStreet, street_scavtable()),
|
||||||
|
(ScavtableType::CitySewer, sewer_scavtable()),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect()
|
.collect()
|
||||||
|
Loading…
Reference in New Issue
Block a user