载入中...
搜索中...
未找到
ShopTransaction.cpp
浏览该文件的文档.
2
4#include "rpg/GameState.h"
5#include "rpg/ShopCatalogue.h"
6
7#include <cmath>
8#include <utility>
9
10namespace eve::rpg {
11namespace {
12
13eve::Result<int> tradeFailure(eve::DiagnosticCode code, std::string message,
14 std::string path, const std::string &itemId) {
16 code, message, path, {{"itemId", itemId}}, "rpg.shop-transaction"));
17}
18
19eve::Result<double> validateTrade(const GameState &gameState, const std::string &currencyId,
20 const std::string &itemId, int quantity, double unitPrice,
21 bool purchase) {
22 if (currencyId.empty() || itemId.empty() || quantity <= 0 ||
23 !std::isfinite(unitPrice) || unitPrice < 0.0)
26 "shop trade requires currency/item ids, positive quantity, and finite non-negative unit price",
27 "trade", {{"itemId", itemId}}, "rpg.shop-transaction"));
28 const double total = unitPrice * static_cast<double>(quantity);
29 const double balance = gameState.getVariable(currencyId);
30 const double result = purchase ? balance - total : balance + total;
31 if (!std::isfinite(total) || !std::isfinite(balance) || !std::isfinite(result))
33 eve::DiagnosticCode::InvalidArgument, "shop trade would produce a non-finite currency value",
34 "unitPrice", {{"itemId", itemId}}, "rpg.shop-transaction"));
35 if (purchase && balance < total)
37 eve::DiagnosticCode::PreconditionViolation, "shop purchase has insufficient currency",
38 "currency", {{"itemId", itemId}}, "rpg.shop-transaction"));
39 return eve::Result<double>::success(total);
40}
41
42eve::Result<int> rollbackFailure(GameState &gameState, const std::string &snapshot,
43 const eve::Status &commitStatus, const std::string &itemId) {
44 auto restored = gameState.restoreSnapshotJson(snapshot);
45 if (!restored.ok())
47 "shop trade rollback could not restore GameState", "rollback", itemId);
48 return eve::Result<int>::failure(commitStatus);
49}
50
51} // namespace
52
54 const std::string &currencyId,
55 const std::string &offerId, int quantity) {
56 const ShopOffer *offer = ShopCatalogue::find(offerId);
57 if (!offer)
58 return tradeFailure(eve::DiagnosticCode::NotFound,
59 "shop purchase references an unknown offer", "offerId", offerId);
60 return buy(gameState, bag, currencyId, offer->itemId, quantity,
61 static_cast<double>(offer->buyPrice));
62}
63
65 const std::string &currencyId,
66 const std::string &offerId, int quantity) {
67 const ShopOffer *offer = ShopCatalogue::find(offerId);
68 if (!offer)
69 return tradeFailure(eve::DiagnosticCode::NotFound,
70 "shop sale references an unknown offer", "offerId", offerId);
71 return sell(gameState, bag, currencyId, offer->itemId, quantity,
72 static_cast<double>(offer->sellPrice));
73}
74
76 const std::string &currencyId, const std::string &itemId,
77 int quantity, double unitPrice) {
78 auto validated = validateTrade(gameState, currencyId, itemId, quantity, unitPrice, true);
79 if (!validated.ok()) return eve::Result<int>::failure(validated.status());
80 const double total = std::move(validated).takeValue();
81 auto prepared = inventory::InventorySystem::prepareAddBatch(&bag, {{itemId, quantity}});
82 if (!prepared.ok()) return eve::Result<int>::failure(prepared.status());
83 auto before = gameState.snapshotJson();
84 if (!before.ok()) return eve::Result<int>::failure(before.status());
85 std::string snapshot = std::move(before).takeValue();
86
87 gameState.addVariable(currencyId, -total);
88 auto committed = inventory::InventorySystem::commitAddBatch(std::move(prepared).takeValue());
89 if (!committed.ok()) return rollbackFailure(gameState, snapshot, committed.status(), itemId);
90 return committed;
91}
92
94 const std::string &currencyId, const std::string &itemId,
95 int quantity, double unitPrice) {
96 auto validated = validateTrade(gameState, currencyId, itemId, quantity, unitPrice, false);
97 if (!validated.ok()) return eve::Result<int>::failure(validated.status());
98 const double total = std::move(validated).takeValue();
99 auto prepared = inventory::InventorySystem::prepareRemove(&bag, itemId, quantity);
100 if (!prepared.ok()) return eve::Result<int>::failure(prepared.status());
101 auto before = gameState.snapshotJson();
102 if (!before.ok()) return eve::Result<int>::failure(before.status());
103 std::string snapshot = std::move(before).takeValue();
104
105 gameState.addVariable(currencyId, total);
106 auto committed = inventory::InventorySystem::commitRemove(std::move(prepared).takeValue());
107 if (!committed.ok()) return rollbackFailure(gameState, snapshot, committed.status(), itemId);
108 return committed;
109}
110
111} // namespace eve::rpg
LogicalId itemId
全局游戏状态:开关(bool)、变量(数字)、独立变量(按作用域)。
std::string message
DiagnosticCode code
std::string path
Definition PlayHost.cpp:110
Strict, atomically replaceable RPG shop content.
Atomic RPG currency and inventory purchase/sale orchestration.
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
格子型物品容器(脚本可直接操作)。
Definition Bag.h:21
static eve::Result< int > commitRemove(PreparedInventoryRemove prepared)
Commit a prepared removal and publish its event after the no-fail slot swap.
static eve::Result< int > commitAddBatch(PreparedInventoryAdd prepared)
Commit one prepared addition and then publish its change events.
static eve::Result< PreparedInventoryAdd > prepareAddBatch(Bag *bag, const std::vector< InventoryItemGrant > &grants)
Validate a multi-item addition against a private candidate Bag.
static eve::Result< PreparedInventoryRemove > prepareRemove(Bag *bag, const std::string &itemId, int quantity)
Prepare removal of one exact item quantity without mutation or events.
一份游戏状态(开关/变量/独立变量)。
Definition GameState.h:25
void addVariable(const std::string &name, double delta)
Adds variable.
Definition GameState.cpp:67
eve::Result< std::string > snapshotJson() const
Serialize this state as the canonical versioned RPG game-state JSON document.
static const ShopOffer * find(const std::string &id)
Find an offer by stable offer id, or null when absent.
static eve::Result< int > sell(GameState &gameState, inventory::Bag &bag, const std::string &currencyId, const std::string &itemId, int quantity, double unitPrice)
Atomically remove an item quantity and credit its sale proceeds.
static eve::Result< int > sellOffer(GameState &gameState, inventory::Bag &bag, const std::string &currencyId, const std::string &offerId, int quantity)
Sell a quantity using the authoritative price and item in ShopCatalogue.
static eve::Result< int > buyOffer(GameState &gameState, inventory::Bag &bag, const std::string &currencyId, const std::string &offerId, int quantity)
Buy a quantity using the authoritative price and item in ShopCatalogue.
static eve::Result< int > buy(GameState &gameState, inventory::Bag &bag, const std::string &currencyId, const std::string &itemId, int quantity, double unitPrice)
Atomically debit currency and add the purchased item quantity.
RPG 模块的基础实体:把属性 / 状态 / 技能三张表挂到一个 ECS 实体上。
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
One immutable offer in the process-local shop catalogue.