Fix price bug (thanks Hrom!); Add cost notifying

This commit is contained in:
Jeff 2024-07-10 22:06:12 -04:00
parent c731c99208
commit fc83c2b37e
4 changed files with 136 additions and 119 deletions

View File

@ -16,6 +16,7 @@ Create a "secrets.toml" file:
# secrets.toml # secrets.toml
username = "my_username" username = "my_username"
password = "my_password" password = "my_password"
character = "my_character"
``` ```
Then create a secret to pass the file securely to the container. Then create a secret to pass the file securely to the container.
@ -29,7 +30,6 @@ You will also need a "config.toml":
```toml ```toml
# config.toml # config.toml
character = "my_character"
position = [0.0, 0.0, 0.0] # Change these to the desired X, Y, Z coordinates position = [0.0, 0.0, 0.0] # Change these to the desired X, Y, Z coordinates
orientation = "West" orientation = "West"

View File

@ -1,4 +1,3 @@
character = "crabobot_test"
position = [17726.0, 14957.0, 237.0] position = [17726.0, 14957.0, 237.0]
orientation = "West" orientation = "West"
@ -6,6 +5,8 @@ orientation = "West"
"common.items.food.cheese" = 50 "common.items.food.cheese" = 50
[sell_prices] [sell_prices]
# Boreal Armor
"common.items.armor.boreal.back" = 250_000 "common.items.armor.boreal.back" = 250_000
"common.items.armor.boreal.belt" = 250_000 "common.items.armor.boreal.belt" = 250_000
"common.items.armor.boreal.chest" = 250_000 "common.items.armor.boreal.chest" = 250_000
@ -13,31 +14,44 @@ orientation = "West"
"common.items.armor.boreal.hand" = 250_000 "common.items.armor.boreal.hand" = 250_000
"common.items.armor.boreal.pants" = 250_000 "common.items.armor.boreal.pants" = 250_000
"common.items.armor.boreal.shoulder" = 250_000 "common.items.armor.boreal.shoulder" = 250_000
"common.items.armor.misc.head.boreal_warhelm" = 450_000 "common.items.armor.misc.head.boreal_warhelm" = 450_000
"common.items.armor.misc.head.cat_capuche" = 600_000
# Hats
"common.items.armor.misc.head.cat_capuche" = 700_000
"common.items.armor.misc.head.hare_hat" = 100_000 "common.items.armor.misc.head.hare_hat" = 100_000
"common.items.armor.misc.head.winged_coronet" = 40_000 "common.items.armor.misc.head.winged_coronet" = 40_000
"common.items.calendar.christmas.armor.misc.head.woolly_wintercap" = 800_000
# Crafting
"common.items.crafting_ing.brinestone" = 2000 "common.items.crafting_ing.brinestone" = 2000
"common.items.crafting_ing.coral_brach" = 1000 "common.items.crafting_ing.coral_brach" = 1000
"common.items.crafting_ing.dwarven_battery" = 40000 "common.items.crafting_ing.dwarven_battery" = 40000
"common.items.consumable.potion_minor" = 150
"common.items.glider.skullgrin" = 20_000
"common.items.log.eldwood" = 3000 "common.items.log.eldwood" = 3000
"common.items.mineral.ingot.orichalcum" = 8000 "common.items.mineral.ingot.orichalcum" = 8000
# Potions
"common.items.consumable.potion_minor" = 150
# Gliders
"common.items.glider.skullgrin" = 20_000
# Recipes
"common.items.recipes.equipment.advanced" = 8000
"common.items.recipes.unique.mindflayer_spellbag" = 10000
"common.items.recipes.unique.abyssal_gorget" = 6000
# Instruments
"common.items.tool.instruments.icy_talharpa" = 500_000 "common.items.tool.instruments.icy_talharpa" = 500_000
"common.items.tool.instruments.steeltonguedrum" = 300_000 "common.items.tool.instruments.steeltonguedrum" = 300_000
# Legendary Weapons
"common.items.weapons.axe.parashu" = 100_000 "common.items.weapons.axe.parashu" = 100_000
"common.items.weapons.sword.caladbolg" = 100_000 "common.items.weapons.sword.caladbolg" = 150_000
"common.items.weapons.staff.laevateinn" = 60_000 "common.items.weapons.staff.laevateinn" = 60_000
"common.items.weapons.hammer.mjolnir" = 100_000 "common.items.weapons.hammer.mjolnir" = 150_000
"common.items.weapons.sceptre.caduceus" = 150_000 "common.items.weapons.sceptre.caduceus" = 150_000
"common.items.lantern.geode_purple" = 20_000 # Lanterns
"common.items.boss_drops.lantern" = 40_000 # Magic Lantern
"common.items.lantern.blue_0" = 20_000
"common.items.lantern.geode_purp" = 20_000

