Start work on shops.

This commit is contained in:
Condorra 2023-01-29 00:26:06 +11:00
parent 2053c9cbb3
commit 722757468f
6 changed files with 159 additions and 8 deletions

View File

@ -15,6 +15,7 @@ mod describe;
mod help;
mod ignore;
mod less_explicit_mode;
mod list;
mod login;
mod look;
mod map;
@ -106,6 +107,8 @@ static REGISTERED_COMMANDS: UserVerbRegistry = phf_map! {
"l" => look::VERB,
"look" => look::VERB,
"read" => look::VERB,
"list" => list::VERB,
"lmap" => map::VERB,

View File

@ -0,0 +1,47 @@
use super::{VerbContext, UserVerb, UserVerbRef, UResult, user_error,
get_player_item_or_fail};
use crate::{
static_content::room,
static_content::possession_type::possession_data,
};
use async_trait::async_trait;
use ansi::ansi;
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?;
if player_item.is_dead {
user_error("Nobody seems to offer you any prices... possibly because you're dead.".to_owned())?
}
let (heretype, herecode) = player_item.location.split_once("/").unwrap_or(("room", "repro_xv_chargen"));
if heretype != "room" {
user_error("Can't list stock because you're not in a shop.".to_owned())?;
}
let room = match room::room_map_by_code().get(herecode) {
None => user_error("Can't find that shop.".to_owned())?,
Some(r) => r
};
if room.stock_list.is_empty() {
user_error("Can't list stock because you're not in a shop.".to_owned())?
}
let mut msg = String::new();
msg.push_str(&format!(ansi!("<bold><bgblue><white>| {:20} | {:15} |<reset>\n"),
ansi!("Item"),
ansi!("Price")));
for stock in &room.stock_list {
if let Some(possession_type) = possession_data().get(&stock.possession_type) {
msg.push_str(&format!("| {:20} | {:15.2} |\n", &possession_type.name, &stock.list_price))
}
}
msg.push('\n');
ctx.trans.queue_for_session(&ctx.session, Some(&msg)).await?;
Ok(())
}
}
static VERB_INT: Verb = Verb;
pub static VERB: UserVerbRef = &VERB_INT as UserVerbRef;

View File

@ -92,7 +92,8 @@ impl Default for NPC {
aliases: vec!(),
says: vec!(),
total_xp: 1000,
total_skills: SkillType::values().into_iter().map(|sk| (sk, 10.0)).collect(),
total_skills: SkillType::values().into_iter()
.map(|sk| (sk.clone(), if &sk == &SkillType::Dodge { 8.0 } else { 10.0 })).collect(),
attackable: false,
aggression: 0,
intrinsic_weapon: None,
@ -155,6 +156,7 @@ pub fn npc_static_items() -> Box<dyn Iterator<Item = StaticItem>> {
is_static: true,
pronouns: c.pronouns.clone(),
is_challenge_attack_only: !c.attackable,
total_xp: c.total_xp.clone(),
total_skills: c.total_skills.clone(),
species: c.species.clone(),
aliases: c.aliases.iter().map(|a| (*a).to_owned()).collect::<Vec<String>>(),

View File

@ -54,13 +54,23 @@ impl Default for WeaponData {
}
pub struct PossessionData {
pub weapon_data: Option<WeaponData>
pub weapon_data: Option<WeaponData>,
pub name: &'static str,
pub name_nonexp: Option<&'static str>,
#[allow(unused)]
pub display: &'static str,
#[allow(unused)]
pub display_nonexp: Option<&'static str>,
}
impl Default for PossessionData {
fn default() -> Self {
Self {
weapon_data: None
weapon_data: None,
name: "Thingy",
name_nonexp: None,
display: "A generic looking thing",
display_nonexp: None,
}
}
}
@ -93,6 +103,7 @@ pub enum PossessionType {
// Special values that substitute for possessions.
Fangs, // Default weapon for certain animals
// Real possessions from here on:
AntennaWhip,
}
pub fn fist() -> &'static WeaponData {
@ -156,6 +167,38 @@ pub fn possession_data() -> &'static BTreeMap<PossessionType, PossessionData> {
..Default::default()
}),
..Default::default()
}),
(AntennaWhip, PossessionData {
name: "Antenna whip",
display: "A crudely fashioned whip made from a broken metal antenna. It looks a bit flimsy, but it \
might do you until you get a better weapon!",
weapon_data: Some(WeaponData {
uses_skill: SkillType::Whips,
raw_min_to_learn: 0.0,
raw_max_to_learn: 2.0,
normal_attack_start_messages: vec!(
Box::new(|attacker, victim, exp|
format!("{} lines up {} antenna whip for a strike on {}",
&attacker.display_for_sentence(exp, 1, true),
&attacker.pronouns.possessive,
&victim.display_for_sentence(exp, 1, false),
)
)
),
normal_attack_success_messages: vec!(
Box::new(|attacker, victim, part, exp|
format!("{}'s antenna whip scores a painful red line across {}'s {}",
&attacker.display_for_sentence(exp, 1, true),
&victim.display_for_sentence(exp, 1, false),
&part.display(victim.sex.clone())
)
)
),
normal_attack_mean_damage: 3.0,
normal_attack_stdev_damage: 3.0,
..Default::default()
}),
..Default::default()
})
).into_iter().collect()
})

