Add a fill command to fill bottles etc...
This commit is contained in:
parent
fab18d604e
commit
3023b5317a
@ -29,6 +29,7 @@ mod describe;
|
||||
pub mod drink;
|
||||
pub mod drop;
|
||||
pub mod eat;
|
||||
pub mod fill;
|
||||
mod fire;
|
||||
pub mod follow;
|
||||
mod gear;
|
||||
@ -159,7 +160,7 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
|
||||
"drink" => drink::VERB,
|
||||
"drop" => drop::VERB,
|
||||
"eat" => eat::VERB,
|
||||
|
||||
"fill" => fill::VERB,
|
||||
"fire" => fire::VERB,
|
||||
|
||||
"follow" => follow::VERB,
|
||||
|
@ -4,9 +4,10 @@ use super::{
|
||||
};
|
||||
use crate::{
|
||||
regular_tasks::queued_command::{
|
||||
queue_command, QueueCommand, QueueCommandHandler, QueuedCommandContext,
|
||||
queue_command_and_save, QueueCommand, QueueCommandHandler, QueuedCommandContext,
|
||||
},
|
||||
services::{
|
||||
capacity::recalculate_container_weight_mut,
|
||||
comms::broadcast_to_room,
|
||||
urges::{hunger_changed, thirst_changed},
|
||||
},
|
||||
@ -177,6 +178,18 @@ impl QueueCommandHandler for QueueHandler {
|
||||
.and_modify(|v| *v -= how_many_drunk as u64);
|
||||
}
|
||||
}
|
||||
match item_mut.liquid_details.as_mut() {
|
||||
None => {}
|
||||
Some(ld) => {
|
||||
ld.contents = ld
|
||||
.contents
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|c| c.1 != 0)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
recalculate_container_weight_mut(&ctx.trans, &mut item_mut).await?;
|
||||
ctx.trans.save_item_model(&item_mut).await?;
|
||||
Ok(())
|
||||
}
|
||||
@ -223,7 +236,6 @@ impl UserVerb for Verb {
|
||||
)?;
|
||||
}
|
||||
|
||||
let mut player_item_mut = (*player_item).clone();
|
||||
for target in targets {
|
||||
if target.item_type != "possession" && target.item_type != "fixed_item" {
|
||||
user_error("You can't drink that!".to_owned())?;
|
||||
@ -231,9 +243,9 @@ impl UserVerb for Verb {
|
||||
if target.liquid_details.is_none() {
|
||||
user_error("There's nothing to drink!".to_owned())?;
|
||||
}
|
||||
queue_command(
|
||||
queue_command_and_save(
|
||||
ctx,
|
||||
&mut player_item_mut,
|
||||
&player_item,
|
||||
&QueueCommand::Drink {
|
||||
item_type: target.item_type.clone(),
|
||||
item_code: target.item_code.clone(),
|
||||
@ -241,7 +253,6 @@ impl UserVerb for Verb {
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
ctx.trans.save_item_model(&player_item_mut).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
341
blastmud_game/src/message_handler/user_commands/fill.rs
Normal file
341
blastmud_game/src/message_handler/user_commands/fill.rs
Normal file
@ -0,0 +1,341 @@
|
||||
use super::{
|
||||
get_player_item_or_fail, search_item_for_user, user_error, ItemSearchParams, UResult, UserVerb,
|
||||
UserVerbRef, VerbContext,
|
||||
};
|
||||
use crate::{
|
||||
models::item::{Item, LiquidDetails, LiquidType},
|
||||
regular_tasks::queued_command::{
|
||||
queue_command_and_save, QueueCommand, QueueCommandHandler, QueuedCommandContext,
|
||||
},
|
||||
services::{capacity::recalculate_container_weight_mut, comms::broadcast_to_room},
|
||||
};
|
||||
use ansi::ansi;
|
||||
use async_trait::async_trait;
|
||||
use std::collections::{btree_map::Entry, BTreeMap};
|
||||
use std::time;
|
||||
|
||||
pub struct QueueHandler;
|
||||
#[async_trait]
|
||||
impl QueueCommandHandler for QueueHandler {
|
||||
async fn start_command(&self, ctx: &mut QueuedCommandContext<'_>) -> UResult<time::Duration> {
|
||||
if ctx.item.death_data.is_some() {
|
||||
user_error(
|
||||
"You try to fill it, but your ghostly hands slip through it uselessly".to_owned(),
|
||||
)?;
|
||||
}
|
||||
let (from_item_type, from_item_code, to_item_type, to_item_code) = match ctx.command {
|
||||
QueueCommand::Fill {
|
||||
from_item_type,
|
||||
from_item_code,
|
||||
to_item_type,
|
||||
to_item_code,
|
||||
} => (from_item_type, from_item_code, to_item_type, to_item_code),
|
||||
_ => user_error("Unexpected command".to_owned())?,
|
||||
};
|
||||
let from_item = match ctx
|
||||
.trans
|
||||
.find_item_by_type_code(&from_item_type, &from_item_code)
|
||||
.await?
|
||||
{
|
||||
None => user_error("Item not found".to_owned())?,
|
||||
Some(it) => it,
|
||||
};
|
||||
let to_item = match ctx
|
||||
.trans
|
||||
.find_item_by_type_code(&to_item_type, &to_item_code)
|
||||
.await?
|
||||
{
|
||||
None => user_error("Item not found".to_owned())?,
|
||||
Some(it) => it,
|
||||
};
|
||||
if to_item.location != ctx.item.location && to_item.location != ctx.item.refstr() {
|
||||
user_error(format!(
|
||||
"You try to fill {} but realise you no longer have it",
|
||||
to_item.display_for_sentence(ctx.explicit().await?, 1, false)
|
||||
))?
|
||||
}
|
||||
if from_item.location != ctx.item.location && from_item.location != ctx.item.refstr() {
|
||||
user_error(format!(
|
||||
"You try to fill from {} but realise you no longer have it",
|
||||
to_item.display_for_sentence(ctx.explicit().await?, 1, false)
|
||||
))?
|
||||
}
|
||||
|
||||
let msg_exp = format!(
|
||||
"{} prepares to fill {} from {}\n",
|
||||
&ctx.item.display_for_sentence(true, 1, true),
|
||||
&to_item.display_for_sentence(true, 1, false),
|
||||
&from_item.display_for_sentence(true, 1, false),
|
||||
);
|
||||
let msg_nonexp = format!(
|
||||
"{} prepares to fill {} from {}\n",
|
||||
&ctx.item.display_for_sentence(false, 1, true),
|
||||
&to_item.display_for_sentence(false, 1, false),
|
||||
&from_item.display_for_sentence(false, 1, false)
|
||||
);
|
||||
broadcast_to_room(
|
||||
ctx.trans,
|
||||
&ctx.item.location,
|
||||
None,
|
||||
&msg_exp,
|
||||
Some(&msg_nonexp),
|
||||
)
|
||||
.await?;
|
||||
Ok(time::Duration::from_secs(1))
|
||||
}
|
||||
|
||||
#[allow(unreachable_patterns)]
|
||||
async fn finish_command(&self, ctx: &mut QueuedCommandContext<'_>) -> UResult<()> {
|
||||
if ctx.item.death_data.is_some() {
|
||||
user_error(
|
||||
"You try to fill it, but your ghostly hands slip through it uselessly".to_owned(),
|
||||
)?;
|
||||
}
|
||||
let (from_item_type, from_item_code, to_item_type, to_item_code) = match ctx.command {
|
||||
QueueCommand::Fill {
|
||||
from_item_type,
|
||||
from_item_code,
|
||||
to_item_type,
|
||||
to_item_code,
|
||||
} => (from_item_type, from_item_code, to_item_type, to_item_code),
|
||||
_ => user_error("Unexpected command".to_owned())?,
|
||||
};
|
||||
let from_item = match ctx
|
||||
.trans
|
||||
.find_item_by_type_code(&from_item_type, &from_item_code)
|
||||
.await?
|
||||
{
|
||||
None => user_error("Item not found".to_owned())?,
|
||||
Some(it) => it,
|
||||
};
|
||||
let to_item = match ctx
|
||||
.trans
|
||||
.find_item_by_type_code(&to_item_type, &to_item_code)
|
||||
.await?
|
||||
{
|
||||
None => user_error("Item not found".to_owned())?,
|
||||
Some(it) => it,
|
||||
};
|
||||
if to_item.location != ctx.item.location && to_item.location != ctx.item.refstr() {
|
||||
user_error(format!(
|
||||
"You try to fill {} but realise you no longer have it",
|
||||
to_item.display_for_sentence(ctx.explicit().await?, 1, false)
|
||||
))?
|
||||
}
|
||||
if from_item.location != ctx.item.location && from_item.location != ctx.item.refstr() {
|
||||
user_error(format!(
|
||||
"You try to fill from {} but realise you no longer have it",
|
||||
to_item.display_for_sentence(ctx.explicit().await?, 1, false)
|
||||
))?
|
||||
}
|
||||
|
||||
let from_liquid_details = match from_item.liquid_details.as_ref() {
|
||||
None => user_error(format!(
|
||||
"{} appears to be empty.",
|
||||
from_item.display_for_sentence(ctx.explicit().await?, 1, true)
|
||||
))?,
|
||||
Some(v) => v,
|
||||
};
|
||||
|
||||
let available_vol = from_liquid_details
|
||||
.contents
|
||||
.iter()
|
||||
.map(|r| r.1.clone())
|
||||
.sum::<u64>();
|
||||
if available_vol == 0 {
|
||||
user_error(format!(
|
||||
"{} appears to be empty.",
|
||||
from_item.display_for_sentence(ctx.explicit().await?, 1, true)
|
||||
))?
|
||||
}
|
||||
|
||||
let into_liqdata = match to_item
|
||||
.static_data()
|
||||
.and_then(|pd| pd.liquid_container_data.as_ref())
|
||||
{
|
||||
None => user_error(format!(
|
||||
"You can't find a way to fill {}.",
|
||||
to_item.display_for_sentence(ctx.explicit().await?, 1, false)
|
||||
))?,
|
||||
Some(v) => v,
|
||||
};
|
||||
|
||||
if let Some(allowed) = &into_liqdata.allowed_contents {
|
||||
for (liq_type, _) in &from_liquid_details.contents {
|
||||
if !allowed.contains(&liq_type) {
|
||||
user_error(format!(
|
||||
"You don't think putting {} into {} is a good idea.",
|
||||
liq_type.display(),
|
||||
to_item.display_for_sentence(ctx.explicit().await?, 1, false)
|
||||
))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let capacity_remaining = into_liqdata.capacity
|
||||
- to_item
|
||||
.liquid_details
|
||||
.as_ref()
|
||||
.map(|ld| ld.contents.iter().map(|c| c.1.clone()).sum::<u64>())
|
||||
.unwrap_or(0);
|
||||
let actually_transferred = available_vol.min(capacity_remaining);
|
||||
|
||||
if actually_transferred == 0 {
|
||||
user_error(format!(
|
||||
"You don't think you can get any more into {}.",
|
||||
to_item.display_for_sentence(ctx.explicit().await?, 1, false)
|
||||
))?;
|
||||
}
|
||||
|
||||
let transfer_frac = (actually_transferred as f64) / (available_vol as f64);
|
||||
let mut remaining_total = actually_transferred;
|
||||
let transfer_volumes: BTreeMap<LiquidType, u64> = from_liquid_details
|
||||
.contents
|
||||
.iter()
|
||||
.flat_map(|(liqtype, vol)| {
|
||||
let move_vol = (((*vol as f64) * transfer_frac).ceil() as u64).min(remaining_total);
|
||||
remaining_total -= move_vol;
|
||||
if move_vol > 0 {
|
||||
Some((liqtype.clone(), move_vol))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut to_item_mut: Item = (*to_item).clone();
|
||||
match to_item_mut.liquid_details.as_mut() {
|
||||
None => {
|
||||
to_item_mut.liquid_details = Some(LiquidDetails {
|
||||
contents: transfer_volumes.clone(),
|
||||
})
|
||||
}
|
||||
Some(ld) => {
|
||||
for (liq, vol) in &transfer_volumes {
|
||||
ld.contents
|
||||
.entry(liq.clone())
|
||||
.and_modify(|v| {
|
||||
*v += *vol;
|
||||
})
|
||||
.or_insert(vol.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut from_item_mut: Item = (*from_item).clone();
|
||||
if let Some(ld) = from_item_mut.liquid_details.as_mut() {
|
||||
for (liq, vol) in &transfer_volumes {
|
||||
match ld.contents.entry(liq.clone()) {
|
||||
Entry::Vacant(_) => {}
|
||||
Entry::Occupied(mut ent) => {
|
||||
if ent.get() <= vol {
|
||||
ent.remove();
|
||||
} else {
|
||||
*(ent.get_mut()) -= *vol;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
recalculate_container_weight_mut(&ctx.trans, &mut from_item_mut).await?;
|
||||
recalculate_container_weight_mut(&ctx.trans, &mut to_item_mut).await?;
|
||||
ctx.trans.save_item_model(&from_item_mut).await?;
|
||||
ctx.trans.save_item_model(&to_item_mut).await?;
|
||||
|
||||
let msg_exp = format!(
|
||||
"{} fills {} from {}\n",
|
||||
&ctx.item.display_for_sentence(true, 1, true),
|
||||
&to_item.display_for_sentence(true, 1, false),
|
||||
&from_item.display_for_sentence(true, 1, false),
|
||||
);
|
||||
let msg_nonexp = format!(
|
||||
"{} fills {} from {}\n",
|
||||
&ctx.item.display_for_sentence(false, 1, true),
|
||||
&to_item.display_for_sentence(false, 1, false),
|
||||
&from_item.display_for_sentence(false, 1, false)
|
||||
);
|
||||
broadcast_to_room(
|
||||
ctx.trans,
|
||||
&ctx.item.location,
|
||||
None,
|
||||
&msg_exp,
|
||||
Some(&msg_nonexp),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Verb;
|
||||
#[async_trait]
|
||||
impl UserVerb for Verb {
|
||||
async fn handle(
|
||||
self: &Self,
|
||||
ctx: &mut VerbContext,
|
||||
_verb: &str,
|
||||
remaining: &str,
|
||||
) -> UResult<()> {
|
||||
let player_item = get_player_item_or_fail(ctx).await?;
|
||||
|
||||
let (to_str, from_str) = match remaining.split_once(" from ") {
|
||||
None => user_error(
|
||||
ansi!("Try <bold>fill<reset> container <bold>from<reset> container.").to_owned(),
|
||||
)?,
|
||||
Some((to_str, from_str)) => (to_str.trim(), from_str.trim()),
|
||||
};
|
||||
|
||||
let to_target = search_item_for_user(
|
||||
ctx,
|
||||
&ItemSearchParams {
|
||||
include_contents: true,
|
||||
include_loc_contents: true,
|
||||
..ItemSearchParams::base(&player_item, to_str)
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let from_target = search_item_for_user(
|
||||
ctx,
|
||||
&ItemSearchParams {
|
||||
include_contents: true,
|
||||
include_loc_contents: true,
|
||||
..ItemSearchParams::base(&player_item, from_str)
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
if player_item.death_data.is_some() {
|
||||
user_error(
|
||||
"You try to fill it, but your ghostly hands slip through it uselessly".to_owned(),
|
||||
)?;
|
||||
}
|
||||
|
||||
if from_target.item_type != "possession" && from_target.item_type != "fixed_item" {
|
||||
user_error("You can't fill from that!".to_owned())?;
|
||||
}
|
||||
if to_target.item_type != "possession" && to_target.item_type != "fixed_item" {
|
||||
user_error("You can't fill that!".to_owned())?;
|
||||
}
|
||||
if to_target.item_type == from_target.item_type
|
||||
&& to_target.item_code == from_target.item_code
|
||||
{
|
||||
user_error(
|
||||
"You can't figure out how to fill something from itself - a shame!".to_owned(),
|
||||
)?;
|
||||
}
|
||||
|
||||
queue_command_and_save(
|
||||
ctx,
|
||||
&player_item,
|
||||
&QueueCommand::Fill {
|
||||
from_item_type: from_target.item_type.clone(),
|
||||
from_item_code: from_target.item_code.clone(),
|
||||
to_item_type: to_target.item_type.clone(),
|
||||
to_item_code: to_target.item_code.clone(),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
static VERB_INT: Verb = Verb;
|
||||
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;
|
@ -172,6 +172,11 @@ impl LiquidType {
|
||||
LiquidType::Water => "water",
|
||||
}
|
||||
}
|
||||
pub fn density(&self) -> f64 {
|
||||
match self {
|
||||
LiquidType::Water => 1.0, // g / mL.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
|
@ -2,7 +2,7 @@ use super::{TaskHandler, TaskRunContext};
|
||||
#[double]
|
||||
use crate::db::DBTrans;
|
||||
use crate::message_handler::user_commands::{
|
||||
close, cut, drink, drop, eat, get, improvise, make, movement, open, put, remove, use_cmd,
|
||||
close, cut, drink, drop, eat, fill, get, improvise, make, movement, open, put, remove, use_cmd,
|
||||
user_error, wear, wield, CommandHandlingError, UResult, VerbContext,
|
||||
};
|
||||
use crate::message_handler::ListenerSession;
|
||||
@ -57,6 +57,12 @@ pub enum QueueCommand {
|
||||
from_corpse: String,
|
||||
what_part: String,
|
||||
},
|
||||
Fill {
|
||||
from_item_type: String,
|
||||
from_item_code: String,
|
||||
to_item_type: String,
|
||||
to_item_code: String,
|
||||
},
|
||||
Drink {
|
||||
item_type: String,
|
||||
item_code: String,
|
||||
@ -121,6 +127,7 @@ impl QueueCommand {
|
||||
Drink { .. } => "Drink",
|
||||
Drop { .. } => "Drop",
|
||||
Eat { .. } => "Eat",
|
||||
Fill { .. } => "Fill",
|
||||
Get { .. } => "Get",
|
||||
GetFromContainer { .. } => "GetFromContainer",
|
||||
Make { .. } => "Make",
|
||||
@ -204,6 +211,10 @@ fn queue_command_registry(
|
||||
"Eat",
|
||||
&eat::QueueHandler as &(dyn QueueCommandHandler + Sync + Send),
|
||||
),
|
||||
(
|
||||
"Fill",
|
||||
&fill::QueueHandler as &(dyn QueueCommandHandler + Sync + Send),
|
||||
),
|
||||
(
|
||||
"Make",
|
||||
&make::QueueHandler as &(dyn QueueCommandHandler + Sync + Send),
|
||||
|
@ -65,7 +65,19 @@ pub async fn check_item_capacity(
|
||||
Ok(CapacityLevel::Unburdened)
|
||||
}
|
||||
|
||||
pub async fn recalculate_container_weight(trans: &DBTrans, container: &Item) -> DResult<()> {
|
||||
pub async fn recalculate_container_weight_mut(
|
||||
trans: &DBTrans,
|
||||
container: &mut Item,
|
||||
) -> DResult<bool> {
|
||||
let liq_weight = match container.liquid_details.as_ref() {
|
||||
None => 0,
|
||||
Some(ld) => ld
|
||||
.contents
|
||||
.iter()
|
||||
.map(|(liq, vol)| (liq.density() * (*vol as f64)).ceil() as u64)
|
||||
.sum(),
|
||||
};
|
||||
|
||||
if let Some(container_data) = container.possession_type.as_ref().and_then(|pt| {
|
||||
possession_data()
|
||||
.get(pt)
|
||||
@ -73,12 +85,39 @@ pub async fn recalculate_container_weight(trans: &DBTrans, container: &Item) ->
|
||||
}) {
|
||||
let stats = trans.get_location_stats(&container.refstr()).await?;
|
||||
let new_weight = container_data.base_weight
|
||||
+ (((stats.total_weight as f64) * container_data.compression_ratio).ceil() as u64);
|
||||
+ (((stats.total_weight as f64) * container_data.compression_ratio).ceil() as u64)
|
||||
+ liq_weight;
|
||||
if new_weight != container.weight {
|
||||
let mut container_mut = container.clone();
|
||||
container_mut.weight = new_weight;
|
||||
trans.save_item_model(&container_mut).await?;
|
||||
container.weight = new_weight;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
} else if liq_weight > 0 {
|
||||
if let Some(pd) = container
|
||||
.possession_type
|
||||
.as_ref()
|
||||
.and_then(|pt| possession_data().get(pt))
|
||||
{
|
||||
let new_weight = liq_weight + pd.weight;
|
||||
if new_weight != container.weight {
|
||||
container.weight = new_weight;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn recalculate_container_weight(trans: &DBTrans, container: &Item) -> DResult<()> {
|
||||
let mut container_mut = (*container).clone();
|
||||
if recalculate_container_weight_mut(trans, &mut container_mut).await? {
|
||||
trans.save_item_model(&container_mut).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ mod bags;
|
||||
mod benches;
|
||||
mod blade;
|
||||
mod books;
|
||||
mod bottles;
|
||||
mod corp_licence;
|
||||
mod fangs;
|
||||
pub mod head_armour;
|
||||
@ -405,6 +406,8 @@ pub enum PossessionType {
|
||||
CertificateOfIncorporation,
|
||||
// Storage
|
||||
DuffelBag,
|
||||
// Fluid containers
|
||||
DrinkBottle,
|
||||
// Security
|
||||
Scanlock,
|
||||
// Food
|
||||
@ -492,6 +495,7 @@ pub fn possession_data() -> &'static BTreeMap<PossessionType, &'static Possessio
|
||||
.chain(bags::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
|
||||
.chain(benches::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
|
||||
.chain(blade::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
|
||||
.chain(bottles::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
|
||||
.chain(trauma_kit::data().iter().map(|v| ((*v).0.clone(), &(*v).1)))
|
||||
.chain(
|
||||
corp_licence::data()
|
||||
|
26
blastmud_game/src/static_content/possession_type/bottles.rs
Normal file
26
blastmud_game/src/static_content/possession_type/bottles.rs
Normal file
@ -0,0 +1,26 @@
|
||||
use super::{PossessionData, PossessionType};
|
||||
use crate::static_content::possession_type::LiquidContainerData;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
pub fn data() -> &'static Vec<(PossessionType, PossessionData)> {
|
||||
static D: OnceCell<Vec<(PossessionType, PossessionData)>> = OnceCell::new();
|
||||
&D.get_or_init(|| {
|
||||
vec![(
|
||||
PossessionType::DrinkBottle,
|
||||
PossessionData {
|
||||
display: "drink bottle",
|
||||
aliases: vec!["bottle", "flask", "canteen"],
|
||||
details: "A stainless steel bottle, dinged up a bit with dents \
|
||||
and scruff marks from use, but still looking perfectly \
|
||||
usable for whatever fluid you might want it to hold. It \
|
||||
seems to be the right size to hold about 1L.",
|
||||
weight: 100,
|
||||
liquid_container_data: Some(LiquidContainerData {
|
||||
capacity: 1000,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
)]
|
||||
})
|
||||
}
|
@ -2555,6 +2555,11 @@ pub fn room_list() -> Vec<Room> {
|
||||
list_price: 250,
|
||||
..Default::default()
|
||||
},
|
||||
RoomStock {
|
||||
possession_type: PossessionType::DrinkBottle,
|
||||
list_price: 80,
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
should_caption: true,
|
||||
..Default::default()
|
||||
|
Loading…
Reference in New Issue
Block a user