View File

@ -33,10 +33,10 @@ pub struct Bot {
sell_prices: HashMap<String, u32>, sell_prices: HashMap<String, u32>,
trade_mode: TradeMode, trade_mode: TradeMode,
is_player_notified: bool,
last_action: Instant, last_action: Instant,
last_announcement: Instant, last_announcement: Instant,
last_ouch: Instant, last_ouch: Instant,
last_cost_difference: i32,
} }
impl Bot { impl Bot {
@ -95,10 +95,10 @@ impl Bot {
buy_prices, buy_prices,
sell_prices, sell_prices,
trade_mode: TradeMode::Trade, trade_mode: TradeMode::Trade,
is_player_notified: false,
last_action: now, last_action: now,
last_announcement: now, last_announcement: now,
last_ouch: now, last_ouch: now,
last_cost_difference: 0,
}) })
} }
@ -135,7 +135,7 @@ impl Bot {
if self.last_announcement.elapsed() > Duration::from_secs(1200) { if self.last_announcement.elapsed() > Duration::from_secs(1200) {
self.client.send_command( self.client.send_command(
"region".to_string(), "region".to_string(),
vec!["I'm a bot. You can trade with me or use /say or /tell to check prices: 'price [item_name]' or 'prices'.".to_string()], vec!["I'm a bot. You can trade with me or use /say or /tell to check prices: 'price [item_name]'".to_string()],
); );
self.last_announcement = Instant::now(); self.last_announcement = Instant::now();
@ -158,58 +158,12 @@ impl Bot {
}; };
let content = message.content().as_plain().unwrap_or_default(); let content = message.content().as_plain().unwrap_or_default();
let (command, item_name) = content.split_once(' ').unwrap_or((content, "")); let (command, item_name) = content.split_once(' ').unwrap_or((content, ""));
let item_name = item_name.to_lowercase();
match command { match command {
"price" => { "price" => {
let player_name = self self.send_price_info(&sender, &item_name)?;
.find_name(&sender)
.ok_or("Failed to find player name")?
.to_string();
let mut found = false;
for (item_id, price) in &self.buy_prices {
if item_id.contains(item_name) {
log::debug!("Sending price info on {item_id} to {player_name}");
self.client.send_command(
"tell".to_string(),
vec![
player_name.clone(),
format!("I buy {item_id} for {price} coins."),
],
);
found = true;
}
}
for (item_id, price) in &self.sell_prices {
if item_id.contains(item_name) {
log::debug!("Sending price info on {item_id} to {player_name}");
self.client.send_command(
"tell".to_string(),
vec![
player_name.clone(),
format!("I sell {item_id} for {price} coins."),
],
);
found = true;
}
}
if !found {
self.client.send_command(
"tell".to_string(),
vec![
player_name.clone(),
format!("I don't have a price for that item."),
],
);
}
} }
"prices" => self.send_all_price_info(&sender)?,
"take" => { "take" => {
if !self.client.is_trading() { if !self.client.is_trading() {
self.trade_mode = TradeMode::Take; self.trade_mode = TradeMode::Take;
@ -249,7 +203,6 @@ impl Bot {
log::info!("Completed trade: {result:?}"); log::info!("Completed trade: {result:?}");
if let TradeMode::Take = self.trade_mode { if let TradeMode::Take = self.trade_mode {
self.is_player_notified = false;
self.trade_mode = TradeMode::Trade; self.trade_mode = TradeMode::Trade;
} }
@ -289,12 +242,6 @@ impl Bot {
.ok_or("Failed to get offer index")?; .ok_or("Failed to get offer index")?;
let their_offer_index = if my_offer_index == 0 { 1 } else { 0 }; let their_offer_index = if my_offer_index == 0 { 1 } else { 0 };
if !self.is_player_notified {
self.send_all_price_info(&trade.parties[their_offer_index])?;
self.is_player_notified = true;
}
if trade.is_empty_trade() { if trade.is_empty_trade() {
return Ok(()); return Ok(());
} }
@ -311,16 +258,14 @@ impl Bot {
) )
}; };
let inventories = self.client.inventories(); let inventories = self.client.inventories();
let my_inventory = inventories.get(self.client.entity()).unwrap(); let my_inventory = inventories
let my_coins = my_inventory .get(self.client.entity())
.get_slot_of_item_by_def_id(&ItemDefinitionIdOwned::Simple(COINS.to_string())) .ok_or("Failed to find inventory")?;
.ok_or("Failed to find coins".to_string())?; let get_my_coins = my_inventory
let their_inventory = inventories .get_slot_of_item_by_def_id(&ItemDefinitionIdOwned::Simple(COINS.to_string()));
.get(them) let their_inventory = inventories.get(them).ok_or("Failed to find inventory")?;
.ok_or("Failed to find inventory".to_string())?; let get_their_coins = their_inventory
let their_coins = their_inventory .get_slot_of_item_by_def_id(&ItemDefinitionIdOwned::Simple(COINS.to_string()));
.get_slot_of_item_by_def_id(&ItemDefinitionIdOwned::Simple(COINS.to_string()))
.ok_or("Failed to find coins")?;
let (mut their_offered_coin_amount, mut my_offered_coin_amount) = (0, 0); let (mut their_offered_coin_amount, mut my_offered_coin_amount) = (0, 0);
let their_offered_items_value = let their_offered_items_value =
their_offer their_offer
@ -439,6 +384,13 @@ impl Bot {
return Ok(()); return Ok(());
} }
let (my_coins, their_coins) =
if let (Some(mine), Some(theirs)) = (get_my_coins, get_their_coins) {
(mine, theirs)
} else {
return Ok(());
};
let difference = their_offered_items_value - my_offered_items_value; let difference = their_offered_items_value - my_offered_items_value;
// If the trade is balanced // If the trade is balanced
@ -447,7 +399,6 @@ impl Bot {
self.client self.client
.perform_trade_action(TradeAction::Accept(trade.phase)); .perform_trade_action(TradeAction::Accept(trade.phase));
return Ok(());
// If they are offering more // If they are offering more
} else if difference.is_positive() { } else if difference.is_positive() {
// If they are offering coins // If they are offering coins
@ -488,6 +439,22 @@ impl Bot {
} }
} }
if difference != self.last_cost_difference {
let their_name = self
.find_name(&trade.parties[their_offer_index])
.ok_or("Failed to find player name")?
.to_string();
log::debug!("Sending cost to {their_name}");
self.client.send_command(
"tell".to_string(),
vec![their_name, format!("My offer: {my_offered_items_value}. Your offer: {their_offered_items_value}.")],
);
}
self.last_cost_difference = difference;
Ok(()) Ok(())
} }
@ -511,23 +478,60 @@ impl Bot {
Ok(()) Ok(())
} }
fn send_all_price_info(&mut self, target: &Uid) -> Result<(), String> { fn send_price_info(&mut self, target: &Uid, item_name: &str) -> Result<(), String> {
let player_name = self let player_name = self
.find_name(target) .find_name(target)
.ok_or("Failed to find player name")? .ok_or("Failed to find player name")?
.to_string(); .to_string();
let mut found = false;
self.client.send_command( for (item_id, price) in &self.buy_prices {
"tell".to_string(), if item_id.contains(item_name) {
vec![ let short_id = item_id.splitn(3, '.').last().unwrap_or_default();
player_name.clone(),
format!("Buy prices: {:?}", self.buy_prices), log::debug!("Sending price info on {short_id} to {player_name}");
],
); self.client.send_command(
self.client.send_command( "tell".to_string(),
"tell".to_string(), vec![
vec![player_name, format!("Sell prices: {:?}", self.sell_prices)], player_name.clone(),
); format!("Buying {short_id} for {price} coins."),
],
);
found = true;
}
}
for (item_id, price) in &self.sell_prices {
if item_id.contains(item_name) {
let short_id = item_id.splitn(3, '.').last().unwrap_or_default();
log::debug!("Sending price info on {short_id} to {player_name}");
self.client.send_command(
"tell".to_string(),
vec![
player_name.clone(),
format!("Selling {short_id} for {price} coins."),
],
);
found = true;
}
}
if !found {
log::debug!("Found no price for \"{item_name}\" for {player_name}");
self.client.send_command(
"tell".to_string(),
vec![
player_name.clone(),
format!("I don't have a price for that item."),
],
);
}
Ok(()) Ok(())
} }
@ -535,34 +539,13 @@ impl Bot {
fn find_name<'a>(&'a self, uid: &Uid) -> Option<&'a String> { fn find_name<'a>(&'a self, uid: &Uid) -> Option<&'a String> {
self.client.player_list().iter().find_map(|(id, info)| { self.client.player_list().iter().find_map(|(id, info)| {
if id == uid { if id == uid {
if let Some(character_info) = &info.character { return Some(&info.player_alias);
return Some(&character_info.name);
}
} }
None None
}) })
} }
fn _find_uid<'a>(&'a self, name: &str) -> Option<&'a Uid> {
self.client.player_list().iter().find_map(|(id, info)| {
if info.player_alias == name {
Some(id)
} else {
None
}
})
}
fn _find_uuid(&self, name: &str) -> Option<String> {
self.client.player_list().iter().find_map(|(_, info)| {
if info.player_alias == name {
Some(info.uuid.to_string())
} else {
None
}
})
}
fn handle_position_and_orientation(&mut self) -> Result<(), String> { fn handle_position_and_orientation(&mut self) -> Result<(), String> {
let current_position = self.client.current::<Pos>(); let current_position = self.client.current::<Pos>();
@ -601,6 +584,26 @@ impl Bot {
Ok(()) Ok(())
} }
fn _find_uid<'a>(&'a self, name: &str) -> Option<&'a Uid> {
self.client.player_list().iter().find_map(|(id, info)| {
if info.player_alias == name {
Some(id)
} else {
None
}
})
}
fn _find_uuid(&self, name: &str) -> Option<String> {
self.client.player_list().iter().find_map(|(_, info)| {
if info.player_alias == name {
Some(info.uuid.to_string())
} else {
None
}
})
}
} }
enum TradeMode { enum TradeMode {

View File

@ -9,11 +9,11 @@ use serde::{Deserialize, Serialize};
pub struct Secrets { pub struct Secrets {
pub username: String, pub username: String,
pub password: String, pub password: String,
pub character: String,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct Config { struct Config {
pub character: String,
pub buy_prices: HashMap<String, u32>, pub buy_prices: HashMap<String, u32>,
pub sell_prices: HashMap<String, u32>, pub sell_prices: HashMap<String, u32>,
pub position: [f32; 3], pub position: [f32; 3],
@ -42,7 +42,7 @@ fn main() {
let mut bot = Bot::new( let mut bot = Bot::new(
&secrets.username, &secrets.username,
&secrets.password, &secrets.password,
&config.character, &secrets.character,
config.buy_prices, config.buy_prices,
config.sell_prices, config.sell_prices,
config.position, config.position,