View File

@ -1,4 +1,7 @@
use super::StaticItem;
use super::{
StaticItem,
possession_type::PossessionType,
};
use once_cell::sync::OnceCell;
use std::collections::BTreeMap;
use async_trait::async_trait;
@ -6,7 +9,9 @@ use serde::{Serialize, Deserialize};
use crate::message_handler::user_commands::{
UResult, VerbContext
};
use crate::models::item::{Item, ItemFlag};
use crate::{
models::item::{Item, ItemFlag}
};
mod repro_xv;
mod melbs;
@ -145,6 +150,20 @@ pub struct SecondaryZoneRecord {
pub caption: Option<&'static str>
}
pub struct RoomStock {
pub possession_type: PossessionType,
pub list_price: f64
}
impl Default for RoomStock {
fn default() -> Self {
Self {
possession_type: PossessionType::AntennaWhip,
list_price: 1000000000.0
}
}
}
pub struct Room {
pub zone: &'static str,
// Other zones where it can be seen on the map and accessed.
@ -158,7 +177,9 @@ pub struct Room {
pub exits: Vec<Exit>,
pub should_caption: bool,
pub repel_npc: bool,
pub item_flags: Vec<ItemFlag>
pub item_flags: Vec<ItemFlag>,
// Empty means not a shop.
pub stock_list: Vec<RoomStock>,
}
impl Default for Room {
@ -176,6 +197,7 @@ impl Default for Room {
should_caption: true,
repel_npc: false,
item_flags: vec!(),
stock_list: vec!(),
}
}

View File

@ -1,8 +1,11 @@
use super::{
Room, GridCoords, Exit, Direction, ExitTarget, ExitType,
Room, RoomStock, GridCoords, Exit, Direction, ExitTarget, ExitType,
SecondaryZoneRecord
};
use crate::models::item::ItemFlag;
use crate::{
models::item::ItemFlag,
static_content::possession_type::PossessionType
};
use ansi::ansi;
pub fn room_list() -> Vec<Room> {
@ -1707,10 +1710,41 @@ pub fn room_list() -> Vec<Room> {
target: ExitTarget::UseGPS,
exit_type: ExitType::Free
},
Exit {
direction: Direction::SOUTH,
target: ExitTarget::UseGPS,
exit_type: ExitType::Free
},
),
should_caption: false,
..Default::default()
},
Room {
zone: "melbs",
secondary_zones: vec!(),
code: "melbs_mckillocks_self_defence",
name: "McKillock's Self Defence",
short: ansi!("<red>MS<reset>"),
description: ansi!("A neatly painted shop with bars covering the window, and a mean looking shop-keeper sitting behind a desk. [Use <bold>list<reset> to see stock for sale here]"),
description_less_explicit: None,
grid_coords: GridCoords { x: 3, y: 0, z: 0 },
exits: vec!(
Exit {
direction: Direction::NORTH,
target: ExitTarget::UseGPS,
exit_type: ExitType::Free
},
),
should_caption: true,
stock_list: vec!(
RoomStock {
possession_type: PossessionType::AntennaWhip,
list_price: 100.0,
..Default::default()
}
),
..Default::default()
},
Room {
zone: "melbs",
secondary_zones: vec!(),