mnml/server/src/item.rs
2019-04-09 16:02:48 +10:00

267 lines
7.0 KiB
Rust

use serde_cbor::{from_slice, to_vec};
use uuid::Uuid;
use postgres::transaction::Transaction;
use failure::Error;
// drops
use rand::prelude::*;
use rand::{thread_rng};
use rand::distributions::{LogNormal,WeightedIndex};
use account::Account;
use rpc::{ItemUseParams};
use cryp::{Stat, cryp_get, cryp_write};
use game::{GameMode};
use spec::{Spec, SpecType};
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum ItemAction {
RerollRedDamage,
RerollBlueDamage,
RerollSpeed,
RerollGreenLife,
RerollRedLife,
RerollBlueLife,
RerollEvasion,
SpecRedDamage5,
SpecBlueDamage5,
SpecRedLife5,
SpecBlueLife5,
SpecBlueEvasion5,
}
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub struct Item {
// mods: Vec<Mod>,
id: Uuid,
account: Uuid,
action: ItemAction,
}
impl Item {
pub fn new(action: ItemAction, account_id: Uuid) -> Item {
let id = Uuid::new_v4();
return Item {
id,
account: account_id,
action,
};
}
fn apply(&mut self, tx: &mut Transaction, target: Uuid) -> Result<(), Error> {
match self.action {
ItemAction::RerollGreenLife => reroll(self, tx, target, Stat::GreenLife),
ItemAction::RerollRedDamage => reroll(self, tx, target, Stat::RedDamage),
ItemAction::RerollBlueDamage => reroll(self, tx, target, Stat::BlueDamage),
ItemAction::RerollSpeed => reroll(self, tx, target, Stat::Speed),
ItemAction::RerollRedLife => reroll(self, tx, target, Stat::RedLife),
ItemAction::RerollBlueLife => reroll(self, tx, target, Stat::BlueLife),
ItemAction::RerollEvasion => reroll(self, tx, target, Stat::Evasion),
ItemAction::SpecRedDamage5 => spec_add(self, tx, target, SpecType::RedDamage5),
ItemAction::SpecBlueDamage5 => spec_add(self, tx, target, SpecType::BlueDamage5),
_ => unimplemented!(),
}
}
}
fn spec_add(item: &mut Item, tx: &mut Transaction, target: Uuid, spec_type: SpecType) -> Result<(), Error> {
let mut cryp = cryp_get(tx, target, item.account)?;
let spec = Spec::new(spec_type);
cryp.spec_add(spec)?;
cryp_write(cryp, tx)?;
return Ok(());
}
fn reroll(item: &mut Item, tx: &mut Transaction, target: Uuid, stat: Stat) -> Result<(), Error> {
let mut cryp = cryp_get(tx, target, item.account)?;
cryp.roll_stat(stat);
cryp_write(cryp, tx)?;
return Ok(());
}
fn mode_drops(mode: GameMode) -> Vec<(ItemAction, usize)> {
match mode {
GameMode::Normal => vec![
(ItemAction::RerollGreenLife, 1),
(ItemAction::RerollRedDamage, 1),
(ItemAction::RerollBlueDamage, 1),
],
GameMode::Pvp => vec![
(ItemAction::RerollSpeed, 1),
],
GameMode::Zone3v2Attack |
GameMode::Zone2v2Caster |
GameMode::Zone3v3MeleeMiniboss => vec![
(ItemAction::RerollEvasion, 1),
(ItemAction::RerollRedLife, 1),
(ItemAction::RerollBlueLife, 1),
],
GameMode::Zone3v3HealerBoss => vec![
(ItemAction::RerollSpeed, 1),
],
// _ => vec![
// (ItemAction::RerollGreenLife, 1),
// (ItemAction::RerollRedDamage, 1),
// (ItemAction::RerollBlueDamage, 1),
// (ItemAction::RerollSpeed, 1),
// (ItemAction::RerollRedLife, 1),
// (ItemAction::RerollBlueLife, 1),
// (ItemAction::RerollEvasion, 1),
// ],
}
}
pub fn item_drop(tx: &mut Transaction, account_id: Uuid, mode: GameMode) -> Result<(), Error> {
let mut rng = thread_rng();
let log_normal = LogNormal::new(1.0, 1.0);
let num_drops = log_normal.sample(&mut rng).floor() as u16;
let actions = mode_drops(mode);
println!("{:?} drops", num_drops);
for _i in 0..num_drops {
let dist = WeightedIndex::new(actions.iter().map(|item| item.1)).unwrap();
let kind = actions[dist.sample(&mut rng)].0;
let item = Item::new(kind, account_id);
println!("{:?} dropped {:?}", account_id, item);
item_create(item, tx, account_id)?;
}
Ok(())
}
pub fn item_create(item: Item, tx: &mut Transaction, account_id: Uuid) -> Result<Item, Error> {
let item_bytes = to_vec(&item)?;
let query = "
INSERT INTO items (id, account, data)
VALUES ($1, $2, $3)
RETURNING id, account, data;
";
let result = tx
.query(query, &[&item.id, &account_id, &item_bytes])?;
result.iter().next().expect("no row returned");
return Ok(item);
}
pub fn item_use(params: ItemUseParams, tx: &mut Transaction, account: &Account) -> Result<(), Error> {
let query = "
SELECT data
FROM items
WHERE id = $1
AND account = $2
FOR UPDATE;
";
let result = tx
.query(query, &[&params.item, &account.id])?;
let returned = result.iter().next().expect("no row returned");
let item_bytes: Vec<u8> = returned.get(0);
let mut item = from_slice::<Item>(&item_bytes)?;
item.apply(tx, params.target)?;
item_delete(tx, params.item)?;
return Ok(());
}
pub fn item_delete(tx: &mut Transaction, id: Uuid) -> Result<(), Error> {
let query = "
DELETE
FROM items
WHERE id = $1;
";
let result = tx
.execute(query, &[&id])?;
if result != 1 {
return Err(format_err!("unable to delete item {:?}", id));
}
// println!("item deleted {:?}", id);
return Ok(());
}
pub fn items_list(tx: &mut Transaction, account: &Account) -> Result<Vec<Item>, Error> {
let query = "
SELECT data, id
FROM items
WHERE account = $1;
";
let result = tx
.query(query, &[&account.id])?;
let mut items = vec![];
for row in result.into_iter() {
let item_bytes: Vec<u8> = row.get(0);
let id = row.get(1);
match from_slice::<Item>(&item_bytes) {
Ok(i) => items.push(i),
Err(_e) => {
item_delete(tx, id)?;
}
};
}
return Ok(items);
}
// # max damage potion
// name
// "MapMonstersCurseEffectOnSelfFinal3": {
// "adds_tags": [],
// "domain": "area",
// "generation_type": "prefix",
// "generation_weights": [],
// "grants_buff": {},
// "grants_effect": {},
// "group": "MapHexproof",
// "is_essence_only": false,
// "name": "Hexwarded",
// "required_level": 1,
// "spawn_weights": [
// {
// "tag": "top_tier_map",
// "weight": 0
// },
// {
// "tag": "default",
// "weight": 0
// }
// ],
// "stats": [
// {
// "id": "map_item_drop_quantity_+%",
// "max": 15,
// "min": 15
// },
// {
// "id": "map_item_drop_rarity_+%",
// "max": 8,
// "min": 8
// },
// {
// "id": "map_monsters_curse_effect_on_self_+%_final",
// "max": -60,
// "min": -60
// }
// ]
// },