mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Allow fast stacking into and out of a trade with {ctrl,shift} click.
Shift click goes 1 at a time, Ctrl click automatically balances the trade w.r.t. that quantity.
This commit is contained in:
parent
aafdc209d3
commit
0122dca3c3
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Trades now display item prices in tooltips.
|
||||
- Admin designated build areas
|
||||
- Indicator text to collectable terrain sprites
|
||||
- You can now autorequest exact change by ctrl-clicking in a trade, and can quick-add individual items with shift-click.
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
comp::inventory::{slot::InvSlotId, Inventory},
|
||||
comp::inventory::{slot::InvSlotId, trade_pricing::TradePricing, Inventory},
|
||||
terrain::BiomeKind,
|
||||
uid::Uid,
|
||||
};
|
||||
@ -349,6 +349,35 @@ pub struct SitePrices {
|
||||
pub values: HashMap<Good, f32>,
|
||||
}
|
||||
|
||||
impl SitePrices {
|
||||
pub fn balance(
|
||||
&self,
|
||||
offers: &[HashMap<InvSlotId, u32>; 2],
|
||||
inventories: &[Option<ReducedInventory>; 2],
|
||||
who: usize,
|
||||
reduce: bool,
|
||||
) -> f32 {
|
||||
offers[who]
|
||||
.iter()
|
||||
.map(|(slot, amount)| {
|
||||
inventories[who]
|
||||
.as_ref()
|
||||
.map(|ri| {
|
||||
ri.inventory.get(slot).map(|item| {
|
||||
let (material, factor) = TradePricing::get_material(&item.name);
|
||||
self.values.get(&material).cloned().unwrap_or_default()
|
||||
* factor
|
||||
* (*amount as f32)
|
||||
* if reduce { material.trade_margin() } else { 1.0 }
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ReducedInventoryItem {
|
||||
pub name: String,
|
||||
|
@ -7,7 +7,7 @@ use common::{
|
||||
compass::{Direction, Distance},
|
||||
dialogue::{MoodContext, MoodState, Subject},
|
||||
group,
|
||||
inventory::{item::ItemTag, slot::EquipSlot, trade_pricing::TradePricing},
|
||||
inventory::{item::ItemTag, slot::EquipSlot},
|
||||
invite::{InviteKind, InviteResponse},
|
||||
item::{
|
||||
tool::{ToolKind, UniqueKind},
|
||||
@ -1150,33 +1150,9 @@ impl<'a> AgentData<'a> {
|
||||
let (tradeid, pending, prices, inventories) = *boxval;
|
||||
if agent.trading {
|
||||
let who: usize = if agent.trading_issuer { 0 } else { 1 };
|
||||
let balance = |who: usize, reduce: bool| {
|
||||
pending.offers[who]
|
||||
.iter()
|
||||
.map(|(slot, amount)| {
|
||||
inventories[who]
|
||||
.as_ref()
|
||||
.map(|ri| {
|
||||
ri.inventory.get(slot).map(|item| {
|
||||
let (material, factor) =
|
||||
TradePricing::get_material(&item.name);
|
||||
prices
|
||||
.values
|
||||
.get(&material)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
* factor
|
||||
* (*amount as f32)
|
||||
* if reduce { material.trade_margin() } else { 1.0 }
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
.unwrap_or_default()
|
||||
})
|
||||
.sum()
|
||||
};
|
||||
let balance0: f32 = balance(1 - who, true);
|
||||
let balance1: f32 = balance(who, false);
|
||||
let balance0: f32 =
|
||||
prices.balance(&pending.offers, &inventories, 1 - who, true);
|
||||
let balance1: f32 = prices.balance(&pending.offers, &inventories, who, false);
|
||||
tracing::debug!("UpdatePendingTrade({}, {})", balance0, balance1);
|
||||
if balance0 >= balance1 {
|
||||
// If the trade is favourable to us, only send an accept message if we're
|
||||
|
@ -30,6 +30,7 @@ use conrod_core::{
|
||||
};
|
||||
|
||||
use crate::hud::slots::SlotKind;
|
||||
use specs::Entity as EcsEntity;
|
||||
use std::sync::Arc;
|
||||
use vek::Vec2;
|
||||
|
||||
@ -78,6 +79,7 @@ pub struct InventoryScroller<'a> {
|
||||
on_right: bool,
|
||||
item_tooltip: &'a ItemTooltip<'a>,
|
||||
playername: String,
|
||||
entity: EcsEntity,
|
||||
is_us: bool,
|
||||
inventory: &'a Inventory,
|
||||
bg_ids: &'a BackgroundIds,
|
||||
@ -99,6 +101,7 @@ impl<'a> InventoryScroller<'a> {
|
||||
on_right: bool,
|
||||
item_tooltip: &'a ItemTooltip<'a>,
|
||||
playername: String,
|
||||
entity: EcsEntity,
|
||||
is_us: bool,
|
||||
inventory: &'a Inventory,
|
||||
bg_ids: &'a BackgroundIds,
|
||||
@ -118,6 +121,7 @@ impl<'a> InventoryScroller<'a> {
|
||||
on_right,
|
||||
item_tooltip,
|
||||
playername,
|
||||
entity,
|
||||
is_us,
|
||||
inventory,
|
||||
bg_ids,
|
||||
@ -274,6 +278,7 @@ impl<'a> InventoryScroller<'a> {
|
||||
InventorySlot {
|
||||
slot: pos,
|
||||
ours: self.is_us,
|
||||
entity: self.entity,
|
||||
},
|
||||
[40.0; 2],
|
||||
)
|
||||
@ -616,6 +621,7 @@ impl<'a> Widget for Bag<'a> {
|
||||
true,
|
||||
&item_tooltip,
|
||||
self.stats.name.to_string(),
|
||||
self.client.entity(),
|
||||
true,
|
||||
&inventory,
|
||||
&state.bg_ids,
|
||||
|
@ -66,19 +66,23 @@ use common::{
|
||||
combat,
|
||||
comp::{
|
||||
self,
|
||||
inventory::trade_pricing::TradePricing,
|
||||
item::{tool::ToolKind, ItemDesc, MaterialStatManifest, Quality},
|
||||
skills::{Skill, SkillGroupKind},
|
||||
BuffKind, Item,
|
||||
},
|
||||
outcome::Outcome,
|
||||
terrain::TerrainChunk,
|
||||
trade::TradeAction,
|
||||
trade::{ReducedInventory, TradeAction},
|
||||
uid::Uid,
|
||||
util::srgba_to_linear,
|
||||
vol::RectRasterableVol,
|
||||
};
|
||||
use common_base::span;
|
||||
use common_net::msg::{world_msg::SiteId, Notification, PresenceKind};
|
||||
use common_net::{
|
||||
msg::{world_msg::SiteId, Notification, PresenceKind},
|
||||
sync::WorldSyncExt,
|
||||
};
|
||||
use conrod_core::{
|
||||
text::cursor::Index,
|
||||
widget::{self, Button, Image, Text},
|
||||
@ -2901,11 +2905,13 @@ impl Hud {
|
||||
}
|
||||
|
||||
// Maintain slot manager
|
||||
for event in self.slot_manager.maintain(ui_widgets) {
|
||||
'slot_events: for event in self.slot_manager.maintain(ui_widgets) {
|
||||
use comp::slot::Slot;
|
||||
use slots::{InventorySlot, SlotKind::*};
|
||||
let to_slot = |slot_kind| match slot_kind {
|
||||
Inventory(InventorySlot { slot, ours: true }) => Some(Slot::Inventory(slot)),
|
||||
Inventory(InventorySlot {
|
||||
slot, ours: true, ..
|
||||
}) => Some(Slot::Inventory(slot)),
|
||||
Inventory(InventorySlot { ours: false, .. }) => None,
|
||||
Equip(e) => Some(Slot::Equip(e)),
|
||||
Hotbar(_) => None,
|
||||
@ -2920,8 +2926,12 @@ impl Hud {
|
||||
slot_b: b,
|
||||
bypass_dialog: false,
|
||||
});
|
||||
} else if let (Inventory(InventorySlot { slot, ours: true }), Hotbar(h)) =
|
||||
(a, b)
|
||||
} else if let (
|
||||
Inventory(InventorySlot {
|
||||
slot, ours: true, ..
|
||||
}),
|
||||
Hotbar(h),
|
||||
) = (a, b)
|
||||
{
|
||||
self.hotbar.add_inventory_link(h, slot);
|
||||
events.push(Event::ChangeHotbarState(Box::new(self.hotbar.to_owned())));
|
||||
@ -3042,6 +3052,127 @@ impl Hud {
|
||||
});
|
||||
}
|
||||
},
|
||||
slot::Event::Request {
|
||||
slot,
|
||||
auto_quantity,
|
||||
} => {
|
||||
if let Some((_, trade, prices)) = client.pending_trade() {
|
||||
let ecs = client.state().ecs();
|
||||
let inventories = ecs.read_component::<common::comp::Inventory>();
|
||||
let get_inventory = |uid: Uid| {
|
||||
if let Some(entity) = ecs.entity_from_uid(uid.0) {
|
||||
inventories.get(entity)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let mut r_inventories = [None, None];
|
||||
for (i, party) in trade.parties.iter().enumerate() {
|
||||
match get_inventory(*party) {
|
||||
Some(inventory) => {
|
||||
r_inventories[i] = Some(ReducedInventory::from(inventory))
|
||||
},
|
||||
None => continue 'slot_events,
|
||||
};
|
||||
}
|
||||
let who = match ecs
|
||||
.uid_from_entity(client.entity())
|
||||
.and_then(|uid| trade.which_party(uid))
|
||||
{
|
||||
Some(who) => who,
|
||||
None => continue 'slot_events,
|
||||
};
|
||||
let do_auto_quantity =
|
||||
|inventory: &common::comp::Inventory,
|
||||
slot,
|
||||
ours,
|
||||
remove,
|
||||
quantity: &mut u32| {
|
||||
if let Some(prices) = prices {
|
||||
let balance0 =
|
||||
prices.balance(&trade.offers, &r_inventories, who, true);
|
||||
let balance1 = prices.balance(
|
||||
&trade.offers,
|
||||
&r_inventories,
|
||||
1 - who,
|
||||
false,
|
||||
);
|
||||
if let Some(item) = inventory.get(slot) {
|
||||
let (material, factor) =
|
||||
TradePricing::get_material(item.item_definition_id());
|
||||
let mut unit_price = prices
|
||||
.values
|
||||
.get(&material)
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
* factor;
|
||||
if ours {
|
||||
unit_price *= material.trade_margin();
|
||||
}
|
||||
let mut float_delta = if ours ^ remove {
|
||||
(balance1 - balance0) / unit_price
|
||||
} else {
|
||||
(balance0 - balance1) / unit_price
|
||||
};
|
||||
if ours ^ remove {
|
||||
float_delta = float_delta.ceil();
|
||||
} else {
|
||||
float_delta = float_delta.floor();
|
||||
}
|
||||
*quantity = float_delta.max(0.0) as u32;
|
||||
}
|
||||
}
|
||||
};
|
||||
match slot {
|
||||
Inventory(i) => {
|
||||
if let Some(inventory) = inventories.get(i.entity) {
|
||||
let mut quantity = 1;
|
||||
if auto_quantity {
|
||||
do_auto_quantity(
|
||||
inventory,
|
||||
i.slot,
|
||||
i.ours,
|
||||
false,
|
||||
&mut quantity,
|
||||
);
|
||||
let inv_quantity = i.amount(inventory).unwrap_or(1);
|
||||
quantity = quantity.min(inv_quantity);
|
||||
}
|
||||
|
||||
events.push(Event::TradeAction(TradeAction::AddItem {
|
||||
item: i.slot,
|
||||
quantity,
|
||||
ours: i.ours,
|
||||
}));
|
||||
}
|
||||
},
|
||||
Trade(t) => {
|
||||
if let Some(inventory) = inventories.get(t.entity) {
|
||||
if let Some(invslot) = t.invslot {
|
||||
let mut quantity = 1;
|
||||
if auto_quantity {
|
||||
do_auto_quantity(
|
||||
inventory,
|
||||
invslot,
|
||||
t.ours,
|
||||
true,
|
||||
&mut quantity,
|
||||
);
|
||||
let inv_quantity = t.amount(inventory).unwrap_or(1);
|
||||
quantity = quantity.min(inv_quantity);
|
||||
}
|
||||
events.push(Event::TradeAction(TradeAction::RemoveItem {
|
||||
item: invslot,
|
||||
quantity,
|
||||
ours: t.ours,
|
||||
}));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
self.hotbar.maintain_ability3(client);
|
||||
@ -3096,6 +3227,7 @@ impl Hud {
|
||||
if let Some(slots::SlotKind::Inventory(InventorySlot {
|
||||
slot: i,
|
||||
ours: true,
|
||||
..
|
||||
})) = slot_manager.selected()
|
||||
{
|
||||
hotbar.add_inventory_link(slot, i);
|
||||
|
@ -31,6 +31,7 @@ pub type SlotManager = slot::SlotManager<SlotKind>;
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct InventorySlot {
|
||||
pub slot: InvSlotId,
|
||||
pub entity: EcsEntity,
|
||||
pub ours: bool,
|
||||
}
|
||||
|
||||
@ -85,6 +86,7 @@ impl SlotKey<Inventory, ItemImgs> for TradeSlot {
|
||||
InventorySlot {
|
||||
slot: inv_id,
|
||||
ours: self.ours,
|
||||
entity: self.entity,
|
||||
}
|
||||
.image_key(source)
|
||||
})
|
||||
@ -96,6 +98,7 @@ impl SlotKey<Inventory, ItemImgs> for TradeSlot {
|
||||
InventorySlot {
|
||||
slot: inv_id,
|
||||
ours: self.ours,
|
||||
entity: self.entity,
|
||||
}
|
||||
.amount(source)
|
||||
})
|
||||
|
@ -305,6 +305,7 @@ impl<'a> Trade<'a> {
|
||||
false,
|
||||
&item_tooltip,
|
||||
name,
|
||||
entity,
|
||||
false,
|
||||
&inventory,
|
||||
&state.bg_ids,
|
||||
|
@ -2,7 +2,7 @@
|
||||
use crate::hud::animate_by_pulse;
|
||||
use conrod_core::{
|
||||
builder_methods, image,
|
||||
input::state::mouse,
|
||||
input::{keyboard::ModifierKey, state::mouse},
|
||||
text::font,
|
||||
widget::{self, Image, Text},
|
||||
widget_ids, Color, Colorable, Positionable, Sizeable, Widget, WidgetCommon,
|
||||
@ -122,6 +122,8 @@ pub enum Event<K> {
|
||||
SplitDragged(K, K),
|
||||
// Clicked while selected
|
||||
Used(K),
|
||||
// {Shift,Ctrl}-clicked
|
||||
Request { slot: K, auto_quantity: bool },
|
||||
}
|
||||
// Handles interactions with slots
|
||||
pub struct SlotManager<S: SumSlot> {
|
||||
@ -369,6 +371,30 @@ where
|
||||
};
|
||||
}
|
||||
|
||||
// Translate ctrl-clicks to stack-requests and shift-clicks to
|
||||
// individual-requests
|
||||
if let Some(click) = input.clicks().left().next() {
|
||||
if !matches!(self.state, ManagerState::Dragging(_, _, _, _)) {
|
||||
match click.modifiers {
|
||||
ModifierKey::CTRL => {
|
||||
self.events.push(Event::Request {
|
||||
slot,
|
||||
auto_quantity: true,
|
||||
});
|
||||
self.state = ManagerState::Idle;
|
||||
},
|
||||
ModifierKey::SHIFT => {
|
||||
self.events.push(Event::Request {
|
||||
slot,
|
||||
auto_quantity: false,
|
||||
});
|
||||
self.state = ManagerState::Idle;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use on right click if not dragging
|
||||
if input.clicks().right().next().is_some() {
|
||||
match self.state {
|
||||
|
Loading…
Reference in New Issue
Block a user