Add a hospital that can heal players who can't heal themselves
This commit is contained in:
parent
92d7b22921
commit
90a6b3f31b
@ -93,6 +93,7 @@ async fn reset_stats(ctx: &mut VerbContext<'_>) -> UResult<()> {
|
||||
user_dat.experience.xp_change_for_this_reroll = 0;
|
||||
user_dat.raw_stats = BTreeMap::new();
|
||||
user_dat.raw_skills = BTreeMap::new();
|
||||
user_dat.wristpad_hacks = vec![];
|
||||
calculate_total_stats_skills_for_user(&mut player_item, &user_dat);
|
||||
ctx.trans.save_user_model(&user_dat).await?;
|
||||
ctx.trans.save_item_model(&player_item).await?;
|
||||
|
@ -29,7 +29,7 @@ use crate::{
|
||||
static_content::{
|
||||
dynzone::{dynzone_by_type, DynzoneType, ExitTarget as DynExitTarget},
|
||||
npc::check_for_instant_aggro,
|
||||
room::{self, Direction, ExitClimb, ExitType, MaterialType},
|
||||
room::{self, check_for_enter_action, Direction, ExitClimb, ExitType, MaterialType},
|
||||
},
|
||||
DResult,
|
||||
};
|
||||
@ -659,6 +659,7 @@ async fn attempt_move_immediate(
|
||||
}
|
||||
|
||||
check_for_instant_aggro(&ctx.trans, &mut ctx.item).await?;
|
||||
check_for_enter_action(ctx).await?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
@ -55,6 +55,9 @@ pub enum TaskDetails {
|
||||
TickUrges,
|
||||
ResetSpawns,
|
||||
ResetHanoi,
|
||||
HospitalERSeePatient {
|
||||
item: String,
|
||||
},
|
||||
}
|
||||
impl TaskDetails {
|
||||
pub fn name(self: &Self) -> &'static str {
|
||||
@ -76,6 +79,7 @@ impl TaskDetails {
|
||||
TickUrges => "TickUrges",
|
||||
ResetSpawns => "ResetSpawns",
|
||||
ResetHanoi => "ResetHanoi",
|
||||
HospitalERSeePatient { .. } => "HospitalERSeePatient",
|
||||
// Don't forget to add to TASK_HANDLER_REGISTRY in regular_tasks.rs too.
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,10 @@ use crate::{
|
||||
message_handler::user_commands::{delete, drop, hire, open, rent},
|
||||
models::task::Task,
|
||||
services::{combat, effect, spawn, urges},
|
||||
static_content::npc::{self, computer_museum_npcs},
|
||||
static_content::{
|
||||
npc::{self, computer_museum_npcs},
|
||||
room::general_hospital,
|
||||
},
|
||||
DResult,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
@ -63,6 +66,10 @@ fn task_handler_registry(
|
||||
"ResetHanoi",
|
||||
computer_museum_npcs::RESET_GAME_HANDLER.clone(),
|
||||
),
|
||||
(
|
||||
"HospitalERSeePatient",
|
||||
general_hospital::SEE_PATIENT_TASK.clone(),
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
|
@ -354,7 +354,7 @@ pub async fn recalculate_urge_growth(_trans: &DBTrans, item: &mut Item) -> DResu
|
||||
..old_urges.thirst
|
||||
}, // To do: climate based?
|
||||
stress: Urge {
|
||||
growth: (-(cool.max(7.0) - 7.0) * 10.0 * relax_action_factor) as i16,
|
||||
growth: (-(cool.max(7.0) - 6.0) * 10.0 * relax_action_factor) as i16,
|
||||
..old_urges.stress
|
||||
},
|
||||
});
|
||||
|
@ -19,6 +19,7 @@ use std::collections::{BTreeMap, BTreeSet};
|
||||
mod chonkers;
|
||||
mod cok_murl;
|
||||
pub mod computer_museum;
|
||||
pub mod general_hospital;
|
||||
mod melbs;
|
||||
mod repro_xv;
|
||||
mod special;
|
||||
@ -63,6 +64,11 @@ pub fn zone_details() -> &'static BTreeMap<&'static str, Zone> {
|
||||
display: "Computer Museum",
|
||||
outdoors: false,
|
||||
},
|
||||
Zone {
|
||||
code: "general_hospital",
|
||||
display: "General Hospital",
|
||||
outdoors: false,
|
||||
},
|
||||
]
|
||||
.into_iter()
|
||||
.map(|x| (x.code, x))
|
||||
@ -317,6 +323,11 @@ pub enum MaterialType {
|
||||
Soft { damage_modifier: f64 },
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait RoomEnterTrigger {
|
||||
async fn handle_enter(self: &Self, ctx: &mut QueuedCommandContext, room: &Room) -> UResult<()>;
|
||||
}
|
||||
|
||||
pub struct Room {
|
||||
pub zone: &'static str,
|
||||
// Other zones where it can be seen on the map and accessed.
|
||||
@ -339,6 +350,7 @@ pub struct Room {
|
||||
pub has_power: bool,
|
||||
pub door_states: Option<BTreeMap<Direction, DoorState>>,
|
||||
pub wristpad_hack_allowed: Option<WristpadHack>,
|
||||
pub enter_trigger: Option<&'static (dyn RoomEnterTrigger + Sync + Send)>,
|
||||
}
|
||||
|
||||
impl Default for Room {
|
||||
@ -362,6 +374,7 @@ impl Default for Room {
|
||||
has_power: false,
|
||||
door_states: None,
|
||||
wristpad_hack_allowed: None,
|
||||
enter_trigger: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -375,6 +388,7 @@ pub fn room_list() -> &'static Vec<Room> {
|
||||
rooms.append(&mut chonkers::room_list());
|
||||
rooms.append(&mut special::room_list());
|
||||
rooms.append(&mut computer_museum::room_list());
|
||||
rooms.append(&mut general_hospital::room_list());
|
||||
rooms.into_iter().collect()
|
||||
})
|
||||
}
|
||||
@ -488,6 +502,23 @@ pub async fn refresh_room_exits(trans: &DBTrans, template: &Item) -> DResult<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn check_for_enter_action(ctx: &mut QueuedCommandContext<'_>) -> UResult<()> {
|
||||
let room_code = match ctx.item.location.split_once("/") {
|
||||
Some((loc_type, _)) if loc_type != "room" => return Ok(()),
|
||||
Some((_, room_code)) => room_code,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
let room = match room_map_by_code().get(room_code) {
|
||||
Some(r) => r,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
match room.enter_trigger {
|
||||
Some(trigger) => trigger.handle_enter(ctx, room).await?,
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
214
blastmud_game/src/static_content/room/general_hospital.rs
Normal file
214
blastmud_game/src/static_content/room/general_hospital.rs
Normal file
@ -0,0 +1,214 @@
|
||||
use crate::{
|
||||
message_handler::user_commands::UResult,
|
||||
models::task::{Task, TaskDetails, TaskMeta},
|
||||
regular_tasks::{queued_command::QueuedCommandContext, TaskHandler, TaskRunContext},
|
||||
services::{combat::max_health, effect::run_effects},
|
||||
static_content::possession_type::UseEffect,
|
||||
DResult,
|
||||
};
|
||||
|
||||
use super::{Direction, Exit, ExitTarget, GridCoords, Room, RoomEnterTrigger, SecondaryZoneRecord};
|
||||
use ansi::ansi;
|
||||
use async_trait::async_trait;
|
||||
use chrono::{self, Utc};
|
||||
use log::warn;
|
||||
use std::time;
|
||||
|
||||
struct EnterERTrigger;
|
||||
#[async_trait]
|
||||
impl RoomEnterTrigger for EnterERTrigger {
|
||||
async fn handle_enter(
|
||||
self: &Self,
|
||||
ctx: &mut QueuedCommandContext,
|
||||
_room: &Room,
|
||||
) -> UResult<()> {
|
||||
ctx.trans
|
||||
.upsert_task(&Task {
|
||||
meta: TaskMeta {
|
||||
task_code: ctx.item.refstr(),
|
||||
next_scheduled: Utc::now() + chrono::Duration::seconds(60),
|
||||
..Default::default()
|
||||
},
|
||||
details: TaskDetails::HospitalERSeePatient {
|
||||
item: ctx.item.refstr(),
|
||||
},
|
||||
})
|
||||
.await?;
|
||||
if let Some((sess, _)) = ctx.get_session().await? {
|
||||
ctx.trans
|
||||
.queue_for_session(
|
||||
&sess,
|
||||
Some(ansi!(
|
||||
"<yellow>The triage nurse says: <reset><bold>\
|
||||
\"Luckily, we are not too busy today - at least not by usual \
|
||||
standards! Take a seat and the doctor will be with you in \
|
||||
about a minute.\"<reset>\n"
|
||||
)),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
static ENTER_ER_TRIGGER: EnterERTrigger = EnterERTrigger;
|
||||
|
||||
pub struct SeePatientTaskHandler;
|
||||
#[async_trait]
|
||||
impl TaskHandler for SeePatientTaskHandler {
|
||||
async fn do_task(&self, ctx: &mut TaskRunContext) -> DResult<Option<time::Duration>> {
|
||||
let see_who = match ctx.task.details {
|
||||
TaskDetails::HospitalERSeePatient { ref item } => item,
|
||||
_ => {
|
||||
warn!(
|
||||
"Unexpected task dispatched to SeePatientTaskHandler: {:?}",
|
||||
ctx.task
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
};
|
||||
let (who_type, who_code) = match see_who.split_once("/") {
|
||||
None => return Ok(None),
|
||||
Some(w) => w,
|
||||
};
|
||||
let who = match ctx.trans.find_item_by_type_code(who_type, who_code).await? {
|
||||
None => return Ok(None),
|
||||
Some(w) => w,
|
||||
};
|
||||
if who.location != "room/general_hospital_waiting_room" {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if who_type == "player" {
|
||||
if let Some((sess, _sess_dat)) = ctx.trans.find_session_for_player(who_code).await? {
|
||||
let mut msg: String = ansi!(
|
||||
"<yellow>The doctor says: <reset><bold>\
|
||||
\"Stay still while I take a close look at what might be wrong.\"<reset>\n\
|
||||
The doctor looks up and down your body closely, her gaze searching for any \
|
||||
anomaly.\n"
|
||||
)
|
||||
.to_owned();
|
||||
let hp_gap = max_health(&who) as i64 - who.health as i64;
|
||||
let mut skip_heal = false;
|
||||
if hp_gap <= 0 {
|
||||
msg += ansi!("<yellow>The doctor says: <reset><bold>\"You're perfectly healthy as far \
|
||||
as I can tell! There's nothing I can do for you here. Eat healthy, try to \
|
||||
maintain a healthy weight, and stay away from radiation, and you'll stay \
|
||||
that way.\"<reset>\n");
|
||||
skip_heal = true;
|
||||
} else {
|
||||
msg += ansi!("<yellow>The doctor says: <reset><bold>\"You're injured. Let me stitch that \
|
||||
up, and then just take bed rest. If it stops healing and doesn't feel better, \
|
||||
come right back in to the clinic.\"<reset>\n\
|
||||
The doctor stitches you up, and applies various gels. It hurts, but you \
|
||||
persevere through the pain.\n");
|
||||
}
|
||||
ctx.trans.queue_for_session(&sess, Some(&msg)).await?;
|
||||
if skip_heal {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut who_mut = (*who).clone();
|
||||
run_effects(
|
||||
&ctx.trans,
|
||||
&vec![
|
||||
UseEffect::ChangeTargetHealth {
|
||||
delay_secs: 0,
|
||||
base_effect: 10,
|
||||
skill_multiplier: 0.0,
|
||||
max_effect: 10,
|
||||
message: Box::new(|_item| {
|
||||
(
|
||||
"That feels better".to_owned(),
|
||||
"That feels better".to_owned(),
|
||||
)
|
||||
}),
|
||||
},
|
||||
UseEffect::ChangeTargetHealth {
|
||||
delay_secs: 10,
|
||||
base_effect: 9,
|
||||
skill_multiplier: 0.0,
|
||||
max_effect: 10,
|
||||
message: Box::new(|_item| {
|
||||
(
|
||||
"That feels better".to_owned(),
|
||||
"That feels better".to_owned(),
|
||||
)
|
||||
}),
|
||||
},
|
||||
UseEffect::ChangeTargetHealth {
|
||||
delay_secs: 20,
|
||||
base_effect: 8,
|
||||
skill_multiplier: 0.0,
|
||||
max_effect: 10,
|
||||
message: Box::new(|_item| {
|
||||
(
|
||||
"That feels better".to_owned(),
|
||||
"That feels better".to_owned(),
|
||||
)
|
||||
}),
|
||||
},
|
||||
UseEffect::ChangeTargetHealth {
|
||||
delay_secs: 30,
|
||||
base_effect: 7,
|
||||
skill_multiplier: 0.0,
|
||||
max_effect: 10,
|
||||
message: Box::new(|_item| {
|
||||
(
|
||||
"That feels better".to_owned(),
|
||||
"That feels better".to_owned(),
|
||||
)
|
||||
}),
|
||||
},
|
||||
],
|
||||
&mut who_mut,
|
||||
&who,
|
||||
&mut None,
|
||||
0.0,
|
||||
"bandages",
|
||||
)
|
||||
.await?;
|
||||
ctx.trans.save_item_model(&who_mut).await?;
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
pub static SEE_PATIENT_TASK: &'static (dyn TaskHandler + Sync + Send) = &SeePatientTaskHandler;
|
||||
|
||||
pub fn room_list() -> Vec<Room> {
|
||||
vec!(
|
||||
Room {
|
||||
zone: "general_hospital",
|
||||
secondary_zones: vec!(
|
||||
SecondaryZoneRecord {
|
||||
zone: "melbs",
|
||||
short: ansi!("<bgwhite><red>++<reset>"),
|
||||
grid_coords: GridCoords { x: 2, y: 10, z: 0 },
|
||||
caption: Some("General Hospital")
|
||||
}
|
||||
),
|
||||
code: "general_hospital_waiting_room",
|
||||
name: "Emergency Waiting Room",
|
||||
short: ansi!("<bgwhite><red>WR<reset>"),
|
||||
description: ansi!("A room apparently designed for patients to wait to seen by doctors. \
|
||||
Heavy-duty grey linoleum lines the floors, and even the tops of the walls, \
|
||||
while stainless steel metal strips cover the bottom of the walls. A line on \
|
||||
floor reserves an area of the floor for sick patients to be rushed past on \
|
||||
stretchers. It smells strongly of phenolic cleaners. At the front of the room \
|
||||
a triage nurse assures everyone coming in they will be assessed by doctors \
|
||||
a minute after arriving. Doctors pace the floor treating patients"),
|
||||
description_less_explicit: None,
|
||||
grid_coords: GridCoords { x: 0, y: 0, z: 0 },
|
||||
exits: vec!(
|
||||
Exit {
|
||||
direction: Direction::WEST,
|
||||
target: ExitTarget::Custom("room/melbs_kingst_120"),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
should_caption: true,
|
||||
enter_trigger: Some(&ENTER_ER_TRIGGER),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
}
|
@ -517,7 +517,14 @@ pub fn room_list() -> Vec<Room> {
|
||||
},
|
||||
Room {
|
||||
zone: "melbs",
|
||||
secondary_zones: vec!(),
|
||||
secondary_zones: vec![
|
||||
SecondaryZoneRecord {
|
||||
zone: "general_hospital",
|
||||
short: ansi!("<bggreen><white>EX<reset>"),
|
||||
grid_coords: GridCoords { x: -1, y: 0, z: 0 },
|
||||
caption: Some("Melbs"),
|
||||
}
|
||||
],
|
||||
code: "melbs_kingst_120",
|
||||
name: "King Street - 120 block",
|
||||
short: ansi!("<yellow>||<reset>"),
|
||||
@ -533,6 +540,11 @@ pub fn room_list() -> Vec<Room> {
|
||||
direction: Direction::SOUTH,
|
||||
..Default::default()
|
||||
},
|
||||
Exit {
|
||||
direction: Direction::EAST,
|
||||
target: ExitTarget::Custom("room/general_hospital_waiting_room"),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
should_caption: false,
|
||||
..Default::default()
|
||||
|
Loading…
Reference in New Issue
Block a user