Skip to main content

tengu_api/state/
barracks.rs

1use super::{shogun::BARRACKS_SLOT_EMPTY, DojosAccount};
2use steel::*;
3
4/// Pack (rarity, element, level, prestige) into u64. BARRACKS_SLOT_EMPTY = u64::MAX.
5#[inline]
6pub fn pack_slot(rarity: u64, element: u64, level: u64, prestige: u64) -> u64 {
7    (rarity & 0xFF)
8        | ((element & 0xFF) << 8)
9        | ((level.min(0xFFFF) & 0xFFFF) << 16)
10        | ((prestige.min(0xFF) & 0xFF) << 32)
11}
12
13#[inline]
14pub fn unpack_slot(v: u64) -> (u64, u64, u64, u64) {
15    (
16        v & 0xFF,
17        (v >> 8) & 0xFF,
18        (v >> 16) & 0xFFFF,
19        (v >> 32) & 0xFF,
20    )
21}
22
23/// Ore-relevant cache per slot. Updated by seat/replace/dine/level_up; read by claim_shards.
24/// Includes dine_count for per-shogun escalate pricing.
25#[repr(C)]
26#[derive(Clone, Copy, Debug, PartialEq, Default, bytemuck::Pod, bytemuck::Zeroable)]
27pub struct SlotCache {
28    pub spirit_power: u64,
29    pub chakra_remaining: u64,
30    pub last_used_slot: u64,
31    /// Times this shogun has been dined. Used for per-shogun escalate pricing (0 = base, >=1 = escalated).
32    pub dine_count: u64,
33}
34
35#[repr(C)]
36#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
37pub struct Barracks {
38    pub dojo: Pubkey,
39    pub level: u64, // 1-4
40    /// Packed: rarity|element|level|prestige. BARRACKS_SLOT_EMPTY = empty.
41    pub slots: [u64; 12],
42    /// Ore-relevant state per slot. Kept in sync by seat/replace/dine/level_up.
43    pub slot_cache: [SlotCache; 12],
44    /// Bits `0..MAX_BARRACKS_SLOTS`: set after first dine on that slot granted [`crate::consts::XP_DINE_FIRST_PER_SLOT`].
45    pub dine_xp_slot_mask: u64,
46}
47
48account!(DojosAccount, Barracks);
49
50impl Barracks {
51    /// Sum of seated shoguns' raw spirit power. Used for pool-split: scene bonuses apply **once**
52    /// to this sum (`Treasury::effective_spirit_power_with_scene`), not per shogun.
53    pub fn sum_seated_raw_spirit_power(&self) -> u64 {
54        let mut sum: u64 = 0;
55        for i in 0..crate::consts::MAX_BARRACKS_SLOTS {
56            if self.slots[i] != BARRACKS_SLOT_EMPTY {
57                sum = sum.saturating_add(self.slot_cache[i].spirit_power);
58            }
59        }
60        sum
61    }
62
63    pub fn max_slots(&self) -> u64 {
64        self.level * crate::consts::SLOTS_PER_LEVEL
65    }
66
67    pub fn is_slot_valid(&self, slot: u64) -> bool {
68        slot < self.max_slots()
69    }
70
71    pub fn is_slot_empty(&self, i: usize) -> bool {
72        self.slots[i] == BARRACKS_SLOT_EMPTY
73    }
74
75    pub fn upgrade_cost_shards(level: u64) -> u64 {
76        match level {
77            1 => crate::consts::BARRACKS_COST_1_2_SHARDS,
78            2 => crate::consts::BARRACKS_COST_2_3_SHARDS,
79            3 => crate::consts::BARRACKS_COST_3_4,
80            _ => u64::MAX,
81        }
82    }
83
84    pub fn upgrade_cost_sol(level: u64) -> u64 {
85        match level {
86            1 => crate::consts::BARRACKS_COST_1_2_SOL,
87            2 => crate::consts::BARRACKS_COST_2_3_SOL,
88            3 => u64::MAX, // 3→4 shards only
89            _ => u64::MAX,
90        }
91    }
92}