mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-21 11:18:08 +00:00
docs: Add comprehensive RFID keycard lock system planning documentation
Planning documentation for new RFID keycard lock system feature: Documentation: - Complete planning docs in planning_notes/rfid_keycard/ - 7 planning documents (~16,000 words) - 90+ implementation tasks with estimates - Technical architecture and code design - Asset specifications and creation guides Assets Created: - 4 keycard sprite variants (CEO, Security, Maintenance, Generic) - RFID cloner device sprite (Flipper Zero-inspired) - 2 icon assets (RFID icon, NFC waves) - Helper scripts for placeholder creation System Documentation: - Lock & key system architecture reference - Lock & key quick start guide Feature Overview: - New lock type: "rfid" - New items: keycard, rfid_cloner - New minigame: Flipper Zero-style RFID reader/cloner - Ink tag support: # clone_keycard:name|hex - Inventory integration: Click cards to clone - Two modes: Unlock (tap/emulate) and Clone (read/save) Implementation: - Estimated time: 91 hours (~11 working days) - 11 new files, 5 modified files - Full test plan included - Ready for immediate implementation All placeholder assets functional for development. Documentation provides complete roadmap from planning to deployment.
This commit is contained in:
285
docs/LOCK_KEY_QUICK_START.md
Normal file
285
docs/LOCK_KEY_QUICK_START.md
Normal file
@@ -0,0 +1,285 @@
|
||||
# Lock & Key System - Quick Start Guide
|
||||
|
||||
## Quick Reference: Where Things Are
|
||||
|
||||
### Adding a Locked Door/Room
|
||||
File: `scenario.json`
|
||||
```json
|
||||
{
|
||||
"rooms": {
|
||||
"office": {
|
||||
"locked": true,
|
||||
"lockType": "key", // key, pin, password, biometric, bluetooth
|
||||
"requires": "office_key", // ID of key/password/etc.
|
||||
"keyPins": [32, 28, 35, 30] // Optional: for pin tumbler locks
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Adding a Key to Inventory
|
||||
File: `scenario.json`
|
||||
```json
|
||||
{
|
||||
"startItemsInInventory": [
|
||||
{
|
||||
"type": "key",
|
||||
"name": "Office Key",
|
||||
"key_id": "office_key", // Must match "requires" in lock
|
||||
"keyPins": [32, 28, 35, 30], // Must match lock's keyPins
|
||||
"observations": "A brass key"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Adding a Key in a Container
|
||||
File: `scenario.json`
|
||||
```json
|
||||
{
|
||||
"type": "safe",
|
||||
"locked": true,
|
||||
"lockType": "password",
|
||||
"requires": "1234",
|
||||
"contents": [
|
||||
{
|
||||
"type": "key",
|
||||
"name": "CEO Key",
|
||||
"key_id": "ceo_key",
|
||||
"keyPins": [40, 35, 38, 32, 36]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## System Entry Points
|
||||
|
||||
### When Player Clicks a Locked Object
|
||||
```
|
||||
interactions.js: handleObjectInteraction(sprite)
|
||||
→ Gets scenario data from sprite
|
||||
→ Calls: handleUnlock(lockable, 'door'|'item')
|
||||
```
|
||||
|
||||
### Unlock System Decision Tree
|
||||
```
|
||||
unlock-system.js: handleUnlock()
|
||||
├─ Lock type: 'key'
|
||||
│ ├─ Has keys in inventory? → Key Selection Minigame
|
||||
│ └─ Has lockpick? → Lockpicking Minigame
|
||||
├─ Lock type: 'pin' → PIN Entry Minigame
|
||||
├─ Lock type: 'password' → Password Entry Minigame
|
||||
├─ Lock type: 'biometric' → Check fingerprint samples
|
||||
└─ Lock type: 'bluetooth' → Check BLE devices
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Code Files
|
||||
|
||||
### Primary Lock Logic
|
||||
```
|
||||
js/systems/
|
||||
├─ unlock-system.js [400 lines] Main unlock handler
|
||||
├─ key-lock-system.js [370 lines] Key-lock mappings & cuts
|
||||
├─ inventory.js [630 lines] Item/key management
|
||||
└─ interactions.js [600 lines] Object interaction detector
|
||||
```
|
||||
|
||||
### Lockpicking Minigame (Pin Tumbler)
|
||||
```
|
||||
js/minigames/lockpicking/
|
||||
├─ lockpicking-game-phaser.js [300+ lines] Main controller
|
||||
├─ pin-management.js [150+ lines] Pin creation
|
||||
├─ key-operations.js [100+ lines] Key handling
|
||||
├─ hook-mechanics.js [100+ lines] Tension simulation
|
||||
├─ pin-visuals.js [80+ lines] Rendering
|
||||
└─ lock-configuration.js [60+ lines] State storage
|
||||
```
|
||||
|
||||
### Minigame Framework
|
||||
```
|
||||
js/minigames/framework/
|
||||
├─ minigame-manager.js [180 lines] Framework lifecycle
|
||||
├─ base-minigame.js [150+ lines] Base class
|
||||
└─ index.js [96 lines] Registration
|
||||
```
|
||||
|
||||
### Conversation Integration
|
||||
```
|
||||
js/minigames/helpers/
|
||||
└─ chat-helpers.js [326 lines] Ink tag processing
|
||||
js/minigames/person-chat/
|
||||
└─ person-chat-minigame.js [300+ lines] Conversation UI
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Data Structures
|
||||
|
||||
### Pin Tumbler Lock (During Gameplay)
|
||||
```javascript
|
||||
pin = {
|
||||
index: 0,
|
||||
binding: 2, // Which pin sets first (0-3)
|
||||
isSet: false,
|
||||
keyPinLength: 32, // Lock pin height (pixels)
|
||||
driverPinLength: 43, // Spring pin height
|
||||
keyPinHeight: 0, // Current key pin position
|
||||
container: Phaser.Container
|
||||
}
|
||||
```
|
||||
|
||||
### Key Data (In Inventory)
|
||||
```javascript
|
||||
key = {
|
||||
scenarioData: {
|
||||
type: 'key',
|
||||
name: 'Office Key',
|
||||
key_id: 'office_key',
|
||||
keyPins: [32, 28, 35, 30], // Lock pin heights this key opens
|
||||
observations: 'A brass key'
|
||||
},
|
||||
objectId: 'inventory_key_office_key'
|
||||
}
|
||||
```
|
||||
|
||||
### Lock Requirements (From Scenario)
|
||||
```javascript
|
||||
lockRequirements = {
|
||||
lockType: 'key', // Type of lock
|
||||
requires: 'office_key', // Key ID / password / etc.
|
||||
keyPins: [32, 28, 35, 30], // For scenario-defined locks
|
||||
difficulty: 'medium' // For lockpicking
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Workflows
|
||||
|
||||
### Scenario Designer: Add New Key-Protected Door
|
||||
|
||||
1. **Define the lock in room:**
|
||||
```json
|
||||
{
|
||||
"room_name": {
|
||||
"locked": true,
|
||||
"lockType": "key",
|
||||
"requires": "storage_key",
|
||||
"keyPins": [30, 32, 28, 35] // IMPORTANT: unique pin heights
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **Add key to inventory or container:**
|
||||
```json
|
||||
{
|
||||
"type": "key",
|
||||
"name": "Storage Key",
|
||||
"key_id": "storage_key", // Must match "requires"
|
||||
"keyPins": [30, 32, 28, 35] // Must match lock exactly
|
||||
}
|
||||
```
|
||||
|
||||
3. **Test: Player should see key icon when near lock**
|
||||
|
||||
### Scenario Designer: Add PIN-Protected Door
|
||||
|
||||
```json
|
||||
{
|
||||
"room_name": {
|
||||
"locked": true,
|
||||
"lockType": "pin",
|
||||
"requires": "4567" // The PIN code
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Scenario Designer: Add Password-Protected Safe
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "safe",
|
||||
"locked": true,
|
||||
"lockType": "password",
|
||||
"requires": "correct_password",
|
||||
"contents": [
|
||||
{ "type": "notes", "name": "Secret Document" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Ink Writer: Give Key During Conversation
|
||||
|
||||
In `.ink` file:
|
||||
```ink
|
||||
=== hub ===
|
||||
# speaker:npc
|
||||
Here's the key you'll need!
|
||||
# give_item:key|Storage Key
|
||||
|
||||
What else can I help with?
|
||||
```
|
||||
|
||||
This triggers:
|
||||
1. NPC gives item to player
|
||||
2. Opens container minigame showing the key
|
||||
3. Player can take it to inventory
|
||||
|
||||
---
|
||||
|
||||
## Debugging Tips
|
||||
|
||||
### Check Key-Lock Mappings
|
||||
In browser console:
|
||||
```javascript
|
||||
window.showKeyLockMappings() // Shows all key-lock pairs
|
||||
```
|
||||
|
||||
### Check Player Inventory
|
||||
```javascript
|
||||
console.log(window.inventory.items) // All items
|
||||
console.log(window.inventory.keyRing) // Keys specifically
|
||||
```
|
||||
|
||||
### Check Lock Requirements
|
||||
```javascript
|
||||
window.getLockRequirementsForDoor(doorSprite) // Door lock details
|
||||
window.getLockRequirementsForItem(item) // Item lock details
|
||||
```
|
||||
|
||||
### Force Unlock (Testing)
|
||||
```javascript
|
||||
window.DISABLE_LOCKS = true // Disables all locks temporarily
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Errors & Solutions
|
||||
|
||||
| Error | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| Key doesn't unlock door | `key_id` doesn't match `requires` | Ensure exact match |
|
||||
| Wrong pins in lock | `keyPins` mismatch | Key's keyPins must match lock's keyPins |
|
||||
| Key doesn't appear in inventory | Item not in `startItemsInInventory` | Add it to scenario or container |
|
||||
| Conversation tag not working | Tag format incorrect | Use `# action:param` format |
|
||||
| Minigame won't start | Framework not initialized | Check if MinigameFramework is loaded |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
For adding a new lock type (e.g., RFID/Keycard):
|
||||
|
||||
- [ ] Add case in `unlock-system.js` switch statement
|
||||
- [ ] Check inventory for matching keycard
|
||||
- [ ] Verify access level (if applicable)
|
||||
- [ ] Call `unlockTarget()` on success
|
||||
- [ ] Show appropriate alert messages
|
||||
- [ ] Update scenario schema with examples
|
||||
- [ ] Add documentation with tag examples
|
||||
- [ ] Test with example scenario
|
||||
|
||||
613
docs/LOCK_KEY_SYSTEM_ARCHITECTURE.md
Normal file
613
docs/LOCK_KEY_SYSTEM_ARCHITECTURE.md
Normal file
@@ -0,0 +1,613 @@
|
||||
# BreakEscape Lock & Key System Architecture
|
||||
|
||||
## Overview
|
||||
The lock and key system in BreakEscape supports multiple lock types with a hierarchical architecture. Keys work with pintumbler locks through a scenario-based configuration system that ensures consistency between keys and locks.
|
||||
|
||||
---
|
||||
|
||||
## 1. Lock Types Definition
|
||||
|
||||
### Supported Lock Types
|
||||
Locks are defined in the scenario JSON with the following types:
|
||||
|
||||
| Lock Type | Definition Location | Unlocking Method | Key File |
|
||||
|-----------|-------------------|------------------|----------|
|
||||
| **key** | Room `lockType` or Object `lockType` | Physical key or lockpicking | `/js/systems/key-lock-system.js` |
|
||||
| **pin** | Room/Object `lockType: "pin"` | PIN code entry | `/js/systems/unlock-system.js` (case: 'pin') |
|
||||
| **password** | Room/Object `lockType: "password"` | Password text entry | `/js/systems/unlock-system.js` (case: 'password') |
|
||||
| **biometric** | Room/Object `lockType: "biometric"` | Fingerprint scanning | `/js/systems/unlock-system.js` (case: 'biometric') |
|
||||
| **bluetooth** | Object `lockType: "bluetooth"` | Bluetooth device connection | `/js/systems/unlock-system.js` (case: 'bluetooth') |
|
||||
|
||||
### Lock Definition in Scenarios
|
||||
|
||||
**Door Lock Example (scenario3.json):**
|
||||
```json
|
||||
{
|
||||
"room_reception": {
|
||||
"locked": true,
|
||||
"lockType": "key",
|
||||
"requires": "briefcase_key", // Key ID that unlocks this
|
||||
"objects": [ ... ]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Object Lock Example:**
|
||||
```json
|
||||
{
|
||||
"type": "safe",
|
||||
"name": "Safe1",
|
||||
"locked": true,
|
||||
"lockType": "password",
|
||||
"requires": "TimeIsMoney_123" // Password required
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Pin Tumbler Lock Minigame Implementation
|
||||
|
||||
### Lockpicking Minigame Architecture
|
||||
The pintumbler minigame simulates a realistic lock picking experience with interactive pin manipulation.
|
||||
|
||||
**Main Implementation:** `/js/minigames/lockpicking/lockpicking-game-phaser.js`
|
||||
|
||||
### Key Components
|
||||
|
||||
#### 2.1 Pin Management (`pin-management.js`)
|
||||
- **createPins()**: Creates pin objects with binding order
|
||||
- **Pin Structure:**
|
||||
```javascript
|
||||
pin = {
|
||||
index: 0-4,
|
||||
binding: randomized binding order,
|
||||
isSet: false,
|
||||
keyPinLength: 25-65 pixels, // Lock pin height
|
||||
driverPinLength: 75 - keyPinLength, // Spring pin height
|
||||
originalHeight: keyPinLength,
|
||||
currentHeight: 0,
|
||||
container: Phaser container,
|
||||
keyPin: graphics object,
|
||||
driverPin: graphics object,
|
||||
spring: graphics object
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 Key Operations (`key-operations.js`)
|
||||
- Handles inserting and manipulating the key
|
||||
- Calculates how much each pin lifts based on key cut depth
|
||||
- Formula: `keyPinHeight = (key.cuts[i] / maxCutDepth) * keyPinLength`
|
||||
|
||||
#### 2.3 Hook Mechanics (`hook-mechanics.js`)
|
||||
- Simulates applying tension to the lock
|
||||
- Implements binding order mechanics
|
||||
- When tension applied, pins in binding order become "biddable"
|
||||
- Player can only lift set pins when tension applied
|
||||
|
||||
#### 2.4 Pin Visuals (`pin-visuals.js`)
|
||||
- Renders pins with visual feedback
|
||||
- Shows alignment with shear line (goal position)
|
||||
- Highlights when pins are set correctly
|
||||
|
||||
### Pintumbler Lock State
|
||||
|
||||
**Lock State Object:**
|
||||
```javascript
|
||||
lockState = {
|
||||
id: "lock_id",
|
||||
pinCount: 4,
|
||||
pinHeights: [32, 28, 35, 30], // Predefined from scenario keyPins
|
||||
difficulty: "medium",
|
||||
pins: [ /* pin objects */ ],
|
||||
bindingOrder: [2, 0, 3, 1], // Random order
|
||||
tensionApplied: false,
|
||||
success: false,
|
||||
progress: 0
|
||||
}
|
||||
```
|
||||
|
||||
### Pin Height Configuration
|
||||
|
||||
**From Scenario Data (keyPins):**
|
||||
```json
|
||||
{
|
||||
"room": {
|
||||
"locked": true,
|
||||
"lockType": "key",
|
||||
"requires": "office_key",
|
||||
"keyPins": [32, 28, 35, 30] // Exact pin heights for this lock
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `keyPins` array directly corresponds to the pintumbler lock's key pin lengths. These values are typically normalized to 25-65 pixel range during gameplay.
|
||||
|
||||
---
|
||||
|
||||
## 3. Key System
|
||||
|
||||
### Key Definition in Scenarios
|
||||
|
||||
**Key in Starting Inventory:**
|
||||
```json
|
||||
{
|
||||
"startItemsInInventory": [
|
||||
{
|
||||
"type": "key",
|
||||
"name": "Office Key",
|
||||
"key_id": "office_key",
|
||||
"keyPins": [32, 28, 35, 30], // Matches lock's pin heights
|
||||
"observations": "A brass key with a distinctive cut"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Key in Container:**
|
||||
```json
|
||||
{
|
||||
"type": "safe",
|
||||
"contents": [
|
||||
{
|
||||
"type": "key",
|
||||
"name": "Briefcase Key",
|
||||
"key_id": "briefcase_key",
|
||||
"keyPins": [40, 35, 38, 32, 36], // 5-pin lock
|
||||
"observations": "Found inside the safe"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Key-Lock Mapping System
|
||||
|
||||
**File:** `/js/systems/key-lock-system.js`
|
||||
|
||||
**Global Key-Lock Mappings:**
|
||||
```javascript
|
||||
window.keyLockMappings = {
|
||||
"office_key": {
|
||||
lockId: "office_room_lock",
|
||||
lockConfig: { /* pin config */ },
|
||||
keyName: "Office Key",
|
||||
roomId: "office",
|
||||
lockName: "Office Door"
|
||||
},
|
||||
"briefcase_key": {
|
||||
lockId: "briefcase_lock",
|
||||
lockConfig: { /* pin config */ },
|
||||
keyName: "Briefcase Key",
|
||||
roomId: "ceo_office",
|
||||
objectIndex: 0,
|
||||
lockName: "Briefcase"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Key Ring System
|
||||
|
||||
**Inventory Key Ring (`inventory.js`):**
|
||||
```javascript
|
||||
window.inventory.keyRing = {
|
||||
keys: [keySprite1, keySprite2, ...],
|
||||
slot: DOM element,
|
||||
itemImg: DOM image
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Multiple keys grouped into single "Key Ring" UI item
|
||||
- Key ring shows single key icon if 1 key, multiple key icon if 2+
|
||||
- Click key ring to see list of available keys
|
||||
- Tooltip shows key names
|
||||
|
||||
### Key Cut Generation
|
||||
|
||||
**File:** `/js/systems/key-lock-system.js` - `generateKeyCutsForLock()`
|
||||
|
||||
**Formula for Converting Lock Pin Heights to Key Cuts:**
|
||||
```javascript
|
||||
cutDepth = keyPinLength - gapFromKeyBladeTopToShearLine
|
||||
|
||||
// World coordinates:
|
||||
keyBladeTop_world = 175 // Top surface of inserted key blade
|
||||
shearLine_world = 155 // Where lock plug meets housing
|
||||
gap = 20 // Distance between them (175 - 155)
|
||||
|
||||
cutDepth = keyPinLength - 20 // So 65 pixel pin → 45 pixel cut
|
||||
clampedCut = Math.max(0, Math.min(110, cutDepth)) // 0-110 range
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Items System
|
||||
|
||||
### Item Definition
|
||||
|
||||
**File:** `/js/systems/inventory.js`
|
||||
|
||||
**Item Types Supported:**
|
||||
- `key` - Physical key for locks
|
||||
- `key_ring` - Container for multiple keys
|
||||
- `lockpick` - Lockpicking tool
|
||||
- `phone` - Phone for chat conversations
|
||||
- `notes` - Written notes/clues
|
||||
- `workstation` - Crypto analysis tool
|
||||
- `fingerprint_kit` - Biometric collection
|
||||
- `bluetooth_scanner` - Bluetooth device scanner
|
||||
- `pc`, `safe`, `tablet` - Interactive objects
|
||||
- `notepad` - Inventory notepad
|
||||
|
||||
### Item Inventory Structure
|
||||
|
||||
**Inventory Object:**
|
||||
```javascript
|
||||
window.inventory = {
|
||||
items: [DOM img elements],
|
||||
container: HTMLElement,
|
||||
keyRing: {
|
||||
keys: [key items],
|
||||
slot: DOM element,
|
||||
itemImg: DOM image
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Item Data Structure:**
|
||||
```javascript
|
||||
inventoryItem = {
|
||||
scenarioData: {
|
||||
type: "key",
|
||||
name: "Office Key",
|
||||
key_id: "office_key",
|
||||
keyPins: [32, 28, 35, 30],
|
||||
locked: false,
|
||||
lockType: "key",
|
||||
observations: "A brass key"
|
||||
},
|
||||
name: "key",
|
||||
objectId: "inventory_key_office_key"
|
||||
}
|
||||
```
|
||||
|
||||
### Item Interaction Flow
|
||||
|
||||
1. **Item Added to Inventory:** `addToInventory(sprite)`
|
||||
- Creates DOM slot and image
|
||||
- Special handling for keys → key ring
|
||||
- Stores scenario data with item
|
||||
- Emits `item_picked_up` event
|
||||
|
||||
2. **Item Clicked:** `handleObjectInteraction(item)`
|
||||
- Checks item type
|
||||
- Triggers appropriate minigame/action
|
||||
|
||||
3. **Key Ring Interaction:** `handleKeyRingInteraction(keyRingItem)`
|
||||
- Single key: opens lock selection
|
||||
- Multiple keys: shows tooltip with key list
|
||||
|
||||
---
|
||||
|
||||
## 5. Minigame Triggering & Management
|
||||
|
||||
### Minigame Framework
|
||||
|
||||
**File:** `/js/minigames/framework/minigame-manager.js`
|
||||
|
||||
**Architecture:**
|
||||
```javascript
|
||||
window.MinigameFramework = {
|
||||
mainGameScene: null,
|
||||
currentMinigame: null,
|
||||
registeredScenes: {
|
||||
'lockpicking': LockpickingMinigamePhaser,
|
||||
'pin': PinMinigame,
|
||||
'password': PasswordMinigame,
|
||||
'person-chat': PersonChatMinigame,
|
||||
// ... more minigames
|
||||
},
|
||||
|
||||
startMinigame(sceneType, container, params),
|
||||
endMinigame(success, result),
|
||||
registerScene(sceneType, SceneClass)
|
||||
}
|
||||
```
|
||||
|
||||
### Registered Minigames
|
||||
|
||||
| Minigame | Scene Type | File | Purpose |
|
||||
|----------|-----------|------|---------|
|
||||
| Lockpicking | `lockpicking` | `lockpicking/lockpicking-game-phaser.js` | Pin tumbler lock picking |
|
||||
| PIN Entry | `pin` | `pin/pin-minigame.js` | Numeric PIN code entry |
|
||||
| Password | `password` | `password/password-minigame.js` | Text password entry |
|
||||
| Person Chat | `person-chat` | `person-chat/person-chat-minigame.js` | In-person NPC conversations |
|
||||
| Phone Chat | `phone-chat` | `phone-chat/phone-chat-minigame.js` | Phone-based conversations |
|
||||
| Container | `container` | `container/container-minigame.js` | Open containers/safes |
|
||||
| Notes | `notes` | `notes/notes-minigame.js` | View notes/evidence |
|
||||
| Biometrics | `biometrics` | `biometrics/biometrics-minigame.js` | Fingerprint scanning |
|
||||
| Bluetooth Scanner | `bluetooth-scanner` | `bluetooth/bluetooth-scanner-minigame.js` | BLE device scanning |
|
||||
|
||||
### Minigame Triggering Flow
|
||||
|
||||
**From Object Interaction:**
|
||||
```
|
||||
Object clicked → handleObjectInteraction()
|
||||
→ Checks scenarioData.type
|
||||
→ If key: Check if locked
|
||||
→ If locked & requires key: startKeySelectionMinigame()
|
||||
→ If locked & no key: Check for lockpick → startLockpickingMinigame()
|
||||
→ If container: handleContainerInteraction()
|
||||
→ If phone/notes/etc: startMinigame('type')
|
||||
```
|
||||
|
||||
**Lock Unlocking Flow:**
|
||||
```
|
||||
handleUnlock(lockable, type='door'|'item')
|
||||
→ getLockRequirements()
|
||||
→ Check lockType:
|
||||
case 'key':
|
||||
→ if keys available: startKeySelectionMinigame()
|
||||
→ else if lockpick: startLockpickingMinigame()
|
||||
case 'pin':
|
||||
→ startPinMinigame()
|
||||
case 'password':
|
||||
→ startPasswordMinigame()
|
||||
case 'biometric':
|
||||
→ Check biometric samples, unlock if match
|
||||
case 'bluetooth':
|
||||
→ Check BLE devices, unlock if found & signal strong enough
|
||||
```
|
||||
|
||||
### Minigame Lifecycle
|
||||
|
||||
```
|
||||
1. startMinigame(sceneType, container, params)
|
||||
↓
|
||||
2. Minigame construction: new MinigameClass(container, params)
|
||||
↓
|
||||
3. init() - Setup UI structure
|
||||
↓
|
||||
4. start() - Begin minigame logic
|
||||
↓
|
||||
5. User interaction/gameplay
|
||||
↓
|
||||
6. onComplete callback(success, result)
|
||||
↓
|
||||
7. cleanup() - Remove DOM elements
|
||||
↓
|
||||
8. Re-enable main game input
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Ink Conversations Integration
|
||||
|
||||
### Ink Engine
|
||||
|
||||
**File:** `/js/systems/ink/ink-engine.js`
|
||||
|
||||
**InkEngine Wrapper:**
|
||||
```javascript
|
||||
export default class InkEngine {
|
||||
loadStory(storyJson) // Load compiled Ink JSON
|
||||
continue() // Get next dialogue + choices
|
||||
goToKnot(knotName) // Jump to specific knot
|
||||
choose(index) // Select a choice
|
||||
getVariable(name) // Read Ink variable
|
||||
setVariable(name, value) // Set Ink variable
|
||||
}
|
||||
```
|
||||
|
||||
### Ink Tag System
|
||||
|
||||
**Tags in Ink Stories (`equipment-officer.ink`):**
|
||||
```ink
|
||||
=== start ===
|
||||
# speaker:npc
|
||||
Welcome to equipment supply!
|
||||
-> hub
|
||||
|
||||
=== show_inventory ===
|
||||
# speaker:npc
|
||||
Here's everything we have!
|
||||
# give_npc_inventory_items
|
||||
What else can I help with?
|
||||
-> hub
|
||||
```
|
||||
|
||||
**Tag Processing:**
|
||||
|
||||
**File:** `/js/minigames/helpers/chat-helpers.js`
|
||||
|
||||
**Supported Action Tags:**
|
||||
|
||||
| Tag | Format | Effect |
|
||||
|-----|--------|--------|
|
||||
| `unlock_door` | `# unlock_door:room_id` | Unlocks specified door |
|
||||
| `give_item` | `# give_item:item_type` | Gives item to player |
|
||||
| `give_npc_inventory_items` | `# give_npc_inventory_items:type1,type2` | Opens container UI |
|
||||
| `set_objective` | `# set_objective:text` | Updates mission objective |
|
||||
| `add_note` | `# add_note:title\|content` | Adds note to collection |
|
||||
| `reveal_secret` | `# reveal_secret:id\|data` | Reveals game secret |
|
||||
| `trigger_minigame` | `# trigger_minigame:name` | Triggers a minigame |
|
||||
| `influence_increased` | `# influence_increased` | Increases NPC influence |
|
||||
| `influence_decreased` | `# influence_decreased` | Decreases NPC influence |
|
||||
| `speaker:player` | `# speaker:player` | Sets speaker to player |
|
||||
| `speaker:npc` | `# speaker:npc` | Sets speaker to NPC |
|
||||
|
||||
### Conversation Minigame Handoff
|
||||
|
||||
**Person Chat Flow:**
|
||||
```
|
||||
PersonChatMinigame.advance()
|
||||
→ call inkEngine.continue()
|
||||
→ get currentText + currentChoices + currentTags
|
||||
→ processGameActionTags(tags, ui)
|
||||
→ For each action tag, call NPCGameBridge methods
|
||||
→ Show notifications
|
||||
→ Display dialogue + choices to player
|
||||
```
|
||||
|
||||
**Tag Execution Example:**
|
||||
```javascript
|
||||
// In equipment-officer.ink: "# give_npc_inventory_items:lockpick,workstation"
|
||||
// becomes:
|
||||
window.NPCGameBridge.showNPCInventory(npcId, ['lockpick', 'workstation'])
|
||||
→ Triggers container minigame
|
||||
→ Shows items filtered by type
|
||||
→ Player can take items
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. RFID & Keycard System
|
||||
|
||||
### Current Status
|
||||
**RFID/Keycard is mentioned in documentation but NOT YET IMPLEMENTED in game logic.**
|
||||
|
||||
**References:**
|
||||
- `equipment-officer.ink` mentions "keycards for security"
|
||||
- No corresponding lock type or unlock logic currently
|
||||
|
||||
### Architecture for Implementation
|
||||
|
||||
**Proposed Keycard Lock Type:**
|
||||
|
||||
**Scenario Definition:**
|
||||
```json
|
||||
{
|
||||
"room": {
|
||||
"locked": true,
|
||||
"lockType": "keycard",
|
||||
"requires": "ceo_keycard", // Keycard ID
|
||||
"requiredAccessLevel": 3 // Optional: access level check
|
||||
},
|
||||
"objects": [
|
||||
{
|
||||
"type": "keycard",
|
||||
"name": "CEO Keycard",
|
||||
"key_id": "ceo_keycard",
|
||||
"accessLevel": 3,
|
||||
"accessRooms": ["ceo_office", "server_room"],
|
||||
"observations": "A magnetic keycard for building access"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Unlock Logic (to be added):**
|
||||
```javascript
|
||||
case 'keycard':
|
||||
const requiredCardId = lockRequirements.requires;
|
||||
const requiredLevel = lockRequirements.requiredAccessLevel || 1;
|
||||
|
||||
// Check inventory for matching keycard
|
||||
const keycard = window.inventory.items.find(item =>
|
||||
item.scenarioData?.type === 'keycard' &&
|
||||
item.scenarioData?.key_id === requiredCardId &&
|
||||
(item.scenarioData?.accessLevel || 1) >= requiredLevel
|
||||
);
|
||||
|
||||
if (keycard) {
|
||||
window.gameAlert(`Keycard accepted!`, 'success');
|
||||
unlockTarget(lockable, type);
|
||||
} else {
|
||||
window.gameAlert(`Access denied - keycard required`, 'error');
|
||||
}
|
||||
break;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Files Reference
|
||||
|
||||
### Lock/Key System Files
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `/js/systems/key-lock-system.js` | Key-lock mapping, cut generation |
|
||||
| `/js/systems/unlock-system.js` | Main unlock logic for all lock types |
|
||||
| `/js/systems/inventory.js` | Inventory management, key ring |
|
||||
| `/js/systems/interactions.js` | Object interaction detection |
|
||||
|
||||
### Lockpicking Minigame
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `/js/minigames/lockpicking/lockpicking-game-phaser.js` | Main minigame controller |
|
||||
| `/js/minigames/lockpicking/pin-management.js` | Pin creation and state |
|
||||
| `/js/minigames/lockpicking/key-operations.js` | Key insertion and manipulation |
|
||||
| `/js/minigames/lockpicking/hook-mechanics.js` | Tension and binding order |
|
||||
| `/js/minigames/lockpicking/pin-visuals.js` | Pin rendering |
|
||||
| `/js/minigames/lockpicking/key-geometry.js` | Key blade mathematics |
|
||||
| `/js/minigames/lockpicking/lock-configuration.js` | Lock state management |
|
||||
|
||||
### Minigame Framework
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `/js/minigames/framework/minigame-manager.js` | Framework lifecycle |
|
||||
| `/js/minigames/framework/base-minigame.js` | Base class for all minigames |
|
||||
| `/js/minigames/index.js` | Minigame registration |
|
||||
|
||||
### Conversation System
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `/js/minigames/person-chat/person-chat-minigame.js` | Person conversation controller |
|
||||
| `/js/minigames/person-chat/person-chat-conversation.js` | Conversation flow |
|
||||
| `/js/minigames/helpers/chat-helpers.js` | Tag processing |
|
||||
| `/js/systems/ink/ink-engine.js` | Ink story wrapper |
|
||||
|
||||
### Scenario Examples
|
||||
| File | Content |
|
||||
|------|---------|
|
||||
| `/scenarios/scenario3.json` | Key locks example |
|
||||
| `/scenarios/scenario1.json` | Biometric/Bluetooth/Password locks |
|
||||
| `/scenarios/scenario2.json` | PIN locks |
|
||||
|
||||
---
|
||||
|
||||
## Summary: Lock Unlock Flow Diagram
|
||||
|
||||
```
|
||||
Player clicks locked door/object
|
||||
↓
|
||||
handleObjectInteraction() / handleUnlock()
|
||||
↓
|
||||
getLockRequirements() - Extract lock type & requirements
|
||||
↓
|
||||
Switch on lockType:
|
||||
├─ 'key' →
|
||||
│ ├─ Has keys? → startKeySelectionMinigame()
|
||||
│ │ └─ Player selects key
|
||||
│ │ └─ Key cuts checked against lock pins
|
||||
│ │ └─ If match: unlock, show success
|
||||
│ │
|
||||
│ └─ Has lockpick? → startLockpickingMinigame()
|
||||
│ └─ Interactive pin tumbler UI
|
||||
│ └─ Player manipulates pins with hook
|
||||
│ └─ All pins set → shear line alignment
|
||||
│ └─ Lock opens
|
||||
│
|
||||
├─ 'pin' → startPinMinigame()
|
||||
│ └─ Numeric keypad UI
|
||||
│ └─ Enter PIN code
|
||||
│ └─ If correct: unlock
|
||||
│
|
||||
├─ 'password' → startPasswordMinigame()
|
||||
│ └─ Text input dialog
|
||||
│ └─ Enter password
|
||||
│ └─ If correct: unlock
|
||||
│
|
||||
├─ 'biometric' →
|
||||
│ └─ Check gameState.biometricSamples
|
||||
│ └─ If fingerprint quality ≥ threshold: unlock
|
||||
│
|
||||
└─ 'bluetooth' →
|
||||
└─ Check gameState.bluetoothDevices
|
||||
└─ If device found & signal strong: unlock
|
||||
↓
|
||||
unlockTarget(lockable, type)
|
||||
├─ If type='door': unlockDoor()
|
||||
└─ If type='item': set locked=false, show container if contents
|
||||
↓
|
||||
Emit 'door_unlocked'/'item_unlocked' event
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user