Skip to main content

tengu_api/state/
shogun.rs

1use steel::*;
2
3/// Empty slot sentinel. Barracks uses u64::MAX for empty slots.
4pub const BARRACKS_SLOT_EMPTY: u64 = u64::MAX;
5
6/// Shogun helpers. Barracks.slots store packed (rarity, element, level, prestige); fodder in Dojo.
7#[repr(C)]
8#[derive(Clone, Copy, Debug, Default, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
9pub struct Shogun {
10    pub dojo: Pubkey,
11    pub index: u64,
12    pub rarity: u64,
13    pub element: u64,
14    pub spirit_power: u64,
15    pub prestige: u64,
16    pub chakra_max: u64,
17    pub chakra_remaining: u64,
18    pub assigned_barracks_slot: u64,
19    pub last_used_slot: u64,
20    pub level: u64,
21    pub buffer1: u64,
22    pub buffer2: u64,
23    pub buffer3: u64,
24    pub buffer4: u64,
25}
26
27impl Shogun {
28    pub fn rarity_from_hash(hash: &[u8; 32]) -> u64 {
29        let v = u64::from_le_bytes(hash[0..8].try_into().unwrap()) % 1000;
30        if v < crate::consts::DROP_N {
31            0
32        } else if v < crate::consts::DROP_N + crate::consts::DROP_R {
33            1
34        } else if v < crate::consts::DROP_N + crate::consts::DROP_R + crate::consts::DROP_SR {
35            2
36        } else if v
37            < crate::consts::DROP_N
38                + crate::consts::DROP_R
39                + crate::consts::DROP_SR
40                + crate::consts::DROP_SSR
41        {
42            3
43        } else {
44            4
45        }
46    }
47
48    pub fn element_from_hash(hash: &[u8; 32]) -> u64 {
49        u64::from_le_bytes(hash[8..16].try_into().unwrap()) % 5
50    }
51
52    pub fn spirit_power_by_rarity(rarity: u64) -> u64 {
53        match rarity {
54            0 => crate::consts::SP_N,
55            1 => crate::consts::SP_R,
56            2 => crate::consts::SP_SR,
57            3 => crate::consts::SP_SSR,
58            4 => crate::consts::SP_UR,
59            _ => crate::consts::SP_N,
60        }
61    }
62
63    pub fn effective_spirit_power(rarity: u64, level: u64, prestige: u64) -> u64 {
64        let base = Self::spirit_power_by_rarity(rarity);
65        let lvl = level.max(1);
66        let prst = prestige.max(1);
67        let level_mult = 100u64.saturating_add((lvl - 1).saturating_mul(10));
68        let prestige_mult = Self::prestige_multiplier(prst);
69        base.saturating_mul(level_mult).saturating_mul(prestige_mult) / 10_000
70    }
71
72    /// Prestige multiplier (1.0x = 100). Ranks 1–5: [100,125,156,195,244]. Rank 6+: 244 + (p-5)*50.
73    pub fn prestige_multiplier(prestige: u64) -> u64 {
74        let p = prestige.max(1);
75        if p <= 5 {
76            [100u64, 125, 156, 195, 244][(p - 1) as usize]
77        } else {
78            244u64.saturating_add((p - 5).saturating_mul(50))
79        }
80    }
81
82    /// Dupes required to prestige from current rank to next. 1→2: 1, 2→3: 2, 3→4: 4, 4→5: 6, 5→6: 8, etc.
83    pub fn prestige_dupes_required(current_prestige: u64) -> u64 {
84        let p = current_prestige.max(1);
85        if p == 1 {
86            1
87        } else {
88            (p - 1).saturating_mul(2)
89        }
90    }
91
92    /// Cost to level up from `level` to `level+1` (raw $DOJO, 6 decimals). 200 + 800*(level-1).
93    pub fn level_up_cost(level: u64) -> u64 {
94        let lvl = level.max(1).min(crate::consts::MAX_SHOGUN_LEVEL);
95        crate::consts::LEVEL_UP_COST_BASE
96            .saturating_add(
97                crate::consts::LEVEL_UP_COST_INCREMENT.saturating_mul(lvl.saturating_sub(1)),
98            )
99    }
100
101    pub fn is_assigned(&self) -> bool {
102        self.assigned_barracks_slot != 0
103    }
104
105    pub fn slot_index(&self) -> u64 {
106        self.assigned_barracks_slot.saturating_sub(1)
107    }
108
109    pub fn is_empty(&self) -> bool {
110        self.rarity == 0 && self.level == 0 && self.spirit_power == 0
111    }
112}