From 986857ff16178fb93fc5e24e5208c63c99c87b4d Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:37:04 +0000 Subject: [PATCH 0001/1495] Update concepts/combat.mdx --- concepts/combat.mdx | 134 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 5 deletions(-) diff --git a/concepts/combat.mdx b/concepts/combat.mdx index 71429e9c..02d4d0c0 100644 --- a/concepts/combat.mdx +++ b/concepts/combat.mdx @@ -111,16 +111,140 @@ DAMAGE_DIVISOR: 640, // Used in max hit calculation ## Ranged Combat -Ranged attacks require: +Ranged combat is now fully implemented with OSRS-accurate mechanics for bows and arrows. -- Bow equipped in weapon slot -- Arrows equipped in arrow slot -- Arrows consumed on each attack +### Requirements + +- **Bow** equipped in weapon slot +- **Arrows** equipped in ammo slot +- Arrows consumed on each attack (100% consumption, no Ava's device yet) + +### Combat Styles + +| Style | Speed | Accuracy | Range | XP Split | +|-------|-------|----------|-------|----------| +| **Accurate** | Base | +3 Ranged | Normal | Ranged + Constitution | +| **Rapid** | -1 tick | Normal | Normal | Ranged + Constitution | +| **Longrange** | Base | Normal | +2 tiles | Ranged + Defense + Constitution | + +### Damage Formula + +```typescript +// Effective Ranged Level +const effectiveRanged = floor(rangedLevel * prayerBonus) + styleBonus + 8; + +// Max Hit +const maxHit = floor(0.5 + effectiveRanged * (rangedStrength + 64) / 640); + +// rangedStrength comes from equipped arrows +``` + +### Projectile System + +Ranged attacks use a projectile system with OSRS-accurate hit delays: + +```typescript +// Hit delay formula: 1 + floor((3 + distance) / 6) +const hitDelayTicks = 1 + Math.floor((3 + distance) / 6); +``` + +Projectiles are rendered as 3D arrow meshes that travel from attacker to target. - Without arrows, the bow cannot attack. Arrows are not recoverable. + Without arrows, the bow cannot attack. Arrows are consumed on every shot (100% loss rate). +## Magic Combat + +Magic combat allows spellcasting with or without a staff equipped. + +### Requirements + +- **Magic level** sufficient for the spell +- **Runes** in inventory (or infinite runes from elemental staff) +- **Spell selected** for autocast (optional) + +### Combat Styles + +| Style | Accuracy | Range | XP Split | +|-------|----------|-------|----------| +| **Accurate** | +3 Magic | Normal | Magic + Constitution | +| **Longrange** | +1 Magic | +2 tiles | Magic + Defense + Constitution | +| **Autocast** | Normal | Normal | Magic + Constitution | + +### Spells (F2P) + +**Strike Tier (Levels 1-13):** +- Wind Strike (L1): 2 max hit +- Water Strike (L5): 4 max hit +- Earth Strike (L9): 6 max hit +- Fire Strike (L13): 8 max hit + +**Bolt Tier (Levels 17-35):** +- Wind Bolt (L17): 9 max hit +- Water Bolt (L23): 10 max hit +- Earth Bolt (L29): 11 max hit +- Fire Bolt (L35): 12 max hit + +### Damage Formula + +```typescript +// Effective Magic Level +const effectiveMagic = floor(magicLevel * prayerBonus) + styleBonus + 8; + +// Max Hit = spell base damage (no equipment modifiers in F2P) +const maxHit = spellBaseMaxHit; +``` + +### Magic Defense + +Magic defense uses a unique formula combining Magic and Defense levels: + +```typescript +// For players +const effectiveDefense = floor(0.7 * magicLevel + 0.3 * defenseLevel) + 9; + +// For NPCs +const effectiveDefense = magicLevel + 9; +``` + +### Rune Consumption + +Runes are consumed on each cast. Elemental staves provide infinite runes: + +- **Staff of Air**: Infinite air runes +- **Staff of Water**: Infinite water runes +- **Staff of Earth**: Infinite earth runes +- **Staff of Fire**: Infinite fire runes + +### Autocast + +Players can select a spell for autocast, which automatically casts that spell when attacking: + +```typescript +// Set autocast spell +world.network.send("setAutocast", { spellId: "fire_strike" }); + +// Clear autocast +world.network.send("setAutocast", { spellId: null }); +``` + +### Projectile Visuals + +Magic spells render as colored orbs with element-specific colors: + +- **Wind**: Light gray/white +- **Water**: Blue +- **Earth**: Brown +- **Fire**: Orange-red + +Projectiles use OSRS-accurate hit delay formula: + +```typescript +// Hit delay formula: 1 + floor((1 + distance) / 3) +const hitDelayTicks = 1 + Math.floor((1 + distance) / 3); +``` + ## Death Mechanics When health reaches zero: From 7c4dd3978cd87d6ae6dcb4cf540bfe7ede9ae4be Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:38:19 +0000 Subject: [PATCH 0002/1495] Update changelog.mdx --- changelog.mdx | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/changelog.mdx b/changelog.mdx index 5c331305..b06619ba 100644 --- a/changelog.mdx +++ b/changelog.mdx @@ -23,6 +23,201 @@ rss: true Subscribe via [RSS](/changelog/rss.xml) for updates. + + +### ⚔️ Ranged Combat System + +Complete implementation of OSRS-style ranged combat with bows and arrows: + +- **Ranged Weapons**: Shortbow, Oak shortbow, Willow shortbow, Maple shortbow +- **Ammunition**: Bronze, Iron, Steel, Mithril, and Adamant arrows + - Arrows equipped in new ammo slot + - 100% consumption rate (no Ava's device yet) + - Arrow strength bonuses affect damage +- **Combat Styles**: Accurate (+3 Ranged), Rapid (-1 tick speed), Longrange (+2 range, Ranged+Defense XP) +- **Damage Calculation**: OSRS-accurate formulas with equipment bonuses + - Effective Ranged = floor(rangedLevel × prayer) + styleBonus + 8 + - Max Hit = floor(0.5 + effectiveRanged × (arrowStrength + 64) / 640) +- **Projectile System**: 3D arrow meshes with arc trajectories + - OSRS-accurate hit delay: 1 + floor((3 + distance) / 6) + - Arrows rotate to face travel direction + - Metal-colored arrow tips based on arrow type +- **Attack Range**: 10 tiles (Chebyshev distance) +- **XP Distribution**: 4 XP per damage to Ranged, 1.33 XP to Constitution + +### ✨ Magic Combat System + +Complete implementation of F2P magic with Strike and Bolt spells: + +- **Spells Panel**: New spellbook UI with spell grid + - Click to select autocast spell + - Right-click for context menu (Autocast/Cast) + - Element-colored spell icons with glow effects + - Level requirements displayed +- **Combat Spells**: 8 F2P spells across 2 tiers + - **Strike**: Wind (L1, 2 dmg), Water (L5, 4 dmg), Earth (L9, 6 dmg), Fire (L13, 8 dmg) + - **Bolt**: Wind (L17, 9 dmg), Water (L23, 10 dmg), Earth (L29, 11 dmg), Fire (L35, 12 dmg) +- **Rune System**: Air, Water, Earth, Fire, Mind, and Chaos runes + - Runes consumed on each cast + - Elemental staves provide infinite runes (Staff of Air/Water/Earth/Fire) +- **Staffless Casting**: Can cast spells without a staff (OSRS-accurate) + - Staff provides magic attack bonus and infinite runes + - Spells work with any weapon or no weapon +- **Combat Styles**: Accurate (+3 Magic), Longrange (+2 range, Magic+Defense XP), Autocast +- **Damage Calculation**: OSRS-accurate formulas + - Effective Magic = floor(magicLevel × prayer) + styleBonus + 8 + - Max Hit = spell base damage (no equipment modifiers in F2P) + - Magic Defense = floor(0.7 × magicLevel + 0.3 × defenseLevel) for players +- **Projectile Visuals**: Colored spell orbs with element-specific effects + - OSRS-accurate hit delay: 1 + floor((1 + distance) / 3) + - Spell trails with glow and pulse effects +- **Database**: New `magicLevel`, `magicXp`, and `selectedSpell` columns +- **XP Distribution**: Base spell XP + (2 × damage) to Magic, 1.33 XP per damage to Constitution + +### 🎯 Combat System Enhancements + +- **Attack Type Detection**: Automatic detection of MELEE/RANGED/MAGIC from equipped weapon or selected spell +- **Pathfinding Updates**: Ranged/magic use Chebyshev distance, melee uses cardinal-only for range 1 +- **Pending Attack Manager**: Updated to support all three attack types with appropriate range checks +- **Equipment Panel**: New ammo slot for arrows (top-right position) +- **Menu Bar**: Added Spells button (🔮) to menu bar +- **Combat Panel**: Updated with icons for Rapid, Longrange, and Autocast styles +- **Timestamp Validation**: Required timestamp validation on all combat packets (prevents replay attacks) + +### 🧪 Testing + +- **Unit Tests**: Comprehensive tests for ranged/magic damage calculators +- **Integration Tests**: Full combat flow testing for all three attack types +- **Type Guards**: Event payload validation throughout combat handlers + + + + + +### ⚔️ Player vs Player Dueling + +Complete OSRS-style duel arena system with rules negotiation and stakes: + +- **Duel Flow**: Challenge → Rules → Stakes → Confirm → Countdown → Fight → Result +- **Challenge System**: Walk to player and send duel challenge (like trading) + - 30-second timeout for acceptance + - Distance-based cancellation (15 tiles) + - Clickable chat messages for acceptance +- **Rules Screen**: 10 combat rule toggles + - No Ranged, No Melee, No Magic, No Special Attack + - No Prayer, No Potions, No Food, No Movement, No Forfeit + - Equipment slot restrictions (11 slots) + - Both players must accept to proceed +- **Stakes Screen**: Item staking with anti-scam features + - Left-click to stake 1, right-click for quantity menu + - Value imbalance warnings + - Opponent modification warnings + - Both players must accept to proceed +- **Confirmation Screen**: Final read-only review + - Shows all rules, equipment restrictions, and stakes + - Both players must accept to start +- **Arena System**: 6 arenas in 2×3 grid layout + - Arena pooling with automatic reservation + - Collision walls prevent escape during combat + - Flattened terrain for fair combat + - Forfeit pillars for surrendering (if allowed) +- **Combat Resolution**: Winner receives all stakes + - Death or forfeit determines winner + - Automatic health restoration + - Teleport to lobby after duel + - Disconnect handling with 30-second timeout +- **UI Components**: + - DuelPanel with screen transitions + - DuelChallengeModal for incoming challenges + - DuelCountdown overlay (3-2-1-FIGHT) + - DuelResultModal showing items won/lost + - DuelHUD during combat with opponent health + +### 🏗️ Architecture + +- **DuelSystem**: Main orchestrator for duel state machine +- **DuelSessionManager**: Session CRUD operations +- **PendingDuelManager**: Challenge tracking and timeouts +- **ArenaPoolManager**: Arena reservation and collision +- **DuelCombatResolver**: Combat resolution and stake transfers +- **Audit Logging**: Economic tracking for all duel outcomes + +### 🧪 Testing + +- **1,066 lines** of DuelSystem unit tests +- **456 lines** of PendingDuelManager tests +- **233 lines** of ArenaPoolManager tests +- Full coverage of state machine transitions and edge cases + + + + + +### 🔒 Security Enhancements + +Comprehensive security improvements across authentication and input validation: + +- **Secure Token Storage**: Configurable auth storage (localStorage, sessionStorage, or memory) + - `AuthStorageType` enum for storage selection + - Memory-only mode for maximum security + - Proper cleanup on logout (clears all storage types) +- **URL Parameter Validation**: Removed authToken from URL params (security vulnerability) + - **BREAKING**: Tokens must now be passed via postMessage, not URL + - Schema-based validation for embedded config parameters + - Prevents token exposure in browser history and server logs +- **CSP Violation Monitoring**: Content-Security-Policy violation event handling + - Throttled reporting to prevent flooding + - Report-URI endpoint for security monitoring + - Documented CSP directives and security implications +- **Async Token Provider**: Fresh token retrieval for API calls + - Registered with API client for automatic token refresh + - Prevents stale token usage +- **Timestamp Validation**: Required on all combat packets + - ±30 second window for replay attack prevention + - Missing timestamps now rejected (was optional) +- **Type Guards**: Comprehensive event payload validation + - `isUIUpdateEvent`, `isPlayerStatsData`, `isSkillsUpdateEvent` + - `isPrayerPointsChangedEvent`, `isPrayerStateSyncEvent` + - Runtime validation at system boundaries + +### 🎨 UI/UX Improvements + +- **Viewport Scaling**: Proportional panel resize on viewport changes + - ViewportScaler component for design resolution-based scaling + - Grid snapping utility for window alignment + - Left/right column panel scaling with proper stacking +- **Minimap Overhaul**: + - Independent width/height resizing (no longer forced square) + - Extracted MinimapOverlayControls component + - Fixed pip desync using cached projection-view matrices + - Default zoom set to 10 for better visibility +- **Panel Layout Redesign**: Right column flush stacking + - Minimap (top) → gap → Combat → Skills → Inventory → Menubar (bottom) + - Consistent panel widths (235px) for alignment + - No overlaps, touching edges +- **Combat Panel**: Attack style buttons changed from 2×2 grid to 1×3 row for better mobile UX + +### ⚡ Performance Optimizations + +- **Object Pooling**: Memory optimization utilities + - ArrayPool for reusable temporary arrays + - EventDataPool for pooled event objects + - PositionPool for {x, y, z} position reuse + - withPooledArray helper for scoped pool usage +- **Notification Helpers**: Convenient error handling functions + - showErrorNotification, showNetworkErrorNotification, showSuccessNotification + - Consistent error logging with console output + +### 🧪 Testing + +- **Security Tests**: URL parameter validation, error boundaries +- **E2E Test Utilities**: Entity introspection, visual testing helpers + - getEntitiesByType, getPlayerInventory, getPlayerEquipment, getPlayerSkills + - clickAtWorldPosition for world coordinate input simulation +- **Unit Tests**: Expanded test patterns in vitest config + + + ### 🎯 Static Tile Collision System From 0b99a0f85004e51f4ae66797cd83b5fb2c03690e Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:38:52 +0000 Subject: [PATCH 0003/1495] Update architecture.mdx --- architecture.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/architecture.mdx b/architecture.mdx index 10108692..5a9cc74c 100644 --- a/architecture.mdx +++ b/architecture.mdx @@ -6,7 +6,7 @@ icon: "boxes" ## Monorepo Structure -Hyperscape is a **Turbo monorepo** with 7 packages: +Hyperscape is a **Turbo monorepo** with 8 packages: ``` hyperscape/ @@ -17,6 +17,7 @@ hyperscape/ │ ├── plugin-hyperscape/ # @hyperscape/plugin-hyperscape - ElizaOS AI agent plugin │ ├── physx-js-webidl/ # @hyperscape/physx-js-webidl - PhysX WASM bindings │ ├── asset-forge/ # 3d-asset-forge - AI asset generation (Elysia + React Three Fiber) +│ ├── website/ # @hyperscape/website - Marketing website (Next.js 15) │ └── docs-site/ # Documentation (Docusaurus) ├── assets/ # Shared game assets ├── world/ # World configuration and manifests From 43fe59688d843ef842f776ddc790f37855fd8a21 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:39:07 +0000 Subject: [PATCH 0004/1495] Update architecture.mdx --- architecture.mdx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/architecture.mdx b/architecture.mdx index 5a9cc74c..c48939d9 100644 --- a/architecture.mdx +++ b/architecture.mdx @@ -138,6 +138,22 @@ AI-powered asset generation using: - **Drizzle ORM**: PostgreSQL database for asset tracking - **TensorFlow.js**: Hand pose detection for VR/AR interactions +### website (`@hyperscape/website`) + +Marketing website built with: + +- **Next.js 15**: Static site generation with App Router +- **React 19**: UI framework +- **Tailwind CSS 4**: Utility-first styling +- **Framer Motion**: Scroll animations and transitions +- **GSAP**: Advanced animations +- **Lenis**: Smooth scroll library +- **React Three Fiber**: 3D background effects + +**Key Pages:** +- `/` - Landing page with hero, features, and CTA +- `/gold` - $GOLD token page with tokenomics + ## Key Patterns ### Entity Component System From 443c0ad85e7874315edcf1dd1f564cfde8c31291 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:40:58 +0000 Subject: [PATCH 0005/1495] Update wiki/game-systems/duel-arena.mdx --- wiki/game-systems/duel-arena.mdx | 847 +++++++++++++++++++++++++++++++ 1 file changed, 847 insertions(+) create mode 100644 wiki/game-systems/duel-arena.mdx diff --git a/wiki/game-systems/duel-arena.mdx b/wiki/game-systems/duel-arena.mdx new file mode 100644 index 00000000..912c4379 --- /dev/null +++ b/wiki/game-systems/duel-arena.mdx @@ -0,0 +1,847 @@ +--- +title: "Duel Arena" +description: "Player vs Player dueling with rules and stakes" +icon: "swords" +--- + +# Duel Arena System + +The Duel Arena allows players to engage in **player-versus-player combat** with customizable rules and optional item stakes. The system is OSRS-accurate with a complete state machine for challenge, negotiation, and combat. + + +Duel Arena code lives in `packages/server/src/systems/DuelSystem/` with client UI in `packages/client/src/game/panels/DuelPanel/`. + + +## Duel Flow + +```mermaid +stateDiagram-v2 + [*] --> Challenge + Challenge --> Rules: Accept + Challenge --> [*]: Decline + Rules --> Stakes: Both Accept + Stakes --> Confirming: Both Accept + Confirming --> Countdown: Both Accept + Countdown --> Fighting: 3-2-1-FIGHT + Fighting --> Finished: Death/Forfeit + Finished --> [*] +``` + +### 1. Challenge + +Players initiate duels by walking to their opponent and sending a challenge: + +```typescript +// Server validates challenge +duelSystem.createChallenge( + challengerId, + challengerName, + targetId, + targetName +); +``` + +**Challenge Rules:** +- Must be in Duel Arena lobby zone +- Cannot challenge yourself +- Cannot challenge if already in a duel +- 30-second timeout for acceptance +- Cancels if players move >15 tiles apart + +**UI:** +- Clickable red chat message: "Player wishes to duel with you." +- DuelChallengeModal shows challenger name and combat level +- Accept/Decline buttons + +### 2. Rules Screen + +Both players negotiate combat rules and equipment restrictions: + +**Combat Rules (10 toggles):** +- No Ranged +- No Melee +- No Magic +- No Special Attack +- No Prayer +- No Potions +- No Food +- No Movement +- No Forfeit +- Fun Weapons (future) + +**Equipment Restrictions (11 slots):** +- Head, Cape, Amulet, Weapon, Body, Shield, Legs, Gloves, Boots, Ring, Ammo + +**Rule Validation:** +```typescript +// Invalid combination check +if (rules.noForfeit && rules.noMovement) { + return { error: "Cannot enable both No Forfeit and No Movement" }; +} +``` + +**Acceptance:** +- Either player can toggle rules +- Toggling resets both players' acceptance +- Both must accept to proceed to Stakes + +### 3. Stakes Screen + +Players stake items from their inventory: + +**Staking:** +- Left-click inventory item: stake 1 +- Right-click: context menu for quantity (1, 5, 10, All) +- Click staked item to remove from stakes +- Staked items show with gold border in inventory + +**Anti-Scam Features:** +- Value imbalance warnings (>50% difference, >10k gp) +- Opponent modification warnings +- Total value display for each player +- Acceptance resets when stakes change + +**Acceptance:** +- Both players must accept to proceed to Confirmation + +### 4. Confirmation Screen + +Final read-only review before combat: + +**Displays:** +- Active rules summary +- Disabled equipment summary +- "If You Win, You Receive:" (opponent's stakes) +- "If You Lose, They Receive:" (your stakes) +- Total values in gold + +**Acceptance:** +- Both players must accept to start countdown +- No modifications allowed on this screen + +### 5. Countdown + +3-2-1-FIGHT countdown with teleportation: + +```typescript +// Arena reservation +const arenaId = arenaPool.reserveArena(duelId); + +// Teleport to spawn points +teleportPlayersToArena(session); + +// Apply equipment restrictions +applyEquipmentRestrictions(session); + +// Start countdown (3 seconds) +session.state = "COUNTDOWN"; +``` + +**Countdown Sequence:** +- Players teleported to arena spawn points +- Restricted equipment automatically unequipped +- 3-2-1 countdown displayed (1 second per tick) +- Players frozen during countdown +- "FIGHT!" displayed when combat begins + +### 6. Fighting + +Active combat with rule enforcement: + +**Rule Enforcement:** +```typescript +// Example: No Food rule +if (duelSystem.canEatFood(playerId)) { + // Allow food consumption +} else { + // Reject with "You cannot eat food in this duel." +} +``` + +**Available Checks:** +- `canUseRanged(playerId)` +- `canUseMelee(playerId)` +- `canUseMagic(playerId)` +- `canUseSpecialAttack(playerId)` +- `canUsePrayer(playerId)` +- `canUsePotions(playerId)` +- `canEatFood(playerId)` +- `canMove(playerId)` +- `canForfeit(playerId)` + +**Combat HUD:** +- Opponent health bar at top center +- Active rule indicators +- Forfeit button (if allowed) +- Disconnect status with countdown + +**Win Conditions:** +- Opponent's health reaches 0 +- Opponent forfeits (if allowed) +- Opponent disconnects for 30 seconds + +### 7. Result + +Duel resolution and stake transfer: + +```typescript +// Combat resolver handles: +// 1. Determine winner/loser +// 2. Transfer stakes to winner +// 3. Restore both players to full health +// 4. Teleport to lobby +// 5. Release arena +// 6. Emit completion event +combatResolver.resolveDuel(session, winnerId, loserId, reason); +``` + +**Result Modal:** +- Victory trophy (🏆) or defeat skull (💀) +- Items won/lost with gold values +- Total value summary +- Forfeit indicator if applicable + +--- + +## Arena System + +### Arena Pool + +6 arenas arranged in a 2×3 grid: + +```typescript +// From ArenaPoolManager.ts +const ARENA_CONFIG = { + arenaCount: 6, + columns: 2, + rows: 3, + arenaWidth: 20, + arenaLength: 24, + arenaGap: 4, + baseX: 70, + baseZ: 90, + baseY: 0, +}; +``` + +**Arena Features:** +- Automatic reservation when duel confirmed +- Collision walls prevent escape +- Flattened terrain for fair combat +- Forfeit pillars (if forfeit allowed) +- Released when duel ends + +### Spawn Points + +Each arena has 2 spawn points (north and south): + +```typescript +// Spawn offset from center +const spawnOffset = 8; + +const spawnPoints: [ArenaSpawnPoint, ArenaSpawnPoint] = [ + { x: centerX, y: baseY, z: centerZ - spawnOffset }, // North + { x: centerX, y: baseY, z: centerZ + spawnOffset }, // South +]; +``` + +Players face each other when teleported to arena. + +### Arena Bounds + +Movement is restricted to arena bounds: + +```typescript +interface ArenaBounds { + min: { x: number; y: number; z: number }; + max: { x: number; y: number; z: number }; +} +``` + +Collision walls are registered in the `CollisionMatrix` to prevent escape. + +--- + +## Server Architecture + +### DuelSystem + +Main orchestrator for the duel state machine: + +```typescript +export class DuelSystem { + // Sub-managers + private readonly pendingDuels: PendingDuelManager; + private readonly arenaPool: ArenaPoolManager; + private readonly sessionManager: DuelSessionManager; + private readonly combatResolver: DuelCombatResolver; + + // Public API + createChallenge(challengerId, challengerName, targetId, targetName); + respondToChallenge(challengeId, responderId, accept); + toggleRule(duelId, playerId, rule); + toggleEquipmentRestriction(duelId, playerId, slot); + acceptRules(duelId, playerId); + addStake(duelId, playerId, inventorySlot, itemId, quantity, value); + removeStake(duelId, playerId, stakeIndex); + acceptStakes(duelId, playerId); + acceptFinal(duelId, playerId); + forfeitDuel(playerId); + cancelDuel(duelId, reason, cancelledBy?); + + // Rule enforcement + isPlayerInActiveDuel(playerId): boolean; + canUseRanged(playerId): boolean; + canUseMelee(playerId): boolean; + canUseMagic(playerId): boolean; + canUsePrayer(playerId): boolean; + canEatFood(playerId): boolean; + canMove(playerId): boolean; + canForfeit(playerId): boolean; + getDuelOpponentId(playerId): string | null; +} +``` + +### DuelSessionManager + +Handles session CRUD operations: + +```typescript +export class DuelSessionManager { + createSession(challengerId, challengerName, targetId, targetName): string; + getSession(duelId): DuelSession | undefined; + getPlayerSession(playerId): DuelSession | undefined; + deleteSession(duelId): DuelSession | undefined; + isPlayerInDuel(playerId): boolean; + getOpponentId(playerId): string | undefined; + resetAcceptance(session): void; + setPlayerAcceptance(session, playerId, accepted): boolean; +} +``` + +### PendingDuelManager + +Tracks challenge requests with timeouts: + +```typescript +export class PendingDuelManager { + createChallenge(challengerId, challengerName, targetId, targetName); + acceptChallenge(challengeId, acceptingPlayerId); + declineChallenge(challengeId, decliningPlayerId); + cancelChallenge(challengeId); + cancelPlayerChallenges(playerId); + processTick(); // Distance checks, timeouts +} +``` + +### ArenaPoolManager + +Manages arena reservation and collision: + +```typescript +export class ArenaPoolManager { + reserveArena(duelId): number | null; + releaseArena(arenaId): boolean; + releaseArenaByDuelId(duelId): boolean; + getSpawnPoints(arenaId): [ArenaSpawnPoint, ArenaSpawnPoint]; + getArenaBounds(arenaId): ArenaBounds; + getArenaCenter(arenaId): { x: number; z: number }; + registerArenaWallCollision(collision: ICollisionMatrix): void; +} +``` + +### DuelCombatResolver + +Handles combat resolution and stake transfers: + +```typescript +export class DuelCombatResolver { + resolveDuel(session, winnerId, loserId, reason): DuelResolutionResult; + returnStakedItems(session): void; +} +``` + +--- + +## Duel Session Structure + +```typescript +export interface DuelSession { + duelId: string; + state: DuelState; // RULES | STAKES | CONFIRMING | COUNTDOWN | FIGHTING | FINISHED + + // Participants + challengerId: string; + challengerName: string; + targetId: string; + targetName: string; + + // Rules & Restrictions + rules: DuelRules; + equipmentRestrictions: EquipmentRestrictions; + + // Stakes + challengerStakes: StakedItem[]; + targetStakes: StakedItem[]; + + // Acceptance state (per screen) + challengerAccepted: boolean; + targetAccepted: boolean; + + // Arena + arenaId: number | null; + + // Timestamps + createdAt: number; + countdownStartedAt?: number; + fightStartedAt?: number; + finishedAt?: number; + + // Result + winnerId?: string; + forfeitedBy?: string; +} +``` + +--- + +## Network Protocol + +### Client → Server + +```typescript +// Challenge +socket.send("duel:challenge:send", { targetId }); +socket.send("duel:challenge:respond", { challengeId, accept }); + +// Rules +socket.send("duel:toggle:rule", { duelId, rule }); +socket.send("duel:toggle:equipment", { duelId, slot }); +socket.send("duel:accept:rules", { duelId }); + +// Stakes +socket.send("duel:add:stake", { duelId, inventorySlot, quantity }); +socket.send("duel:remove:stake", { duelId, stakeIndex }); +socket.send("duel:accept:stakes", { duelId }); + +// Confirmation +socket.send("duel:accept:final", { duelId }); + +// Combat +socket.send("duel:forfeit", { duelId }); +socket.send("duel:cancel", { duelId }); +``` + +### Server → Client + +```typescript +// UI updates via UI_UPDATE event +world.emit(EventType.UI_UPDATE, { + component: "duel", + data: { isOpen: true, duelId, opponent, isChallenger } +}); + +world.emit(EventType.UI_UPDATE, { + component: "duelRulesUpdate", + data: { duelId, rules, challengerAccepted, targetAccepted, modifiedBy } +}); + +world.emit(EventType.UI_UPDATE, { + component: "duelStakesUpdate", + data: { duelId, challengerStakes, targetStakes, challengerAccepted, targetAccepted, modifiedBy } +}); + +world.emit(EventType.UI_UPDATE, { + component: "duelStateChange", + data: { duelId, state } +}); + +world.emit(EventType.UI_UPDATE, { + component: "duelCompleted", + data: { duelId, won, opponentName, itemsReceived, itemsLost, totalValueWon, totalValueLost, forfeit } +}); + +// Countdown events +world.emit("duel:countdown:tick", { duelId, count, challengerId, targetId }); +world.emit("duel:fight:start", { duelId, challengerId, targetId, arenaId, bounds }); +``` + +--- + +## Configuration + +### Timing Constants + +All timing values use game ticks (600ms each): + +```typescript +// From packages/server/src/systems/DuelSystem/config.ts +export const CHALLENGE_TIMEOUT_TICKS = 50; // 30 seconds +export const DISCONNECT_TIMEOUT_TICKS = 50; // 30 seconds +export const SESSION_MAX_AGE_TICKS = 3000; // 30 minutes +export const DEATH_RESOLUTION_DELAY_TICKS = 8; // 4.8 seconds +export const CLEANUP_INTERVAL_TICKS = 17; // 10.2 seconds +``` + +### Distance Constants + +```typescript +export const CHALLENGE_DISTANCE_TILES = 15; // Max distance for challenge +``` + +### Arena Configuration + +```typescript +export const ARENA_COUNT = 6; +export const ARENA_GRID_COLS = 2; +export const ARENA_GRID_ROWS = 3; +export const ARENA_WIDTH = 20; +export const ARENA_LENGTH = 24; +export const ARENA_GAP_X = 4; +export const ARENA_GAP_Z = 4; +export const SPAWN_OFFSET_Z = 8; +``` + +### Spawn Locations + +```typescript +export const LOBBY_SPAWN_WINNER = { x: 102, y: 0, z: 60 }; +export const LOBBY_SPAWN_LOSER = { x: 108, y: 0, z: 60 }; +export const LOBBY_SPAWN_CENTER = { x: 105, y: 0, z: 60 }; +``` + +--- + +## Rule Enforcement + +The DuelSystem provides rule enforcement APIs for other systems: + +### Combat System Integration + +```typescript +// From CombatSystem.ts +handleMeleeAttack(attackerId, targetId) { + // Check if in duel with no melee rule + if (!duelSystem.canUseMelee(attackerId)) { + return { error: "You cannot use melee in this duel." }; + } + // ... proceed with attack +} +``` + +### Food System Integration + +```typescript +// From PlayerSystem.ts +handleEatFood(playerId, itemId) { + // Check if in duel with no food rule + if (!duelSystem.canEatFood(playerId)) { + return { error: "You cannot eat food in this duel." }; + } + // ... consume food +} +``` + +### Movement System Integration + +```typescript +// From TileMovementManager.ts +movePlayer(playerId, destination) { + // Check if in duel with no movement rule or during countdown + if (!duelSystem.canMove(playerId)) { + return; // Silently reject movement + } + // ... process movement +} +``` + +--- + +## Disconnect Handling + +### During Setup (RULES/STAKES/CONFIRMING) + +Immediate cancellation: +- Duel cancelled +- Stakes returned to both players +- Arena released (if reserved) +- Opponent notified + +### During Combat (FIGHTING) + +30-second grace period: + +```typescript +// Start disconnect timer +startDisconnectTimer(playerId, session); + +// If player reconnects within 30s +onPlayerReconnect(playerId); +// Timer cleared, duel continues + +// If timeout expires +// Auto-forfeit: disconnected player loses +``` + +**Special Case: No Forfeit Rule** +- Instant loss on disconnect (can't forfeit, so disconnect = loss) + +--- + +## Death Handling + +When a player dies during a duel: + +```typescript +// From DuelSystem.ts +handlePlayerDeath(playerId) { + const session = getPlayerDuel(playerId); + if (!session || session.state !== "FIGHTING") return; + + // Set state to FINISHED immediately (prevents double-resolution) + session.state = "FINISHED"; + + // Delay resolution for death animation (8 ticks = 4.8s) + setTimeout(() => { + resolveDuel(session, winnerId, loserId, "death"); + }, ticksToMs(DEATH_RESOLUTION_DELAY_TICKS)); +} +``` + +**Death Resolution:** +1. Winner receives loser's stakes +2. Both players restored to full health +3. Winner teleported to LOBBY_SPAWN_WINNER +4. Loser teleported to LOBBY_SPAWN_LOSER +5. Arena released +6. Session deleted + + +**OSRS-Accurate**: Players don't actually die in duels. Health is restored after combat ends. + + +--- + +## Stake Transfer + +Stakes are transferred atomically when duel ends: + +```typescript +// From DuelCombatResolver.ts +transferStakes(session, winnerId, loserId, winnerStakes, loserStakes) { + // Combine winner's own stakes + loser's stakes + const allWinnerItems = [...winnerStakes, ...loserStakes]; + + // Single atomic operation prevents race conditions + world.emit("duel:stakes:settle", { + playerId: winnerId, + ownStakes: winnerStakes, + wonStakes: loserStakes, + fromPlayerId: loserId, + reason: "duel_won", + }); +} +``` + +**Security:** +- Server-authoritative stake validation +- Atomic database transactions +- Audit logging for economic tracking +- Prevents item duplication exploits + +--- + +## Testing + +Comprehensive test coverage for all duel functionality: + +### Unit Tests + +**DuelSystem.test.ts** (1,066 lines): +- Challenge creation and validation +- State transitions through all screens +- Rule toggling and validation +- Equipment restriction toggling +- Stake operations (add/remove) +- Combat outcomes (death, forfeit) +- Disconnect handling +- Error cases and edge conditions + +**PendingDuelManager.test.ts** (456 lines): +- Challenge creation and validation +- Acceptance and decline +- Expiration cleanup +- Distance-based cancellation +- Player disconnect cleanup + +**ArenaPoolManager.test.ts** (233 lines): +- Arena initialization (6 arenas) +- Reservation and release +- Spawn point and bounds retrieval +- Pool exhaustion handling +- Grid layout validation + +### Integration Tests + +- Full duel flow from challenge to result +- Rule enforcement during combat +- Stake transfer verification +- Disconnect/reconnect scenarios + +--- + +## UI Components + +### DuelPanel + +Main duel interface with screen switching: + +```typescript +export function DuelPanel({ + state, + inventory, + onToggleRule, + onToggleEquipment, + onAcceptRules, + onAddStake, + onRemoveStake, + onAcceptStakes, + onAcceptFinal, + onCancel, +}: DuelPanelProps) +``` + +**Screens:** +- `RulesScreen` - Rules and equipment negotiation +- `StakesScreen` - Item staking with three panels (my stakes, opponent stakes, inventory) +- `ConfirmScreen` - Final read-only review + +### DuelChallengeModal + +Incoming challenge popup: + +```typescript +export function DuelChallengeModal({ + state: { + visible, + challengeId, + fromPlayer: { id, name, level } + }, + onAccept, + onDecline, +}: DuelChallengeModalProps) +``` + +### DuelCountdown + +Full-screen countdown overlay: + +```typescript +export function DuelCountdown({ + state: { + visible, + count, // 3, 2, 1, 0 (FIGHT!) + opponentName + } +}: DuelCountdownProps) +``` + +**Features:** +- Large centered countdown number +- Color-coded stages (red → orange → yellow → green) +- Expanding ring pulse effect +- "FIGHT!" display on 0 + +### DuelHUD + +In-combat overlay: + +```typescript +export function DuelHUD({ + state: { + visible, + opponentName, + opponentHealth, + opponentMaxHealth, + rules, + opponentDisconnected, + disconnectCountdown + }, + onForfeit +}: DuelHUDProps) +``` + +**Features:** +- Opponent health bar with color coding +- Active rule indicators +- Forfeit button (if allowed) +- Disconnect status with countdown + +### DuelResultModal + +Post-duel result display: + +```typescript +export function DuelResultModal({ + state: { + visible, + won, + opponentName, + itemsReceived, + itemsLost, + totalValueWon, + totalValueLost, + forfeit + }, + onClose +}: DuelResultModalProps) +``` + +**Features:** +- Animated entrance with icon pop +- Victory trophy or defeat skull +- Items won/lost with gold values +- Total value summary + +--- + +## Audit Logging + +All duel outcomes are logged for economic tracking: + +```typescript +// From AuditLogger.ts +logDuelComplete( + duelId, + winnerId, + loserId, + loserStakes, + winnerStakes, + winnerReceivesValue, + reason +); + +logDuelCancelled( + duelId, + cancelledBy, + reason, + challengerId, + targetId, + challengerStakes, + targetStakes +); +``` + +**Logged Events:** +- Duel completion (winner, loser, stakes transferred) +- Duel cancellation (if stakes were involved) +- Large stake transfers (≥1M gold value) + +--- + +## Related Documentation + +- [Combat System](/wiki/game-systems/combat) - Combat mechanics and damage formulas +- [Inventory System](/wiki/game-systems/inventory) - Item staking and transfers +- [Movement System](/wiki/game-systems/movement) - Arena bounds and pathfinding +- [Death System](/wiki/game-systems/death) - Death handling and respawning From 36b1a2f315e789a540571844fc3933abed6ba783 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:41:33 +0000 Subject: [PATCH 0006/1495] Update devops/configuration.mdx --- devops/configuration.mdx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/devops/configuration.mdx b/devops/configuration.mdx index 53279b1a..b30ccaa8 100644 --- a/devops/configuration.mdx +++ b/devops/configuration.mdx @@ -93,6 +93,17 @@ ASSET_FORGE_PORT=3400 ASSET_FORGE_API_PORT=3401 ``` +## Website Environment + +```bash +# External URLs +NEXT_PUBLIC_DOCS_URL=https://hyperscape-ai.mintlify.app/ +NEXT_PUBLIC_GAME_URL=https://play.hyperscape.club +NEXT_PUBLIC_DISCORD_URL=https://discord.gg/f4ZwhAbKye +NEXT_PUBLIC_TWITTER_URL=https://x.com/hyperscapeai +NEXT_PUBLIC_GITHUB_URL=https://github.com/hyperscape-ai +``` + ## Port Allocation All services have unique default ports: From 85c4da4a94a1e5412c07c1fc531348b25c72b69c Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:41:48 +0000 Subject: [PATCH 0007/1495] Update devops/configuration.mdx --- devops/configuration.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/devops/configuration.mdx b/devops/configuration.mdx index b30ccaa8..aa8e63ae 100644 --- a/devops/configuration.mdx +++ b/devops/configuration.mdx @@ -111,6 +111,7 @@ All services have unique default ports: | Port | Service | Environment Variable | Started By | |------|---------|---------------------|------------| | 3333 | Game Client | `VITE_PORT` | `bun run dev` | +| 3334 | Website | - | `bun run dev:website` | | 5555 | Game Server | `PORT` | `bun run dev` | | 8080 | Asset CDN | - | Docker | | 5432 | PostgreSQL | - | Docker | From 73dbb8a33b4413a1ed63e79b36296d67b18d92fd Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:42:16 +0000 Subject: [PATCH 0008/1495] Update wiki/game-systems/combat.mdx --- wiki/game-systems/combat.mdx | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/wiki/game-systems/combat.mdx b/wiki/game-systems/combat.mdx index 741911bd..47bd2676 100644 --- a/wiki/game-systems/combat.mdx +++ b/wiki/game-systems/combat.mdx @@ -25,11 +25,24 @@ export const COMBAT_CONSTANTS = { MELEE_RANGE_STANDARD: 1, // Cardinal only (N/S/E/W) MELEE_RANGE_HALBERD: 2, // Can attack diagonally RANGED_RANGE: 10, // Maximum ranged attack distance + MAGIC_RANGE: 10, // Maximum magic attack distance // Attack speeds (in ticks) DEFAULT_ATTACK_SPEED_TICKS: 4, // 2.4 seconds (standard sword) - FAST_ATTACK_SPEED_TICKS: 3, // 1.8 seconds (scimitar, dagger) + FAST_ATTACK_SPEED_TICKS: 3, // 1.8 seconds (scimitar, dagger, shortbow) SLOW_ATTACK_SPEED_TICKS: 6, // 3.6 seconds (2H sword) + MAGIC_ATTACK_SPEED_TICKS: 5, // 3.0 seconds (standard spells) + + // Hit delay formulas (OSRS-accurate) + HIT_DELAY: { + MELEE: 1, + RANGED_BASE: 1, + RANGED_DISTANCE_OFFSET: 3, + RANGED_DISTANCE_DIVISOR: 6, // Formula: 1 + floor((3 + distance) / 6) + MAGIC_BASE: 1, + MAGIC_DISTANCE_OFFSET: 1, + MAGIC_DISTANCE_DIVISOR: 3, // Formula: 1 + floor((1 + distance) / 3) + }, // Damage MIN_DAMAGE: 0, @@ -40,6 +53,8 @@ export const COMBAT_CONSTANTS = { COMBAT_XP_PER_DAMAGE: 4, // 4 XP per damage for main skill HITPOINTS_XP_PER_DAMAGE: 1.33, // 1.33 XP for Constitution CONTROLLED_XP_PER_DAMAGE: 1.33, // Split across all combat skills + MAGIC_BASE_XP: 5.5, // Base XP for casting (varies by spell) + MAGIC_DAMAGE_XP_MULTIPLIER: 2, // 2 XP per damage for Magic }, // Food consumption (OSRS-accurate) @@ -49,11 +64,6 @@ export const COMBAT_CONSTANTS = { // Combat timeout COMBAT_TIMEOUT_TICKS: 16, // 9.6 seconds out of combat - - // Food consumption (OSRS-accurate) - EAT_DELAY_TICKS: 3, // 1.8s cooldown between eating - EAT_ATTACK_DELAY_TICKS: 3, // Attack delay when eating during combat - MAX_HEAL_AMOUNT: 99, // Maximum heal per food item }; ``` From 0ab515aa6f9b92a922856fb3bb7bc8e5137e1902 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:42:37 +0000 Subject: [PATCH 0009/1495] Update wiki/game-systems/combat.mdx --- wiki/game-systems/combat.mdx | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/wiki/game-systems/combat.mdx b/wiki/game-systems/combat.mdx index 47bd2676..fea79c6b 100644 --- a/wiki/game-systems/combat.mdx +++ b/wiki/game-systems/combat.mdx @@ -168,6 +168,8 @@ this.healPlayer(playerId, healAmount); Combat styles determine which skill gains XP and provide stat bonuses. +### Melee Styles + | Style | XP Distribution | Bonus | |-------|-----------------|-------| | **Accurate** | Attack only | +3 Attack | @@ -185,6 +187,40 @@ const STYLE_BONUSES: Record = { }; ``` +### Ranged Styles + +| Style | Speed | Accuracy | Range | XP Distribution | +|-------|-------|----------|-------|-----------------| +| **Accurate** | Base | +3 Ranged | Normal | Ranged + Constitution | +| **Rapid** | -1 tick | Normal | Normal | Ranged + Constitution | +| **Longrange** | Base | Normal | +2 tiles | Ranged + Defense + Constitution | + +```typescript +// From WeaponStyleConfig.ts +export const RANGED_STYLE_BONUSES = { + accurate: { attackBonus: 3, speedModifier: 0, rangeModifier: 0, xpSplit: "ranged" }, + rapid: { attackBonus: 0, speedModifier: -1, rangeModifier: 0, xpSplit: "ranged" }, + longrange: { attackBonus: 0, speedModifier: 0, rangeModifier: 2, xpSplit: "ranged_defence" }, +}; +``` + +### Magic Styles + +| Style | Accuracy | Range | XP Distribution | +|-------|----------|-------|-----------------| +| **Accurate** | +3 Magic | Normal | Magic + Constitution | +| **Longrange** | +1 Magic | +2 tiles | Magic + Defense + Constitution | +| **Autocast** | Normal | Normal | Magic + Constitution | + +```typescript +// From WeaponStyleConfig.ts +export const MAGIC_STYLE_BONUSES = { + accurate: { attackBonus: 3, speedModifier: 0, rangeModifier: 0, xpSplit: "magic" }, + longrange: { attackBonus: 1, speedModifier: 0, rangeModifier: 2, xpSplit: "magic_defence" }, + autocast: { attackBonus: 0, speedModifier: 0, rangeModifier: 0, xpSplit: "magic" }, +}; +``` + --- ## Damage Calculation From f79576f55f1c7c088e5f820aa34cc9c3eefb583f Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:43:35 +0000 Subject: [PATCH 0010/1495] Update wiki/game-systems/combat.mdx --- wiki/game-systems/combat.mdx | 304 +++++++++++++++++++++++++++++++++++ 1 file changed, 304 insertions(+) diff --git a/wiki/game-systems/combat.mdx b/wiki/game-systems/combat.mdx index fea79c6b..fefb9300 100644 --- a/wiki/game-systems/combat.mdx +++ b/wiki/game-systems/combat.mdx @@ -299,6 +299,310 @@ const damage = didHit ? rng.damageRoll(maxHit) : 0; --- +## Ranged Combat + +Ranged combat uses bows and arrows with OSRS-accurate mechanics. + +### Requirements + +- **Bow** equipped in weapon slot +- **Arrows** equipped in ammo slot +- Sufficient Ranged level for bow + +### Available Bows (F2P) + +| Bow | Level | Attack Bonus | Speed | +|-----|-------|--------------|-------| +| Shortbow | 1 | +8 | 4 ticks (2.4s) | +| Oak Shortbow | 5 | +14 | 4 ticks | +| Willow Shortbow | 20 | +20 | 4 ticks | +| Maple Shortbow | 30 | +29 | 4 ticks | + +### Available Arrows (F2P) + +| Arrow | Strength Bonus | Required Bow Tier | +|-------|----------------|-------------------| +| Bronze | +7 | Any | +| Iron | +10 | Any | +| Steel | +16 | Oak+ | +| Mithril | +22 | Willow+ | +| Adamant | +31 | Maple+ | + +### Ranged Damage Formula + +```typescript +// Effective Ranged Level +const prayeredRanged = floor(rangedLevel * prayerMultiplier); +const effectiveRanged = prayeredRanged + styleBonus + 8; + +// Attack Roll +const attackRoll = effectiveRanged * (rangedAttackBonus + 64); + +// Defense Roll +const defenseRoll = (defenseLevel + 9) * (rangedDefenseBonus + 64); + +// Hit Chance +if (attackRoll > defenseRoll) { + hitChance = 1 - (defenseRoll + 2) / (2 * (attackRoll + 1)); +} else { + hitChance = attackRoll / (2 * (defenseRoll + 1)); +} + +// Max Hit +const maxHit = floor(0.5 + effectiveRanged * (arrowStrength + 64) / 640); + +// Damage (if hit succeeds) +const damage = didHit ? random(0, maxHit) : 0; +``` + +### Projectile System + +Ranged attacks create projectiles with OSRS-accurate hit delays: + +```typescript +// Hit delay formula +const hitDelayTicks = 1 + Math.floor((3 + distance) / 6); + +// Examples: +// Distance 0-2: 1 tick delay +// Distance 3-8: 2 tick delay +// Distance 9-14: 3 tick delay +``` + +**Projectile Rendering:** +- 3D arrow meshes with metal-colored tips +- Arc trajectory (straight line, no gravity arc in F2P) +- Rotates to face travel direction +- Delayed hit based on distance + +### Ammunition Consumption + +Arrows are consumed on every shot: + +```typescript +// 100% consumption rate (no Ava's device in F2P) +const consumed = 1; +const remaining = currentQuantity - consumed; +``` + + +Arrows are NOT recoverable. Each shot consumes 1 arrow permanently. + + +### Ranged XP + +```typescript +// XP per damage dealt +const rangedXP = damage * 4; +const constitutionXP = damage * 1.33; + +// Longrange style splits XP +if (style === "longrange") { + grantXP(playerId, "ranged", damage * 2); + grantXP(playerId, "defense", damage * 2); + grantXP(playerId, "constitution", damage * 1.33); +} +``` + +--- + +## Magic Combat + +Magic combat allows spellcasting with or without a staff. + +### Requirements + +- **Magic level** sufficient for the spell +- **Runes** in inventory (or infinite from elemental staff) +- **Spell selected** for autocast (optional) + + +**OSRS-Accurate**: You can cast spells without a staff. The staff provides magic attack bonus and infinite runes for its element. + + +### Available Spells (F2P) + +**Strike Tier (Levels 1-13):** + +| Spell | Level | Max Hit | XP | Runes | +|-------|-------|---------|----|----| +| Wind Strike | 1 | 2 | 5.5 | 1 Air, 1 Mind | +| Water Strike | 5 | 4 | 7.5 | 1 Air, 1 Water, 1 Mind | +| Earth Strike | 9 | 6 | 9.5 | 1 Air, 2 Earth, 1 Mind | +| Fire Strike | 13 | 8 | 11.5 | 2 Air, 3 Fire, 1 Mind | + +**Bolt Tier (Levels 17-35):** + +| Spell | Level | Max Hit | XP | Runes | +|-------|-------|---------|----|----| +| Wind Bolt | 17 | 9 | 13.5 | 2 Air, 1 Chaos | +| Water Bolt | 23 | 10 | 16.5 | 2 Air, 2 Water, 1 Chaos | +| Earth Bolt | 29 | 11 | 19.5 | 2 Air, 3 Earth, 1 Chaos | +| Fire Bolt | 35 | 12 | 22.5 | 3 Air, 4 Fire, 1 Chaos | + +### Elemental Staves + +Staves provide infinite runes for their element: + +| Staff | Infinite Runes | Magic Attack Bonus | +|-------|----------------|-------------------| +| Staff | - | +4 | +| Magic Staff | - | +10 | +| Staff of Air | Air | +10 | +| Staff of Water | Water | +10 | +| Staff of Earth | Earth | +10 | +| Staff of Fire | Fire | +10 | + +### Magic Damage Formula + +```typescript +// Effective Magic Level +const prayeredMagic = floor(magicLevel * prayerMultiplier); +const effectiveMagic = prayeredMagic + styleBonus + 8; + +// Attack Roll +const attackRoll = effectiveMagic * (magicAttackBonus + 64); + +// Defense Roll (Players) +const effectiveDefense = floor(0.7 * magicLevel + 0.3 * defenseLevel) + 9; +const defenseRoll = effectiveDefense * (magicDefenseBonus + 64); + +// Defense Roll (NPCs) +const defenseRoll = (magicLevel + 9) * (magicDefenseBonus + 64); + +// Hit Chance (same formula as melee/ranged) +if (attackRoll > defenseRoll) { + hitChance = 1 - (defenseRoll + 2) / (2 * (attackRoll + 1)); +} else { + hitChance = attackRoll / (2 * (defenseRoll + 1)); +} + +// Max Hit = spell base damage (no equipment modifiers in F2P) +const maxHit = spellBaseMaxHit; + +// Damage (if hit succeeds) +const damage = didHit ? random(0, maxHit) : 0; +``` + + +**Key Difference**: Magic defense for players uses 70% Magic level + 30% Defense level. NPCs only use Magic level. + + +### Rune Consumption + +Runes are consumed on each cast: + +```typescript +// Check required runes +const hasRunes = runeService.hasRequiredRunes(playerId, spell.runes); + +// Consume runes (accounting for elemental staff) +const infiniteRunes = getInfiniteRunesFromStaff(playerId); +const runesToConsume = spell.runes.filter(r => !infiniteRunes.includes(r.runeId)); +runeService.consumeRunes(playerId, runesToConsume); +``` + +### Autocast + +Players can select a spell for autocast: + +```typescript +// Set autocast spell +world.network.send("setAutocast", { spellId: "fire_strike" }); + +// Clear autocast +world.network.send("setAutocast", { spellId: null }); + +// Autocast is cleared on weapon swap +``` + +**Autocast Behavior:** +- Selected spell automatically casts when attacking +- Spell selection persists across sessions (saved in database) +- Cleared when weapon is changed +- Shown in Spells panel with checkmark + +### Spell Projectiles + +Magic spells render as colored orbs: + +```typescript +// From spell-visuals.ts +export const SPELL_VISUALS = { + wind_strike: { color: 0xcccccc, size: 0.25, glowIntensity: 0.25 }, + water_strike: { color: 0x3b82f6, size: 0.25, glowIntensity: 0.35 }, + earth_strike: { color: 0x8b4513, size: 0.25, glowIntensity: 0.2 }, + fire_strike: { color: 0xff4500, size: 0.25, glowIntensity: 0.5 }, + // Bolt spells are larger with more glow + wind_bolt: { color: 0xcccccc, size: 0.35, glowIntensity: 0.35 }, + water_bolt: { color: 0x3b82f6, size: 0.35, glowIntensity: 0.45 }, + earth_bolt: { color: 0x8b4513, size: 0.35, glowIntensity: 0.3 }, + fire_bolt: { color: 0xff4500, size: 0.35, glowIntensity: 0.6 }, +}; +``` + +**Hit Delay:** +```typescript +// Magic hit delay formula +const hitDelayTicks = 1 + Math.floor((1 + distance) / 3); + +// Examples: +// Distance 0-1: 1 tick delay +// Distance 2-4: 2 tick delay +// Distance 5-7: 3 tick delay +``` + +### Magic XP + +```typescript +// XP formula: Base spell XP + (2 × damage dealt) +const magicXP = spellBaseXP + (damage * 2); +const constitutionXP = damage * 1.33; + +// Example: Fire Strike (11.5 base XP) dealing 6 damage +// Magic XP: 11.5 + (6 * 2) = 23.5 +// Constitution XP: 6 * 1.33 = 8 +``` + +--- + +## Attack Type Detection + +The combat system automatically detects attack type from equipped weapon or selected spell: + +```typescript +// From ServerNetwork/index.ts +getPlayerAttackType(playerId): AttackType { + // Check if player has a spell selected - if so, use magic regardless of weapon + const selectedSpell = playerEntity.data.selectedSpell; + if (selectedSpell) { + return AttackType.MAGIC; + } + + // Check equipped weapon + const weapon = equipmentSystem.getPlayerEquipment(playerId).weapon; + if (weapon?.attackType) { + return weapon.attackType; // MELEE, RANGED, or MAGIC + } + + // Check weapon type for legacy compatibility + if (weapon?.weaponType === WeaponType.BOW) { + return AttackType.RANGED; + } + if (weapon?.weaponType === WeaponType.STAFF || weapon?.weaponType === WeaponType.WAND) { + return AttackType.MAGIC; + } + + return AttackType.MELEE; +} +``` + + +**Spell Priority**: If a spell is selected for autocast, the attack type is MAGIC regardless of equipped weapon. This allows staffless casting (OSRS-accurate). + + +--- + ## Attack Range System ### Melee Range From cf18bd8dda466cf4aaefc2890c6196b1775a1c05 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:43:59 +0000 Subject: [PATCH 0011/1495] Update wiki/game-systems/combat.mdx --- wiki/game-systems/combat.mdx | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/wiki/game-systems/combat.mdx b/wiki/game-systems/combat.mdx index fefb9300..017f5189 100644 --- a/wiki/game-systems/combat.mdx +++ b/wiki/game-systems/combat.mdx @@ -798,6 +798,8 @@ When a mob dies: XP is granted based on damage dealt and combat style. +### Melee XP + ```typescript // From SkillsSystem.ts handleCombatKill(data: CombatKillData): void { @@ -834,6 +836,47 @@ handleCombatKill(data: CombatKillData): void { } ``` +### Ranged XP + +```typescript +// Ranged XP: 4 per damage +const rangedXP = damage * 4; +const constitutionXP = damage * 1.33; + +// Longrange style splits XP +if (style === "longrange") { + grantXP(playerId, "ranged", damage * 2); + grantXP(playerId, "defense", damage * 2); + grantXP(playerId, "constitution", damage * 1.33); +} else { + grantXP(playerId, "ranged", damage * 4); + grantXP(playerId, "constitution", damage * 1.33); +} +``` + +### Magic XP + +```typescript +// Magic XP: Base spell XP + (2 × damage dealt) +const magicXP = spellBaseXP + (damage * 2); +const constitutionXP = damage * 1.33; + +// Example: Fire Strike (11.5 base XP) dealing 6 damage +// Magic XP: 11.5 + (6 * 2) = 23.5 +// Constitution XP: 6 * 1.33 = 8 + +// Longrange style splits XP +if (style === "longrange") { + grantXP(playerId, "magic", spellBaseXP + damage); + grantXP(playerId, "defense", damage); + grantXP(playerId, "constitution", damage * 1.33); +} +``` + + +Magic grants XP even on a splash (0 damage). You still get the base spell XP, just no damage bonus. + + --- ## Food & Combat Interaction From 7bda94c04e93e3256f60d8480d3db1809e8c3756 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:45:15 +0000 Subject: [PATCH 0012/1495] Update wiki/game-systems/overview.mdx --- wiki/game-systems/overview.mdx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/wiki/game-systems/overview.mdx b/wiki/game-systems/overview.mdx index a26f06a6..f557a2e8 100644 --- a/wiki/game-systems/overview.mdx +++ b/wiki/game-systems/overview.mdx @@ -299,7 +299,10 @@ export enum EventType { - OSRS-accurate damage formulas, attack styles, aggro system, and death mechanics. + OSRS-accurate damage formulas, attack styles, aggro system, and death mechanics. Includes melee, ranged, and magic combat. + + + Player-versus-player dueling with rules negotiation, item stakes, and arena combat. RuneScape XP curves, leveling, combat level formula, and skill requirements. From ffd61926babea2f16162c7c623fdbbb47ff52982 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:45:37 +0000 Subject: [PATCH 0013/1495] Update packages/overview.mdx --- packages/overview.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/overview.mdx b/packages/overview.mdx index bec870e6..2b5abf1f 100644 --- a/packages/overview.mdx +++ b/packages/overview.mdx @@ -6,7 +6,7 @@ icon: "layout-grid" ## Monorepo Structure -Hyperscape is organized as a Turbo monorepo with 7 packages: +Hyperscape is organized as a Turbo monorepo with 8 packages: ``` packages/ @@ -16,6 +16,7 @@ packages/ ├── plugin-hyperscape/ # ElizaOS AI plugin (@hyperscape/plugin-hyperscape) ├── physx-js-webidl/ # PhysX WASM bindings (@hyperscape/physx-js-webidl) ├── asset-forge/ # AI asset generation (3d-asset-forge) +├── website/ # Marketing website (@hyperscape/website) └── docs-site/ # Documentation (Docusaurus) ``` From 14cb8ad467fa7ec8a1473033e27a74c9132c6ed1 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:45:52 +0000 Subject: [PATCH 0014/1495] Update packages/overview.mdx --- packages/overview.mdx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/overview.mdx b/packages/overview.mdx index 2b5abf1f..c15dd714 100644 --- a/packages/overview.mdx +++ b/packages/overview.mdx @@ -52,6 +52,9 @@ flowchart TD AI-powered 3D asset generation + + Marketing website with Next.js 15 and React Three Fiber + ## Build Order From f57290ecd9736ca2e343926142fccbaf54ea8e60 Mon Sep 17 00:00:00 2001 From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 21:46:40 +0000 Subject: [PATCH 0015/1495] Update packages/website.mdx --- packages/website.mdx | 393 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 393 insertions(+) create mode 100644 packages/website.mdx diff --git a/packages/website.mdx b/packages/website.mdx new file mode 100644 index 00000000..e71dc0e9 --- /dev/null +++ b/packages/website.mdx @@ -0,0 +1,393 @@ +--- +title: "website" +description: "Marketing website built with Next.js 15" +icon: "globe" +--- + +# @hyperscape/website + +The Hyperscape marketing website built with Next.js 15, featuring scroll animations, 3D effects, and responsive design. + +## Tech Stack + +| Technology | Purpose | +|------------|---------| +| **Next.js 15** | Static site generation with App Router | +| **React 19** | UI framework | +| **Tailwind CSS 4** | Utility-first styling | +| **Framer Motion** | Scroll animations and transitions | +| **GSAP** | Advanced animations | +| **Lenis** | Smooth scroll library | +| **React Three Fiber** | 3D background effects | +| **detect-gpu** | GPU capability detection | + +## Project Structure + +``` +packages/website/ +├── src/ +│ ├── app/ # Next.js App Router +│ │ ├── page.tsx # Landing page +│ │ ├── gold/page.tsx # $GOLD token page +│ │ ├── layout.tsx # Root layout +│ │ └── globals.css # Global styles +│ ├── components/ # React components +│ │ ├── Header.tsx # Navigation header +│ │ ├── Footer.tsx # Site footer +│ │ ├── Background.tsx # Reusable background +│ │ ├── Hero/ # Landing page hero +│ │ ├── Features/ # Features section +│ │ ├── CTA/ # Call-to-action +│ │ └── GoldToken/ # $GOLD token page +│ └── lib/ +│ └── fonts.ts # Font configuration +├── public/ +│ ├── images/ # Static images +│ └── fonts/ # Custom fonts +├── next.config.ts # Next.js configuration +├── tailwind.config.ts # Tailwind configuration +└── package.json +``` + +## Pages + +### Landing Page (`/`) + +Features: +- Hero section with logo, tagline, and CTA button +- Features grid with AI agents, classic MMORPG, and economy highlights +- Call-to-action banner with gradient overlay +- Responsive design for mobile and desktop + +### $GOLD Token Page (`/gold`) + +Features: +- Two-column hero layout with token image +- Scroll-style design with parchment aesthetic +- Token details (1 $GOLD = 1 gold in-game) +- Features grid (Play-to-Earn, Fair Launch, Community Driven) +- How It Works section +- CTA section with banner background + +## Components + +### Background + +Reusable background component with customizable image and opacity: + +```typescript + +``` + +**Features:** +- Horizontal gradient overlay +- Fixed background attachment +- Configurable opacity +- Per-page customization + +### Header + +Navigation header with logo and links: + +```typescript +
+``` + +**Links:** +- Play Now (game URL) +- Docs (documentation) +- $GOLD (token page) +- Discord, Twitter, GitHub (social) + +### Footer + +Site footer with links and social icons: + +```typescript +