scroll.server/crates/titan/src/logic_long.rs
WiseDev 0451fcdf43 grant achievement progress and roll exp into levels
two separate breakages behind "no achievement, no level up", both
checked against the client in IDA.

level up: LogicClientAvatar::xpGainHelper adds a gain to exp_points -
which is progress WITHIN the level, not a running total - then rolls it
into levels: while exp_points >= ExpToNextLevel(level) it subtracts that
threshold and increments the level, calling levelUp for the deck slots
and diamond reward. our add_exp only grew exp_points, so the bar filled
past full and the level never moved. it now rolls over the same way,
against the exp_levels table.

achievement: the client only offers a claim when progress >= ActionCount
(LogicClientAvatar::isAchievementCompleted reads commodity 2), and the
claim command re-reads that same commodity - and build_avatar populated
none of it, so every claim came back "not completed". build_avatar now
serves real progress: findcard from the count of distinct cards owned,
reacharena from the arena reached. donate / jointeam / watchtv need
social features we do not have and stay at zero.

tests cover both and fail without the fixes: 25 exp reaches level 2 with
5 carried, and a completed findcard tier claims once, grants exp, and is
rejected the second time.
2026-08-24 09:15:22 +03:00

39 lines
1 KiB
Rust

use std::fmt;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct LogicLong {
pub high: i32,
pub low: i32,
}
impl LogicLong {
pub const ZERO: LogicLong = LogicLong { high: 0, low: 0 };
pub const fn new(high: i32, low: i32) -> Self {
Self { high, low }
}
pub const fn from_i64(value: i64) -> Self {
Self {
high: (value >> 32) as i32,
low: value as i32,
}
}
pub const fn to_i64(self) -> i64 {
((self.high as i64) << 32) | (self.low as u32 as i64)
}
pub const fn is_zero(self) -> bool {
self.high == 0 && self.low == 0
}
}
impl fmt::Display for LogicLong {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "LogicLong({}-{})", self.high, self.low)
}
}
impl From<i64> for LogicLong {
fn from(value: i64) -> Self {
Self::from_i64(value)
}
}
impl From<LogicLong> for i64 {
fn from(value: LogicLong) -> Self {
value.to_i64()
}
}