Add support for modular weapons
This commit is contained in:
parent
c52613fa20
commit
19d0aa7e44
10
README.md
10
README.md
@ -101,11 +101,17 @@ announcement = "I love cheese! I am at {location}."
|
|||||||
# values are the price in coins. You may type in-game "/give_item common.items." and press Tab to
|
# values are the price in coins. You may type in-game "/give_item common.items." and press Tab to
|
||||||
# explore the item definition IDs. Then just leave off the "common.items." part in this file.
|
# explore the item definition IDs. Then just leave off the "common.items." part in this file.
|
||||||
|
|
||||||
[buy_prices]
|
[buy_prices.simple]
|
||||||
"food.cheese" = 50
|
"food.cheese" = 50
|
||||||
|
|
||||||
[sell_prices]
|
[sell_prices.simple]
|
||||||
"consumable.potion_minor" = 150
|
"consumable.potion_minor" = 150
|
||||||
|
|
||||||
|
[sell_prices.modular]
|
||||||
|
material = "mineral.ingot.orichalcum"
|
||||||
|
primary = "sword.greatsword"
|
||||||
|
seconday = "sword.long"
|
||||||
|
price = 45_000
|
||||||
```
|
```
|
||||||
|
|
||||||
### Running
|
### Running
|
||||||
|
@ -1,63 +1,20 @@
|
|||||||
position = [17720.0, 14951.0, 237.0]
|
position = [17720.0, 14951.0, 237.0]
|
||||||
orientation = 0
|
orientation = 0
|
||||||
|
|
||||||
[buy_prices]
|
[buy_prices.simple]
|
||||||
"food.cheese" = 50
|
"food.cheese" = 50
|
||||||
|
|
||||||
[sell_prices]
|
[[buy_prices.modular]]
|
||||||
# Armor
|
material = "Iron"
|
||||||
"armor.misc.neck.carcanet_of_wrath" = 20_000
|
primary = "sword.greatsword"
|
||||||
|
secondary = "sword.long"
|
||||||
|
price = 1000
|
||||||
|
|
||||||
## Boreal Armor
|
[sell_prices.simple]
|
||||||
"armor.boreal.back" = 250_000
|
|
||||||
"armor.boreal.belt" = 250_000
|
|
||||||
"armor.boreal.chest" = 250_000
|
|
||||||
"armor.boreal.foot" = 250_000
|
|
||||||
"armor.boreal.hand" = 250_000
|
|
||||||
"armor.boreal.pants" = 250_000
|
|
||||||
"armor.boreal.shoulder" = 250_000
|
|
||||||
"armor.misc.head.boreal_warhelm" = 450_000
|
|
||||||
|
|
||||||
# Hats
|
|
||||||
"armor.misc.head.cat_capuche" = 750_000
|
|
||||||
"armor.misc.head.hare_hat" = 150_000
|
|
||||||
"armor.misc.head.winged_coronet" = 40_000
|
|
||||||
"calendar.christmas.armor.misc.head.woolly_wintercap" = 250_000
|
|
||||||
|
|
||||||
# Crafting
|
|
||||||
"crafting_ing.alkahest" = 6_000
|
|
||||||
"crafting_ing.brinestone" = 2_000
|
|
||||||
"crafting_ing.coral_branch" = 1_000
|
|
||||||
"crafting_ing.hide.dragon_scale" = 5_000
|
|
||||||
"crafting_ing.dwarven_battery" = 40_000
|
|
||||||
"log.eldwood" = 3_000
|
|
||||||
"mineral.ingot.orichalcum" = 8_000
|
|
||||||
"mineral.ore.ancient_gold" = 10_000
|
|
||||||
|
|
||||||
# Potions
|
|
||||||
"consumable.potion_minor" = 150
|
"consumable.potion_minor" = 150
|
||||||
|
|
||||||
# Gliders
|
[[sell_prices.modular]]
|
||||||
"glider.skullgrin" = 20_000
|
material = "Iron"
|
||||||
|
primary = "sword.greatsword"
|
||||||
# Recipes
|
secondary = "sword.long"
|
||||||
"recipes.armor.brinestone" = 8_000
|
price = 1000
|
||||||
"recipes.equipment.advanced" = 8_000
|
|
||||||
"recipes.unique.mindflayer_spellbag" = 10_000
|
|
||||||
"recipes.unique.abyssal_gorget" = 6_000
|
|
||||||
|
|
||||||
# Instruments
|
|
||||||
"tool.instruments.icy_talharpa" = 500_000
|
|
||||||
"tool.instruments.steeltonguedrum" = 300_000
|
|
||||||
|
|
||||||
# Legendary Weapons
|
|
||||||
"weapons.axe.parashu" = 100_000
|
|
||||||
"weapons.sword.caladbolg" = 150_000
|
|
||||||
"weapons.staff.laevateinn" = 60_000
|
|
||||||
"weapons.hammer.mjolnir" = 150_000
|
|
||||||
"weapons.sceptre.caduceus" = 150_000
|
|
||||||
|
|
||||||
# Lanterns
|
|
||||||
"boss_drops.lantern" = 40_000 # Magic Lantern
|
|
||||||
"lantern.blue_0" = 20_000
|
|
||||||
"lantern.geode_purp" = 20_000
|
|
||||||
|
167
src/bot.rs
167
src/bot.rs
@ -16,7 +16,10 @@ use veloren_common::{
|
|||||||
clock::Clock,
|
clock::Clock,
|
||||||
comp::{
|
comp::{
|
||||||
invite::InviteKind,
|
invite::InviteKind,
|
||||||
item::{ItemDefinitionId, ItemDefinitionIdOwned, ItemDesc, ItemI18n, MaterialStatManifest},
|
item::{
|
||||||
|
modular, ItemBase, ItemDef, ItemDefinitionId, ItemDefinitionIdOwned, ItemDesc,
|
||||||
|
ItemI18n, MaterialStatManifest,
|
||||||
|
},
|
||||||
slot::InvSlotId,
|
slot::InvSlotId,
|
||||||
tool::AbilityMap,
|
tool::AbilityMap,
|
||||||
ChatType, ControllerInputs, Item, Ori, Pos,
|
ChatType, ControllerInputs, Item, Ori, Pos,
|
||||||
@ -30,7 +33,10 @@ use veloren_common::{
|
|||||||
};
|
};
|
||||||
use veloren_common_net::sync::WorldSyncExt;
|
use veloren_common_net::sync::WorldSyncExt;
|
||||||
|
|
||||||
|
use crate::PriceList;
|
||||||
|
|
||||||
const COINS: &str = "common.items.utility.coins";
|
const COINS: &str = "common.items.utility.coins";
|
||||||
|
const COINS_ID: ItemDefinitionId = ItemDefinitionId::Simple(Cow::Borrowed(COINS));
|
||||||
|
|
||||||
/// An active connection to the Veloren server that will attempt to run every time the `tick`
|
/// An active connection to the Veloren server that will attempt to run every time the `tick`
|
||||||
/// function is called.
|
/// function is called.
|
||||||
@ -50,8 +56,8 @@ pub struct Bot {
|
|||||||
item_i18n: ItemI18n,
|
item_i18n: ItemI18n,
|
||||||
localization: LocalizationHandle,
|
localization: LocalizationHandle,
|
||||||
|
|
||||||
buy_prices: HashMap<String, u32>,
|
buy_prices: PriceList,
|
||||||
sell_prices: HashMap<String, u32>,
|
sell_prices: PriceList,
|
||||||
trade_mode: TradeMode,
|
trade_mode: TradeMode,
|
||||||
|
|
||||||
previous_offer: Option<(HashMap<InvSlotId, u32>, HashMap<InvSlotId, u32>)>,
|
previous_offer: Option<(HashMap<InvSlotId, u32>, HashMap<InvSlotId, u32>)>,
|
||||||
@ -72,8 +78,8 @@ impl Bot {
|
|||||||
password: &str,
|
password: &str,
|
||||||
character: &str,
|
character: &str,
|
||||||
admins: Vec<String>,
|
admins: Vec<String>,
|
||||||
buy_prices: HashMap<String, u32>,
|
buy_prices: PriceList,
|
||||||
sell_prices: HashMap<String, u32>,
|
sell_prices: PriceList,
|
||||||
position: Option<[f32; 3]>,
|
position: Option<[f32; 3]>,
|
||||||
orientation: Option<f32>,
|
orientation: Option<f32>,
|
||||||
announcement: Option<String>,
|
announcement: Option<String>,
|
||||||
@ -547,9 +553,9 @@ impl Bot {
|
|||||||
inventories.get(them).ok_or("Failed to find inventory")?,
|
inventories.get(them).ok_or("Failed to find inventory")?,
|
||||||
);
|
);
|
||||||
|
|
||||||
let coins = ItemDefinitionIdOwned::Simple(COINS.to_string());
|
let coins_owned = COINS_ID.to_owned();
|
||||||
let get_my_coins = my_inventory.get_slot_of_item_by_def_id(&coins);
|
let get_my_coins = my_inventory.get_slot_of_item_by_def_id(&coins_owned);
|
||||||
let get_their_coins = their_inventory.get_slot_of_item_by_def_id(&coins);
|
let get_their_coins = their_inventory.get_slot_of_item_by_def_id(&coins_owned);
|
||||||
|
|
||||||
let (mut my_offered_coin_amount, mut their_offered_coin_amount) = (0, 0);
|
let (mut my_offered_coin_amount, mut their_offered_coin_amount) = (0, 0);
|
||||||
let my_offered_items_value =
|
let my_offered_items_value =
|
||||||
@ -557,18 +563,20 @@ impl Bot {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.fold(0, |acc: i32, (slot_id, quantity)| {
|
.fold(0, |acc: i32, (slot_id, quantity)| {
|
||||||
if let Some(item) = my_inventory.get(*slot_id) {
|
if let Some(item) = my_inventory.get(*slot_id) {
|
||||||
let item_id = item.persistence_item_id();
|
let item_id = item.item_definition_id();
|
||||||
|
|
||||||
let item_value = if item_id == COINS {
|
let item_value = if item_id == COINS_ID {
|
||||||
my_offered_coin_amount = *quantity as i32;
|
my_offered_coin_amount = *quantity as i32;
|
||||||
|
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
self.sell_prices
|
self.sell_prices
|
||||||
|
.simple
|
||||||
.get(&item_id)
|
.get(&item_id)
|
||||||
.map(|int| *int as i32)
|
.map(|int| *int as i32)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
self.buy_prices
|
self.buy_prices
|
||||||
|
.simple
|
||||||
.get(&item_id)
|
.get(&item_id)
|
||||||
.map(|int| 0 - *int as i32)
|
.map(|int| 0 - *int as i32)
|
||||||
.unwrap_or(i32::MIN)
|
.unwrap_or(i32::MIN)
|
||||||
@ -585,18 +593,20 @@ impl Bot {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.fold(0, |acc: i32, (slot_id, quantity)| {
|
.fold(0, |acc: i32, (slot_id, quantity)| {
|
||||||
if let Some(item) = their_inventory.get(*slot_id) {
|
if let Some(item) = their_inventory.get(*slot_id) {
|
||||||
let item_id = item.persistence_item_id();
|
let item_id = item.item_definition_id();
|
||||||
|
|
||||||
let item_value = if item_id == COINS {
|
let item_value = if item_id == COINS_ID {
|
||||||
their_offered_coin_amount = *quantity as i32;
|
their_offered_coin_amount = *quantity as i32;
|
||||||
|
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
self.buy_prices
|
self.buy_prices
|
||||||
|
.simple
|
||||||
.get(&item_id)
|
.get(&item_id)
|
||||||
.map(|int| *int as i32)
|
.map(|int| *int as i32)
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
self.sell_prices
|
self.sell_prices
|
||||||
|
.simple
|
||||||
.get(&item_id)
|
.get(&item_id)
|
||||||
.map(|int| 0 - *int as i32)
|
.map(|int| 0 - *int as i32)
|
||||||
.unwrap_or(0)
|
.unwrap_or(0)
|
||||||
@ -613,13 +623,13 @@ impl Bot {
|
|||||||
|
|
||||||
for (slot_id, amount) in my_offer {
|
for (slot_id, amount) in my_offer {
|
||||||
let item = my_inventory.get(*slot_id).ok_or("Failed to get item")?;
|
let item = my_inventory.get(*slot_id).ok_or("Failed to get item")?;
|
||||||
let item_id = item.persistence_item_id();
|
let item_id = item.item_definition_id();
|
||||||
|
|
||||||
if item_id == COINS {
|
if item_id == COINS_ID {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.sell_prices.contains_key(&item_id) {
|
if !self.sell_prices.simple.contains_key(&item_id) {
|
||||||
my_item_to_remove = Some((slot_id, amount));
|
my_item_to_remove = Some((slot_id, amount));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -628,13 +638,13 @@ impl Bot {
|
|||||||
|
|
||||||
for (slot_id, amount) in their_offer {
|
for (slot_id, amount) in their_offer {
|
||||||
let item = their_inventory.get(*slot_id).ok_or("Failed to get item")?;
|
let item = their_inventory.get(*slot_id).ok_or("Failed to get item")?;
|
||||||
let item_id = item.persistence_item_id();
|
let item_id = item.item_definition_id();
|
||||||
|
|
||||||
if item_id == COINS {
|
if item_id == COINS_ID {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.buy_prices.contains_key(&item_id) {
|
if !self.buy_prices.simple.contains_key(&item_id) {
|
||||||
their_item_to_remove = Some((slot_id, amount));
|
their_item_to_remove = Some((slot_id, amount));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -741,8 +751,8 @@ impl Bot {
|
|||||||
.to_string();
|
.to_string();
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
|
|
||||||
for (item_id, price) in &self.buy_prices {
|
for (item_id, price) in &self.buy_prices.simple {
|
||||||
let item_name = self.get_item_name(item_id);
|
let item_name = self.get_item_name(item_id.as_ref());
|
||||||
|
|
||||||
if item_name.to_lowercase().contains(&search_term) {
|
if item_name.to_lowercase().contains(&search_term) {
|
||||||
log::info!("Sending price info on {item_name} to {player_name}");
|
log::info!("Sending price info on {item_name} to {player_name}");
|
||||||
@ -756,16 +766,60 @@ impl Bot {
|
|||||||
);
|
);
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
} else if item_id.contains(&search_term) {
|
|
||||||
let short_id = item_id.splitn(3, '.').last().unwrap_or_default();
|
|
||||||
|
|
||||||
log::info!("Sending price info on {short_id} to {player_name}");
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(item_id_string) = item_id.as_ref().itemdef_id() {
|
||||||
|
if item_id_string.to_lowercase().contains(&search_term) {
|
||||||
|
log::info!("Sending price info on {item_id_string} to {player_name}");
|
||||||
|
|
||||||
self.client.send_command(
|
self.client.send_command(
|
||||||
"tell".to_string(),
|
"tell".to_string(),
|
||||||
vec![
|
vec![
|
||||||
player_name.clone(),
|
player_name.clone(),
|
||||||
format!("Buying {short_id} for {price} coins."),
|
format!("Buying {item_id_string} for {price} coins."),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for modular_item_listing in &self.buy_prices.modular {
|
||||||
|
let material = ItemDefinitionId::Simple(Cow::Borrowed(
|
||||||
|
modular_item_listing
|
||||||
|
.material
|
||||||
|
.asset_identifier()
|
||||||
|
.ok_or(format!(
|
||||||
|
"{:?} is not a valid material for modular crafted items",
|
||||||
|
modular_item_listing.material
|
||||||
|
))?,
|
||||||
|
));
|
||||||
|
let primary = modular_item_listing.primary.as_ref();
|
||||||
|
let secondary = ItemDefinitionId::Compound {
|
||||||
|
// This unwrap is safe because the ItemDefinitionId is always Simple.
|
||||||
|
simple_base: primary.itemdef_id().unwrap(),
|
||||||
|
components: vec![material],
|
||||||
|
};
|
||||||
|
let item_id = ItemDefinitionId::Modular {
|
||||||
|
pseudo_base: "veloren.core.pseudo_items.modular.tool",
|
||||||
|
components: vec![secondary],
|
||||||
|
};
|
||||||
|
let item_name = self.get_item_name(item_id);
|
||||||
|
|
||||||
|
if item_name.to_lowercase().contains(&search_term) {
|
||||||
|
log::info!("Sending price info on {item_name} to {player_name}");
|
||||||
|
|
||||||
|
self.client.send_command(
|
||||||
|
"tell".to_string(),
|
||||||
|
vec![
|
||||||
|
player_name.clone(),
|
||||||
|
format!(
|
||||||
|
"Buying {item_name} for {} coins.",
|
||||||
|
modular_item_listing.price
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -773,8 +827,8 @@ impl Bot {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (item_id, price) in &self.sell_prices {
|
for (item_id, price) in &self.sell_prices.simple {
|
||||||
let item_name = self.get_item_name(item_id);
|
let item_name = self.get_item_name(item_id.as_ref());
|
||||||
|
|
||||||
if item_name.to_lowercase().contains(&search_term) {
|
if item_name.to_lowercase().contains(&search_term) {
|
||||||
log::info!("Sending price info on {item_name} to {player_name}");
|
log::info!("Sending price info on {item_name} to {player_name}");
|
||||||
@ -788,16 +842,60 @@ impl Bot {
|
|||||||
);
|
);
|
||||||
|
|
||||||
found = true;
|
found = true;
|
||||||
} else if item_id.contains(&search_term) {
|
|
||||||
let short_id = item_id.splitn(3, '.').last().unwrap_or_default();
|
|
||||||
|
|
||||||
log::info!("Sending price info on {short_id} to {player_name}");
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(item_id_string) = item_id.as_ref().itemdef_id() {
|
||||||
|
if item_id_string.to_lowercase().contains(&search_term) {
|
||||||
|
log::info!("Sending price info on {item_id_string} to {player_name}");
|
||||||
|
|
||||||
self.client.send_command(
|
self.client.send_command(
|
||||||
"tell".to_string(),
|
"tell".to_string(),
|
||||||
vec![
|
vec![
|
||||||
player_name.clone(),
|
player_name.clone(),
|
||||||
format!("Selling {short_id} for {price} coins."),
|
format!("Selling {item_id_string} for {price} coins."),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for modular_item_listing in &self.sell_prices.modular {
|
||||||
|
let material = ItemDefinitionId::Simple(Cow::Borrowed(
|
||||||
|
modular_item_listing
|
||||||
|
.material
|
||||||
|
.asset_identifier()
|
||||||
|
.ok_or(format!(
|
||||||
|
"{:?} is not a valid material for modular crafted items",
|
||||||
|
modular_item_listing.material
|
||||||
|
))?,
|
||||||
|
));
|
||||||
|
let primary = modular_item_listing.primary.as_ref();
|
||||||
|
let secondary = ItemDefinitionId::Compound {
|
||||||
|
// This unwrap is safe because the ItemDefinitionId is always Simple.
|
||||||
|
simple_base: primary.itemdef_id().unwrap(),
|
||||||
|
components: vec![material],
|
||||||
|
};
|
||||||
|
let item_id = ItemDefinitionId::Modular {
|
||||||
|
pseudo_base: "veloren.core.pseudo_items.modular.tool",
|
||||||
|
components: vec![secondary],
|
||||||
|
};
|
||||||
|
let item_name = self.get_item_name(item_id);
|
||||||
|
|
||||||
|
if item_name.to_lowercase().contains(&search_term) {
|
||||||
|
log::info!("Sending price info on {item_name} to {player_name}");
|
||||||
|
|
||||||
|
self.client.send_command(
|
||||||
|
"tell".to_string(),
|
||||||
|
vec![
|
||||||
|
player_name.clone(),
|
||||||
|
format!(
|
||||||
|
"Selling {item_name} for {} coins.",
|
||||||
|
modular_item_listing.price
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -866,12 +964,9 @@ impl Bot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the name of an item from its id.
|
/// Gets the name of an item from its id.
|
||||||
fn get_item_name(&self, item_id: &str) -> String {
|
fn get_item_name(&self, item_id: ItemDefinitionId) -> String {
|
||||||
let item = Item::new_from_item_definition_id(
|
let item =
|
||||||
ItemDefinitionId::Simple(Cow::Borrowed(item_id)),
|
Item::new_from_item_definition_id(item_id, &self.ability_map, &self.material_manifest)
|
||||||
&self.ability_map,
|
|
||||||
&self.material_manifest,
|
|
||||||
)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (item_name_i18n_id, _) = item.i18n(&self.item_i18n);
|
let (item_name_i18n_id, _) = item.i18n(&self.item_i18n);
|
||||||
|
|
||||||
|
175
src/main.rs
175
src/main.rs
@ -2,24 +2,165 @@
|
|||||||
|
|
||||||
mod bot;
|
mod bot;
|
||||||
|
|
||||||
use std::{env::var, fs::read_to_string};
|
use std::{borrow::Cow, env::var, fs::read_to_string, str::FromStr};
|
||||||
|
|
||||||
use bot::Bot;
|
use bot::Bot;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{de::Visitor, Deserialize};
|
||||||
|
use veloren_common::comp::item::{ItemDefinitionId, ItemDefinitionIdOwned, Material};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
pub struct PriceList {
|
||||||
|
pub simple: HashMap<ItemDefinitionIdOwned, u32>,
|
||||||
|
pub modular: Vec<ModularItemPrice>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for PriceList {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_map(PriceListVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PriceListVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for PriceListVisitor {
|
||||||
|
type Value = PriceList;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("a map with simple and/or modular keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: serde::de::MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut simple = None;
|
||||||
|
let mut modular = None;
|
||||||
|
|
||||||
|
while let Some(key) = map.next_key::<String>()? {
|
||||||
|
match key.as_str() {
|
||||||
|
"simple" => {
|
||||||
|
let simple_prices_with_item_string =
|
||||||
|
map.next_value::<HashMap<String, u32>>()?;
|
||||||
|
let simple_prices_with_item_id = simple_prices_with_item_string
|
||||||
|
.into_iter()
|
||||||
|
.map(|(mut key, value)| {
|
||||||
|
key.insert_str(0, "common.items.");
|
||||||
|
|
||||||
|
(ItemDefinitionIdOwned::Simple(key), value)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
simple = Some(simple_prices_with_item_id);
|
||||||
|
}
|
||||||
|
"modular" => {
|
||||||
|
modular = Some(map.next_value()?);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(serde::de::Error::unknown_field(
|
||||||
|
&key,
|
||||||
|
&["simple", "modular"],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(PriceList {
|
||||||
|
simple: simple.ok_or_else(|| serde::de::Error::missing_field("simple"))?,
|
||||||
|
modular: modular.ok_or_else(|| serde::de::Error::missing_field("modular"))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct ModularItemPrice {
|
||||||
|
pub material: Material,
|
||||||
|
pub primary: ItemDefinitionIdOwned,
|
||||||
|
pub secondary: ItemDefinitionIdOwned,
|
||||||
|
pub price: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for ModularItemPrice {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
deserializer.deserialize_map(ModularPriceVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ModularPriceVisitor;
|
||||||
|
|
||||||
|
impl<'de> Visitor<'de> for ModularPriceVisitor {
|
||||||
|
type Value = ModularItemPrice;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
formatter.write_str("a map with material, primary, secondary and price keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
|
||||||
|
where
|
||||||
|
A: serde::de::MapAccess<'de>,
|
||||||
|
{
|
||||||
|
let mut material = None;
|
||||||
|
let mut primary = None;
|
||||||
|
let mut secondary = None;
|
||||||
|
let mut price = None;
|
||||||
|
|
||||||
|
while let Some(key) = map.next_key::<String>()? {
|
||||||
|
match key.as_str() {
|
||||||
|
"material" => {
|
||||||
|
material = Some(map.next_value()?);
|
||||||
|
}
|
||||||
|
"primary" => {
|
||||||
|
let mut primary_string = map.next_value::<String>()?;
|
||||||
|
|
||||||
|
primary_string.insert_str(0, "common.items.modular.weapon.primary.");
|
||||||
|
|
||||||
|
primary = Some(ItemDefinitionIdOwned::Simple(primary_string));
|
||||||
|
}
|
||||||
|
"secondary" => {
|
||||||
|
let mut secondary_string = map.next_value::<String>()?;
|
||||||
|
|
||||||
|
secondary_string.insert_str(0, "common.items.modular.weapon.secondary.");
|
||||||
|
|
||||||
|
secondary = Some(ItemDefinitionIdOwned::Simple(secondary_string));
|
||||||
|
}
|
||||||
|
"price" => {
|
||||||
|
price = Some(map.next_value()?);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(serde::de::Error::unknown_field(
|
||||||
|
&key,
|
||||||
|
&["material", "primary", "secondary", "price"],
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ModularItemPrice {
|
||||||
|
material: material.ok_or_else(|| serde::de::Error::missing_field("material"))?,
|
||||||
|
primary: primary.ok_or_else(|| serde::de::Error::missing_field("primary"))?,
|
||||||
|
secondary: secondary.ok_or_else(|| serde::de::Error::missing_field("secondary"))?,
|
||||||
|
price: price.ok_or_else(|| serde::de::Error::missing_field("price"))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
struct Config {
|
struct Config {
|
||||||
pub game_server: Option<String>,
|
pub game_server: Option<String>,
|
||||||
pub auth_server: Option<String>,
|
pub auth_server: Option<String>,
|
||||||
pub position: Option<[f32; 3]>,
|
pub position: Option<[f32; 3]>,
|
||||||
pub orientation: Option<f32>,
|
pub orientation: Option<f32>,
|
||||||
pub announcement: Option<String>,
|
pub announcement: Option<String>,
|
||||||
pub buy_prices: HashMap<String, u32>,
|
pub buy_prices: PriceList,
|
||||||
pub sell_prices: HashMap<String, u32>,
|
pub sell_prices: PriceList,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct Secrets {
|
pub struct Secrets {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
@ -50,24 +191,6 @@ fn main() {
|
|||||||
let auth_server = config
|
let auth_server = config
|
||||||
.auth_server
|
.auth_server
|
||||||
.unwrap_or("https://auth.veloren.net".to_string());
|
.unwrap_or("https://auth.veloren.net".to_string());
|
||||||
let buy_prices_with_full_id = config
|
|
||||||
.buy_prices
|
|
||||||
.into_iter()
|
|
||||||
.map(|(mut item_id, price)| {
|
|
||||||
item_id.insert_str(0, "common.items.");
|
|
||||||
|
|
||||||
(item_id, price)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let sell_prices_with_full_id = config
|
|
||||||
.sell_prices
|
|
||||||
.into_iter()
|
|
||||||
.map(|(mut item_id, price)| {
|
|
||||||
item_id.insert_str(0, "common.items.");
|
|
||||||
|
|
||||||
(item_id, price)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let mut bot = Bot::new(
|
let mut bot = Bot::new(
|
||||||
game_server,
|
game_server,
|
||||||
&auth_server,
|
&auth_server,
|
||||||
@ -75,8 +198,8 @@ fn main() {
|
|||||||
&secrets.password,
|
&secrets.password,
|
||||||
&secrets.character,
|
&secrets.character,
|
||||||
secrets.admins,
|
secrets.admins,
|
||||||
buy_prices_with_full_id,
|
config.buy_prices,
|
||||||
sell_prices_with_full_id,
|
config.sell_prices,
|
||||||
config.position,
|
config.position,
|
||||||
config.orientation,
|
config.orientation,
|
||||||
config.announcement,
|
config.announcement,
|
||||||
|
Loading…
Reference in New Issue
Block a user