Files
OpenFUT-Core/src/models/profile.rs
T
funman300 a034e74c16
CI / Build, lint & test (push) Successful in 2m19s
style(core): apply cargo fmt across routes, services, models, tests
Pure rustfmt reflow (import grouping, array/match-arm/call-arg wrapping,
alphabetized module decls, comment realignment). No semantic change:
full-diff and `git diff -w` both confirm logic byte-identical to 271c363;
workspace builds and all 74 core tests pass. Retained pre-existing WIP
brought forward after verification.
2026-08-20 16:05:41 +00:00

93 lines
2.5 KiB
Rust

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct Profile {
pub id: String,
pub username: String,
pub level: i64,
pub xp: i64,
/// The game this profile belongs to (e.g. "fifa17", "fifa23"). Scopes all of
/// this profile's downstream state so multiple games share one core + DB.
pub game_id: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
impl Profile {
pub fn new(username: impl Into<String>, game_id: impl Into<String>) -> Self {
let now = Utc::now();
Self {
id: Uuid::new_v4().to_string(),
username: username.into(),
level: 1,
xp: 0,
game_id: game_id.into(),
created_at: now,
updated_at: now,
}
}
}
#[derive(Debug, Deserialize)]
pub struct CreateProfileRequest {
pub username: Option<String>,
}
/// XP required to reach each level (cumulative total from level 1).
/// Level 1 starts at 0 XP. Level 2 needs 500 total XP, etc.
pub const XP_THRESHOLDS: &[i64] = &[
0, // level 1
500, // level 2
1200, // level 3
2000, // level 4
3000, // level 5
4200, // level 6
5600, // level 7
7200, // level 8
9000, // level 9
11000, // level 10
];
/// Compute the level for a given cumulative XP total.
pub fn level_for_xp(total_xp: i64) -> i64 {
let base = XP_THRESHOLDS
.iter()
.rposition(|&t| total_xp >= t)
.map(|i| i as i64 + 1)
.unwrap_or(1);
// Beyond the table: every 2500 XP is another level.
let overflow_xp = total_xp - XP_THRESHOLDS.last().copied().unwrap_or(0);
if overflow_xp > 0 {
let table_max = XP_THRESHOLDS.len() as i64;
table_max + (overflow_xp / 2500)
} else {
base
}
}
/// Coins awarded per new level gained.
pub fn coins_for_level(new_level: i64) -> i64 {
new_level * 500
}
/// Pack granted at milestone levels (5, 10, 15, 20, …).
pub fn pack_for_level(new_level: i64) -> Option<&'static str> {
match new_level {
5 => Some("bronze_pack"),
10 => Some("silver_pack"),
15 => Some("gold_pack"),
20 => Some("rare_gold_pack"),
l if l > 20 && l % 5 == 0 => Some("gold_pack"),
_ => None,
}
}
#[derive(Debug, Clone, Serialize)]
pub struct LevelUpEvent {
pub new_level: i64,
pub coins_granted: i64,
pub pack_granted: Option<String>,
}