Add the start of Ronald's House

And minor bugfixes on stability of item indexes, making stings less
aggressive.
This commit is contained in:
Condorra 2024-05-22 22:57:36 +10:00
parent e238abd832
commit 8aff296e03
11 changed files with 251 additions and 75 deletions

View File

@ -1001,38 +1001,38 @@ impl DBTrans {
if search.include_contents { if search.include_contents {
ctes.push(format!("contents AS (\ ctes.push(format!("contents AS (\
SELECT details, details->'aliases' AS aliases FROM items WHERE details->>'location' = ${}\ SELECT item_id, details, details->'aliases' AS aliases FROM items WHERE details->>'location' = ${}\
)", param_no)); )", param_no));
param_no += 1; param_no += 1;
params.push(&player_desig); params.push(&player_desig);
include_tables.push("SELECT details, aliases FROM contents"); include_tables.push("SELECT item_id, details, aliases FROM contents");
} }
if search.include_loc_contents { if search.include_loc_contents {
ctes.push(format!("loc_contents AS (\ ctes.push(format!("loc_contents AS (\
SELECT details, details->'aliases' AS aliases FROM items WHERE details->>'location' = ${}\ SELECT item_id, details, details->'aliases' AS aliases FROM items WHERE details->>'location' = ${}\
)", param_no)); )", param_no));
#[allow(dropping_copy_types)] #[allow(dropping_copy_types)]
drop(param_no); // or increment if this is a problem. drop(param_no); // or increment if this is a problem.
params.push(&player_loc); params.push(&player_loc);
include_tables.push("SELECT details, aliases FROM loc_contents"); include_tables.push("SELECT item_id, details, aliases FROM loc_contents");
} }
if search.include_active_players { if search.include_active_players {
ctes.push( ctes.push(
"active_players AS (\ "active_players AS (\
SELECT i.details, ('[]'::JSONB) AS aliases FROM items i \ SELECT i.item_id, i.details, ('[]'::JSONB) AS aliases FROM items i \
JOIN users u ON u.username = i.details->>'item_code' \ JOIN users u ON u.username = i.details->>'item_code' \
WHERE i.details->>'item_type' = 'player' \ WHERE i.details->>'item_type' = 'player' \
AND u.current_session IS NOT NULL \ AND u.current_session IS NOT NULL \
)" )"
.to_owned(), .to_owned(),
); );
include_tables.push("SELECT details, aliases FROM active_players"); include_tables.push("SELECT item_id, details, aliases FROM active_players");
} }
if search.include_all_players { if search.include_all_players {
ctes.push("all_players AS (\ ctes.push("all_players AS (\
SELECT details, ('[]'::JSONB) AS aliases FROM items WHERE details->>'item_type' = 'player' SELECT item_id, details, ('[]'::JSONB) AS aliases FROM items WHERE details->>'item_type' = 'player'
)".to_owned()); )".to_owned());
include_tables.push("SELECT details, aliases FROM all_players"); include_tables.push("SELECT item_id, details, aliases FROM all_players");
} }
ctes.push(format!( ctes.push(format!(
"relevant_items AS ({})", "relevant_items AS ({})",
@ -1050,7 +1050,7 @@ impl DBTrans {
(lower(details->>'display') LIKE $1) \ (lower(details->>'display') LIKE $1) \
OR EXISTS (SELECT 1 FROM jsonb_array_elements(aliases) AS al(alias) WHERE \ OR EXISTS (SELECT 1 FROM jsonb_array_elements(aliases) AS al(alias) WHERE \
LOWER(alias#>>'{{}}') LIKE $1) {} \ LOWER(alias#>>'{{}}') LIKE $1) {} \
ORDER BY {} ABS(length(details->>'display')-$3) ASC \ ORDER BY {} ABS(length(details->>'display')-$3) ASC, item_id ASC \
LIMIT $4 OFFSET $2", LIMIT $4 OFFSET $2",
&cte_str, &extra_where, &extra_order &cte_str, &extra_where, &extra_order
), ),

View File

@ -668,18 +668,6 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
) )
) )
}, },
Effect::ChangeTargetParameter {
delay_secs: 10,
base_effect: -12,
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
message: Box::new(|target|
format!("{} howls out in pain from a sting",
target.display_for_sentence(1, false),
)
)
},
Effect::ChangeTargetParameter { Effect::ChangeTargetParameter {
delay_secs: 20, delay_secs: 20,
base_effect: -12, base_effect: -12,
@ -692,68 +680,56 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
) )
) )
}, },
Effect::ChangeTargetParameter {
delay_secs: 30,
base_effect: -8,
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
message: Box::new(|target|
format!("{} yelps in pain from the sting",
target.display_for_sentence(1, false),
)
)
},
Effect::ChangeTargetParameter { Effect::ChangeTargetParameter {
delay_secs: 40, delay_secs: 40,
base_effect: -8, base_effect: -12,
skill_multiplier: 0.0, skill_multiplier: 0.0,
max_effect: -12, max_effect: -12,
parameter: EffectParameter::Health, parameter: EffectParameter::Health,
message: Box::new(|target| message: Box::new(|target|
format!("{} yelps in pain from the sting", format!("{} howls out in pain from a sting",
target.display_for_sentence(1, false),
)
)
},
Effect::ChangeTargetParameter {
delay_secs: 50,
base_effect: -8,
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
message: Box::new(|target|
format!("{} yelps in pain from the sting",
target.display_for_sentence(1, false), target.display_for_sentence(1, false),
) )
) )
}, },
Effect::ChangeTargetParameter { Effect::ChangeTargetParameter {
delay_secs: 60, delay_secs: 60,
base_effect: -6, base_effect: -8,
skill_multiplier: 0.0, skill_multiplier: 0.0,
max_effect: -10, max_effect: -12,
parameter: EffectParameter::Health, parameter: EffectParameter::Health,
message: Box::new(|target| message: Box::new(|target|
format!("{}'s cries as the sting really hurts", format!("{} yelps in pain from the sting",
target.display_for_sentence(1, false),
)
)
},
Effect::ChangeTargetParameter {
delay_secs: 70,
base_effect: -6,
skill_multiplier: 0.0,
max_effect: -10,
parameter: EffectParameter::Health,
message: Box::new(|target|
format!("{}'s cries as the sting really hurts",
target.display_for_sentence(1, false), target.display_for_sentence(1, false),
) )
) )
}, },
Effect::ChangeTargetParameter { Effect::ChangeTargetParameter {
delay_secs: 80, delay_secs: 80,
base_effect: -8,
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
message: Box::new(|target|
format!("{} yelps in pain from the sting",
target.display_for_sentence(1, false),
)
)
},
Effect::ChangeTargetParameter {
delay_secs: 100,
base_effect: -8,
skill_multiplier: 0.0,
max_effect: -12,
parameter: EffectParameter::Health,
message: Box::new(|target|
format!("{} yelps in pain from the sting",
target.display_for_sentence(1, false),
)
)
},
Effect::ChangeTargetParameter {
delay_secs: 120,
base_effect: -6, base_effect: -6,
skill_multiplier: 0.0, skill_multiplier: 0.0,
max_effect: -10, max_effect: -10,
@ -765,7 +741,31 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
) )
}, },
Effect::ChangeTargetParameter { Effect::ChangeTargetParameter {
delay_secs: 90, delay_secs: 140,
base_effect: -6,
skill_multiplier: 0.0,
max_effect: -10,
parameter: EffectParameter::Health,
message: Box::new(|target|
format!("{}'s cries as the sting really hurts",
target.display_for_sentence(1, false),
)
)
},
Effect::ChangeTargetParameter {
delay_secs: 160,
base_effect: -6,
skill_multiplier: 0.0,
max_effect: -10,
parameter: EffectParameter::Health,
message: Box::new(|target|
format!("{}'s cries as the sting really hurts",
target.display_for_sentence(1, false),
)
)
},
Effect::ChangeTargetParameter {
delay_secs: 170,
base_effect: -4, base_effect: -4,
skill_multiplier: 0.0, skill_multiplier: 0.0,
max_effect: -8, max_effect: -8,
@ -777,7 +777,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
) )
}, },
Effect::ChangeTargetParameter { Effect::ChangeTargetParameter {
delay_secs: 100, delay_secs: 180,
base_effect: -4, base_effect: -4,
skill_multiplier: 0.0, skill_multiplier: 0.0,
max_effect: -8, max_effect: -8,
@ -789,7 +789,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
) )
}, },
Effect::ChangeTargetParameter { Effect::ChangeTargetParameter {
delay_secs: 110, delay_secs: 190,
base_effect: -4, base_effect: -4,
skill_multiplier: 0.0, skill_multiplier: 0.0,
max_effect: -8, max_effect: -8,
@ -801,7 +801,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
) )
}, },
Effect::ChangeTargetParameter { Effect::ChangeTargetParameter {
delay_secs: 120, delay_secs: 200,
base_effect: -2, base_effect: -2,
skill_multiplier: 0.0, skill_multiplier: 0.0,
max_effect: -8, max_effect: -8,
@ -813,7 +813,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
) )
}, },
Effect::ChangeTargetParameter { Effect::ChangeTargetParameter {
delay_secs: 130, delay_secs: 220,
base_effect: -2, base_effect: -2,
skill_multiplier: 0.0, skill_multiplier: 0.0,
max_effect: -8, max_effect: -8,
@ -825,7 +825,7 @@ pub fn default_effects_for_type() -> &'static BTreeMap<EffectType, EffectSet> {
) )
}, },
Effect::ChangeTargetParameter { Effect::ChangeTargetParameter {
delay_secs: 140, delay_secs: 230,
base_effect: -2, base_effect: -2,
skill_multiplier: 0.0, skill_multiplier: 0.0,
max_effect: -8, max_effect: -8,

View File

@ -51,7 +51,7 @@ pub async fn ensure_appropriate_environment_handler_after_movement(
MaterialType::WaterSurface | MaterialType::Underwater => true, MaterialType::WaterSurface | MaterialType::Underwater => true,
_ => false, _ => false,
}; };
let env_effects = room.environment.radiation > 0; let env_effects = room.environment.radiation > 0 || room.environment.passive_health != 0;
water_effects || env_effects water_effects || env_effects
}; };
@ -126,7 +126,8 @@ impl TaskHandler for EffectEnvironmentHandler {
}; };
let need_save = water_environment_effects(ctx, &room, &mut player).await? let need_save = water_environment_effects(ctx, &room, &mut player).await?
|| rad_environment_effects(ctx, &room, &mut player).await?; || rad_environment_effects(ctx, &room, &mut player).await?
|| passive_health_environment_effects(ctx, &room, &mut player).await?;
if need_save { if need_save {
ctx.trans.save_item_model(&player).await?; ctx.trans.save_item_model(&player).await?;
@ -306,6 +307,31 @@ pub async fn rad_environment_effects(
Ok(true) Ok(true)
} }
pub async fn passive_health_environment_effects(
ctx: &mut TaskRunContext<'_>,
room: &Room,
player_item: &mut Item,
) -> DResult<bool> {
if room.environment.passive_health == 0 {
return Ok(false);
}
let msg = format!(
"{} {}",
player_item.display_for_sentence(1, true),
room.environment.passive_health_message
);
change_health(
ctx.trans,
room.environment.passive_health as i64,
player_item,
&msg,
)
.await?;
Ok(true)
}
pub static EFFECT_TASK_HANDLER: &'static (dyn TaskHandler + Sync + Send) = pub static EFFECT_TASK_HANDLER: &'static (dyn TaskHandler + Sync + Send) =
&EffectEnvironmentHandler; &EffectEnvironmentHandler;
pub static CONSEQUENCE_TASK_HANDLER: &'static (dyn TaskHandler + Sync + Send) = pub static CONSEQUENCE_TASK_HANDLER: &'static (dyn TaskHandler + Sync + Send) =

View File

@ -150,6 +150,18 @@ fn spawn_list() -> &'static BTreeMap<&'static str, SpawnDistribution> {
)], )],
}, },
), ),
(
"fixed_item/ronalds_health_room_fountain",
SpawnDistribution::SpawnOne {
pvec: vec![(
1.0,
Box::new(SpawnDistribution::SpawnLiquid {
what: LiquidType::Water,
how_much: 1000000,
}),
)],
},
),
] ]
.into_iter() .into_iter()
.collect() .collect()

