feat(discord)!: Added string formatter
- Added string formatter - Added Strings struct for passing strings from file - Refactored /info and /quest * to use formatter BREAKING CHANGE: Changed DiscordConfig fields
This commit is contained in:
parent
b6ea2d8958
commit
aec4ef8339
7 changed files with 386 additions and 84 deletions
|
|
@ -1,4 +1,5 @@
|
|||
use squad_quest::{account::Account, config::Config};
|
||||
use poise::serenity_prelude::UserId;
|
||||
use squad_quest::{account::Account, config::Config, map::Map};
|
||||
|
||||
pub fn fetch_or_init_account(conf: &Config, id: String) -> Account {
|
||||
let accounts = conf.load_accounts();
|
||||
|
|
@ -10,3 +11,23 @@ pub fn fetch_or_init_account(conf: &Config, id: String) -> Account {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn account_rooms_value(account: &Account, map: &Map) -> u32 {
|
||||
map.room.iter().filter_map(|r| {
|
||||
if account.rooms_unlocked.contains(&r.id) {
|
||||
Some(r.value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
pub fn account_full_balance(account: &Account, map: &Map) -> u32 {
|
||||
let rooms_value = account_rooms_value(account, map);
|
||||
account.balance + rooms_value
|
||||
}
|
||||
|
||||
pub fn account_user_id(account: &Account) -> UserId {
|
||||
UserId::new(account.id.clone().parse::<u64>().expect("automatically inserted"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use poise::serenity_prelude::UserId;
|
||||
use squad_quest::{SquadObject, account::Account, map::Map};
|
||||
|
||||
use crate::{Context, Error, account::fetch_or_init_account};
|
||||
use crate::{Context, Error, account::{account_full_balance, account_rooms_value, account_user_id, fetch_or_init_account}};
|
||||
|
||||
async fn account_balance_string(ctx: &Context<'_>, account: &Account, map: &Map) -> String {
|
||||
let rooms_value = account_rooms_value(account, map);
|
||||
|
|
@ -20,26 +20,6 @@ async fn account_balance_string(ctx: &Context<'_>, account: &Account, map: &Map)
|
|||
)
|
||||
}
|
||||
|
||||
fn account_rooms_value(account: &Account, map: &Map) -> u32 {
|
||||
map.room.iter().filter_map(|r| {
|
||||
if account.rooms_unlocked.contains(&r.id) {
|
||||
Some(r.value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn account_full_balance(account: &Account, map: &Map) -> u32 {
|
||||
let rooms_value = account_rooms_value(account, map);
|
||||
account.balance + rooms_value
|
||||
}
|
||||
|
||||
fn account_user_id(account: &Account) -> UserId {
|
||||
UserId::new(account.id.clone().parse::<u64>().expect("automatically inserted"))
|
||||
}
|
||||
|
||||
#[poise::command(
|
||||
prefix_command,
|
||||
slash_command,
|
||||
|
|
|
|||
|
|
@ -21,12 +21,10 @@ pub async fn register(ctx: Context<'_>) -> Result<(), Error> {
|
|||
slash_command,
|
||||
)]
|
||||
pub async fn info(ctx: Context<'_>) -> Result<(), Error> {
|
||||
let reply_string = format!("\
|
||||
SquadQuest version {ver}\n\
|
||||
Find the map here: {url}",
|
||||
ver = env!("CARGO_PKG_VERSION"),
|
||||
url = "not implemented yet!",
|
||||
);
|
||||
let strings = &ctx.data().strings;
|
||||
let formatter = strings.formatter();
|
||||
let reply_string = formatter.fmt(&strings.info);
|
||||
|
||||
ctx.say(reply_string).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{future, path::Path, str::FromStr};
|
||||
use std::{future, str::FromStr};
|
||||
|
||||
use poise::serenity_prelude::{CreateMessage, EditMessage, Message, futures::StreamExt};
|
||||
use squad_quest::{SquadObject, quest::{Quest, QuestDifficulty}};
|
||||
|
|
@ -24,16 +24,10 @@ async fn find_quest_message(ctx: Context<'_>, id: u16) -> Result<Option<Message>
|
|||
Ok(messages.first().cloned())
|
||||
}
|
||||
|
||||
fn make_quest_message_content(quest: &Quest) -> String {
|
||||
format!("### `#{id}` {name} (+{reward})\n\
|
||||
Difficulty: *{difficulty:?}*\n\
|
||||
{description}",
|
||||
id = quest.id,
|
||||
name = quest.name,
|
||||
reward = quest.reward,
|
||||
difficulty = quest.difficulty,
|
||||
description = quest.description,
|
||||
)
|
||||
fn make_quest_message_content(ctx: Context<'_>, quest: &Quest) -> String {
|
||||
let strings = &ctx.data().strings;
|
||||
let formatter = strings.formatter().quest(quest);
|
||||
formatter.fmt(&strings.quest.message_format)
|
||||
}
|
||||
|
||||
#[poise::command(
|
||||
|
|
@ -60,13 +54,12 @@ pub async fn list(
|
|||
) -> Result<(), Error> {
|
||||
let conf = &ctx.data().config;
|
||||
let quests = conf.load_quests();
|
||||
let mut reply_string = format!("Listing {} quests:", quests.len());
|
||||
let strings = &ctx.data().strings;
|
||||
let mut formatter = strings.formatter().value(quests.len());
|
||||
let mut reply_string = formatter.fmt(&strings.quest.list);
|
||||
for quest in quests {
|
||||
reply_string.push_str(format!("\n#{}: {}\n\tDescription: {}",
|
||||
quest.id,
|
||||
quest.name,
|
||||
quest.description,
|
||||
).as_str());
|
||||
formatter = formatter.quest(&quest);
|
||||
reply_string.push_str(formatter.fmt(&strings.quest.list_item).as_str());
|
||||
}
|
||||
ctx.reply(reply_string).await?;
|
||||
Ok(())
|
||||
|
|
@ -168,7 +161,10 @@ pub async fn create(
|
|||
let path = conf.full_quests_path();
|
||||
|
||||
quest.save(path)?;
|
||||
let reply_string = format!("Created quest #{}", quest.id);
|
||||
|
||||
let strings = &ctx.data().strings;
|
||||
let formatter = strings.formatter().quest(&quest);
|
||||
let reply_string = formatter.fmt(&strings.quest.create);
|
||||
|
||||
ctx.reply(reply_string).await?;
|
||||
|
||||
|
|
@ -228,7 +224,6 @@ pub async fn update(
|
|||
},
|
||||
}
|
||||
|
||||
|
||||
let new_quest = Quest {
|
||||
id,
|
||||
difficulty,
|
||||
|
|
@ -241,23 +236,25 @@ pub async fn update(
|
|||
deadline: dead_line,
|
||||
};
|
||||
|
||||
let strings = &ctx.data().strings;
|
||||
let formatter = strings.formatter().quest(&new_quest);
|
||||
|
||||
if new_quest.public {
|
||||
let content = make_quest_message_content(&new_quest);
|
||||
let content = make_quest_message_content(ctx, &new_quest);
|
||||
let builder = EditMessage::new().content(content);
|
||||
|
||||
let message = find_quest_message(ctx, id).await?;
|
||||
if let Some(mut message) = message {
|
||||
message.edit(ctx, builder).await?;
|
||||
} else {
|
||||
let reply_string = format!("Quest #{id} is public, but its message was not found in the quest channel",
|
||||
);
|
||||
let reply_string = formatter.fmt(&strings.quest.message_not_found);
|
||||
ctx.reply(reply_string).await?;
|
||||
}
|
||||
}
|
||||
|
||||
let path = conf.full_quests_path();
|
||||
new_quest.save(path)?;
|
||||
let reply_string = format!("Updated quest #{id}");
|
||||
let reply_string = formatter.fmt(&strings.quest.update);
|
||||
ctx.reply(reply_string).await?;
|
||||
|
||||
Ok(())
|
||||
|
|
@ -286,7 +283,7 @@ pub async fn publish(
|
|||
|
||||
quest.public = true;
|
||||
|
||||
let content = make_quest_message_content(&quest);
|
||||
let content = make_quest_message_content(ctx, &quest);
|
||||
|
||||
let builder = CreateMessage::new()
|
||||
.content(content);
|
||||
|
|
@ -297,19 +294,15 @@ pub async fn publish(
|
|||
guard.quests_channel
|
||||
};
|
||||
|
||||
let message = channel.send_message(ctx, builder).await?;
|
||||
|
||||
{
|
||||
let mut guard = dc.lock().expect("shouldn't be locked");
|
||||
guard.quests_messages.push(message.id);
|
||||
let path = ctx.data().config.full_impl_path().unwrap();
|
||||
guard.save(path.parent().unwrap_or(Path::new("")).to_owned())?
|
||||
};
|
||||
channel.send_message(ctx, builder).await?;
|
||||
|
||||
let quests_path = ctx.data().config.full_quests_path();
|
||||
quest.save(quests_path)?;
|
||||
|
||||
let reply_string = format!("Published quest #{id}");
|
||||
let strings = &ctx.data().strings;
|
||||
let formatter = strings.formatter().quest(&quest);
|
||||
|
||||
let reply_string = formatter.fmt(&strings.quest.publish);
|
||||
ctx.reply(reply_string).await?;
|
||||
|
||||
Ok(())
|
||||
|
|
@ -342,7 +335,15 @@ pub async fn delete(
|
|||
account.save(accounts_path.clone())?;
|
||||
}
|
||||
|
||||
let reply_string = format!("Successfully deleted quest #{id}");
|
||||
let mock_quest = Quest {
|
||||
id,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let strings = &ctx.data().strings;
|
||||
let formatter = strings.formatter().quest(&mock_quest);
|
||||
|
||||
let reply_string = formatter.fmt(&strings.quest.delete);
|
||||
ctx.reply(reply_string).await?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1,35 +1,55 @@
|
|||
use std::{io::Write, path::{Path, PathBuf}};
|
||||
|
||||
use poise::serenity_prelude::{ChannelId, GuildId, MessageId};
|
||||
use poise::serenity_prelude::{ChannelId, GuildId};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use squad_quest::{SquadObject, config::Config, error::Error};
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Clone, Debug)]
|
||||
use crate::strings::Strings;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct DiscordConfig {
|
||||
pub guild: GuildId,
|
||||
pub quests_channel: ChannelId,
|
||||
pub answers_channel: ChannelId,
|
||||
pub quests_messages: Vec<MessageId>,
|
||||
pub strings_path: PathBuf,
|
||||
}
|
||||
|
||||
impl Default for DiscordConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
guild: GuildId::default(),
|
||||
quests_channel: ChannelId::default(),
|
||||
answers_channel: ChannelId::default(),
|
||||
strings_path: "strings.toml".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ConfigImpl {
|
||||
fn discord_impl(&self) -> Result<DiscordConfig, Error>;
|
||||
fn discord_impl(&self) -> Result<(DiscordConfig, Strings), Error>;
|
||||
fn init_impl(&self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
impl ConfigImpl for Config {
|
||||
fn discord_impl(&self) -> Result<DiscordConfig, Error> {
|
||||
let Some(path) = &self.full_impl_path() else {
|
||||
fn discord_impl(&self) -> Result<(DiscordConfig, Strings), Error> {
|
||||
let Some(path) = self.full_impl_path() else {
|
||||
return Err(Error::IsNotImplemented);
|
||||
};
|
||||
DiscordConfig::load(path.clone())
|
||||
let discord = DiscordConfig::load(path.clone())?;
|
||||
let mut strings_path: PathBuf = path.parent().unwrap_or(Path::new("")).to_owned();
|
||||
strings_path.push(discord.strings_path.clone());
|
||||
let strings = Strings::load(strings_path)?;
|
||||
Ok((discord, strings))
|
||||
}
|
||||
fn init_impl(&self) -> Result<(), Error> {
|
||||
let Some(path) = self.full_impl_path() else {
|
||||
return Err(Error::IsNotImplemented);
|
||||
};
|
||||
let folder = path.parent().unwrap_or(Path::new("")).to_owned();
|
||||
let dc = DiscordConfig::default();
|
||||
dc.save(path.parent().unwrap_or(Path::new("")).to_owned())
|
||||
dc.save(folder.clone())?;
|
||||
let strings = Strings::default();
|
||||
strings.save(folder)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -46,17 +66,8 @@ impl SquadObject for DiscordConfig {
|
|||
}
|
||||
}
|
||||
|
||||
fn delete(path: PathBuf) -> Result<(), Error> {
|
||||
match Self::load(path.clone()) {
|
||||
Ok(_) => {
|
||||
if let Err(error) = std::fs::remove_file(path) {
|
||||
return Err(Error::IoError(error));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
},
|
||||
Err(error) => Err(error)
|
||||
}
|
||||
fn delete(_path: PathBuf) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn save(&self, path: PathBuf) -> Result<(), Error> {
|
||||
|
|
|
|||
|
|
@ -5,20 +5,23 @@ use dotenvy::dotenv;
|
|||
use poise::serenity_prelude as serenity;
|
||||
use squad_quest::config::Config;
|
||||
|
||||
use crate::{commands::error_handler, config::{ConfigImpl, DiscordConfig}, error::Error};
|
||||
use crate::{commands::error_handler, config::{ConfigImpl, DiscordConfig}, error::Error, strings::Strings};
|
||||
|
||||
mod commands;
|
||||
mod cli;
|
||||
mod config;
|
||||
mod account;
|
||||
mod error;
|
||||
mod strings;
|
||||
|
||||
const CONFIG_PATH: &str = "cfg/config.toml";
|
||||
const DISCORD_TOKEN: &str = "DISCORD_TOKEN";
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Data {
|
||||
pub config: Config,
|
||||
pub discord: Arc<Mutex<DiscordConfig>>,
|
||||
pub strings: Strings,
|
||||
}
|
||||
type Context<'a> = poise::Context<'a, Data, Error>;
|
||||
|
||||
|
|
@ -28,12 +31,12 @@ async fn main() {
|
|||
|
||||
let cli = cli::Cli::parse();
|
||||
let config = Config::load(cli.config.clone().unwrap_or(CONFIG_PATH.into()));
|
||||
let discord = config.discord_impl().unwrap_or_else(|_| {
|
||||
let (discord, strings) = config.discord_impl().unwrap_or_else(|_| {
|
||||
config.init_impl().unwrap();
|
||||
config.discord_impl().unwrap()
|
||||
});
|
||||
|
||||
let token = std::env::var("DISCORD_TOKEN").expect("missing DISCORD_TOKEN");
|
||||
let token = std::env::var(DISCORD_TOKEN).expect("missing DISCORD_TOKEN");
|
||||
let intents = serenity::GatewayIntents::non_privileged();
|
||||
|
||||
let framework = poise::Framework::builder()
|
||||
|
|
@ -60,6 +63,7 @@ async fn main() {
|
|||
Ok(Data {
|
||||
config,
|
||||
discord: Arc::new(Mutex::new(discord)),
|
||||
strings,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
287
discord/src/strings.rs
Normal file
287
discord/src/strings.rs
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
use std::{collections::HashMap, fmt::Display, io::Write, path::PathBuf};
|
||||
|
||||
use poise::serenity_prelude::{Mentionable, User};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use squad_quest::{SquadObject, account::Account, map::Map, quest::{Quest, QuestDifficulty}, error::Error};
|
||||
|
||||
use crate::account::{account_full_balance, account_rooms_value};
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct StringFormatter {
|
||||
tags: HashMap<String, String>,
|
||||
difficulty: Difficulty,
|
||||
}
|
||||
|
||||
impl StringFormatter {
|
||||
pub fn new() -> Self {
|
||||
let newline = ("{n}".to_string(), '\n'.to_string());
|
||||
let version = ("{v}".to_string(), env!("CARGO_PKG_VERSION").to_string());
|
||||
let new_tags = vec![ newline, version ];
|
||||
|
||||
Self::default().with_tags(new_tags).to_owned()
|
||||
}
|
||||
|
||||
pub fn with_tags(mut self, tags: Vec<(String, String)>) -> Self {
|
||||
for tag in tags {
|
||||
self.tags.insert(tag.0, tag.1);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn strings(mut self, strings: &Strings) -> Self {
|
||||
self.difficulty = strings.difficulty.clone();
|
||||
|
||||
let url = ("{url}".to_string(), strings.url.clone());
|
||||
let points = ("{pt}".to_string(), strings.points.clone());
|
||||
let new_tags = vec![ url, points ];
|
||||
|
||||
self.with_tags(new_tags)
|
||||
}
|
||||
|
||||
pub fn quest(self, quest: &Quest) -> Self {
|
||||
let id = ("{q.id}".to_string(), id(quest.id));
|
||||
let difficulty = ("{q.difficulty}".to_string(), self.difficulty.as_string(&quest.difficulty));
|
||||
let reward = ("{q.reward}".to_string(), self.points(quest.reward.to_string()));
|
||||
let name = ("{q.name}".to_string(), quest.name.clone());
|
||||
let description = ("{q.description}".to_string(), quest.description.clone());
|
||||
let answer = ("{q.answer}".to_string(), quest.answer.clone());
|
||||
let new_tags = vec![ id, difficulty, reward, name, description, answer ];
|
||||
|
||||
self.with_tags(new_tags)
|
||||
}
|
||||
|
||||
pub fn user(self, user: &User) -> Self {
|
||||
let mention = ("{u.mention}".to_string(), user.mention().to_string());
|
||||
let name = ("{u.name}".to_string(), user.display_name().to_string());
|
||||
let new_tags = vec![ mention, name ];
|
||||
|
||||
self.with_tags(new_tags)
|
||||
}
|
||||
|
||||
pub fn balance(self, account: &Account, map: &Map) -> Self {
|
||||
let balance = ("{b.current}".to_string(), self.points(account.balance));
|
||||
let full_balance = (
|
||||
"{b.full}".to_string(),
|
||||
self.points(account_full_balance(account, map)),
|
||||
);
|
||||
let rooms_balance = (
|
||||
"{b.rooms}".to_string(),
|
||||
self.points(account_rooms_value(account, map)),
|
||||
);
|
||||
let new_tags = vec![ balance, full_balance, rooms_balance ];
|
||||
|
||||
self.with_tags(new_tags)
|
||||
}
|
||||
|
||||
pub fn text(self, text: impl ToString) -> Self {
|
||||
let text = ("{text}".to_string(), text.to_string());
|
||||
|
||||
self.with_tags(vec![text])
|
||||
}
|
||||
|
||||
pub fn value(self, value: impl ToString) -> Self {
|
||||
let value = ("{value}".to_string(), value.to_string());
|
||||
|
||||
self.with_tags(vec![value])
|
||||
}
|
||||
|
||||
|
||||
fn points(&self, str: impl Display) -> String {
|
||||
let template = format!("{str} {pt}", pt = "{pt}");
|
||||
self.fmt(&template)
|
||||
}
|
||||
|
||||
pub fn fmt(&self, string: &str) -> String {
|
||||
let mut formatted = string.to_string();
|
||||
for (tag, replacement) in self.tags.iter() {
|
||||
formatted = formatted.replace(tag, replacement);
|
||||
}
|
||||
formatted
|
||||
}
|
||||
}
|
||||
|
||||
fn id(str: impl Display) -> String {
|
||||
format!("#{str}")
|
||||
}
|
||||
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct Strings {
|
||||
pub url: String,
|
||||
pub points: String,
|
||||
pub info: String,
|
||||
pub answer: Answer,
|
||||
pub difficulty: Difficulty,
|
||||
pub scoreboard: Scoreboard,
|
||||
pub quest: QuestStrings,
|
||||
}
|
||||
|
||||
impl Default for Strings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
url: "not implemented!".to_string(),
|
||||
points: "points".to_string(),
|
||||
info: "SquadQuest version {v}\
|
||||
{n}Find the map here: {url}".to_string(),
|
||||
answer: Answer::default(),
|
||||
difficulty: Difficulty::default(),
|
||||
scoreboard: Scoreboard::default(),
|
||||
quest: QuestStrings::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SquadObject for Strings {
|
||||
fn load(path: PathBuf) -> Result<Self, Error> {
|
||||
match std::fs::read_to_string(path) {
|
||||
Ok(string) => {
|
||||
match toml::from_str::<Self>(&string) {
|
||||
Ok(object) => Ok(object),
|
||||
Err(error) => Err(Error::TomlDeserializeError(error))
|
||||
}
|
||||
},
|
||||
Err(error) => Err(Error::IoError(error))
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(_path: PathBuf) -> Result<(), Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn save(&self, path: PathBuf) -> Result<(), Error> {
|
||||
let filename = "strings.toml".to_string();
|
||||
let mut full_path = path;
|
||||
full_path.push(filename);
|
||||
|
||||
let str = match toml::to_string_pretty(&self) {
|
||||
Ok(string) => string,
|
||||
Err(error) => {
|
||||
return Err(Error::TomlSerializeError(error));
|
||||
}
|
||||
};
|
||||
|
||||
let mut file = match std::fs::File::create(full_path) {
|
||||
Ok(f) => f,
|
||||
Err(error) => {
|
||||
return Err(Error::IoError(error));
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(error) = file.write_all(str.as_bytes()) {
|
||||
return Err(Error::IoError(error));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Strings {
|
||||
pub fn formatter(&self) -> StringFormatter {
|
||||
StringFormatter::new().strings(self).to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[serde(default)]
|
||||
pub struct Answer {
|
||||
pub from: String,
|
||||
pub quest: String,
|
||||
pub expected: String,
|
||||
pub text: String,
|
||||
pub attachment_notice: String,
|
||||
pub accepted_by: String,
|
||||
pub rejected_by: String,
|
||||
}
|
||||
|
||||
impl Default for Answer {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
from: "## From: {u.mention}{n}".to_string(),
|
||||
quest: "### Quest {q.id}: {q.name}{n}".to_string(),
|
||||
expected: "### Expected answer:{n}||{q.answer}||".to_string(),
|
||||
text: "### Passed answer:{n}{text}".to_string(),
|
||||
attachment_notice: "Passed answer has attachments.".to_string(),
|
||||
accepted_by: "{text}{n}Accepted by: {u.mention}".to_string(),
|
||||
rejected_by: "~~{text}~~{n}Rejected by: {u.mention}".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug)]
|
||||
#[serde(default)]
|
||||
pub struct Difficulty {
|
||||
pub easy: String,
|
||||
pub normal: String,
|
||||
pub hard: String,
|
||||
pub secret: String,
|
||||
}
|
||||
|
||||
impl Default for Difficulty {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
easy: "Easy".to_string(),
|
||||
normal: "Normal".to_string(),
|
||||
hard: "Hard".to_string(),
|
||||
secret: "Secret".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Difficulty {
|
||||
pub fn as_string(&self, difficulty: &QuestDifficulty) -> String {
|
||||
match difficulty {
|
||||
QuestDifficulty::Easy => self.easy.clone(),
|
||||
QuestDifficulty::Normal => self.normal.clone(),
|
||||
QuestDifficulty::Hard => self.hard.clone(),
|
||||
QuestDifficulty::Secret => self.secret.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct Scoreboard {
|
||||
pub header: String,
|
||||
pub line_format: String,
|
||||
pub you_format: String,
|
||||
}
|
||||
|
||||
impl Default for Scoreboard {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
header: "Current scoreboard:".to_string(),
|
||||
line_format: "{n}{u.name}: **{b.full}** (**{b.current}** on balance\
|
||||
+ **{b.rooms}** unlocked rooms networth)".to_string(),
|
||||
you_format: "__{text}__ << You".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct QuestStrings {
|
||||
pub list: String,
|
||||
pub list_item: String,
|
||||
pub create: String,
|
||||
pub update: String,
|
||||
pub publish: String,
|
||||
pub delete: String,
|
||||
pub message_format: String,
|
||||
pub message_not_found: String,
|
||||
}
|
||||
|
||||
impl Default for QuestStrings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
list: "Listing {value} quests:".to_string(),
|
||||
list_item: "{n}{q.id}: {q.name}{n} Description: {q.description}".to_string(),
|
||||
create: "Created quest {q.id}".to_string(),
|
||||
update: "Updated quest {q.id}".to_string(),
|
||||
publish: "Published quest {q.id}: {text}".to_string(),
|
||||
delete: "Deleted quest {q.id}".to_string(),
|
||||
message_format: "### `{q.id}` {q.name} (+{q.reward}){n}\
|
||||
Difficulty: *{q.difficulty}*{n}\
|
||||
{q.description}".to_string(),
|
||||
message_not_found: "Warning: quest {q.id} message not found".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue