screeps/game/
market.rs

1//! Access the in-game market to buy or sell resources.
2//!
3//! [Screeps documentation](https://docs.screeps.com/api/#Game-market)
4use js_sys::{Array, JsString, Object};
5use wasm_bindgen::prelude::*;
6
7use crate::{
8    constants::{MarketResourceType, OrderType, ResourceType},
9    enums::action_error_codes::game::market::*,
10    local::{LodashFilter, RoomName},
11    prelude::*,
12};
13
14#[wasm_bindgen]
15extern "C" {
16    #[wasm_bindgen(js_name = "market")]
17    type Market;
18
19    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, getter, js_name = credits)]
20    fn credits() -> f64;
21
22    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, getter, js_name = incomingTransactions)]
23    fn incoming_transactions() -> Array;
24
25    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, getter, js_name = outgoingTransactions)]
26    fn outgoing_transactions() -> Array;
27
28    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, getter, js_name = orders)]
29    fn orders() -> Object;
30
31    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, js_name = calcTransactionCost)]
32    fn calc_transaction_cost(amount: u32, room_1: &JsString, room_2: &JsString) -> u32;
33
34    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, js_name = cancelOrder)]
35    fn cancel_order(order_id: &JsString) -> i8;
36
37    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, js_name = changeOrderPrice)]
38    fn change_order_price(order_id: &JsString, new_price: f64) -> i8;
39
40    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, js_name = createOrder)]
41    fn create_order(order_parameters: &Object) -> i8;
42
43    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, js_name = deal)]
44    fn deal(order_id: &JsString, amount: u32, room_name: Option<&JsString>) -> i8;
45
46    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, js_name = extendOrder)]
47    fn extend_order(order_id: &JsString, add_amount: u32) -> i8;
48
49    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, js_name = getAllOrders)]
50    fn get_all_orders(filter: Option<&LodashFilter>) -> Array;
51
52    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, js_name = getHistory)]
53    fn get_history(resource: Option<ResourceType>) -> Option<Array>;
54
55    #[wasm_bindgen(js_namespace = ["Game"], js_class = "market", static_method_of = Market, js_name = getOrderById)]
56    fn get_order_by_id(order_id: &JsString) -> Option<Order>;
57}
58
59/// Your current credit balance.
60///
61/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.credits)
62pub fn credits() -> f64 {
63    Market::credits()
64}
65
66/// An [`Array`] of the last 100 [`Transaction`]s sent to your terminals.
67///
68/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.incomingTransactions)
69pub fn incoming_transactions() -> Vec<Transaction> {
70    Market::incoming_transactions()
71        .iter()
72        .map(Into::into)
73        .collect()
74}
75
76/// An [`Array`] of the last 100 [`Transaction`]s sent from your terminals.
77///
78/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.outgoingTransactions)
79pub fn outgoing_transactions() -> Vec<Transaction> {
80    Market::outgoing_transactions()
81        .iter()
82        .map(Into::into)
83        .collect()
84}
85
86/// An [`Object`] with your current buy and sell orders on the market, with
87/// order ID [`String`] keys and [`MyOrder`] values.
88///
89/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.orders)
90pub fn orders() -> JsHashMap<String, MyOrder> {
91    Market::orders().into()
92}
93
94/// An [`Object`] with your current buy and sell orders on the market, with
95/// order ID [`JsString`] keys and [`MyOrder`] values.
96///
97/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.orders)
98pub fn orders_jsstring() -> JsHashMap<JsString, MyOrder> {
99    Market::orders().into()
100}
101
102// todo maybe just implement a native version of this instead?
103/// Get the amount of energy required to send a given amount of any resource
104/// from one room to another.  See [`TERMINAL_SEND_COST_SCALE`] for
105/// information about the calculation.
106///
107/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.calcTransactionCost)
108///
109/// [`TERMINAL_SEND_COST_SCALE`]: crate::constants::TERMINAL_SEND_COST_SCALE
110pub fn calc_transaction_cost(amount: u32, room_1: &JsString, room_2: &JsString) -> u32 {
111    Market::calc_transaction_cost(amount, room_1, room_2)
112}
113
114/// Cancel one of your existing orders on the market, without refunding
115/// associated fees.
116///
117/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.cancelOrder)
118pub fn cancel_order(order_id: &JsString) -> Result<(), MarketCancelOrderErrorCode> {
119    MarketCancelOrderErrorCode::result_from_i8(Market::cancel_order(order_id))
120}
121
122/// Change the price of an existing order. If new_price is greater than old
123/// price, you will be charged
124/// (newPrice-oldPrice)*remainingAmount*[`MARKET_FEE`] credits.
125///
126/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.changeOrderPrice)
127///
128/// [`MARKET_FEE`]: crate::constants::MARKET_FEE
129pub fn change_order_price(
130    order_id: &JsString,
131    new_price: f64,
132) -> Result<(), ChangeOrderPriceErrorCode> {
133    ChangeOrderPriceErrorCode::result_from_i8(Market::change_order_price(order_id, new_price))
134}
135
136// todo type to serialize call options into
137/// Create a new order on the market.
138///
139/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.createOrder)
140pub fn create_order(order_parameters: &Object) -> Result<(), CreateOrderErrorCode> {
141    CreateOrderErrorCode::result_from_i8(Market::create_order(order_parameters))
142}
143
144/// Execute a trade on an order on the market. Name of a room with a
145/// terminal from which to send or receive resources is required unless the
146/// order is for an account resource.
147///
148/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.deal)
149pub fn deal(
150    order_id: &JsString,
151    amount: u32,
152    room_name: Option<RoomName>,
153) -> Result<(), DealErrorCode> {
154    DealErrorCode::result_from_i8(match room_name {
155        Some(r) => Market::deal(order_id, amount, Some(&r.into())),
156        None => Market::deal(order_id, amount, None),
157    })
158}
159
160/// Adds more capacity to one of your existing orders, offering or
161/// requesting more of the resource and incurring additional fees.
162///
163/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.extendOrder)
164pub fn extend_order(order_id: &JsString, add_amount: u32) -> Result<(), ExtendOrderErrorCode> {
165    ExtendOrderErrorCode::result_from_i8(Market::extend_order(order_id, add_amount))
166}
167
168/// Get all [`Order`]s on the market, with an optional
169/// filter. Note that a `resourceType` filter has special handling in the engine
170/// to be more efficient ([source]).
171///
172/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.getAllOrders)
173///
174/// [source]: https://github.com/screeps/engine/blob/f7a09e637c20689084fcf4eb43eacdfd51d31476/src/game/market.js#L37
175pub fn get_all_orders(filter: Option<&LodashFilter>) -> Vec<Order> {
176    Market::get_all_orders(filter)
177        .iter()
178        .map(Into::into)
179        .collect()
180}
181
182/// Get information about the price history on the market for the last 14
183/// days.
184///
185/// Gets information for a given resource, or for all resources if `None`.
186///
187/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.getHistory)
188pub fn get_history(resource: Option<ResourceType>) -> Vec<OrderHistoryRecord> {
189    Market::get_history(resource)
190        .map(|arr| arr.iter().map(Into::into).collect())
191        .unwrap_or_default()
192}
193
194/// Get an object with information about a specific order, in the same
195/// format as returned by [`get_all_orders`]
196///
197/// [Screeps documentation](https://docs.screeps.com/api/#Game.market.getOrderById)
198pub fn get_order_by_id(order_id: &str) -> Option<Order> {
199    let order_id: JsString = order_id.into();
200
201    Market::get_order_by_id(&order_id)
202}
203
204#[wasm_bindgen]
205extern "C" {
206    /// An object that represents an order on the market.
207    #[wasm_bindgen]
208    #[derive(Debug)]
209    pub type Order;
210    /// The order ID, which can be used to retrieve the order, or execute a
211    /// trade using [`MarketInfo::deal`].
212    #[wasm_bindgen(method, getter)]
213    pub fn id(this: &Order) -> JsString;
214    /// Tick of order creation, `None` for intershard orders.
215    #[wasm_bindgen(method, getter)]
216    pub fn created(this: &Order) -> Option<u32>;
217    // note: f64 due to https://github.com/rustwasm/wasm-bindgen/issues/4113
218    /// Timestamp of order creation in milliseconds since epoch.
219    #[wasm_bindgen(method, getter = createdTimestamp)]
220    pub fn created_timestamp(this: &Order) -> f64;
221    /// The [`OrderType`] of the order (whether the owner is looking to buy or
222    /// sell the given resource).
223    #[wasm_bindgen(method, getter = type)]
224    pub fn order_type(this: &Order) -> OrderType;
225    /// The resource type this order is for.
226    #[wasm_bindgen(method, getter = resourceType)]
227    pub fn resource_type(this: &Order) -> MarketResourceType;
228    /// Room that owns the order, `None` for intershard orders.
229    #[wasm_bindgen(method, getter = roomName)]
230    pub fn room_name(this: &Order) -> Option<JsString>;
231    /// The amount of resource currently ready to be traded (loaded in the
232    /// terminal).
233    #[wasm_bindgen(method, getter)]
234    pub fn amount(this: &Order) -> u32;
235    /// The total remaining amount of the resource to be traded before this
236    /// order has been completely filled and removed.
237    #[wasm_bindgen(method, getter = remainingAmount)]
238    pub fn remaining_amount(this: &Order) -> u32;
239    /// Price of the order per unit of the resource the order is for.
240    #[wasm_bindgen(method, getter)]
241    pub fn price(this: &Order) -> f64;
242}
243
244// todo docs
245#[wasm_bindgen]
246extern "C" {
247    #[wasm_bindgen]
248    #[derive(Debug)]
249    pub type Transaction;
250    #[wasm_bindgen(method, getter = transactionId)]
251    pub fn transaction_id(this: &Transaction) -> JsString;
252    #[wasm_bindgen(method, getter)]
253    pub fn time(this: &Transaction) -> u32;
254    /// The player who sent resources for this transaction, or `None` if it was
255    /// an NPC terminal
256    #[wasm_bindgen(method, getter)]
257    pub fn sender(this: &Transaction) -> Option<Player>;
258    /// The recipient of the resources for this transaction, or `None` if it was
259    /// an NPC terminal
260    #[wasm_bindgen(method, getter)]
261    pub fn recipient(this: &Transaction) -> Option<Player>;
262    #[wasm_bindgen(method, getter = resourceType)]
263    pub fn resource_type(this: &Transaction) -> ResourceType;
264    #[wasm_bindgen(method, getter)]
265    pub fn amount(this: &Transaction) -> u32;
266    /// The room that sent resources for this transaction
267    #[wasm_bindgen(method, getter)]
268    pub fn from(this: &Transaction) -> JsString;
269    /// The room that received resources in this transaction
270    #[wasm_bindgen(method, getter)]
271    pub fn to(this: &Transaction) -> JsString;
272    /// The description set in the sender's `StructureTerminal::send()` call, if
273    /// any
274    #[wasm_bindgen(method, getter)]
275    pub fn description(this: &Transaction) -> Option<JsString>;
276    /// Information about the market order that this transaction was fulfilling,
277    /// if any
278    #[wasm_bindgen(method, getter = order)]
279    pub fn order(this: &Transaction) -> Option<TransactionOrder>;
280}
281
282#[wasm_bindgen]
283extern "C" {
284    #[wasm_bindgen]
285    #[derive(Debug)]
286    pub type Player;
287    #[wasm_bindgen(method, getter)]
288    pub fn username(this: &Player) -> JsString;
289}
290
291#[wasm_bindgen]
292extern "C" {
293    #[wasm_bindgen]
294    #[derive(Debug)]
295    pub type TransactionOrder;
296    #[wasm_bindgen(method, getter)]
297    pub fn id(this: &TransactionOrder) -> JsString;
298    #[wasm_bindgen(method, getter = type)]
299    pub fn order_type(this: &TransactionOrder) -> OrderType;
300    #[wasm_bindgen(method, getter)]
301    pub fn price(this: &TransactionOrder) -> f64;
302}
303
304#[wasm_bindgen]
305extern "C" {
306    #[wasm_bindgen]
307    #[derive(Debug)]
308    pub type MyOrder;
309    #[wasm_bindgen(method, getter)]
310    pub fn id(this: &MyOrder) -> JsString;
311    /// Tick of order creation, `None` for intershard orders
312    #[wasm_bindgen(method, getter)]
313    pub fn created(this: &MyOrder) -> Option<u32>;
314    // note: f64 due to https://github.com/rustwasm/wasm-bindgen/issues/4113
315    /// Timestamp of order creation in milliseconds since epoch
316    #[wasm_bindgen(method, getter = createdTimestamp)]
317    pub fn created_timestamp(this: &MyOrder) -> f64;
318    #[wasm_bindgen(method, getter)]
319    pub fn active(this: &MyOrder) -> bool;
320    #[wasm_bindgen(method, getter = type)]
321    pub fn order_type(this: &MyOrder) -> OrderType;
322    #[wasm_bindgen(method, getter = resourceType)]
323    pub fn resource_type(this: &MyOrder) -> MarketResourceType;
324    /// Room that owns the order, `None` for intershard orders
325    #[wasm_bindgen(method, getter = roomName)]
326    pub fn room_name(this: &MyOrder) -> Option<JsString>;
327    #[wasm_bindgen(method, getter)]
328    pub fn amount(this: &MyOrder) -> u32;
329    #[wasm_bindgen(method, getter = remainingAmount)]
330    pub fn remaining_amount(this: &MyOrder) -> u32;
331    #[wasm_bindgen(method, getter = totalAmount)]
332    pub fn total_amount(this: &MyOrder) -> u32;
333    #[wasm_bindgen(method, getter)]
334    pub fn price(this: &MyOrder) -> f64;
335}
336
337impl JsCollectionFromValue for MyOrder {
338    fn from_value(val: JsValue) -> Self {
339        val.unchecked_into()
340    }
341}
342
343#[wasm_bindgen]
344extern "C" {
345    #[wasm_bindgen]
346    #[derive(Debug)]
347    pub type OrderHistoryRecord;
348    #[wasm_bindgen(method, getter = resourceType)]
349    pub fn resource_type(this: &OrderHistoryRecord) -> MarketResourceType;
350    /// Calendar date in string format, eg "2018-12-31"
351    #[wasm_bindgen(method, getter)]
352    pub fn date(this: &OrderHistoryRecord) -> JsString;
353    /// Total number of transactions for this resource on this day
354    #[wasm_bindgen(method, getter)]
355    pub fn transactions(this: &OrderHistoryRecord) -> u32;
356    /// Total volume of this resource bought and sold on this day
357    #[wasm_bindgen(method, getter)]
358    pub fn volume(this: &OrderHistoryRecord) -> u32;
359    #[wasm_bindgen(method, getter = avgPrice)]
360    pub fn avg_price(this: &OrderHistoryRecord) -> f64;
361    #[wasm_bindgen(method, getter = stddevPrice)]
362    pub fn stddev_price(this: &OrderHistoryRecord) -> f64;
363}