Artifact Pickup Flow
Collecting an artifact box opens the reward UI. The box is the on-field handoff between the artifact-type world event and the upgrade picker — artifact-type events that complete with the player owning 2+ artifacts spawn a box, ship-proximity collects it, and the resulting reward family artifact_box rolls 2 upgrade cards from owned non-legendary artifacts.
Spawn — only from completed artifact events
Boxes live in world.artifactBoxes. The dormant KPM-based timed-drop scheduler (engine/bridge.ts:3413-3462, gated if (false)) is the only other path that could push to this list, so in shipped builds every artifact box originates from one place: an artifact-type world event finishing.
The completed-event handler at bridge.ts:2611-2631 applies two gates before queuing the reward:
- Gate 1 — owned count.
countOwnedArtifacts() >= 2. Below that the starting-artifact loop is still bootstrapping and the artifact event silently falls through. - Gate 2 — upgradeable pool.
canRollArtifactUpgrade()— at least one owned artifact must be belowARTIFACT_TIER_MAX(legendary).
If both gates pass: game.rewardQueue.push({ type: 'artifact_box' }) queues the reward directly (no on-field crate required for this path) — but in practice the artifact-event spawn path constructs a physical artifactBoxes entry at the event center as the player-facing handoff. If either gate fails, the artifact event substitutes a weapon_box chest at cev.x, cev.y instead so the event still pays out.
Pickup — proximity collect with weapon-chest pattern
The artifact-box update loop at bridge.ts:3007-3054 mirrors the weapon-chest collect:
- Elite-drop pop-and-fly.
_updateBoxFlight(ab, rawDt)handles the arc when the box dropped from an elite. - Proximity trigger.
Math.hypot(ship.x - ab.x, ship.y - ab.y) < 50 + ship.radius— same 50px mandala radius as weapon chests. - Fly-to-center animation. On trigger the box flips
_collecting=true,_collectDur=0.48seconds. The ship’s camera position is the target; cubic-eased position lerp + scale shrink to 0.15. Teal#44ffccand#22aa88spark particles trail the box and burst at completion.game._weaponChestFreeze=trueandgame.timeDilation=0halt the world during the animation. - Collect completion. When
t >= 1, the box is swap-removed fromworld.artifactBoxes,Juice.fire('pickup_weapon')plays the chest pickup cue, andgame.rewardQueue.push({ type: 'artifact_box' })queues the reward.
Reward dispatch — artifact_box family
The reward dispatcher at bridge.ts:3085-3092 consumes the queued entry:
choices = rollArtifactChoices(2, 'upgrade_only');
rollArtifactChoices (engine/world/artifacts.ts:1251) in 'upgrade_only' mode:
- Filters
ARTIFACT_DEFSto artifacts the player owns AND that are belowARTIFACT_TIER_MAX. Banishedartifact_upgrade|<id>keys are excluded. - Picks up to
count=2distinct artifact IDs — never duplicates the same artifact across both cards. - Each card carries
artifactIsLevelUp: true, the tier-specificstatLabel, and the next tier’s rarity name (e.g.'uncommon'→'rare'). - The completion-path gates above guarantee at least 1 upgradeable artifact exists when this branch runs, so the result should never be empty. The function may still return length
< countwhen the player owns exactly one upgradeable artifact — the picker then shows a single forced card with no skip.
Fallback when the upgrade pool can’t fill two cards
The pre-spawn gates only guarantee >= 1 upgradeable artifact. The owned-count-< 2 case is handled before the reward queues — the event substitutes a weapon_box chest at the event center (bridge.ts:2625-2630). So the “falls back to weapon chest if owned < 2” rule is enforced at event-completion time, not at pickup time. Once an artifact_box reward is in the queue, the upgrade-only roll proceeds even if it returns only 1 card.
Reroll behavior
If the player burns a reroll charge while the artifact_box picker is open, bridge.ts:8231-8232 re-runs rollArtifactChoices(2, 'upgrade_only'). An empty roll no-ops without consuming the charge.
Confirmation SFX + level-up animation
When the player picks a card:
_rewardFamily === 'artifact_box'routes toSampleSfx.playGong()(bridge.ts:8199-8200) — shared withweapon_boxandshooting_star.prepareUpgradeShow(choice, ship, game)snapshots the old artifact tier,applyRewardbumps the runtime tier via the artifact grant path, thenfadeoutRewardkicks the standard fly-to-slot upgrade-show animation.
Source files
engine/bridge.ts— completed-event gates (2611-2631), artifact-box update + collect (3007-3054), reward dispatch (3085-3092), reroll (8231-8232), gong SFX (8199-8201).engine/world/artifacts.ts—rollArtifactChoices(line1251),canRollArtifactUpgrade(1209),countOwnedArtifacts(1226),ArtifactRollModetype (1204),ARTIFACT_TIER_MAXlegendary cap.- See also:
crate-drop-systemfor the full crate taxonomy (artifact boxes vs. weapon chests vs. destructibles).