View File

@ -2,7 +2,7 @@
use super::{possession_type::PossessionData, StaticItem}; use super::{possession_type::PossessionData, StaticItem};
use crate::{ use crate::{
models::item::{Item, ItemStaticSpecialData, LocationActionType, Pronouns}, models::item::{Item, ItemStaticSpecialData, LocationActionType, Pronouns},
static_content::room::{computer_museum, melbs, northern_radfields}, static_content::room::{computer_museum, melbs, northern_radfields, ronalds_house},
}; };
use ansi::ansi; use ansi::ansi;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
@ -64,6 +64,7 @@ fn fixed_item_list() -> &'static Vec<FixedItem> {
.chain(computer_museum::fixed_items().into_iter()) .chain(computer_museum::fixed_items().into_iter())
.chain(melbs::fixed_items().into_iter()) .chain(melbs::fixed_items().into_iter())
.chain(northern_radfields::fixed_items().into_iter()) .chain(northern_radfields::fixed_items().into_iter())
.chain(ronalds_house::fixed_items().into_iter())
.collect() .collect()
}) })
} }
@ -75,6 +76,8 @@ pub fn fixed_item_properties() -> &'static BTreeMap<&'static str, PossessionData
.into_iter() .into_iter()
.chain(computer_museum::fixed_item_properties().into_iter()) .chain(computer_museum::fixed_item_properties().into_iter())
.chain(melbs::fixed_item_properties().into_iter()) .chain(melbs::fixed_item_properties().into_iter())
.chain(northern_radfields::fixed_item_properties().into_iter())
.chain(ronalds_house::fixed_item_properties().into_iter())
.collect() .collect()
}) })
} }

