give the shop a typed error instead of string literals
ShopError variants are matchable; the message is the Display, rendered at the log site, not baked into the error type.
This commit is contained in:
parent
a509529aef
commit
849c090e28
1 changed files with 38 additions and 16 deletions
|
|
@ -22,6 +22,28 @@ fn gold() -> LogicDataRef {
|
||||||
fn diamonds() -> LogicDataRef {
|
fn diamonds() -> LogicDataRef {
|
||||||
LogicDataRef::by_name(table::RESOURCES, RESOURCE_DIAMONDS)
|
LogicDataRef::by_name(table::RESOURCES, RESOURCE_DIAMONDS)
|
||||||
}
|
}
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum ShopError {
|
||||||
|
#[error("not a card")]
|
||||||
|
NotACard,
|
||||||
|
#[error("not on sale")]
|
||||||
|
NotOnSale,
|
||||||
|
#[error("buy limit reached")]
|
||||||
|
BuyLimitReached,
|
||||||
|
#[error("nothing sells that")]
|
||||||
|
NotSold,
|
||||||
|
#[error("chest not sold")]
|
||||||
|
ChestNotSold,
|
||||||
|
#[error("no market price")]
|
||||||
|
NoMarketPrice,
|
||||||
|
#[error("price is not a resource")]
|
||||||
|
PriceNotResource,
|
||||||
|
#[error("not enough funds")]
|
||||||
|
CannotAfford,
|
||||||
|
#[error("unsupported currency")]
|
||||||
|
UnsupportedCurrency,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct TurnResult {
|
pub struct TurnResult {
|
||||||
pub claims: Vec<LogicClaimRewardCommand>,
|
pub claims: Vec<LogicClaimRewardCommand>,
|
||||||
|
|
@ -92,11 +114,11 @@ impl HomeMode {
|
||||||
_ => LogicDataRef::treasure_chest_for_arena(CHEST_FREE, arena),
|
_ => LogicDataRef::treasure_chest_for_arena(CHEST_FREE, arena),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn market_cost(&self, give: &ShopEntry) -> Result<ShopEntry, &'static str> {
|
fn market_cost(&self, give: &ShopEntry) -> Result<ShopEntry, ShopError> {
|
||||||
if let Some(chest) = give.data.as_treasure_chest() {
|
if let Some(chest) = give.data.as_treasure_chest() {
|
||||||
let price = chest.shop_price();
|
let price = chest.shop_price();
|
||||||
if price < 1 {
|
if price < 1 {
|
||||||
return Err("that chest is not sold in the shop");
|
return Err(ShopError::ChestNotSold);
|
||||||
}
|
}
|
||||||
return Ok(ShopEntry::new(diamonds(), price));
|
return Ok(ShopEntry::new(diamonds(), price));
|
||||||
}
|
}
|
||||||
|
|
@ -106,27 +128,27 @@ impl HomeMode {
|
||||||
LogicGlobals::resource_diamond_cost(give.count),
|
LogicGlobals::resource_diamond_cost(give.count),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Err("that item has no market price")
|
Err(ShopError::NoMarketPrice)
|
||||||
}
|
}
|
||||||
fn pay(&mut self, cost: &ShopEntry) -> Result<(), &'static str> {
|
fn pay(&mut self, cost: &ShopEntry) -> Result<(), ShopError> {
|
||||||
if !cost.is_resource() {
|
if !cost.is_resource() {
|
||||||
return Err("the price must be a resource");
|
return Err(ShopError::PriceNotResource);
|
||||||
}
|
}
|
||||||
match cost.data.name() {
|
match cost.data.name() {
|
||||||
RESOURCE_GOLD if self.logic.spend_gold(cost.count) => Ok(()),
|
RESOURCE_GOLD if self.logic.spend_gold(cost.count) => Ok(()),
|
||||||
RESOURCE_DIAMONDS if self.logic.spend_diamonds(cost.count) => Ok(()),
|
RESOURCE_DIAMONDS if self.logic.spend_diamonds(cost.count) => Ok(()),
|
||||||
RESOURCE_GOLD | RESOURCE_DIAMONDS => Err("the player cannot afford it"),
|
RESOURCE_GOLD | RESOURCE_DIAMONDS => Err(ShopError::CannotAfford),
|
||||||
_ => Err("only gold and diamonds are accepted"),
|
_ => Err(ShopError::UnsupportedCurrency),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn buy_card(&mut self, card: &LogicDataRef) -> Result<Purchase, &'static str> {
|
fn buy_card(&mut self, card: &LogicDataRef) -> Result<Purchase, ShopError> {
|
||||||
let spell = card.as_spell().ok_or("that is not a card")?;
|
let spell = card.as_spell().ok_or(ShopError::NotACard)?;
|
||||||
let buy_times = self
|
let buy_times = self
|
||||||
.logic
|
.logic
|
||||||
.shop_buy_times(card)
|
.shop_buy_times(card)
|
||||||
.ok_or("that card is not on sale right now")?;
|
.ok_or(ShopError::NotOnSale)?;
|
||||||
if buy_times >= spell.shop_buy_limit() {
|
if buy_times >= spell.shop_buy_limit() {
|
||||||
return Err("the buy limit for that card is reached");
|
return Err(ShopError::BuyLimitReached);
|
||||||
}
|
}
|
||||||
let cost = ShopEntry::new(gold(), spell.cost_in_shop(buy_times));
|
let cost = ShopEntry::new(gold(), spell.cost_in_shop(buy_times));
|
||||||
self.pay(&cost)?;
|
self.pay(&cost)?;
|
||||||
|
|
@ -143,14 +165,14 @@ impl HomeMode {
|
||||||
data: &LogicDataRef,
|
data: &LogicDataRef,
|
||||||
count: i32,
|
count: i32,
|
||||||
shop: &ShopCatalog,
|
shop: &ShopCatalog,
|
||||||
) -> Result<Purchase, &'static str> {
|
) -> Result<Purchase, ShopError> {
|
||||||
if data.as_spell().is_some() {
|
if data.as_spell().is_some() {
|
||||||
return self.buy_card(data);
|
return self.buy_card(data);
|
||||||
}
|
}
|
||||||
let offer = shop
|
let offer = shop
|
||||||
.offer_for(data)
|
.offer_for(data)
|
||||||
.filter(|offer| offer.give.count == count)
|
.filter(|offer| offer.give.count == count)
|
||||||
.ok_or("nothing in the shop sells that")?
|
.ok_or(ShopError::NotSold)?
|
||||||
.clone();
|
.clone();
|
||||||
let cost = match &offer.cost {
|
let cost = match &offer.cost {
|
||||||
Some(cost) => cost.clone(),
|
Some(cost) => cost.clone(),
|
||||||
|
|
@ -161,7 +183,7 @@ impl HomeMode {
|
||||||
match offer.give.data.name() {
|
match offer.give.data.name() {
|
||||||
RESOURCE_GOLD => self.logic.add_gold(offer.give.count),
|
RESOURCE_GOLD => self.logic.add_gold(offer.give.count),
|
||||||
RESOURCE_DIAMONDS => self.logic.add_diamonds(offer.give.count),
|
RESOURCE_DIAMONDS => self.logic.add_diamonds(offer.give.count),
|
||||||
_ => return Err("only gold and diamonds can be sold"),
|
_ => return Err(ShopError::UnsupportedCurrency),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Purchase {
|
Ok(Purchase {
|
||||||
|
|
@ -221,11 +243,11 @@ impl HomeMode {
|
||||||
result.changed = true;
|
result.changed = true;
|
||||||
result.purchases.push(purchase);
|
result.purchases.push(purchase);
|
||||||
}
|
}
|
||||||
Err(reason) => {
|
Err(error) => {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
command = command.command_type(),
|
command = command.command_type(),
|
||||||
item = %data,
|
item = %data,
|
||||||
reason,
|
reason = %error,
|
||||||
"shop purchase rejected"
|
"shop purchase rejected"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue