How it works · write-up
Shop item catalog (Xenon 2: Megablast)
Extracted directly from the ROM via ReVa/Ghidra disassembly of the shop's sell-menu handler
(FUN_0001122e), item-description printer (FUN_000114ce) and buy-cost printer
(ShopPanel_PrintBuyCost, FUN_00011346). Names come from the pointer table at 0x00010ea8
and prices from the word table at 0x00010f7a; both are indexed by the one-based runtime item
type stored in each grid slot (offset +6) and in attachment object +$44: runtime type 1 is
FORWARD SHOT, 2 is ADVICE, 25 is BITMAP SHADES. Python and protocol code (pickup_catalog.py)
use these runtime values end to end, and this table uses them too.
Both lookups are literally table + type * 4 / table + type * 2 with the one-based type (the
cost code is lea $10f7c,a1; move.w -2(a1,d0.w),d0 after add.w d0,d0), so the word at
0x00010f7a itself (6128) and the pointer at 0x00010ea8 are never used -- they are whatever
precedes the tables, not a "type 0" entry. An earlier version of this catalog indexed both tables
zero-based, which shifted every price and the leveled-item list one row up (it made ADVICE look
free and gave it power levels); the values below are re-verified against the disassembly and
xenon_tools/pickup_catalog.py, which agree.
The game has no separate flavor-text blurb per item beyond its display name -- the name is the
in-game description, printed via blitter_printSmallTextWrapped_FUN_000122d0 when you select an
item to sell (or presumably an equivalent path on the buy side).
Catalog
The sprite column shows one frame of each item's shop-icon loop (icon table at 0x00010f0c,
one looping sprite list per item), rendered from assets/stram.bin and the packed atlas by
xenon_tools/render_shop_icons.py; the image files are named by the runtime type.
| Type | Sprite | Name (as printed in-game) | Price | Notes |
|---|---|---|---|---|
| 1 | ![]() |
FORWARD SHOT | (0) | The player's starting weapon: price slot is 0 and item type 0 means "empty slot" (the sell handler's != 0 guard), so it is never bought. Has power levels. |
| 2 | ![]() |
ADVICE | 200 | Crispin's hint. Not free: 200 credits. |
| 3 | ![]() |
SPEEDUP | 500 | |
| 4 | ![]() |
HEALTH\rPOWER 1 | 500 | Displayed as "HEALTH / POWER 1" (embedded ` |
| ` line break). | ||||
| 5 | ![]() |
AUTOFIRE | 500 | |
| 6 | ![]() |
SUPER NASHWAN POWER | 600 | |
| 7 | ![]() |
HEALTH\rPOWER 2 | 1000 | "HEALTH / POWER 2" -- second health tier. |
| 8 | ![]() |
REAR SHOT | 1000 | Leveled item (gets a POWER <n> suffix, see below). |
| 9 | ![]() |
MINE | 1000 | Leveled item. |
| 10 | ![]() |
SIDE SHOT | 1000 | Leveled item. |
| 11 | ![]() |
ELECTRO BALL | 1200 | |
| 12 | ![]() |
POWERUP | 2000 | |
| 13 | ![]() |
MINE | 3000 | Second MINE entry (same display text, different type and price -- a second mine tier). Leveled item. |
| 14 | ![]() |
DOUBLE SHOT | 3000 | Leveled item. |
| 15 | ![]() |
CANNON | 4000 | |
| 16 | ![]() |
DIVE | 4000 | |
| 17 | ![]() |
MISSILE LAUNCHER | 4000 | |
| 18 | ![]() |
LASER | 4000 | Leveled item. |
| 19 | ![]() |
DRONE | 4500 | Leveled item. |
| 20 | ![]() |
FLAMER | 5000 | |
| 21 | ![]() |
BOMB | 5500 | |
| 22 | ![]() |
EXTRA LIFE | 6000 | |
| 23 | ![]() |
HOMING MISSILE | 6000 | |
| 24 | ![]() |
PROTECTION | 6000 | Shield. |
| 25 | ![]() |
BITMAP SHADES | 6000 | Bitmap Brothers in-joke / cosmetic item. |
Prices are halved when $E1A (the current / just-completed level number) is 5, i.e. in the
Level 5 shops (cmpi.w #5,$e1a; bne; lsr.w #1,d0 in FUN_00011346).
"Leveled" items
FUN_000114ce special-cases runtime item types {1, 8, 9, 10, 13, 14, 18, 19} (FORWARD SHOT,
REAR SHOT, MINE, SIDE SHOT, MINE (second tier), DOUBLE SHOT, LASER, DRONE): after printing the
base name, if DAT_00011788 is set it also prints "\rPOWER " (string at 0x0001163c) followed
by the attachment's power level plus one (d1 = attachment object +$42, read by the sell
handler from the grid slot's object pointer at +$c, incremented, then FUN_0001234e). These
are exactly the weapons that can be upgraded in place.
Source addresses (for future reference)
- Name table:
0x00010ea8(4-byte pointer per runtime item type; the entry for type 1 is at0x00010eac) - Price table:
0x00010f7a(2-byte unsigned cost per runtime item type; the entry for type 1 is at0x00010f7c) - Icon table:
0x00010f0c(4-byte pointer per runtime item type to a{sprite...}, 0, looplist) - Sell-confirmation flow:
FUN_0001122e->FUN_000114ce(print name) ->ShopPanel_PlayHandPayAnimation_FUN_00011380("I WILL PAY") -> next cursor-poll pass -> clr.w (0x6,A0)at0x000112f0finalizes the sale (seeSHOP_ITEM_SOLD_PCinsrc/drawCommandStream.c) - Buy-side cost message:
FUN_00011346("THAT WILL COST")
