View File

@ -31,6 +31,7 @@ pub mod melbs;
pub mod melbs_sewers; pub mod melbs_sewers;
pub mod northern_radfields; pub mod northern_radfields;
mod repro_xv; mod repro_xv;
pub mod ronalds_house;
mod special; mod special;
pub struct Zone { pub struct Zone {
@ -91,13 +92,18 @@ pub fn zone_details() -> &'static BTreeMap<&'static str, Zone> {
Zone { Zone {
code: "northern_radfields", code: "northern_radfields",
display: "Northern Radfields", display: "Northern Radfields",
outdoors: false, outdoors: true,
}, },
Zone { Zone {
code: "oorans", code: "oorans",
display: "OORANS", display: "OORANS",
outdoors: false, outdoors: false,
}, },
Zone {
code: "ronalds_house",
display: "Ronald's House",
outdoors: false,
},
] ]
.into_iter() .into_iter()
.map(|x| (x.code, x)) .map(|x| (x.code, x))
@ -581,6 +587,8 @@ impl Default for Room {
pub struct RoomEnvironment { pub struct RoomEnvironment {
pub radiation: u64, // mSv/10s tick pub radiation: u64, // mSv/10s tick
pub temperature: u16, // in 100ths of degrees celsius. pub temperature: u16, // in 100ths of degrees celsius.
pub passive_health_message: String,
pub passive_health: i16, // in health points/10s tick. Can be negative to harm.
} }
impl Default for RoomEnvironment { impl Default for RoomEnvironment {
@ -588,6 +596,8 @@ impl Default for RoomEnvironment {
Self { Self {
radiation: 0, radiation: 0,
temperature: 2000, // 20 degrees celsius temperature: 2000, // 20 degrees celsius
passive_health_message: "feels healthy".to_owned(),
passive_health: 0,
} }
} }
} }
@ -711,6 +721,7 @@ pub fn room_list() -> &'static Vec<Room> {
rooms.append(&mut general_hospital::room_list()); rooms.append(&mut general_hospital::room_list());
rooms.append(&mut melbs_sewers::room_list()); rooms.append(&mut melbs_sewers::room_list());
rooms.append(&mut northern_radfields::room_list()); rooms.append(&mut northern_radfields::room_list());
rooms.append(&mut ronalds_house::room_list());
rooms.into_iter().collect() rooms.into_iter().collect()
}) })
} }

