GetAuctionItemInfo names the bidder on the player's own owner-list auctions, nil until the name lands, and 1 or nil on every other row (#259) (#492)
The auction house's Auctions tab now names the high bidder on each of your auctions, and shows "No Bids" only for an auction with none, as in 1.12.1; before, every auction read "No Bids". `GetAuctionItemInfo`'s eleventh value follows `0x4cee40`: on the owner list, for a row the player owns (`0x4cf12a`-`0x4cf142`), it is the bidder's name from the name cache (`0x4cf144`-`0x4cf174`), nil with no bid (`0x4cf150`) or while the name has not arrived; on every other row it is 1 when the player holds the bid, else nil (`0x4cf17b`-`0x4cf1a0`). A bidder name that arrives later refreshes the list with `AUCTION_OWNED_LIST_UPDATE`, the event the reference's callback `0x4cf2b0` fires. vmangos sends the bidder guid with each row (`AuctionHouseMgr.cpp:840`). Fixes #259 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a6cbb932fa
commit
476da9b2f0
@@ -11,8 +11,8 @@ use bevy::prelude::*;
|
||||
|
||||
use benilla_protocol::messages::{auction_filter, AuctionListEntry};
|
||||
use benilla_ui::script::{
|
||||
AuctionCategory, AuctionItemRow, AuctionListState, AuctionState, AuctionSubCategory, UiScript,
|
||||
BIDDER, LIST, OWNER,
|
||||
AuctionCategory, AuctionHighBidder, AuctionItemRow, AuctionListState, AuctionState,
|
||||
AuctionSubCategory, UiScript, BIDDER, LIST, OWNER,
|
||||
};
|
||||
|
||||
use crate::entities::ItemDisplays;
|
||||
@@ -87,8 +87,10 @@ pub(crate) struct AuctionRow {
|
||||
pub(crate) current_bid: u32,
|
||||
/// The time-left bucket, `1..=4`.
|
||||
pub(crate) time_left: u32,
|
||||
/// Whether the player holds the high bid.
|
||||
pub(crate) high_bidder: bool,
|
||||
/// Whether the player holds the high bid; the status sort's key.
|
||||
pub(crate) player_holds_bid: bool,
|
||||
/// `GetAuctionItemInfo`'s eleventh value, per [`high_bidder`].
|
||||
pub(crate) high_bidder: AuctionHighBidder,
|
||||
pub(crate) owner: Option<String>,
|
||||
pub(crate) link: Option<String>,
|
||||
}
|
||||
@@ -301,9 +303,41 @@ fn time_left_bucket(ms: u32) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
/// One wire entry as a display row, through the ask-once template and name caches; a `None`
|
||||
/// fills in when its answer lands. The link carries the row's enchant, property and suffix.
|
||||
/// `GetAuctionItemInfo`'s `highBidder` for a row of `list` (`0x4cf12a`–`0x4cf1a0`). On an
|
||||
/// `"owner"` row the player owns (`0x4cf131`–`0x4cf142` compare its owner guid with the player's),
|
||||
/// the bidder guid (vmangos `AuctionHouseMgr.cpp:840`) goes through the name cache: nil with no
|
||||
/// bid (`0x4cf150`) or until the asked name lands (`0x4cf16e`), whose arrival re-fires the owned
|
||||
/// list's update (`0x4cf2b0`), here the feed's diff. Any other row: `1` if the player holds the
|
||||
/// bid, else nil (`0x4cf17b`–`0x4cf1a0`).
|
||||
fn high_bidder(
|
||||
list: usize,
|
||||
entry: &AuctionListEntry,
|
||||
self_guid: Option<u64>,
|
||||
names: &NameCache,
|
||||
commands: &NetCommands,
|
||||
) -> AuctionHighBidder {
|
||||
let owned = list == OWNER && self_guid.is_some_and(|g| g == entry.owner_guid);
|
||||
if owned {
|
||||
if entry.bidder_guid == 0 {
|
||||
return AuctionHighBidder::Nil;
|
||||
}
|
||||
return names
|
||||
.resolve(entry.bidder_guid, commands)
|
||||
.map_or(AuctionHighBidder::Nil, |n| {
|
||||
AuctionHighBidder::Name(n.to_string())
|
||||
});
|
||||
}
|
||||
if self_guid.is_some_and(|g| g == entry.bidder_guid) {
|
||||
AuctionHighBidder::Player
|
||||
} else {
|
||||
AuctionHighBidder::Nil
|
||||
}
|
||||
}
|
||||
|
||||
/// One wire entry of `list` as a display row, through the ask-once template and name caches; a
|
||||
/// `None` fills in when its answer lands. The link carries the row's enchant, property and suffix.
|
||||
fn resolve_row(
|
||||
list: usize,
|
||||
entry: &AuctionListEntry,
|
||||
self_guid: Option<u64>,
|
||||
items: &Items,
|
||||
@@ -343,7 +377,8 @@ fn resolve_row(
|
||||
buyout: entry.buyout,
|
||||
current_bid: entry.current_bid,
|
||||
time_left: time_left_bucket(entry.time_left_ms),
|
||||
high_bidder: self_guid.is_some_and(|g| g == entry.bidder_guid),
|
||||
player_holds_bid: self_guid.is_some_and(|g| g == entry.bidder_guid),
|
||||
high_bidder: high_bidder(list, entry, self_guid, names, commands),
|
||||
owner: names
|
||||
.resolve(entry.owner_guid, commands)
|
||||
.map(str::to_string),
|
||||
@@ -390,7 +425,9 @@ pub(crate) fn categories(
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// List `list`'s rows in display order.
|
||||
fn rows_for(
|
||||
list: usize,
|
||||
slot: &AuctionListSlot,
|
||||
self_guid: Option<u64>,
|
||||
items: &Items,
|
||||
@@ -402,7 +439,7 @@ fn rows_for(
|
||||
let mut rows: Vec<AuctionRow> = slot
|
||||
.entries
|
||||
.iter()
|
||||
.map(|e| resolve_row(e, self_guid, items, icons, names, commands, rolls))
|
||||
.map(|e| resolve_row(list, e, self_guid, items, icons, names, commands, rolls))
|
||||
.collect();
|
||||
slot.sort.apply(&mut rows);
|
||||
rows
|
||||
@@ -422,7 +459,7 @@ fn to_script_row(r: &AuctionRow) -> AuctionItemRow {
|
||||
min_increment: r.min_increment,
|
||||
buyout_price: r.buyout,
|
||||
bid_amount: r.current_bid,
|
||||
high_bidder: r.high_bidder,
|
||||
high_bidder: r.high_bidder.clone(),
|
||||
owner: r.owner.clone(),
|
||||
time_left: r.time_left,
|
||||
link: r.link.clone(),
|
||||
@@ -512,6 +549,7 @@ fn feed_auction(
|
||||
let lists = [LIST, BIDDER, OWNER].map(|i| {
|
||||
let slot = &auction.lists[i];
|
||||
let rows = rows_for(
|
||||
i,
|
||||
slot,
|
||||
self_guid,
|
||||
&items,
|
||||
@@ -685,6 +723,7 @@ fn drain_auction(
|
||||
if !bids.is_empty() || !cancels.is_empty() {
|
||||
for bid in bids {
|
||||
let rows = rows_for(
|
||||
bid.list,
|
||||
&auction.lists[bid.list],
|
||||
self_guid,
|
||||
&items,
|
||||
@@ -703,6 +742,7 @@ fn drain_auction(
|
||||
}
|
||||
for index in cancels {
|
||||
let rows = rows_for(
|
||||
OWNER,
|
||||
&auction.lists[OWNER],
|
||||
self_guid,
|
||||
&items,
|
||||
@@ -900,6 +940,203 @@ mod tests {
|
||||
assert!(!open.sell_slot_taken);
|
||||
}
|
||||
|
||||
const ME: u64 = 0x77;
|
||||
const BIDDER_GUID: u64 = 0x99;
|
||||
|
||||
/// A wire row owned by `owner` with `bidder` on it (0 for no bid).
|
||||
fn entry(owner: u64, bidder: u64) -> AuctionListEntry {
|
||||
AuctionListEntry {
|
||||
auction_id: 1,
|
||||
item_entry: 2589,
|
||||
perm_enchant: 0,
|
||||
random_property_id: 0,
|
||||
suffix_factor: 0,
|
||||
count: 1,
|
||||
spell_charges: 0,
|
||||
owner_guid: owner,
|
||||
start_bid: 1000,
|
||||
min_increment: if bidder == 0 { 0 } else { 125 },
|
||||
buyout: 0,
|
||||
time_left_ms: 60_000,
|
||||
bidder_guid: bidder,
|
||||
current_bid: if bidder == 0 { 0 } else { 2500 },
|
||||
}
|
||||
}
|
||||
|
||||
fn name_queries(rx: &crossbeam_channel::Receiver<ClientCommand>) -> Vec<u64> {
|
||||
rx.try_iter()
|
||||
.filter_map(|c| match c {
|
||||
ClientCommand::NameQuery { guid } => Some(guid),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// The player's own auction names its bidder from the cache (`0x4cf144`–`0x4cf174`).
|
||||
#[test]
|
||||
fn an_owned_row_names_its_bidder() {
|
||||
let (tx, rx) = crossbeam_channel::unbounded();
|
||||
let commands = NetCommands(tx);
|
||||
let mut names = NameCache::default();
|
||||
names.insert_player(BIDDER_GUID, "Bidder".into(), None);
|
||||
assert_eq!(
|
||||
high_bidder(OWNER, &entry(ME, BIDDER_GUID), Some(ME), &names, &commands),
|
||||
AuctionHighBidder::Name("Bidder".into())
|
||||
);
|
||||
assert!(name_queries(&rx).is_empty(), "a cached name asks nothing");
|
||||
}
|
||||
|
||||
/// An unknown bidder is nil and asked for; no bid is nil and asks nothing (`0x4cf150`).
|
||||
#[test]
|
||||
fn an_owned_row_is_nil_until_the_name_lands_and_with_no_bid() {
|
||||
let (tx, rx) = crossbeam_channel::unbounded();
|
||||
let commands = NetCommands(tx);
|
||||
let mut names = NameCache::default();
|
||||
assert_eq!(
|
||||
high_bidder(OWNER, &entry(ME, BIDDER_GUID), Some(ME), &names, &commands),
|
||||
AuctionHighBidder::Nil
|
||||
);
|
||||
assert_eq!(
|
||||
name_queries(&rx),
|
||||
vec![BIDDER_GUID],
|
||||
"the name goes out once"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
high_bidder(OWNER, &entry(ME, 0), Some(ME), &names, &commands),
|
||||
AuctionHighBidder::Nil
|
||||
);
|
||||
assert!(name_queries(&rx).is_empty(), "no bidder, no query");
|
||||
|
||||
names.insert_player(BIDDER_GUID, "Bidder".into(), None);
|
||||
assert_eq!(
|
||||
high_bidder(OWNER, &entry(ME, BIDDER_GUID), Some(ME), &names, &commands),
|
||||
AuctionHighBidder::Name("Bidder".into())
|
||||
);
|
||||
}
|
||||
|
||||
/// Browse, Bids and an owner row the player does not own answer `1` or nil, never a name,
|
||||
/// even with the bidder's name cached (`0x4cf17b`–`0x4cf1a0`).
|
||||
#[test]
|
||||
fn other_rows_answer_one_or_nil() {
|
||||
let (tx, rx) = crossbeam_channel::unbounded();
|
||||
let commands = NetCommands(tx);
|
||||
let mut names = NameCache::default();
|
||||
names.insert_player(BIDDER_GUID, "Bidder".into(), None);
|
||||
const SELLER: u64 = 0x55;
|
||||
for list in [LIST, BIDDER] {
|
||||
assert_eq!(
|
||||
high_bidder(list, &entry(SELLER, ME), Some(ME), &names, &commands),
|
||||
AuctionHighBidder::Player
|
||||
);
|
||||
assert_eq!(
|
||||
high_bidder(
|
||||
list,
|
||||
&entry(SELLER, BIDDER_GUID),
|
||||
Some(ME),
|
||||
&names,
|
||||
&commands
|
||||
),
|
||||
AuctionHighBidder::Nil
|
||||
);
|
||||
assert_eq!(
|
||||
high_bidder(list, &entry(SELLER, 0), Some(ME), &names, &commands),
|
||||
AuctionHighBidder::Nil
|
||||
);
|
||||
// The player's own auction, seen on a browse page, is not named either.
|
||||
assert_eq!(
|
||||
high_bidder(list, &entry(ME, BIDDER_GUID), Some(ME), &names, &commands),
|
||||
AuctionHighBidder::Nil
|
||||
);
|
||||
}
|
||||
// The owner list gates on the row's owner, not the list alone (`0x4cf131`).
|
||||
assert_eq!(
|
||||
high_bidder(OWNER, &entry(SELLER, ME), Some(ME), &names, &commands),
|
||||
AuctionHighBidder::Player
|
||||
);
|
||||
assert_eq!(
|
||||
high_bidder(
|
||||
OWNER,
|
||||
&entry(SELLER, BIDDER_GUID),
|
||||
Some(ME),
|
||||
&names,
|
||||
&commands
|
||||
),
|
||||
AuctionHighBidder::Nil
|
||||
);
|
||||
assert!(name_queries(&rx).is_empty());
|
||||
}
|
||||
|
||||
/// A bidder's name landing after the owned list re-fires `AUCTION_OWNED_LIST_UPDATE` and the
|
||||
/// row then answers it (the `0x4cf2b0` callback fires event `0x1a9`).
|
||||
#[test]
|
||||
fn a_landed_bidder_name_refires_the_owned_list() {
|
||||
let mut app = App::new();
|
||||
app.init_resource::<AuctionOpen>()
|
||||
.init_resource::<Items>()
|
||||
.init_resource::<NameCache>()
|
||||
.init_resource::<Time>()
|
||||
.init_resource::<crate::ui_chat::ChatLog>()
|
||||
.init_resource::<crate::sound::MessageSounds>();
|
||||
let (tx, rx) = crossbeam_channel::unbounded();
|
||||
app.insert_resource(NetCommands(tx));
|
||||
app.world_mut()
|
||||
.resource_mut::<NameCache>()
|
||||
.insert_player(ME, "Seller".into(), None);
|
||||
let script = UiScript::new().unwrap();
|
||||
script
|
||||
.run(
|
||||
r#"
|
||||
SEEN = {}
|
||||
local f = CreateFrame("Frame")
|
||||
f:RegisterEvent("AUCTION_OWNED_LIST_UPDATE")
|
||||
f:SetScript("OnEvent", function() table.insert(SEEN, event) end)
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
app.insert_non_send_resource(script);
|
||||
app.world_mut().spawn((SelfPlayer, crate::net::Guid(ME)));
|
||||
{
|
||||
let mut open = app.world_mut().resource_mut::<AuctionOpen>();
|
||||
open.open(0x1234, 1);
|
||||
open.set_list(OWNER, vec![entry(ME, BIDDER_GUID)], 1);
|
||||
}
|
||||
|
||||
// One registered system, so its `VmMemo` locals persist from frame to frame.
|
||||
let feed = app.world_mut().register_system(feed_auction);
|
||||
let step = |app: &mut App| -> (Vec<String>, Option<String>) {
|
||||
app.world_mut().run_system(feed).unwrap();
|
||||
let mut s = app.world_mut().non_send_resource_mut::<UiScript>();
|
||||
s.resolve();
|
||||
let seen = s.eval::<Vec<String>>("return SEEN").unwrap();
|
||||
s.run("SEEN = {}").unwrap();
|
||||
let bidder = s
|
||||
.eval::<Option<String>>(
|
||||
r#"local _,_,_,_,_,_,_,_,_,_,h = GetAuctionItemInfo("owner", 1) return h"#,
|
||||
)
|
||||
.unwrap();
|
||||
(seen, bidder)
|
||||
};
|
||||
|
||||
// The window opens with the page; the bidder is unknown, so nil and asked for.
|
||||
let (_, bidder) = step(&mut app);
|
||||
assert_eq!(bidder, None);
|
||||
assert_eq!(name_queries(&rx), vec![BIDDER_GUID]);
|
||||
|
||||
let (seen, bidder) = step(&mut app);
|
||||
assert!(seen.is_empty(), "nothing new, nothing fired: {seen:?}");
|
||||
assert_eq!(bidder, None);
|
||||
|
||||
app.world_mut().resource_mut::<NameCache>().insert_player(
|
||||
BIDDER_GUID,
|
||||
"Bidder".into(),
|
||||
None,
|
||||
);
|
||||
let (seen, bidder) = step(&mut app);
|
||||
assert_eq!(bidder.as_deref(), Some("Bidder"));
|
||||
assert_eq!(seen, vec!["AUCTION_OWNED_LIST_UPDATE".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_unbid_row_shows_its_opening_price() {
|
||||
let unbid = AuctionRow {
|
||||
|
||||
@@ -69,7 +69,7 @@ fn compare_by(key: &str, a: &AuctionRow, b: &AuctionRow) -> Ordering {
|
||||
// Highest first, which also sinks a 0 (no buyout) to the bottom.
|
||||
"buyout" => b.buyout.cmp(&a.buyout),
|
||||
// Our own bids first; the reference also ranks another bidder's above no bid.
|
||||
"status" => b.high_bidder.cmp(&a.high_bidder),
|
||||
"status" => b.player_holds_bid.cmp(&a.player_holds_bid),
|
||||
"name" => name_of(a).cmp(name_of(b)),
|
||||
"seller" => owner_of(a).cmp(owner_of(b)),
|
||||
_ => Ordering::Equal,
|
||||
@@ -179,13 +179,13 @@ mod tests {
|
||||
let mut rows = vec![
|
||||
AuctionRow {
|
||||
quality: Some(1),
|
||||
high_bidder: false,
|
||||
player_holds_bid: false,
|
||||
name: Some("Common".into()),
|
||||
..Default::default()
|
||||
},
|
||||
AuctionRow {
|
||||
quality: Some(4),
|
||||
high_bidder: true,
|
||||
player_holds_bid: true,
|
||||
name: Some("Epic".into()),
|
||||
..Default::default()
|
||||
},
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
//! `AuctionState` and the app's own show and list events, and asserts what it paints and queues.
|
||||
|
||||
use benilla_ui::script::{
|
||||
AuctionCategory, AuctionItemRow, AuctionListState, AuctionState, AuctionSubCategory, UiScript,
|
||||
UnitState, BIDDER, LIST, OWNER,
|
||||
AuctionCategory, AuctionHighBidder, AuctionItemRow, AuctionListState, AuctionState,
|
||||
AuctionSubCategory, UiScript, UnitState, BIDDER, LIST, OWNER,
|
||||
};
|
||||
|
||||
mod common;
|
||||
@@ -78,7 +78,7 @@ fn row(name: &str, min_bid: u32, buyout: u32, bid: u32, owner: &str) -> AuctionI
|
||||
min_increment: if bid > 0 { 100 } else { 0 },
|
||||
buyout_price: buyout,
|
||||
bid_amount: bid,
|
||||
high_bidder: false,
|
||||
high_bidder: AuctionHighBidder::Nil,
|
||||
owner: Some(owner.to_string()),
|
||||
time_left: 4,
|
||||
link: Some("|cffffffff|Hitem:2589:0:0:0|h[Linen Cloth]|h|r".into()),
|
||||
@@ -279,6 +279,49 @@ fn the_auctions_tab_cannot_repaint_before_it_has_been_shown() {
|
||||
);
|
||||
}
|
||||
|
||||
/// The Auctions tab's High Bidder column prints `GetAuctionItemInfo`'s eleventh value, and
|
||||
/// `NO_BIDS` in red only for nil (`AuctionFrameAuctions_Update`): a bid-on auction names its
|
||||
/// bidder, as the reference answers on the owner list (`0x4cf144`–`0x4cf174`).
|
||||
#[test]
|
||||
fn the_auctions_tab_names_the_high_bidder() {
|
||||
let _data = benilla_formats::wow_data_or_skip!();
|
||||
let mut s = UiScript::new().unwrap();
|
||||
load_ui_with_classes(&mut s);
|
||||
seat_player(&mut s, 500_000);
|
||||
let mut bid_on = row("Linen Cloth", 1000, 5000, 2500, "Buyer");
|
||||
bid_on.auction_id = 1;
|
||||
bid_on.high_bidder = AuctionHighBidder::Name("Bidder".into());
|
||||
let mut unbid = row("Wool Cloth", 1000, 0, 0, "Buyer");
|
||||
unbid.auction_id = 2;
|
||||
let mut snapshot = state(Vec::new());
|
||||
snapshot.lists[OWNER] = AuctionListState {
|
||||
rows: vec![bid_on, unbid],
|
||||
total: 2,
|
||||
sort: Vec::new(),
|
||||
};
|
||||
s.set_auction(Some(snapshot));
|
||||
|
||||
s.fire_event("AUCTION_HOUSE_SHOW", vec![]);
|
||||
s.run("AuctionFrameTab_OnClick(3)").unwrap();
|
||||
s.fire_event("AUCTION_OWNED_LIST_UPDATE", vec![]);
|
||||
|
||||
assert_eq!(
|
||||
s.eval::<String>("return AuctionsButton1HighBidder:GetText()")
|
||||
.unwrap(),
|
||||
"Bidder",
|
||||
"a bid-on auction names its bidder, not No Bids"
|
||||
);
|
||||
assert!(
|
||||
s.eval::<bool>(
|
||||
"return AuctionsButton2HighBidder:GetText() \
|
||||
== RED_FONT_COLOR_CODE..NO_BIDS..FONT_COLOR_CODE_CLOSE"
|
||||
)
|
||||
.unwrap(),
|
||||
"an unbid auction reads No Bids in red"
|
||||
);
|
||||
assert!(s.errors().is_empty(), "clean repaint: {:?}", s.errors());
|
||||
}
|
||||
|
||||
/// Each pane assigns `page` only in its `OnShow`. `AuctionFrameBid` alone is declared
|
||||
/// `hidden="false"` (`Blizzard_AuctionUI.xml:960`), so it shows with the window on the first
|
||||
/// `AUCTION_HOUSE_SHOW` before tab 1 hides it; the Auctions pane has no `page` until tab 3.
|
||||
|
||||
@@ -59,8 +59,8 @@ pub struct AuctionItemRow {
|
||||
pub buyout_price: u32,
|
||||
/// The current high bid; 0 before any bid.
|
||||
pub bid_amount: u32,
|
||||
/// Whether the player holds the high bid: a flag, not a name.
|
||||
pub high_bidder: bool,
|
||||
/// `GetAuctionItemInfo`'s eleventh value, which the app picks per list and row.
|
||||
pub high_bidder: AuctionHighBidder,
|
||||
/// The seller's name; `None` while the name query is in flight.
|
||||
pub owner: Option<String>,
|
||||
/// The time-left bucket, `1..=4` (Short, Medium, Long, Very Long); 0 when not known yet.
|
||||
@@ -69,6 +69,20 @@ pub struct AuctionItemRow {
|
||||
pub link: Option<String>,
|
||||
}
|
||||
|
||||
/// `GetAuctionItemInfo`'s `highBidder` (`0x4cf12a`–`0x4cf1a0`): on an `"owner"` row the player
|
||||
/// owns, the bidder's name from the name cache; on any other row, `1` when the player holds the
|
||||
/// bid. Nil otherwise, which the stock Auctions tab prints as `NO_BIDS`.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub enum AuctionHighBidder {
|
||||
/// No bid, another player's bid on a row the player does not own, or a name not arrived.
|
||||
#[default]
|
||||
Nil,
|
||||
/// The player holds the bid on a row it does not own: `1`.
|
||||
Player,
|
||||
/// The bidder on the player's own auction, by name.
|
||||
Name(String),
|
||||
}
|
||||
|
||||
/// One of the three lists: the batch the server sent, the pre-cap match count and its sort stack.
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct AuctionListState {
|
||||
@@ -303,7 +317,7 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
})?,
|
||||
)?;
|
||||
|
||||
// GetAuctionItemInfo(type, index) → twelve values; `highBidder` is a flag, not the seller.
|
||||
// GetAuctionItemInfo(type, index) → twelve values; `highBidder` is a name, `1` or nil.
|
||||
g.set(
|
||||
"GetAuctionItemInfo",
|
||||
lua.create_function(|lua, (kind, index): (String, usize)| {
|
||||
@@ -354,7 +368,11 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
Value::Integer(i64::from(r.min_increment)),
|
||||
Value::Integer(i64::from(r.buyout_price)),
|
||||
Value::Integer(i64::from(r.bid_amount)),
|
||||
flag(r.high_bidder),
|
||||
match &r.high_bidder {
|
||||
AuctionHighBidder::Nil => Value::Nil,
|
||||
AuctionHighBidder::Player => flag(true),
|
||||
AuctionHighBidder::Name(n) => Value::String(lua.create_string(n)?),
|
||||
},
|
||||
opt_str(&r.owner)?,
|
||||
]))
|
||||
})?,
|
||||
@@ -918,6 +936,30 @@ mod tests {
|
||||
assert_eq!(n, 0, "zero values, not one nil");
|
||||
}
|
||||
|
||||
/// The eleventh value is the name, `1` or nil as the app picked it (`0x4cf12a`–`0x4cf1a0`).
|
||||
#[test]
|
||||
fn high_bidder_answers_a_name_a_one_or_nil() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
let mut state = page(&[1, 2, 3]);
|
||||
state.lists[LIST].rows[0].high_bidder = AuctionHighBidder::Name("Bidder".into());
|
||||
state.lists[LIST].rows[1].high_bidder = AuctionHighBidder::Player;
|
||||
s.set_auction(Some(state));
|
||||
let eleventh = |i: usize| {
|
||||
s.eval::<Value>(&format!(
|
||||
r#"local _,_,_,_,_,_,_,_,_,_,h = GetAuctionItemInfo("list", {i}) return h"#
|
||||
))
|
||||
.unwrap()
|
||||
};
|
||||
assert_eq!(
|
||||
eleventh(1)
|
||||
.as_string()
|
||||
.map(|v| v.to_str().unwrap().to_string()),
|
||||
Some("Bidder".into())
|
||||
);
|
||||
assert_eq!(eleventh(2), Value::Integer(1));
|
||||
assert_eq!(eleventh(3), Value::Nil);
|
||||
}
|
||||
|
||||
/// A 9-copper stack at 5% over 24 h deposits 0 here, where vmangos charges 5.
|
||||
#[test]
|
||||
fn the_deposit_is_the_clients_arithmetic_truncation_and_all() {
|
||||
|
||||
@@ -144,7 +144,7 @@ pub use addon::AddOnInfo;
|
||||
pub use addon_enable::EnableHash;
|
||||
pub use addon_message::{AddonDistribution, AddonSend};
|
||||
pub use auction::{
|
||||
AuctionBid, AuctionCategory, AuctionItemRow, AuctionListState, AuctionQuery,
|
||||
AuctionBid, AuctionCategory, AuctionHighBidder, AuctionItemRow, AuctionListState, AuctionQuery,
|
||||
AuctionStartRequest, AuctionState, AuctionSubCategory, BIDDER, LIST, OWNER, SORT_KEYS,
|
||||
};
|
||||
pub use aura::{AuraState, TrackingState};
|
||||
|
||||
Reference in New Issue
Block a user