roll chest loot from the csv instead of a fixed config
RandomSpells / DifferentSpells / RareChance / EpicChance / MinGold / MaxGold all come off the chest row now, gold is scaled by the player's arena. magic chest is 30 cards over 8 cards with 1 epic and 6 rares, like the client says it is. arena chest rows inherit everything through BaseChest, so Free_Arena1 reads Free.
This commit is contained in:
parent
573d70ff08
commit
ca8e41c2c5
8 changed files with 208 additions and 66 deletions
|
|
@ -73,25 +73,6 @@ impl Default for StarterProfile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub struct ChestRewardConfig {
|
|
||||||
pub gold_min: i32,
|
|
||||||
pub gold_max: i32,
|
|
||||||
pub diamonds: i32,
|
|
||||||
pub different_cards: usize,
|
|
||||||
pub copies_per_card: i32,
|
|
||||||
}
|
|
||||||
impl Default for ChestRewardConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
gold_min: 15,
|
|
||||||
gold_max: 45,
|
|
||||||
diamonds: 0,
|
|
||||||
different_cards: 2,
|
|
||||||
copies_per_card: 3,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct GameConfig {
|
pub struct GameConfig {
|
||||||
pub database: Option<DatabaseConfig>,
|
pub database: Option<DatabaseConfig>,
|
||||||
|
|
@ -99,7 +80,6 @@ pub struct GameConfig {
|
||||||
pub csv_root: Option<PathBuf>,
|
pub csv_root: Option<PathBuf>,
|
||||||
pub shop_path: Option<PathBuf>,
|
pub shop_path: Option<PathBuf>,
|
||||||
pub starter: StarterProfile,
|
pub starter: StarterProfile,
|
||||||
pub chest_reward: ChestRewardConfig,
|
|
||||||
pub random_seed: i32,
|
pub random_seed: i32,
|
||||||
}
|
}
|
||||||
impl Default for GameConfig {
|
impl Default for GameConfig {
|
||||||
|
|
@ -110,7 +90,6 @@ impl Default for GameConfig {
|
||||||
csv_root: Some(PathBuf::from("assets")),
|
csv_root: Some(PathBuf::from("assets")),
|
||||||
shop_path: Some(PathBuf::from("config/shop.json")),
|
shop_path: Some(PathBuf::from("config/shop.json")),
|
||||||
starter: StarterProfile::default(),
|
starter: StarterProfile::default(),
|
||||||
chest_reward: ChestRewardConfig::default(),
|
|
||||||
random_seed: 0x5EED,
|
random_seed: 0x5EED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -135,7 +114,6 @@ impl GameConfig {
|
||||||
.map(PathBuf::from)
|
.map(PathBuf::from)
|
||||||
.or(defaults.shop_path),
|
.or(defaults.shop_path),
|
||||||
starter,
|
starter,
|
||||||
chest_reward: defaults.chest_reward,
|
|
||||||
random_seed: env_string("SCROLL_RANDOM_SEED")
|
random_seed: env_string("SCROLL_RANDOM_SEED")
|
||||||
.and_then(|value| value.parse().ok())
|
.and_then(|value| value.parse().ok())
|
||||||
.unwrap_or(defaults.random_seed),
|
.unwrap_or(defaults.random_seed),
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,13 @@ use logic::{
|
||||||
use logic::{table, LogicGlobals};
|
use logic::{table, LogicGlobals};
|
||||||
use service_rpc::AccountRef;
|
use service_rpc::AccountRef;
|
||||||
use tokio::sync::{Mutex, RwLock};
|
use tokio::sync::{Mutex, RwLock};
|
||||||
use crate::config::ChestRewardConfig;
|
|
||||||
use crate::home::{build_avatar, build_home};
|
use crate::home::{build_avatar, build_home};
|
||||||
use crate::rewards::RewardRoller;
|
use crate::rewards::RewardRoller;
|
||||||
use crate::shop::{Purchase, ShopCatalog, ShopCycle, ShopEntry};
|
use crate::shop::{Purchase, ShopCatalog, ShopCycle, ShopEntry};
|
||||||
use crate::store::{OwnedCard, PlayerProfile, StoredChest};
|
use crate::store::{OwnedCard, PlayerProfile, StoredChest};
|
||||||
pub const MAX_FAST_FORWARD_TICKS: i32 = 20 * 60 * 60;
|
pub const MAX_FAST_FORWARD_TICKS: i32 = 20 * 60 * 60;
|
||||||
|
pub const CHEST_FREE: &str = "Free";
|
||||||
|
pub const CHEST_CROWN: &str = "Star";
|
||||||
fn gold() -> LogicDataRef {
|
fn gold() -> LogicDataRef {
|
||||||
LogicDataRef::by_name(table::RESOURCES, RESOURCE_GOLD)
|
LogicDataRef::by_name(table::RESOURCES, RESOURCE_GOLD)
|
||||||
}
|
}
|
||||||
|
|
@ -65,6 +66,18 @@ impl HomeMode {
|
||||||
random_seed,
|
random_seed,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
fn chest_for(&self, source: i32, chest_id: i32) -> LogicDataRef {
|
||||||
|
let arena = &self.logic.avatar().arena;
|
||||||
|
match source {
|
||||||
|
chest_source::SLOT => self
|
||||||
|
.logic
|
||||||
|
.chest_with_id(chest_id)
|
||||||
|
.map(|chest| chest.data.clone())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
chest_source::CROWN => LogicDataRef::treasure_chest_for_arena(CHEST_CROWN, 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, &'static str> {
|
||||||
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();
|
||||||
|
|
@ -146,7 +159,6 @@ impl HomeMode {
|
||||||
pub fn end_client_turn(
|
pub fn end_client_turn(
|
||||||
&mut self,
|
&mut self,
|
||||||
turn: &EndClientTurnMessage,
|
turn: &EndClientTurnMessage,
|
||||||
reward: &ChestRewardConfig,
|
|
||||||
roller: &RewardRoller,
|
roller: &RewardRoller,
|
||||||
shop: &ShopCatalog,
|
shop: &ShopCatalog,
|
||||||
) -> TurnResult {
|
) -> TurnResult {
|
||||||
|
|
@ -161,7 +173,8 @@ impl HomeMode {
|
||||||
for command in &turn.commands {
|
for command in &turn.commands {
|
||||||
match self.logic.execute(command.as_ref()) {
|
match self.logic.execute(command.as_ref()) {
|
||||||
CommandOutcome::ClaimStarted { source, chest_id } => {
|
CommandOutcome::ClaimStarted { source, chest_id } => {
|
||||||
pending.push((source, chest_id));
|
let chest = self.chest_for(source, chest_id);
|
||||||
|
pending.push((source, chest_id, chest));
|
||||||
}
|
}
|
||||||
CommandOutcome::Upgraded {
|
CommandOutcome::Upgraded {
|
||||||
spell,
|
spell,
|
||||||
|
|
@ -182,7 +195,11 @@ impl HomeMode {
|
||||||
match self.purchase(&data, count, shop) {
|
match self.purchase(&data, count, shop) {
|
||||||
Ok(purchase) => {
|
Ok(purchase) => {
|
||||||
if purchase.give.is_chest() {
|
if purchase.give.is_chest() {
|
||||||
pending.push((chest_source::PURCHASED, 0));
|
pending.push((
|
||||||
|
chest_source::PURCHASED,
|
||||||
|
0,
|
||||||
|
purchase.give.data.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
result.changed = true;
|
result.changed = true;
|
||||||
result.purchases.push(purchase);
|
result.purchases.push(purchase);
|
||||||
|
|
@ -209,8 +226,16 @@ impl HomeMode {
|
||||||
tick: turn.tick,
|
tick: turn.tick,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (source, chest_id) in pending {
|
let arena = self.logic.avatar().arena.clone();
|
||||||
let rolled = roller.roll(reward);
|
for (source, chest_id, chest) in pending {
|
||||||
|
let rolled = roller.roll(&chest, &arena);
|
||||||
|
tracing::info!(
|
||||||
|
chest = %chest,
|
||||||
|
gold = rolled.gold,
|
||||||
|
cards = rolled.spell_count(),
|
||||||
|
copies = rolled.card_copies(),
|
||||||
|
"chest opened"
|
||||||
|
);
|
||||||
self.logic.apply_reward(&rolled);
|
self.logic.apply_reward(&rolled);
|
||||||
result.changed = true;
|
result.changed = true;
|
||||||
result
|
result
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ pub mod shop;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub use catalog::{Catalog, ARENA_FALLBACK_INSTANCE, GOLD_RESOURCE_FALLBACK_INSTANCE};
|
pub use catalog::{Catalog, ARENA_FALLBACK_INSTANCE, GOLD_RESOURCE_FALLBACK_INSTANCE};
|
||||||
pub use config::{CardRef, ChestRewardConfig, DataSelector, GameConfig, StarterProfile};
|
pub use config::{CardRef, DataSelector, GameConfig, StarterProfile};
|
||||||
pub use home::{build_avatar, build_home};
|
pub use home::{build_avatar, build_home};
|
||||||
pub use home_mode::{HomeMode, HomeModeRegistry, TurnResult};
|
pub use home_mode::{HomeMode, HomeModeRegistry, TurnResult};
|
||||||
pub use rewards::RewardRoller;
|
pub use rewards::RewardRoller;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
|
use logic::data::{RARITY_COMMON, RARITY_EPIC, RARITY_RARE};
|
||||||
use logic::model::LogicSpell;
|
use logic::model::LogicSpell;
|
||||||
use logic::{LogicDataRef, LogicReward};
|
use logic::{LogicDataRef, LogicReward};
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use crate::catalog::Catalog;
|
use crate::catalog::Catalog;
|
||||||
use crate::config::ChestRewardConfig;
|
|
||||||
pub struct RewardRoller {
|
pub struct RewardRoller {
|
||||||
pool: Vec<LogicDataRef>,
|
pool: Vec<LogicDataRef>,
|
||||||
}
|
}
|
||||||
|
|
@ -18,23 +18,93 @@ impl RewardRoller {
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.pool.is_empty()
|
self.pool.is_empty()
|
||||||
}
|
}
|
||||||
pub fn roll(&self, config: &ChestRewardConfig) -> LogicReward {
|
fn by_rarity(&self, rarity: &str) -> Vec<LogicDataRef> {
|
||||||
let mut rng = rand::thread_rng();
|
self.pool
|
||||||
let gold = if config.gold_max > config.gold_min {
|
.iter()
|
||||||
rng.gen_range(config.gold_min..=config.gold_max)
|
.filter(|card| {
|
||||||
} else {
|
card.as_spell()
|
||||||
config.gold_min
|
.map(|spell| spell.rarity() == rarity)
|
||||||
};
|
.unwrap_or(false)
|
||||||
let mut cards: Vec<LogicDataRef> = self
|
})
|
||||||
.pool
|
|
||||||
.choose_multiple(&mut rng, config.different_cards)
|
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect()
|
||||||
cards.sort_by_key(|card| card.global_id().map(|id| (id.class_id, id.instance_id)));
|
}
|
||||||
let spells = cards
|
pub fn roll(&self, chest: &LogicDataRef, arena: &LogicDataRef) -> LogicReward {
|
||||||
|
let Some(data) = chest.as_treasure_chest() else {
|
||||||
|
return LogicReward::default();
|
||||||
|
};
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let scale = |value: i32| match arena.as_arena() {
|
||||||
|
Some(arena) => arena.scaled_chest_reward(value),
|
||||||
|
None => value,
|
||||||
|
};
|
||||||
|
let gold = {
|
||||||
|
let low = scale(data.min_gold());
|
||||||
|
let high = scale(data.max_gold());
|
||||||
|
if high > low {
|
||||||
|
rng.gen_range(low..=high)
|
||||||
|
} else {
|
||||||
|
low
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let diamonds = {
|
||||||
|
let low = data.min_diamonds();
|
||||||
|
let high = data.max_diamonds();
|
||||||
|
if high > low {
|
||||||
|
rng.gen_range(low..=high)
|
||||||
|
} else {
|
||||||
|
low
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let copies = data.random_spells().max(0);
|
||||||
|
let distinct = data.different_spells().max(1) as usize;
|
||||||
|
let mut picked: Vec<LogicDataRef> = Vec::new();
|
||||||
|
let guaranteed = data.guaranteed_spell();
|
||||||
|
if !guaranteed.is_none() {
|
||||||
|
picked.push(guaranteed);
|
||||||
|
}
|
||||||
|
for (rarity, count) in [
|
||||||
|
(RARITY_EPIC, data.guaranteed_epics()),
|
||||||
|
(RARITY_RARE, data.guaranteed_rares()),
|
||||||
|
] {
|
||||||
|
let mut candidates = self.by_rarity(rarity);
|
||||||
|
candidates.retain(|card| !picked.contains(card));
|
||||||
|
candidates.shuffle(&mut rng);
|
||||||
|
for card in candidates.into_iter().take(count.max(0) as usize) {
|
||||||
|
if picked.len() >= distinct {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
picked.push(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if picked.len() < distinct {
|
||||||
|
let mut candidates = self.by_rarity(RARITY_COMMON);
|
||||||
|
candidates.retain(|card| !picked.contains(card));
|
||||||
|
candidates.shuffle(&mut rng);
|
||||||
|
for card in candidates {
|
||||||
|
if picked.len() >= distinct {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
picked.push(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if picked.is_empty() {
|
||||||
|
return LogicReward::new(Vec::new(), gold, diamonds);
|
||||||
|
}
|
||||||
|
picked.sort_by_key(|card| card.global_id().map(|id| (id.class_id, id.instance_id)));
|
||||||
|
let share = copies / picked.len() as i32;
|
||||||
|
let mut remainder = copies % picked.len() as i32;
|
||||||
|
let spells = picked
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|card| LogicSpell::card(card, 0, config.copies_per_card))
|
.map(|card| {
|
||||||
|
let mut count = share;
|
||||||
|
if remainder > 0 {
|
||||||
|
count += 1;
|
||||||
|
remainder -= 1;
|
||||||
|
}
|
||||||
|
LogicSpell::card(card, 0, count.max(1))
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
LogicReward::new(spells, gold, config.diamonds)
|
LogicReward::new(spells, gold, diamonds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,7 @@ impl GameApi for GameService {
|
||||||
let roller = self.roller(account).await?;
|
let roller = self.roller(account).await?;
|
||||||
let result = {
|
let result = {
|
||||||
let mut home = session.lock().await;
|
let mut home = session.lock().await;
|
||||||
home.end_client_turn(&turn, &self.config.chest_reward, &roller, &self.shop)
|
home.end_client_turn(&turn, &roller, &self.shop)
|
||||||
};
|
};
|
||||||
tracing::debug!(
|
tracing::debug!(
|
||||||
%account,
|
%account,
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,18 @@ impl LogicDataRef {
|
||||||
pub fn treasure_chest(name: &str) -> Self {
|
pub fn treasure_chest(name: &str) -> Self {
|
||||||
Self::by_name(table::TREASURE_CHESTS, name)
|
Self::by_name(table::TREASURE_CHESTS, name)
|
||||||
}
|
}
|
||||||
|
pub fn treasure_chest_for_arena(base: &str, arena: &Self) -> Self {
|
||||||
|
if let Some(data) = arena.as_arena() {
|
||||||
|
if !data.is_training_camp() {
|
||||||
|
let scoped =
|
||||||
|
Self::by_name(table::TREASURE_CHESTS, &format!("{base}_{}", data.name()));
|
||||||
|
if !scoped.is_none() {
|
||||||
|
return scoped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self::by_name(table::TREASURE_CHESTS, base)
|
||||||
|
}
|
||||||
pub fn is_none(&self) -> bool {
|
pub fn is_none(&self) -> bool {
|
||||||
matches!(self, LogicDataRef::None)
|
matches!(self, LogicDataRef::None)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,6 @@ pub use logic_data_tables::{
|
||||||
};
|
};
|
||||||
pub use tables::{table, DataId, RESOURCE_DIAMONDS, RESOURCE_FREE_GOLD, RESOURCE_GOLD};
|
pub use tables::{table, DataId, RESOURCE_DIAMONDS, RESOURCE_FREE_GOLD, RESOURCE_GOLD};
|
||||||
pub use typed::{
|
pub use typed::{
|
||||||
LogicArenaData, LogicRarityData, LogicResourceData, LogicResourcePackData, LogicSpellData,
|
arena_by_index, LogicArenaData, LogicRarityData, LogicResourceData, LogicResourcePackData,
|
||||||
LogicTreasureChestData,
|
LogicSpellData, LogicTreasureChestData,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,36 @@ impl LogicArenaData {
|
||||||
pub fn scaled_chest_price(&self, price: i32) -> i32 {
|
pub fn scaled_chest_price(&self, price: i32) -> i32 {
|
||||||
(self.chest_shop_price_multiplier() as i64 * price as i64 / 100) as i32
|
(self.chest_shop_price_multiplier() as i64 * price as i64 / 100) as i32
|
||||||
}
|
}
|
||||||
|
pub fn chest_reward_multiplier(&self) -> i32 {
|
||||||
|
self.int("ChestRewardMultiplier")
|
||||||
|
}
|
||||||
|
pub fn previous(&self) -> Option<LogicArenaData> {
|
||||||
|
let index = self.index();
|
||||||
|
if index <= 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
arena_by_index(index - 1)
|
||||||
|
}
|
||||||
|
pub fn scaled_chest_reward(&self, value: i32) -> i32 {
|
||||||
|
let multiplier = self.chest_reward_multiplier();
|
||||||
|
let scaled = ((multiplier as i64 * value as i64 + 50) / 100) as i32;
|
||||||
|
match self.previous() {
|
||||||
|
Some(previous) if previous.chest_reward_multiplier() < multiplier => previous
|
||||||
|
.scaled_chest_reward(value)
|
||||||
|
.saturating_add(1)
|
||||||
|
.max(scaled),
|
||||||
|
_ => scaled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn arena_by_index(index: i32) -> Option<LogicArenaData> {
|
||||||
|
let tables = crate::data::logic_data_tables::LogicDataTables::instance();
|
||||||
|
let rows = tables.table(crate::data::tables::table::ARENAS)?;
|
||||||
|
let found = rows
|
||||||
|
.iter()
|
||||||
|
.map(|row| LogicArenaData::new(Arc::clone(row)))
|
||||||
|
.find(|arena| arena.index() == index);
|
||||||
|
found
|
||||||
}
|
}
|
||||||
fn clamp_low_first(value: i64, low: i64, high: i64) -> i64 {
|
fn clamp_low_first(value: i64, low: i64, high: i64) -> i64 {
|
||||||
if value > low {
|
if value > low {
|
||||||
|
|
@ -114,6 +144,51 @@ impl LogicTreasureChestData {
|
||||||
let quotient = (cost * seconds_left as i64 + total - 1) / total;
|
let quotient = (cost * seconds_left as i64 + total - 1) / total;
|
||||||
clamp_low_first(quotient, 1, cost) as i32
|
clamp_low_first(quotient, 1, cost) as i32
|
||||||
}
|
}
|
||||||
|
pub fn random_spells(&self) -> i32 {
|
||||||
|
self.root().int("RandomSpells")
|
||||||
|
}
|
||||||
|
pub fn different_spells(&self) -> i32 {
|
||||||
|
self.root().int("DifferentSpells")
|
||||||
|
}
|
||||||
|
pub fn rare_chance(&self) -> i32 {
|
||||||
|
self.root().int("RareChance")
|
||||||
|
}
|
||||||
|
pub fn epic_chance(&self) -> i32 {
|
||||||
|
self.root().int("EpicChance")
|
||||||
|
}
|
||||||
|
pub fn guaranteed_spell(&self) -> LogicDataRef {
|
||||||
|
let name = self.root().string("GuaranteedSpells").to_owned();
|
||||||
|
if name.is_empty() {
|
||||||
|
return LogicDataRef::None;
|
||||||
|
}
|
||||||
|
LogicDataRef::by_name(crate::data::tables::table::SPELLS, &name)
|
||||||
|
}
|
||||||
|
pub fn min_gold(&self) -> i32 {
|
||||||
|
self.root().int("MinGold")
|
||||||
|
}
|
||||||
|
pub fn max_gold(&self) -> i32 {
|
||||||
|
self.root().int("MaxGold")
|
||||||
|
}
|
||||||
|
pub fn min_diamonds(&self) -> i32 {
|
||||||
|
self.root().int("MinDiamonds")
|
||||||
|
}
|
||||||
|
pub fn max_diamonds(&self) -> i32 {
|
||||||
|
self.root().int("MaxDiamonds")
|
||||||
|
}
|
||||||
|
pub fn guaranteed_rares(&self) -> i32 {
|
||||||
|
let chance = self.rare_chance();
|
||||||
|
if chance <= 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
self.random_spells() / chance
|
||||||
|
}
|
||||||
|
pub fn guaranteed_epics(&self) -> i32 {
|
||||||
|
let chance = self.epic_chance();
|
||||||
|
if chance <= 0 {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
self.random_spells() / chance
|
||||||
|
}
|
||||||
pub fn unlock_seconds(&self) -> i32 {
|
pub fn unlock_seconds(&self) -> i32 {
|
||||||
let root = self.root();
|
let root = self.root();
|
||||||
root.int("TimeTakenDays").saturating_mul(86_400)
|
root.int("TimeTakenDays").saturating_mul(86_400)
|
||||||
|
|
@ -207,24 +282,6 @@ impl LogicTreasureChestData {
|
||||||
pub fn arena(&self) -> &str {
|
pub fn arena(&self) -> &str {
|
||||||
self.string("Arena")
|
self.string("Arena")
|
||||||
}
|
}
|
||||||
pub fn total_time_taken_seconds(&self) -> i32 {
|
|
||||||
self.int("TimeTakenDays") * 86_400
|
|
||||||
+ self.int("TimeTakenHours") * 3_600
|
|
||||||
+ self.int("TimeTakenMinutes") * 60
|
|
||||||
+ self.int("TimeTakenSeconds")
|
|
||||||
}
|
|
||||||
pub fn random_spells(&self) -> i32 {
|
|
||||||
self.int("RandomSpells")
|
|
||||||
}
|
|
||||||
pub fn different_spells(&self) -> i32 {
|
|
||||||
self.int("DifferentSpells")
|
|
||||||
}
|
|
||||||
pub fn min_gold(&self) -> i32 {
|
|
||||||
self.int("MinGold")
|
|
||||||
}
|
|
||||||
pub fn max_gold(&self) -> i32 {
|
|
||||||
self.int("MaxGold")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl LogicRarityData {
|
impl LogicRarityData {
|
||||||
pub fn level_count(&self) -> i32 {
|
pub fn level_count(&self) -> i32 {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue