Skip to main content 首页 创作者 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.
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/JohannesRabauer/fxgl-skills --skill fxgl-economy命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... 同仓库更多 Skills 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.
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.