Skip to main content Home Creators johannesrabauer fxgl-skills fxgl-economy
fxgl-economy Implement inventory and trade/shop systems in FXGL — create typed Inventory objects, add/remove/query items, display inventory via InventoryListView, build a Shop with TradeItem buy/sell prices, open a ShopView or TradeView UI, implement buy and sell transactions, manage item stacks and capacity, and wire the shop to NPC collisions. Use this skill when adding an inventory system, item collection, a merchant shop, an upgrade store, or any economy mechanic.
Jump to install Skills Marketplace Discover and explore AI skills built by the community.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/JohannesRabauer/fxgl-skills --skill fxgl-economyThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... More from this repository Add particle systems and visual effects to an FXGL game — configure ParticleEmitter with function-based setters (setVelocityFunction, setAccelerationFunction, setExpireFunction, setScaleFunction, setSpawnPointFunction); use built-in factory emitters (fire, explosion, smoke, rain); apply image-textured particles with multiplyColor / toColor; write custom per-particle physics via setControl; attach TrailParticleComponent for motion trails; apply SlowTimeEffect for bullet-time; apply WobbleEffect for screen-shake; stack multiple effects with EffectComponent; build fireworks displays. Use this skill when adding explosions, fire, smoke, rain, motion trails, impact bursts, bullet-time slow-motion, screen shake, or any particle-based visual polish.
Start a new FXGL project through a bounded Socratic discovery flow, then turn the answers into a docs-first starter specification with ADRs, an arc42-lite architecture summary, and Mermaid use-case diagrams before scaffolding the project itself. Continue using the same questioning approach for initialization choices, and require documentation updates plus automated tests for every implemented step.
Build a Breakout or Arkanoid style game in FXGL — control a paddle horizontally, keep a ball at constant speed, bounce it off walls, paddle, and bricks, load brick layouts from level data, angle the reflection based on paddle hit position, handle lives and bottom-drain loss, spawn falling power-ups, and finish the level when all breakable bricks are destroyed.
Related occupations SOC
Based on SOC occupation classification
name fxgl-economy description Implement inventory and trade/shop systems in FXGL — create typed Inventory objects, add/remove/query items, display inventory via InventoryListView, build a Shop with TradeItem buy/sell prices, open a ShopView or TradeView UI, implement buy and sell transactions, manage item stacks and capacity, and wire the shop to NPC collisions. Use this skill when adding an inventory system, item collection, a merchant shop, an upgrade store, or any economy mechanic.
triggers ["inventory","shop","item","TradeItem","buy","sell","InventoryListView","ShopView","trade","merchant","store","collectible item","economy"] compatibility Java 17+, FXGL 21.x
category fxgl/economy tags ["fxgl","java","javafx","economy"] metadata {"author":"fxgl-skills","version":"1.0","fxgl-version":"21.1"} allowed-tools ["Read","Write","Edit","Bash"]
FXGL Inventory & Trade System
Define Your Item Type
public class Item implements Serializable {
private final String name;
private final String iconPath;
private final String description;
private int quantity;
public Item (String name, String iconPath, String description) {
this .name = name;
this .iconPath = iconPath;
this .description = description;
this .quantity = 1 ;
}
public String getName () { return name; }
public String getIconPath () { return iconPath; }
public String getDescription () { return description; }
public int getQuantity () { return quantity; }
public void setQuantity (int q) { this .quantity = q; }
@Override String { name; }
}
public
toString
()
return
Inventory API
Inventory<Item> playerInventory = new Inventory <>(20 );
boolean added = playerInventory.add(new Item ("Sword" , "sword.png" , "A sharp blade." ));
boolean removed = playerInventory.remove(swordItem);
boolean hasSword = playerInventory.contains(swordItem);
int slotCount = playerInventory.size();
boolean isFull = playerInventory.isFull();
ObservableList<Item> items = playerInventory.getItems();
playerInventory.setMaxCapacity(30 );
int max = playerInventory.getMaxCapacity();
Optional<Item> found = playerInventory.getItems().stream()
.filter(i -> i.getName().equals("Potion" ))
.findFirst();
playerInventory.getItems().forEach(item -> System.out.println(item.getName()));
InventoryListView — Display the Inventory InventoryListView<Item> listView = new InventoryListView <>(playerInventory);
listView.setCellFactory(lv -> new ListCell <>() {
@Override protected void updateItem (Item item, boolean empty) {
super .updateItem(item, empty);
if (empty || item == null ) {
setGraphic(null );
setText(null );
} else {
ImageView icon = new ImageView (getAssetLoader().loadTexture(item.getIconPath()).getImage());
icon.setFitWidth(32 ); icon.setFitHeight(32 );
Label name = new Label (item.getName() + (item.getQuantity() > 1 ? " x" + item.getQuantity() : "" ));
name.setTextFill(Color.WHITE);
setGraphic(new HBox (10 , icon, name));
}
}
});
listView.getSelectionModel().selectedItemProperty().addListener((obs, old, selected) -> {
if (selected != null ) showItemDetails(selected);
});
addUINode(listView, 50 , 50 );
Shop — Define Items for Sale
Shop<Item> merchantShop = new Shop <>();
merchantShop.addItem(new TradeItem <>(new Item ("Sword" , "sword.png" , "Sharp." ), 200 , 100 ));
merchantShop.addItem(new TradeItem <>(new Item ("Shield" , "shield.png" , "Sturdy." ), 150 , 75 ));
merchantShop.addItem(new TradeItem <>(new Item ("Potion" , "potion.png" , "Heals." ), 50 , 20 ));
Trade Logic — Buy & Sell
public boolean buyItem (TradeItem<Item> tradeItem) {
int price = tradeItem.getBuyPrice();
if (geti("gold" ) < price) {
showMessage("Not enough gold!" );
return false ;
}
if (playerInventory.isFull()) {
showMessage("Inventory full!" );
return false ;
}
inc("gold" , -price);
playerInventory.add(tradeItem.getItem());
play("sounds/purchase.wav" );
return true ;
}
public boolean sellItem (Item item) {
TradeItem<Item> shopEntry = merchantShop.getItems().stream()
.filter(t -> t.getItem().getName().equals(item.getName()))
.findFirst().orElse(null );
int sellPrice = shopEntry != null ? shopEntry.getSellPrice() : 10 ;
playerInventory.remove(item);
inc("gold" , +sellPrice);
play("sounds/sell.wav" );
return true ;
}
ShopView — Built-in Shop UI ShopView<Item> shopView = new ShopView <>(merchantShop, playerInventory);
shopView.getStylesheets().add("shop-style.css" );
public class ShopSubScene extends GameSubScene {
@Override
public void onOpen () {
ShopView<Item> view = new ShopView <>(merchantShop, playerInventory);
Button close = getUIFactoryService().newButton("Close Shop" , () ->
getSceneService().popSubScene());
VBox root = new VBox (20 , view, close);
root.setPadding(new Insets (20 ));
getRoot().getChildren().add(root);
}
}
Stacking Items (Quantity Management)
public void addOrStack (Item newItem) {
Optional<Item> existing = playerInventory.getItems().stream()
.filter(i -> i.getName().equals(newItem.getName()))
.findFirst();
if (existing.isPresent()) {
existing.get().setQuantity(existing.get().getQuantity() + newItem.getQuantity());
playerInventory.getItems().remove(existing.get());
playerInventory.getItems().add(existing.get());
} else {
playerInventory.add(newItem);
}
}
Collision-Triggered Shop
onCollisionBegin(EntityType.PLAYER, EntityType.MERCHANT_NPC, (player, merchant) -> {
if (shopSubScene == null ) {
shopSubScene = new ShopSubScene ();
}
getSceneService().pushSubScene(shopSubScene);
});
Saving Inventory State
@Override
public void writeSaveState (DataFile data) {
var inv = data.getBundle("inventory" );
inv.put("items" , new ArrayList <>(playerInventory.getItems()));
inv.put("gold" , geti("gold" ));
}
@Override
public void readSaveState (DataFile data) {
var inv = data.getBundle("inventory" );
List<Item> savedItems = inv.get("items" );
savedItems.forEach(item -> playerInventory.add(item));
set("gold" , inv.get("gold" ));
}
Gotchas
Item must implement Serializable if you save/load the inventory. All fields
(including nested objects like icons) must also be serializable. Avoid storing Node
or Texture objects directly on items — store file paths as Strings instead.
ObservableList from getItems() is live — modifications to it update the
InventoryListView automatically. Do not replace the list; add/remove from it directly.
Inventory capacity is enforced by add() — it returns false when full.
Always check the return value before assuming the item was added.
TradeItem.getBuyPrice() vs getSellPrice() — buy price is what the player pays;
sell price is what the player receives. The sell price is typically 40-60% of buy price.
ShopView uses the default toString() of your item for item names in the built-in
cells. Override toString() to return the item name or implement a custom cell factory.
InventoryListView does not auto-refresh when you mutate item properties (like quantity).
Force a refresh with listView.refresh() after mutating items already in the inventory.