Files
lootalot/lootalot_db/src/models/player.rs
2019-10-15 14:16:45 +02:00

188 lines
5.0 KiB
Rust

use diesel::prelude::*;
use crate::{DbConnection, QueryResult};
use crate::schema::players;
/// Representation of a player in database
#[derive(Debug, Queryable, Serialize)]
pub struct Player {
/// DB Identitier
pub id: i32,
/// Full name of the character
pub name: String,
/// Amount of gold coins owed to the group.
/// Taking a looted items will increase the debt by it's sell value
pub debt: i32,
/// Count of copper pieces
pub cp: i32,
/// Count of silver pieces
pub sp: i32,
/// Count of gold pieces
pub gp: i32,
/// Count of platinum pieces
pub pp: i32,
}
pub struct Players<'q>(pub &'q DbConnection);
impl<'q> Players<'q> {
pub fn all(&self) -> QueryResult<Vec<Player>> {
players::table
.load(self.0)
}
pub fn add(&self, name: &str, wealth: f32) -> QueryResult<Player> {
diesel::insert_into(players::table)
.values(&NewPlayer::create(name, wealth))
.execute(self.0)?;
players::table
.order(players::dsl::id.desc())
.first(self.0)
}
}
pub struct AsPlayer<'q>(pub &'q DbConnection, pub i32);
impl<'q> AsPlayer<'q> {
pub fn update_wealth(&self, value_in_gp: f32) -> QueryResult<Wealth> {
use crate::schema::players::dsl::*;
let current_wealth = players
.find(self.1)
.select((cp, sp, gp, pp))
.first::<Wealth>(self.0)?;
let updated_wealth = Wealth::from_gp(current_wealth.to_gp() + value_in_gp);
// Difference in coins that is sent back
let difference = Wealth {
cp: updated_wealth.cp - current_wealth.cp,
sp: updated_wealth.sp - current_wealth.sp,
gp: updated_wealth.gp - current_wealth.gp,
pp: updated_wealth.pp - current_wealth.pp,
};
diesel::update(players)
.filter(id.eq(self.1))
.set(&updated_wealth)
.execute(self.0)?;
Ok(difference)
}
pub fn update_debt(&self, value_in_gp: i32) -> QueryResult<()> {
diesel::update(players::table.find(self.1))
.set(players::dsl::debt.eq(
players::dsl::debt + value_in_gp
))
.execute(self.0)?;
Ok(())
}
}
/// Unpack a floating value of gold pieces to integer
/// values of copper, silver, gold and platinum pieces
///
/// # Note
///
/// The conversion is slightly different than standard rules :
/// ``` 1pp = 100gp = 1000sp = 10000 cp ```
///
fn unpack_gold_value(gold: f32) -> (i32, i32, i32, i32) {
let rest = (gold.fract() * 100.0).round() as i32;
let gold = gold.trunc() as i32;
let pp = gold / 100;
let gp = gold % 100;
let sp = rest / 10;
let cp = rest % 10;
(cp, sp, gp, pp)
}
/// State of a player's wealth
///
/// Values are held as individual pieces counts.
/// Allows conversion from and to a floating amount of gold pieces.
#[derive(Queryable, AsChangeset, Debug)]
#[table_name = "players"]
pub struct Wealth {
pub cp: i32,
pub sp: i32,
pub gp: i32,
pub pp: i32,
}
impl Wealth {
/// Unpack individual pieces counts from gold value
///
/// # Examples
/// ```
/// # use lootalot_db::models::Wealth;
/// let wealth = Wealth::from_gp(403.21);
/// assert_eq!(wealth.as_tuple(), (1, 2, 3, 4));
/// ```
pub fn from_gp(gp: f32) -> Self {
let (cp, sp, gp, pp) = unpack_gold_value(gp);
Self { cp, sp, gp, pp }
}
/// Convert total value to a floating value in gold pieces
///
/// # Examples
/// ```
/// # use lootalot_db::models::Wealth;
/// let wealth = Wealth{ pp: 4, gp: 3, sp: 2, cp: 1};
/// assert_eq!(wealth.to_gp(), 403.21);
/// ```
pub fn to_gp(&self) -> f32 {
let i = self.pp * 100 + self.gp;
let f = (self.sp * 10 + self.cp) as f32 / 100.0;
i as f32 + f
}
/// Pack the counts inside a tuple, from lower to higher coin value.
pub fn as_tuple(&self) -> (i32, i32, i32, i32) {
(self.cp, self.sp, self.gp, self.pp)
}
}
/// Representation of a new player record
#[derive(Insertable)]
#[table_name = "players"]
pub(crate) struct NewPlayer<'a> {
name: &'a str,
cp: i32,
sp: i32,
gp: i32,
pp: i32,
}
impl<'a> NewPlayer<'a> {
pub(crate) fn create(name: &'a str, wealth_in_gp: f32) -> Self {
let (cp, sp, gp, pp) = Wealth::from_gp(wealth_in_gp).as_tuple();
Self {
name,
cp,
sp,
gp,
pp,
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_unpack_gold_values() {
use super::unpack_gold_value;
let test_values = [
(1.0, (0, 0, 1, 0)),
(1.23, (3, 2, 1, 0)),
(1.03, (3, 0, 1, 0)),
(100.23, (3, 2, 0, 1)),
(-100.23, (-3, -2, -0, -1)),
(10189.23, (3, 2, 89, 101)),
(-8090.20, (0, -2, -90, -80)),
];
for (tested, expected) in test_values.into_iter() {
assert_eq!(unpack_gold_value(*tested), *expected);
}
}
}