use serde_cbor::{from_slice, to_vec}; use uuid::Uuid; use postgres::transaction::Transaction; use failure::Error; use failure::err_msg; // drops use rand::prelude::*; use rand::{thread_rng, Rng}; use rand::distributions::{WeightedIndex}; use account::Account; use rpc::{ItemUseParams}; use cryp::{cryp_get, cryp_write}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum ItemAction { RerollPhysDamage, RerollSpellDamage, RerollStamina, } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct Item { // mods: Vec, 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::RerollStamina => revive(self, tx, target), ItemAction::RerollPhysDamage => revive(self, tx, target), ItemAction::RerollSpellDamage => revive(self, tx, target), } } } fn revive(item: &mut Item, tx: &mut Transaction, target: Uuid) -> Result<(), Error> { let mut cryp = cryp_get(tx, target, item.account)?; cryp.rez(); cryp_write(cryp, tx)?; return Ok(()); } pub fn item_drop(tx: &mut Transaction, account_id: Uuid) -> Result { let mut rng = thread_rng(); let actions = [ (ItemAction::RerollStamina, 1), (ItemAction::RerollPhysDamage, 1), (ItemAction::RerollSpellDamage, 1) ]; 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); return item_create(item, tx, account_id); } pub fn item_create(item: Item, tx: &mut Transaction, account_id: Uuid) -> Result { 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"); println!("{:?} wrote item {:}", account_id, item.id); 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, &[¶ms.item, &account.id])?; let returned = result.iter().next().expect("no row returned"); let item_bytes: Vec = returned.get(0); let mut item = from_slice::(&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!("invalid item deleted {:?}", id); return Ok(()); } pub fn items_list(tx: &mut Transaction, account: &Account) -> Result, 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 = row.get(0); let id = row.get(1); match from_slice::(&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 // } // ] // },