Temporary page. I'll be filling out the contents later. --Roots 21:30, 31 January 2010 (UTC)
The user interface presented to the player whenever they are able to buy wares or sell objects from their inventory is implemented by shop mode. The purpose of this document is to provide an overview of how this code works and why it is structured in this manner. This documentation does dive down into every detail of the code structure and operation. That is something that you should learn by actually studying the code or referring to the doxygen-generated API documentation. The shop code is almost entirely in C++ and there is very little interaction with Lua.
Shopping in Allacrost is a little more advanced than the rather basic shop menus found in most 2D RPGs. Before we can even begin discussing some of the structures and interfaces the reader must first understand the underlying concepts of Allacrost's shopping interface. Once these concepts are understood it will be much easier to grasp the function and purpose of the major code structures and flow of operation.
Allacrost's shop operation and interface is designed to stay true to the main goals outlined in our project's Statement of Purpose. The interface is designed to be as efficient as possible and to limit any micromanagement or tedious operation. The design of shops in the world of Allacrost actually help to satisfy the game's strategic goals by limiting availability and providing variation in pricing. The three primary actions the player can take while shopping are to purchase wares, sell objects from their inventory, or trade equipped weapons and armor for new equipment sold by the shop. The primary concepts of shop mode are explained further in the sub-sections that follow.
The most fundamental underlying concept of our shop design is that Allacrost uses what we call a transaction model. It works similar to an online retailer where you mark the objects you wish to buy and their quantity and they are placed in a virtual shopping cart. In a web interface, before finalizing your transaction you are presented with a list of all items in your cart, which you may change the purchase quantity of or remove entirely before you make any form of payment.
Allacrost's shop interface works much the same way. The player is presented with a list of all objects which can be purchased from the shop where they can mark how many they wish to purchase of each object. Similarly, the player may go through a list of all objects currently in their inventory and select quantities of each object to sell to the shop. The player is also able to use a special trade interface to automatically purchase weapons and armor, equip them on a character, and sell the old equipment. Once the player has done this, they must go to a confirm or "check out" interface to finalize their transaction. In this view, the player is allowed to browse through all marked purchases, sales, and trades and change quantities, including setting the quantity to zero which will negate the transaction of that object.
The player's drunes (money) are not modified until the transaction is confirmed. While the player is marking their purchases, sales, and trades, financial data in the shop interface is updated to show the player what their total cost and revenue would be for the current transaction should it be confirmed. The player can not mark a purchase if their costs exceed their current drunes plus the revenue they would gain in the transaction. The player can also not cancel a sale if it would cause the costs to exceed the available funds. Ultimately, the following logic must always remain true in order for a purchase, sale, or trade to be marked or a quantity changed.
<math> New Total = Current Funds - Transaction Costs + Transaction Revenue New Total >= 0 </math>
All shops in the world of Allacrost have what we call price ratings. Each shop has two price ratings defined; one for purchases (buy price rating) and one for sales (sell price rating). The buy price rating determines how much each object sold by the shop costs while the sell price rating determines the revenue the player will receive for each object in their inventory. There are five possible values for both buy and sell price ratings and this information is conveyed to the player via assigning the shop one to five stars for boy buy and sell price levels. The more starts a buy/sell rating has, the better the deal is for the player. A higher buy rating means cheaper prices on wares for sale while a higher sell rating indicates a larger return for objects sold back to the shop.
The purpose of price levels is to introduce strategy into the game's shopping. For example if a local shop has a very bad sell rating, the player might wish to choose to not sell unneeded objects in their inventory and wait to find a better deal somewhere else. All objects in the game have a standard price defined, which we will label as x. The table below shows what the buy/sell prices would be for a given x, rounded to the nearest whole value of course. Note that even if you buy an object at the cheapest amount possible and sell it at the highest price possible, there is still a net loss for the purchase and sale. This is designed so that the player can not exploit the game and earn drunes for doing nothing.
|Price Rating||Buy Price||Sell Price|
There are eight categories of objects in the game which may be sold by shops: items, weapons, head armor, torso armor, arm armor, leg armor, shards, and key items. Obviously not all shops in the game will sell objects of all eight types. What types of wares the shop sells also determines what types of objects that the player can sell back to the shop. For example, if a shop only sells items and weapons, the player can not sell any type of armor or shards to the shop. Key items are special in that they can never be sold to shop even if the shop sells key items to the player.
Another strategic feature for shop in Allacrost is the concept of limited stock. What this means is that the shop does not have an infinite amount of any object that the player may purchase. This prevents the player from, for example, purchasing 100 healing potions at once from a shop. This limited availability makes items more valuable to the player as they can't be consumed and replaced en masse like they are in many other RPGs. The player will have to take care to ration out things like restorative items and take advantage of a shop's current stock when they come across one.
Each shop has an initial stock for every object that they sell. A shop will regenerate its stock over time but will never exceed its capped level.
|Note: Stock regeneration rates and function has not yet been implemented or the design finalized. This section will be updated with that information when it becomes available.|
A common operation that the player will want to perform in the game is to purchase new equipment for their characters, equip it and remove their older equipment, and sell the older equipment back to the shop. The shop code provides an interface to allow the player to achieve all of these steps with a single action.
|Note: The design and implementation of equipment trading is not yet finalized. This section will be updated with that information when it becomes available.|
One of the most prominent features about the shop mode code is that it has an extensive amount of code re-use. The various interfaces (buy screen, sell screen, etc.) display many of the same elements, such as a list of objects. Below is a list of classes available in shop mode and their general purpose. The most fundamental classes we'll explain in more detail in the sub-sections that follow.
|File Name||Class Name||Class Purpose|
|shop||ShopMedia||A data management class storing shared media files|
|ShopMode||Master class that processes the game's execution while in shop mode|
|ShopObjectViewer||A display class for showing detailed information about a selected object|
|shop_root||CategoryDrawData||Assists the root interface in the proper drawing of object category icons and text|
|RootInterface||The highest level shop interface displaying an overview of the shop's properties|
|shop_buy||BuyInterface||Manages shop while in buy state, allowing player to view and purchase wares|
|BuyListDisplay||A display class that draws list of objects that may be marked to buy|
|shop_sell||SellInterface||Manages shop while in sell state, allowing player to view and sell off their inventory|
|SellListDisplay||A display class that manages and draws lists of objects that may be marked to sell|
|shop_trade||TradeInterface||Manages shop while in trade state, allowing player to trade character equipment|
|shop_confirm||ConfirmInterface||Manages shop while in confirm state, allowing player to view, modify, and complete their transaction|
|shop_leave||LeaveInterface||Manages shop while in leave state, preventing player from leaving the shop with marked transactions|
|shop_utils||ObjectCategoryDisplay||A display class for drawing object category icon/text|
|ObjectListDisplay||An abstract display class for drawing a list of objects and their properties|
|ShopInterface||An an abstract class for all shop interfaces|
|ShopObject||Represents objects that are bought, sold, and traded within the shop|
The ShopObject class is best thought of as an extension of the GlobalObject class. Every GlobalObject that is sold by the shop, is in the player's inventory, or is equipped on active party members has a ShopObject object created for it and a pointer to the GlobalObject data is stored within this new class object. ShopObject's are unique to each GlobalObject that is contained. That is to say, if the "Fire Helm" armor is sold by the shop, equipped on one or more characters, and is in the party's inventory, only a single ShopObject will be created to represent all of these different GlobalObject instances.
In addition to the GlobalObject pointer, the ShopObject class contains the following member data.
- A boolean that determines whether or not the object is sold by the shop
- The buy price of the object
- The sell price of the object
- The number of instances of the object that the party currently owns
- The number of instances of the object that are in stock at the shop
- The amount of this object that the player has marked for purchase
- The amount of this object that the player has marked for sale
Using these members it is possible to account for any sort and number of transactions whether it be purchases, sales, or trades. Read access methods are provided for all of these private members and several of them have public increment/decrement methods made available that automatically check for invalid conditions such as trying to buy more of an object than the shop has stock available.
You can think of an interface as a low-level manager of some aspect of the shop. For instance, the BuyInterface class is responsible for all input processing, updating, and draw code after the player selects the buy option from the shop's root menu. The ShopInterface class is an abstract class that all of these interfaces derive from, making it simple for the ShopMode class to switch states appropriately. The ShopInterface class contains nothing but the following purely virtual methods in the table below, along with their purpose.
|void Initialize()||Performs any initialization that could not be done when the class was constructed|
|void MakeActive()||Invoked to notify when the shop state has changed and the interface has become active|
|void TransactionNotification()||Invoked whenever a transaction is made to allow the interface to update itself appropriately|
|void Update()||Updates the state of the interface and operates on user input|
|void Draw()||Draws the interface's contents to the screen|
One issue with interfaces is that sometimes actions that the player takes in one interface effect how the other interfaces act. Imagine the following scenario. The player selects to buy four potions in the buy interface, goes to the confirm interface and modifies their order to reduce the buy quantity to two potions, then returns back to the buy interface. The buy interface needs to be notified that the buy quantity of potions has changed from four to two so that it does not display incorrect/outdated information. The MakeActive() method is the solution to this problem. Because it would be complicated to keep track of all transaction modifications when switching between interfaces, we simply refresh all mutable display data when entering a new interface. The TransactionNotification() method, on the other hand, is more aggressive and interfaces will reconstruct their entire data displays from scratch.