View File

@ -1,4 +1,10 @@
use crate::static_content::fixed_item::FixedItem; use crate::{
models::item::LiquidType,
static_content::{
fixed_item::FixedItem,
possession_type::{LiquidContainerData, PossessionData},
},
};
use super::{Room, SimpleRoom}; use super::{Room, SimpleRoom};
use ansi::ansi; use ansi::ansi;
@ -25,3 +31,17 @@ pub fn fixed_items() -> Vec<FixedItem> {
}, },
] ]
} }
pub fn fixed_item_properties() -> Vec<(&'static str, PossessionData)> {
vec![(
"northrad_oasis_billabong",
PossessionData {
liquid_container_data: Some(LiquidContainerData {
capacity: 5000000, // mL
allowed_contents: Some(vec![LiquidType::Water]),
..Default::default()
}),
..Default::default()
},
)]
}

View File

@ -295,10 +295,17 @@ your skin"
x: 4 x: 4
y: 7 y: 7
z: 0 z: 0
description: A narrow rocky passage winds its way through the desert here, offering some shelter from the relentless sun and wind. It smells dusty here. To the south you can barely make out a building of some kind description: A flat open structure, made of a large array of solar panels supported on ornately decorated carved stone pillars. The structure protects the entrance to what appears to be a well-maintained primarily subterranean dwelling. A staircase leading down into the ground appears to be the primary entrance. Outside the structure is a cobweb covered and clearly long-disused letterbox. On a sign hanging below the letterbox, and gentling clinking in the hot desert breeze, you can make out the words "Ronald Fairburn's Outback Abode. Trespass met with lethal force". Some additional words at the bottom of the sign have been painted over, but you can make out "Regional Praefect of His Majesty the Emperor"
environment: environment:
radiation: 120 radiation: 120
temperature: 4210 temperature: 4210
secondary_zones:
- zone: ronalds_house
short: <yellow>EX<reset>
grid_coords:
x: 0
y: 0
z: 1
exits: exits:
- direction: north - direction: north
- direction: northeast - direction: northeast
@ -307,6 +314,8 @@ your skin"
- direction: south - direction: south
- direction: southwest - direction: southwest
- direction: west - direction: west
- direction: down
target: !Custom room/ronalds_entrance_hallway
- zone: northern_radfields - zone: northern_radfields
code: northrad_g5 code: northrad_g5
name: Dust plain name: Dust plain

