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.experience.xp_change_for_this_reroll = 0;
|
||||||
user_dat.raw_stats = BTreeMap::new();
|
user_dat.raw_stats = BTreeMap::new();
|
||||||
user_dat.raw_skills = BTreeMap::new();
|
user_dat.raw_skills = BTreeMap::new();
|
||||||
|
user_dat.wristpad_hacks = vec![];
|
||||||
calculate_total_stats_skills_for_user(&mut player_item, &user_dat);
|
calculate_total_stats_skills_for_user(&mut player_item, &user_dat);
|
||||||
ctx.trans.save_user_model(&user_dat).await?;
|
ctx.trans.save_user_model(&user_dat).await?;
|
||||||
ctx.trans.save_item_model(&player_item).await?;
|
ctx.trans.save_item_model(&player_item).await?;
|
||||||
|
@ -29,7 +29,7 @@ use crate::{
|
|||||||
static_content::{
|
static_content::{
|
||||||
dynzone::{dynzone_by_type, DynzoneType, ExitTarget as DynExitTarget},
|
dynzone::{dynzone_by_type, DynzoneType, ExitTarget as DynExitTarget},
|
||||||
npc::check_for_instant_aggro,
|
npc::check_for_instant_aggro,
|
||||||
room::{self, Direction, ExitClimb, ExitType, MaterialType},
|
room::{self, check_for_enter_action, Direction, ExitClimb, ExitType, MaterialType},
|
||||||
},
|
},
|
||||||
DResult,
|
DResult,
|
||||||
};
|
};
|
||||||
@ -659,6 +659,7 @@ async fn attempt_move_immediate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
check_for_instant_aggro(&ctx.trans, &mut ctx.item).await?;
|
check_for_instant_aggro(&ctx.trans, &mut ctx.item).await?;
|
||||||
|
check_for_enter_action(ctx).await?;
|
||||||
|
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,9 @@ pub enum TaskDetails {
|
|||||||
TickUrges,
|
TickUrges,
|
||||||
ResetSpawns,
|
ResetSpawns,
|
||||||
ResetHanoi,
|
ResetHanoi,
|
||||||
|
HospitalERSeePatient {
|
||||||
|
item: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
impl TaskDetails {
|
impl TaskDetails {
|
||||||
pub fn name(self: &Self) -> &'static str {
|
pub fn name(self: &Self) -> &'static str {
|
||||||
@ -76,6 +79,7 @@ impl TaskDetails {
|
|||||||
TickUrges => "TickUrges",
|
TickUrges => "TickUrges",
|
||||||
ResetSpawns => "ResetSpawns",
|
ResetSpawns => "ResetSpawns",
|
||||||
ResetHanoi => "ResetHanoi",
|
ResetHanoi => "ResetHanoi",
|
||||||
|
HospitalERSeePatient { .. } => "HospitalERSeePatient",
|
||||||
// Don't forget to add to TASK_HANDLER_REGISTRY in regular_tasks.rs too.
|
// 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},
|
message_handler::user_commands::{delete, drop, hire, open, rent},
|
||||||
models::task::Task,
|
models::task::Task,
|
||||||
services::{combat, effect, spawn, urges},
|
services::{combat, effect, spawn, urges},
|
||||||
static_content::npc::{self, computer_museum_npcs},
|
static_content::{
|
||||||
|
npc::{self, computer_museum_npcs},
|
||||||
|
room::general_hospital,
|
||||||
|
},
|
||||||
DResult,
|
DResult,
|
||||||
};
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
@ -63,6 +66,10 @@ fn task_handler_registry(
|
|||||||
"ResetHanoi",
|
"ResetHanoi",
|
||||||
computer_museum_npcs::RESET_GAME_HANDLER.clone(),
|
computer_museum_npcs::RESET_GAME_HANDLER.clone(),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"HospitalERSeePatient",
|
||||||
|
general_hospital::SEE_PATIENT_TASK.clone(),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -354,7 +354,7 @@ pub async fn recalculate_urge_growth(_trans: &DBTrans, item: &mut Item) -> DResu
|
|||||||
..old_urges.thirst
|
..old_urges.thirst
|
||||||
}, // To do: climate based?
|
}, // To do: climate based?
|
||||||
stress: Urge {
|
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
|
..old_urges.stress
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -19,6 +19,7 @@ use std::collections::{BTreeMap, BTreeSet};
|
|||||||
mod chonkers;
|
mod chonkers;
|
||||||
mod cok_murl;
|
mod cok_murl;
|
||||||
pub mod computer_museum;
|
pub mod computer_museum;
|
||||||
|
pub mod general_hospital;
|
||||||
mod melbs;
|
mod melbs;
|
||||||
mod repro_xv;
|
mod repro_xv;
|
||||||
mod special;
|
mod special;
|
||||||
@ -63,6 +64,11 @@ pub fn zone_details() -> &'static BTreeMap<&'static str, Zone> {
|
|||||||
display: "Computer Museum",
|
display: "Computer Museum",
|
||||||
outdoors: false,
|
outdoors: false,
|
||||||
},
|
},
|
||||||
|
Zone {
|
||||||
|
code: "general_hospital",
|
||||||
|
display: "General Hospital",
|
||||||
|
outdoors: false,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|x| (x.code, x))
|
.map(|x| (x.code, x))
|
||||||
@ -317,6 +323,11 @@ pub enum MaterialType {
|
|||||||
Soft { damage_modifier: f64 },
|
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 struct Room {
|
||||||
pub zone: &'static str,
|
pub zone: &'static str,
|
||||||
// Other zones where it can be seen on the map and accessed.
|
// Other zones where it can be seen on the map and accessed.
|
||||||
@ -339,6 +350,7 @@ pub struct Room {
|
|||||||
pub has_power: bool,
|
pub has_power: bool,
|
||||||
pub door_states: Option<BTreeMap<Direction, DoorState>>,
|
pub door_states: Option<BTreeMap<Direction, DoorState>>,
|
||||||
pub wristpad_hack_allowed: Option<WristpadHack>,
|
pub wristpad_hack_allowed: Option<WristpadHack>,
|
||||||
|
pub enter_trigger: Option<&'static (dyn RoomEnterTrigger + Sync + Send)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Room {
|
impl Default for Room {
|
||||||
@ -362,6 +374,7 @@ impl Default for Room {
|
|||||||
has_power: false,
|
has_power: false,
|
||||||
door_states: None,
|
door_states: None,
|
||||||
wristpad_hack_allowed: 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 chonkers::room_list());
|
||||||
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.into_iter().collect()
|
rooms.into_iter().collect()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -488,6 +502,23 @@ pub async fn refresh_room_exits(trans: &DBTrans, template: &Item) -> DResult<()>
|
|||||||
Ok(())
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
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 {
|
Room {
|
||||||
zone: "melbs",
|
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",
|
code: "melbs_kingst_120",
|
||||||
name: "King Street - 120 block",
|
name: "King Street - 120 block",
|
||||||
short: ansi!("<yellow>||<reset>"),
|
short: ansi!("<yellow>||<reset>"),
|
||||||
@ -533,6 +540,11 @@ pub fn room_list() -> Vec<Room> {
|
|||||||
direction: Direction::SOUTH,
|
direction: Direction::SOUTH,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
|
Exit {
|
||||||
|
direction: Direction::EAST,
|
||||||
|
target: ExitTarget::Custom("room/general_hospital_waiting_room"),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
),
|
),
|
||||||
should_caption: false,
|
should_caption: false,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
Loading…
Reference in New Issue
Block a user