use jzon::{array, object, JsonValue}; use rand::RngExt; use actix_web::{web, Responder, http::header::{HeaderMap, HeaderValue}}; use crate::router::{userdata, global, databases, Session, Api}; pub fn remove_gems(user: &mut JsonValue, amount: i64) { let mut amount = amount; let mut free = user["gem"]["free"].as_i64().unwrap(); let mut paid = user["gem"]["charge"].as_i64().unwrap(); free -= amount; if free < 0 { amount = -free; free = 0; } paid -= amount; if paid < 0 { paid = 0; } user["gem"]["free"] = free.into(); user["gem"]["charge"] = paid.into(); user["gem"]["total"] = (free + paid).into(); } pub fn remove_paid_gems(user: &mut JsonValue, amount: i64) { let free = user["gem"]["free"].as_i64().unwrap(); let mut paid = user["gem"]["charge"].as_i64().unwrap(); paid -= amount; if paid < 0 { paid = 0; } user["gem"]["charge"] = paid.into(); user["gem"]["total"] = (free + paid).into(); } pub fn get_region(headers: &HeaderMap) -> bool { let blank_header = HeaderValue::from_static(""); let asset_version = headers.get("aoharu-asset-version").unwrap_or(&blank_header).to_str().unwrap_or(""); global::get_player_region(asset_version).unwrap_or(String::from("JP")) == "JP" } pub fn check_for_region(user: &mut JsonValue, headers: &HeaderMap) { // returns true if jp if !get_region(headers) { return; } let targets = [ 15570001, 15570002, 15570003, 15570004, 15570005, 15570006, 15570007, 15570008 ]; let list = if !user["data"]["updated_value_list"]["item_list"].is_empty() { &mut user["data"]["updated_value_list"]["item_list"] } else { &mut user["data"]["item_list"] }; let mut to_remove: Vec = list .members() .enumerate() .filter(|(_, item)| { targets.contains(&item["master_item_id"].as_i64().unwrap_or(0)) }) .map(|(i, _)| i) .collect(); to_remove.reverse(); for i in to_remove { list.array_remove(i); } } // true - limit reached // false - all good pub const GIFT_LIMIT: usize = 100000; pub const LIMIT_ITEMS: i64 = 9999999; pub const LIMIT_COINS: i64 = 999999999; pub const LIMIT_PRIMOGEMS: i64 = 9999999; fn give(array: &mut JsonValue, shop_id: &str, master_item_id: i64, limit: i64, count_id: &str, count: i64, default_push: JsonValue) -> bool { for data in array.members_mut() { if data[shop_id].as_i64().unwrap() == master_item_id { if data[count_id].as_i64().unwrap() >= limit { return true; } let mut new_amount = data[count_id].as_i64().unwrap() + count; if new_amount > limit { new_amount = limit; } data[count_id] = new_amount.into(); return false; } } array.push(default_push).unwrap(); false } pub fn give_shop(master_item_id: i64, count: i64, user: &mut JsonValue) -> bool { give( &mut user["shop_list"], "master_shop_item_id", master_item_id, LIMIT_ITEMS, "count", count, object!{ master_shop_item_id: master_item_id, count: count } ) } pub fn give_item(master_item_id: i64, amount: i64, user: &mut JsonValue) -> bool { give( &mut user["item_list"], "master_item_id", master_item_id, LIMIT_ITEMS, "amount", amount, object!{ id: master_item_id, master_item_id: master_item_id, amount: amount, expire_date_time: null } ) } pub fn use_item(item: &JsonValue, multiplier: i64, user: &mut JsonValue) { if item["consumeType"] == 0 { // Is anything really ever free...? } else if item["consumeType"] == 1 { remove_gems(user, item["amount"].as_i64().unwrap()); } else if item["consumeType"] == 2 { remove_paid_gems(user, item["amount"].as_i64().unwrap()); } else if item["consumeType"] == 4 { use_itemm(item["value"].as_i64().unwrap(), item["amount"].as_i64().unwrap() * multiplier, user); } else { println!("Unknown consume type {}", item["consumeType"]); } } pub fn give_gift(data: &JsonValue, user: &mut JsonValue, missions: &mut JsonValue, clear_missions: &mut JsonValue, chats: &mut JsonValue) -> bool { if data.is_empty() { return false; } if data["reward_type"] == 1 { // basically primogems!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! return !give_primogems(data["amount"].as_i64().unwrap(), user); } else if data["reward_type"] == 2 { //character give_character(data["value"].as_i64().unwrap(), user, missions, clear_missions, chats); return true; } else if data["reward_type"] == 3 { return !give_item(data["value"].as_i64().unwrap(), data["amount"].as_i64().unwrap(), user); } else if data["reward_type"] == 4 { // basically moraa!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! return !give_points(data["value"].as_i64().unwrap(), data["amount"].as_i64().unwrap(), user, missions, clear_missions); } else if data["reward_type"] == 8 { // title let title = data["value"].as_i64().unwrap(); if !user["master_title_ids"].contains(title) { user["master_title_ids"].push(title).unwrap(); return true; } return false; } println!("Redeeming reward not implemented for reward type {}", data["reward_type"]); false } pub fn give_gift_basic(ty_pe: i32, id: i64, amount: i64, user: &mut JsonValue, missions: &mut JsonValue, clear_missions: &mut JsonValue, chats: &mut JsonValue) -> bool { give_gift(&object!{ reward_type: ty_pe, amount: amount, value: id }, user, missions, clear_missions, chats) } pub fn give_points(master_item_id: i64, amount: i64, user: &mut JsonValue, missions: &mut JsonValue, clear_missions: &mut JsonValue) -> bool { if master_item_id == 1 { let cleared = advance_variable_mission(1121001, 1121019, amount, missions); for data in cleared.members() { if !clear_missions.contains(data.as_i64().unwrap()) { clear_missions.push(data.clone()).unwrap(); } } } give( &mut user["point_list"], "type", master_item_id, LIMIT_COINS, "amount", amount, object!{ type: master_item_id, amount: amount } ) } pub fn use_itemm(master_item_id: i64, amount: i64, user: &mut JsonValue) { for data in user["item_list"].members_mut() { if data["master_item_id"].as_i64().unwrap() == master_item_id { if data["amount"].as_i64().unwrap() >= amount { data["amount"] = (data["amount"].as_i64().unwrap() - amount).into(); } else { data["amount"] = (0).into(); } break; } } } pub fn give_primogems(amount: i64, user: &mut JsonValue) -> bool { if user["gem"]["free"].as_i64().unwrap() >= LIMIT_PRIMOGEMS { return true; } let new_amount = user["gem"]["free"].as_i64().unwrap() + amount; if user["gem"]["free"].as_i64().unwrap() > LIMIT_PRIMOGEMS { user["gem"]["free"] = LIMIT_PRIMOGEMS.into(); } user["gem"]["free"] = new_amount.into(); false } pub fn gift_item(item: &JsonValue, reason: &str, user: &mut JsonValue) -> JsonValue { let to_push = object!{ id: item["id"].clone(), reward_type: item["type"].clone(), is_receive: 0, reason_text: reason, value: item["value"].clone(), level: item["level"].clone(), amount: item["amount"].clone(), created_date_time: global::timestamp(), expire_date_time: global::timestamp() + (300 * (24 * 60 * 60)), received_date_time: 0 }; if user["home"]["gift_list"].len() >= GIFT_LIMIT { return to_push; } user["home"]["gift_list"].push(to_push.clone()).unwrap(); to_push } fn random_number(lowest: usize, highest: usize) -> usize { if lowest == highest { return lowest; } assert!(lowest < highest); rand::rng().random_range(lowest..highest + 1) } pub fn gift_item_basic(id: i32, value: i64, ty_pe: i32, reason: &str, user: &mut JsonValue) -> JsonValue { gift_item(&object!{ id: random_number(0, global::timestamp_msec() as usize), type: ty_pe, level: 0, amount: value, value: id }, reason, user) } pub fn lp_modification(user: &mut JsonValue, change_amount: u64, remove: bool) -> bool { let now = global::timestamp(); let max = get_user_rank_data(user["user"]["exp"].as_i64().unwrap())["maxLp"].as_u64().unwrap(); let orig_stamina = user["stamina"]["stamina"].as_u64().unwrap(); let orig_anchor = user["stamina"]["last_updated_time"].as_u64().unwrap(); let mut stamina = orig_stamina; let mut anchor = orig_anchor; if stamina < max && now > anchor { let restored = (now - anchor) / 300; stamina = (stamina + restored).min(max); anchor += restored * 300; } if change_amount > 0 { if remove { let was_full = stamina >= max; stamina -= change_amount; if was_full { anchor = now; } } else { stamina += change_amount; } } user["stamina"]["stamina"] = stamina.into(); user["stamina"]["last_updated_time"] = anchor.into(); stamina != orig_stamina || anchor != orig_anchor } pub fn get_rarity(id: i64) -> i32 { databases::CARD_LIST[id.to_string()]["rarity"].as_i32().unwrap_or(0) } // true - added // false - already has pub fn give_character(id: i64, user: &mut JsonValue, missions: &mut JsonValue, clear_missions: &mut JsonValue, chats: &mut JsonValue) -> bool { let character_rarity = get_rarity(id); if character_rarity == 0 { println!("Attempted to give user undefined card!! Card id: {}", id); return false; } if !databases::CHARACTER_CHATS[id.to_string()]["51"].is_empty() { let chat = &databases::CHARACTER_CHATS[id.to_string()]["51"]; let mission_id = databases::MISSION_REWARD[chat[0].to_string()]["value"].as_i64().unwrap(); if crate::router::chat::add_chat_from_chapter_id(mission_id, chats) { update_mission_status(chat[1].as_i64().unwrap(), 0, true, true, 1, missions); if !clear_missions.contains(chat[1].as_i64().unwrap()) { clear_missions.push(chat[1].clone()).unwrap(); } } } for data in user["card_list"].members() { if data["master_card_id"] == id || data["id"] == id { let amount = if character_rarity == 1 { 20 } else if character_rarity == 2 { 50 } else if character_rarity == 3 { 500 } else { 0 }; give_item(19100001, amount, user); return false; } } let cleared = advance_variable_mission(1112001, 1112033, 1, missions); for data in cleared.members() { if !clear_missions.contains(data.as_i64().unwrap()) { clear_missions.push(data.clone()).unwrap(); } } let to_push = object!{ "id": id, "master_card_id": id, "exp": 0, "skill_exp": 0, "evolve": [], "created_date_time": global::timestamp() }; user["card_list"].push(to_push.clone()).unwrap(); true } pub fn get_user_rank_data(exp: i64) -> JsonValue { for (i, rank) in databases::RANKS.members().enumerate() { if exp < rank["exp"].as_i64().unwrap() { return databases::RANKS[i - 1].clone(); } } databases::RANKS[databases::RANKS.len() - 1].clone() } pub fn give_exp(amount: i32, user: &mut JsonValue, mission: &mut JsonValue, rv: &mut JsonValue) { let current_rank = get_user_rank_data(user["user"]["exp"].as_i64().unwrap()); user["user"]["exp"] = (user["user"]["exp"].as_i32().unwrap() + amount).into(); let new_rank = get_user_rank_data(user["user"]["exp"].as_i64().unwrap()); if current_rank["rank"] != new_rank["rank"] { user["stamina"]["stamina"] = (user["stamina"]["stamina"].as_i64().unwrap() + new_rank["maxLp"].as_i64().unwrap()).into(); let from = current_rank["rank"].as_i64().unwrap(); let to = new_rank["rank"].as_i64().unwrap(); for rank in databases::RANKS.members() { let num = rank["rank"].as_i64().unwrap(); if num <= from || num > to { continue; } for reward in databases::USER_RANK_REWARD[rank["masterUserRankRewardId"].to_string()].members() { give_gift(&object!{ reward_type: reward["type"].clone(), value: reward["value"].clone(), amount: reward["amount"].clone() }, user, mission, &mut array![], &mut array![]); } } let status = get_mission_status(get_variable_mission_num(1101001, 1101030, mission), mission); if status.is_empty() { return; } let to_advance = new_rank["rank"].as_i64().unwrap() - status["progress"].as_i64().unwrap(); let rvv = advance_variable_mission(1101001, 1101030, to_advance, mission); for id in rvv.members() { rv.push(id.as_i64().unwrap()).unwrap(); } } } pub fn update_mission_status(master_mission_id: i64, expire: u64, completed: bool, claimed: bool, advance: i64, missions: &mut JsonValue) -> Option { for mission in missions.members_mut() { if mission["master_mission_id"].as_i64().unwrap() == master_mission_id { let was_completed = mission["status"] == 2; mission["status"] = if claimed { 3 } else if completed { 2 } else { 1 }.into(); if expire != 0 { mission["expire_date_time"] = expire.into(); } if (mission["expire_date_time"].as_u64().unwrap() < global::timestamp() || expire != 0) && (mission["expire_date_time"].as_u64().unwrap() != 0 || expire != 0) { mission["progress"] = 0.into(); } if advance > 0 { mission["progress"] = (mission["progress"].as_i64().unwrap() + advance).into(); } if completed && !claimed && !was_completed { return Some(master_mission_id); } return None; } } None } pub fn update_mission_status_multi(master_mission_id: JsonValue, expire: u64, completed: bool, claimed: bool, advance: i64, missions: &mut JsonValue) -> JsonValue { let mut rv = array![]; for mission in master_mission_id.members() { let val = update_mission_status(mission.as_i64().unwrap(), expire, completed, claimed, advance, missions); if let Some(val2) = val { rv.push(val2).unwrap(); } } rv } pub fn get_mission_status(id: i64, missions: &JsonValue) -> JsonValue { for mission in missions.members() { if mission["master_mission_id"].as_i64().unwrap() == id { return mission.clone(); } } JsonValue::Null } pub fn change_mission_id(old: i64, new: i64, missions: &mut JsonValue) { for mission in missions.members_mut() { if mission["master_mission_id"].as_i64().unwrap() == old { mission["master_mission_id"] = new.into(); return; } } } pub fn get_variable_mission_num(min: i64, max: i64, missions: &JsonValue) -> i64 { for i in min..=max { let mission_status = get_mission_status(i, missions); if mission_status.is_empty() { continue; } return i; } 0 } pub fn advance_variable_mission(min: i64, max: i64, count: i64, missions: &mut JsonValue) -> JsonValue { let mut rv = array![]; for i in min..=max { let mission_status = get_mission_status(i, missions); if mission_status.is_empty() { continue; } let mission_info = &databases::MISSION_LIST[i.to_string()]; if i == max && mission_info["conditionNumber"].as_i64().unwrap() <= mission_status["progress"].as_i64().unwrap() { break; } if mission_info["conditionNumber"].as_i64().unwrap() > mission_status["progress"].as_i64().unwrap() + count { if update_mission_status(i, 0, false, false, count, missions).is_some() { rv.push(i).unwrap(); } } else if update_mission_status(i, 0, true, false, count, missions).is_some() { rv.push(i).unwrap(); } break; } rv } pub fn advance_mission(id: i64, count: i64, max: i64, missions: &mut JsonValue) -> Option { let mission = get_mission_status(id, missions); if mission["status"].as_i32().unwrap() > 1 { return None; } let mut new = mission["progress"].as_i64().unwrap() + count; if new > max { new = max; } let completed = new == max; let advanced = new - mission["progress"].as_i64().unwrap(); if update_mission_status(id, 0, completed, false, advanced, missions).is_some() { return Some(id); } None } pub const DAILY_MISSION_IDS: [i64; 6] = [1224003, 1253003, 1273009, 1273010, 1273011, 1273012]; pub fn get_expire(now: u64) -> u64 { let jst = now + 9 * 60 * 60; ((jst / 86400 + 1) * 86400 - 9 * 60 * 60) - 1 } pub fn refresh_dailies(missions: &mut JsonValue, now: u64) -> bool { let target = get_expire(now); let mut changed = false; for id in DAILY_MISSION_IDS { for mission in missions.members_mut() { if mission["master_mission_id"].as_i64() != Some(id) { continue; } let expire = mission["expire_date_time"].as_u64().unwrap_or(0); if expire == 0 { mission["expire_date_time"] = target.into(); changed = true; } else if now > expire { mission["status"] = (1).into(); mission["progress"] = (0).into(); mission["expire_date_time"] = target.into(); changed = true; } break; } } changed } pub fn completed_daily_mission(id: i64, missions: &mut JsonValue) -> JsonValue { let now = global::timestamp(); refresh_dailies(missions, now); let mission = get_mission_status(id, missions); if mission.is_empty() || mission["status"].as_i32().unwrap_or(0) > 1 { return array![]; } let mut rv = array![]; if id == 1253003 { rv = advance_variable_mission(1153001, 1153019, 1, missions); if update_mission_status(1653001, 0, true, false, 1, missions).is_some() { rv.push(1653001).unwrap(); } } for mission in missions.members_mut() { if mission["master_mission_id"].as_i64() != Some(1224003) { continue; } let was_completed = mission["status"] == 2; let progress = mission["progress"].as_i64().unwrap_or(0) + 1; mission["progress"] = progress.into(); if progress >= 5 { mission["status"] = (2).into(); if !was_completed { rv.push(1224003).unwrap(); } } break; } if update_mission_status(id, get_expire(now), true, false, 1, missions).is_some() { rv.push(id).unwrap(); } rv } pub fn routes(cfg: &mut web::ServiceConfig) { cfg.route("/item/use", web::post().to(use_item_req)); } async fn use_item_req(Session { key, body }: Session) -> impl Responder { let mut user = userdata::get_acc(&key); let item = &databases::ITEM_INFO[body["id"].to_string()]; let amount = body["amount"].as_i64().unwrap(); if item["effectType"].as_i32().unwrap() == 1 { lp_modification(&mut user, item["effectValue"].as_u64().unwrap() * (amount as u64), false); } else { println!("Use item not implemented for effect type {}", item["effectType"]); } use_item(&object!{ value: body["id"].as_i64().unwrap(), amount: 1, consumeType: 4 }, amount, &mut user); userdata::save_acc(&key, user.clone()); let mut used = object!{}; for it in user["item_list"].members() { if it["master_item_id"].as_i64() == body["id"].as_i64() { used = it.clone(); break; } } Api(Some(object!{ item_list: [used], stamina: user["stamina"].clone() })) }