View File

@ -0,0 +1,62 @@
use super::{Room, SimpleRoom};
use crate::{
models::item::{LiquidType, Scavtype},
static_content::{
fixed_item::FixedItem,
possession_type::{LiquidContainerData, PossessionData, PossessionType},
scavtable::Scavinfo,
},
};
use ansi::ansi;
use serde_yaml::from_str as from_yaml_str;
pub fn ronalds_house_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 fixed_items() -> Vec<FixedItem> {
vec![
FixedItem {
code: "ronalds_health_room_fountain".to_owned(),
name: "water fountain".to_owned(),
description: ansi!("A large round white stone carved fountain in the centre of the \
room. Ornate carved seahorses eject jets of water which \
recirculates into a pool of clear water around the outside \
of the fountain. The air feels moist here from tiny droplets of water. \
[Try <bold>drink from fountain<reset> or, if you have a suitable \
container, <bold>fill<reset> container <bold>from fountain<reset>].").to_owned(),
location: "room/ronalds_health_room".to_owned(),
proper_noun: false,
aliases: vec!["fountain".to_owned()],
..Default::default()
},
]
}
pub fn fixed_item_properties() -> Vec<(&'static str, PossessionData)> {
vec![(
"ronalds_health_room_fountain",
PossessionData {
liquid_container_data: Some(LiquidContainerData {
capacity: 5000000, // mL
allowed_contents: Some(vec![LiquidType::Water]),
..Default::default()
}),
..Default::default()
},
)]
}
pub fn room_list() -> Vec<Room> {
from_yaml_str::<Vec<SimpleRoom<()>>>(include_str!("ronalds_house.yaml"))
.unwrap()
.into_iter()
.map(|r| r.into())
.collect()
}

View File

@ -0,0 +1,28 @@
- zone: ronalds_house
code: ronalds_entrance_hallway
name: Ronald's Vestibule
description: An ornate entrance hall in Ronald's subterranean home. The air here is cool, despite the location in the desert. Artistically designed glass panels with colourful swirly shapes line the walls, and the floor is covered with marble tiles
short: <bgblue><white>VE<reset>
grid_coords:
x: 0
y: 0
z: 0
exits:
- direction: up
target: !Custom room/northrad_g4
- direction: north
repel_npc: true
- zone: ronalds_house
code: ronalds_health_room
name: Health Room
short: <bgred><white>HR<reset>
grid_coords:
x: 0
y: -1
z: 0
description: A large round room, the walls lined with fish tanks filled with brightly coloured fish slowly swimming around. The room has something of a serene feel. A sign indicates that this room has a field that activates nanites from the wristpad implant to heal
exits:
- direction: south
environment:
passive_health: 10
passive_health_message: feels healthy

View File

@ -1,4 +1,7 @@
use crate::{models::item::Scavtype, static_content::room::melbs_sewers::sewer_scavtable}; use crate::{
models::item::Scavtype,
static_content::room::{melbs_sewers::sewer_scavtable, ronalds_house::ronalds_house_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;
@ -19,6 +22,7 @@ pub enum ScavtableType {
Nothing, Nothing,
CityStreet, CityStreet,
CitySewer, CitySewer,
RonaldsHouse,
} }
pub fn scavtable_map() -> &'static BTreeMap<ScavtableType, Vec<Scavinfo>> { pub fn scavtable_map() -> &'static BTreeMap<ScavtableType, Vec<Scavinfo>> {
@ -28,6 +32,7 @@ pub fn scavtable_map() -> &'static BTreeMap<ScavtableType, Vec<Scavinfo>> {
(ScavtableType::Nothing, vec![]), (ScavtableType::Nothing, vec![]),
(ScavtableType::CityStreet, street_scavtable()), (ScavtableType::CityStreet, street_scavtable()),
(ScavtableType::CitySewer, sewer_scavtable()), (ScavtableType::CitySewer, sewer_scavtable()),
(ScavtableType::RonaldsHouse, ronalds_house_scavtable()),
] ]
.into_iter() .into_iter()
.collect() .collect()