mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-21 11:18:08 +00:00
docs(rfid): Add Ink integration and comprehensive variable documentation
Add Ink integration for RFID card protocols, allowing conversation scripts
to detect and respond to different card security levels and protocols.
## Ink Integration (Phase 5):
### 1. Added syncCardProtocolsToInk() Method
- Location: js/minigames/person-chat/person-chat-conversation.js
- Auto-syncs when syncItemsToInk() is called
- Generates rfid_data if card uses card_id pattern
- Syncs variables for each keycard NPC holds
### 2. Per-Card Variables Synced:
- {prefix}_protocol - Protocol name
- {prefix}_name - Card display name
- {prefix}_card_id - Logical identifier
- {prefix}_security - "low", "medium", "high"
- {prefix}_instant_clone - Boolean (EM4100, weak MIFARE)
- {prefix}_needs_attack - Boolean (custom key MIFARE)
- {prefix}_uid_only - Boolean (DESFire)
- {prefix}_uid - Card UID (MIFARE)
- {prefix}_hex - Card hex ID (EM4100)
### 3. Prefix Pattern:
- First card: card_protocol, card_name, card_card_id, etc.
- Second card: card2_protocol, card2_name, card2_card_id, etc.
## Documentation:
### scenarios/ink/README_RFID_VARIABLES.md (NEW)
Comprehensive guide for scenario designers covering:
1. **Variable Declarations** - Required Ink variable setup
2. **Variable Reference** - Complete table of all variables
3. **Protocol Characteristics** - Details for each of 4 protocols
4. **Usage Examples**:
- Simple EM4100 clone
- Multi-protocol detection
- Conditional dialogue based on protocol
5. **Ink Tags** - Suggested tag patterns for RFID actions
6. **Scenario JSON Format** - How to define keycards
7. **Tips for Scenario Designers** - Best practices
8. **Complete Example Scenario** - Full working Ink script
9. **Troubleshooting** - Common issues and solutions
## Example Ink Usage:
```ink
VAR card_protocol = ""
VAR card_security = ""
VAR card_instant_clone = false
{card_security == "low":
"This is a low-security card. Easy to clone!"
# clone_keycard:{card_card_id}
-> cloned
}
{card_needs_attack:
"Need to run Darkside attack..."
# save_uid_and_start_attack:{card_card_id}|{card_uid}
-> wait_for_attack
}
```
## Benefits:
1. **Scenario designers** can write protocol-aware dialogue
2. **NPCs** can react realistically to card security levels
3. **Players** get different experiences based on card type
4. **Automatic** - no manual variable management needed
## Files Modified:
- js/minigames/person-chat/person-chat-conversation.js
- scenarios/ink/README_RFID_VARIABLES.md (NEW)
## Next Steps:
- Create test scenarios for each protocol
- Add Ink tag handlers for suggested patterns
- Test with various card combinations
Phase 5 (Ink Integration) complete!
This commit is contained in:
@@ -120,29 +120,29 @@ export default class PersonChatConversation {
|
||||
*/
|
||||
syncItemsToInk() {
|
||||
if (!this.inkEngine || !this.inkEngine.story) return;
|
||||
|
||||
|
||||
const npc = this.npc;
|
||||
if (!npc || !npc.itemsHeld) return;
|
||||
|
||||
|
||||
const varState = this.inkEngine.story.variablesState;
|
||||
if (!varState._defaultGlobalVariables) return;
|
||||
|
||||
|
||||
// Count items by type
|
||||
const itemCounts = {};
|
||||
npc.itemsHeld.forEach(item => {
|
||||
itemCounts[item.type] = (itemCounts[item.type] || 0) + 1;
|
||||
});
|
||||
|
||||
|
||||
// Get all declared has_* variables from the story
|
||||
const declaredVars = Array.from(varState._defaultGlobalVariables.keys());
|
||||
const hasItemVars = declaredVars.filter(varName => varName.startsWith('has_'));
|
||||
|
||||
|
||||
// Sync all has_* variables - set to true if NPC has item, false if not
|
||||
hasItemVars.forEach(varName => {
|
||||
// Extract item type from variable name (e.g., "has_lockpick" -> "lockpick")
|
||||
const itemType = varName.replace(/^has_/, '');
|
||||
const hasItem = (itemCounts[itemType] || 0) > 0;
|
||||
|
||||
|
||||
try {
|
||||
this.inkEngine.setVariable(varName, hasItem);
|
||||
console.log(`✅ Synced ${varName} = ${hasItem} for NPC ${npc.id} (${itemCounts[itemType] || 0} items)`);
|
||||
@@ -150,6 +150,76 @@ export default class PersonChatConversation {
|
||||
console.warn(`⚠️ Could not sync ${varName}:`, err.message);
|
||||
}
|
||||
});
|
||||
|
||||
// Also sync card protocol information
|
||||
this.syncCardProtocolsToInk();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync RFID card protocol information to Ink variables
|
||||
* Allows Ink scripts to detect and respond to different card protocols
|
||||
*/
|
||||
syncCardProtocolsToInk() {
|
||||
if (!this.inkEngine || !this.npc || !this.npc.itemsHeld) return;
|
||||
|
||||
// Filter for keycards
|
||||
const keycards = this.npc.itemsHeld.filter(item => item.type === 'keycard');
|
||||
|
||||
// Get RFID data manager if available
|
||||
const dataManager = window.rfidDataManager || (window.RFIDDataManager ? new window.RFIDDataManager() : null);
|
||||
|
||||
keycards.forEach((card, index) => {
|
||||
const protocol = card.rfid_protocol || 'EM4100';
|
||||
const prefix = index === 0 ? 'card' : `card${index + 1}`;
|
||||
|
||||
// Ensure rfid_data exists (generate if using card_id)
|
||||
if (!card.rfid_data && card.card_id && dataManager) {
|
||||
card.rfid_data = dataManager.generateRFIDDataFromCardId(card.card_id, protocol);
|
||||
}
|
||||
|
||||
try {
|
||||
// Basic card info
|
||||
this.inkEngine.setVariable(`${prefix}_protocol`, protocol);
|
||||
this.inkEngine.setVariable(`${prefix}_name`, card.name || 'Card');
|
||||
this.inkEngine.setVariable(`${prefix}_card_id`, card.card_id || card.key_id || '');
|
||||
|
||||
// Security level (low, medium, high)
|
||||
let security = 'low';
|
||||
if (protocol === 'MIFARE_Classic_Custom_Keys') {
|
||||
security = 'medium';
|
||||
} else if (protocol === 'MIFARE_DESFire') {
|
||||
security = 'high';
|
||||
}
|
||||
this.inkEngine.setVariable(`${prefix}_security`, security);
|
||||
|
||||
// Simplified booleans for common checks
|
||||
const isInstantClone = protocol === 'EM4100' || protocol === 'MIFARE_Classic_Weak_Defaults';
|
||||
this.inkEngine.setVariable(`${prefix}_instant_clone`, isInstantClone);
|
||||
|
||||
const needsAttack = protocol === 'MIFARE_Classic_Custom_Keys';
|
||||
this.inkEngine.setVariable(`${prefix}_needs_attack`, needsAttack);
|
||||
|
||||
const isUIDOnly = protocol === 'MIFARE_DESFire';
|
||||
this.inkEngine.setVariable(`${prefix}_uid_only`, isUIDOnly);
|
||||
|
||||
// Set UID or hex based on protocol
|
||||
if (card.rfid_data?.uid) {
|
||||
this.inkEngine.setVariable(`${prefix}_uid`, card.rfid_data.uid);
|
||||
} else {
|
||||
this.inkEngine.setVariable(`${prefix}_uid`, '');
|
||||
}
|
||||
|
||||
if (card.rfid_data?.hex) {
|
||||
this.inkEngine.setVariable(`${prefix}_hex`, card.rfid_data.hex);
|
||||
} else {
|
||||
this.inkEngine.setVariable(`${prefix}_hex`, '');
|
||||
}
|
||||
|
||||
console.log(`✅ Synced ${prefix}: ${protocol} (card_id: ${card.card_id || card.key_id})`);
|
||||
} catch (err) {
|
||||
console.warn(`⚠️ Could not sync card protocol for ${prefix}:`, err.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
347
scenarios/ink/README_RFID_VARIABLES.md
Normal file
347
scenarios/ink/README_RFID_VARIABLES.md
Normal file
@@ -0,0 +1,347 @@
|
||||
# RFID Protocol Variables for Ink
|
||||
|
||||
This document describes the RFID card protocol variables that are automatically synced to Ink scripts when an NPC holds keycards.
|
||||
|
||||
## Overview
|
||||
|
||||
When an NPC has keycards in their `itemsHeld` array, the conversation system automatically syncs protocol-specific information to Ink variables. This allows your Ink scripts to react differently based on the card's security level and protocol.
|
||||
|
||||
## Required Variable Declarations
|
||||
|
||||
Add these to the top of your Ink script (adjust based on how many cards the NPC might have):
|
||||
|
||||
```ink
|
||||
// Card protocol info (auto-synced from NPC itemsHeld)
|
||||
VAR card_protocol = "" // Protocol name
|
||||
VAR card_name = "" // Display name
|
||||
VAR card_card_id = "" // Logical card ID
|
||||
VAR card_uid = "" // UID (for MIFARE cards)
|
||||
VAR card_hex = "" // Hex ID (for EM4100 cards)
|
||||
VAR card_security = "" // "low", "medium", "high"
|
||||
VAR card_instant_clone = false // true for EM4100 and weak MIFARE
|
||||
VAR card_needs_attack = false // true for custom key MIFARE
|
||||
VAR card_uid_only = false // true for DESFire
|
||||
|
||||
// For second card (if NPC has multiple keycards)
|
||||
VAR card2_protocol = ""
|
||||
VAR card2_name = ""
|
||||
// ... (same pattern as above)
|
||||
```
|
||||
|
||||
## Variable Reference
|
||||
|
||||
### Per-Card Variables
|
||||
|
||||
Each card gets a prefix: `card`, `card2`, `card3`, etc.
|
||||
|
||||
| Variable | Type | Description | Example Values |
|
||||
|----------|------|-------------|----------------|
|
||||
| `{prefix}_protocol` | string | RFID protocol name | `"EM4100"`, `"MIFARE_Classic_Weak_Defaults"`, `"MIFARE_Classic_Custom_Keys"`, `"MIFARE_DESFire"` |
|
||||
| `{prefix}_name` | string | Card display name | `"Employee Badge"`, `"Security Card"` |
|
||||
| `{prefix}_card_id` | string | Logical card identifier | `"employee_badge"`, `"master_card"` |
|
||||
| `{prefix}_security` | string | Security level | `"low"`, `"medium"`, `"high"` |
|
||||
| `{prefix}_instant_clone` | boolean | Can clone instantly? | `true` for EM4100 and weak MIFARE |
|
||||
| `{prefix}_needs_attack` | boolean | Needs key attack? | `true` for custom key MIFARE |
|
||||
| `{prefix}_uid_only` | boolean | UID-only emulation? | `true` for DESFire |
|
||||
| `{prefix}_uid` | string | Card UID (if MIFARE) | `"A1B2C3D4"` |
|
||||
| `{prefix}_hex` | string | Card hex ID (if EM4100) | `"01AB34CD56"` |
|
||||
|
||||
## Protocol Characteristics
|
||||
|
||||
### EM4100 (Low Security)
|
||||
- **Instant clone**: Yes
|
||||
- **Attack required**: No
|
||||
- **Full emulation**: Yes
|
||||
- **Use case**: Entry-level cards, parking garage, old hotel keys
|
||||
|
||||
```ink
|
||||
{card_protocol == "EM4100":
|
||||
+ [Scan badge]
|
||||
# clone_keycard:{card_card_id}
|
||||
Easy! This old 125kHz card clones instantly.
|
||||
-> cloned
|
||||
}
|
||||
```
|
||||
|
||||
### MIFARE Classic - Weak Defaults (Low Security)
|
||||
- **Instant clone**: Dictionary attack succeeds (~95%)
|
||||
- **Attack required**: Dictionary (instant)
|
||||
- **Full emulation**: Yes
|
||||
- **Use case**: Cheap hotels, old transit cards, poorly maintained systems
|
||||
|
||||
```ink
|
||||
{card_instant_clone && card_protocol == "MIFARE_Classic_Weak_Defaults":
|
||||
+ [Scan badge]
|
||||
# clone_keycard:{card_card_id}
|
||||
This card uses factory default keys - dictionary attack works!
|
||||
-> cloned
|
||||
}
|
||||
```
|
||||
|
||||
### MIFARE Classic - Custom Keys (Medium Security)
|
||||
- **Instant clone**: No
|
||||
- **Attack required**: Darkside (~30 seconds)
|
||||
- **Full emulation**: Yes (after cracking keys)
|
||||
- **Use case**: Corporate badges, banks, government facilities
|
||||
|
||||
```ink
|
||||
{card_needs_attack:
|
||||
+ [Scan badge]
|
||||
# save_uid_and_start_attack:{card_card_id}|{card_uid}
|
||||
Custom keys detected. Need to run Darkside attack...
|
||||
This will take about 30 seconds.
|
||||
-> wait_for_attack
|
||||
}
|
||||
```
|
||||
|
||||
### MIFARE DESFire (High Security)
|
||||
- **Instant clone**: No
|
||||
- **Attack required**: N/A (impossible to crack)
|
||||
- **Full emulation**: UID only (works on poorly-configured readers)
|
||||
- **Use case**: High-security government, military, modern banking
|
||||
|
||||
```ink
|
||||
{card_uid_only:
|
||||
+ [Try to scan]
|
||||
# save_uid_only:{card_card_id}|{card_uid}
|
||||
High security card - I can only save the UID.
|
||||
It might work on poorly-configured readers.
|
||||
-> uid_saved
|
||||
}
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Example 1: Simple EM4100 Clone
|
||||
|
||||
```ink
|
||||
=== meet_security_guard ===
|
||||
{has_keycard:
|
||||
+ [Ask about the badge]
|
||||
You see a badge clipped to their belt.
|
||||
{card_protocol == "EM4100":
|
||||
"It's just a basic proximity card. Nothing special."
|
||||
-> offer_to_scan
|
||||
}
|
||||
}
|
||||
|
||||
=== offer_to_scan ===
|
||||
+ [Offer to "check" their badge]
|
||||
You offer to scan their badge with your Flipper Zero.
|
||||
{card_instant_clone:
|
||||
# clone_keycard:{card_card_id}
|
||||
"Sure, go ahead!" They hold it out to you.
|
||||
Your device quickly reads and clones the card.
|
||||
-> cloned_success
|
||||
}
|
||||
```
|
||||
|
||||
### Example 2: Multi-Protocol Detection
|
||||
|
||||
```ink
|
||||
=== analyze_card ===
|
||||
{card_security == "low":
|
||||
"This is a low-security card. Easy to clone!"
|
||||
-> easy_clone
|
||||
}
|
||||
{card_security == "medium":
|
||||
"Medium security. I'll need to run an attack."
|
||||
-> needs_attack
|
||||
}
|
||||
{card_security == "high":
|
||||
"High security DESFire. I can only get the UID."
|
||||
-> uid_only
|
||||
}
|
||||
|
||||
=== easy_clone ===
|
||||
+ [Clone it]
|
||||
# clone_keycard:{card_card_id}
|
||||
Done! Cloned instantly.
|
||||
-> END
|
||||
|
||||
=== needs_attack ===
|
||||
+ [Run Darkside attack]
|
||||
# save_uid_and_start_attack:{card_card_id}|{card_uid}
|
||||
Starting attack... this will take about 30 seconds.
|
||||
-> wait_for_crack
|
||||
|
||||
=== uid_only ===
|
||||
+ [Save UID]
|
||||
# save_uid_only:{card_card_id}|{card_uid}
|
||||
UID saved. Might work on weak readers.
|
||||
-> END
|
||||
```
|
||||
|
||||
### Example 3: Conditional Dialogue Based on Protocol
|
||||
|
||||
```ink
|
||||
=== guard_conversation ===
|
||||
Guard: "This is my access badge."
|
||||
|
||||
+ [Ask about security]
|
||||
You: "What kind of badge is it?"
|
||||
{card_protocol == "EM4100":
|
||||
Guard: "Just a basic prox card. Works fine."
|
||||
// Easy target
|
||||
}
|
||||
{card_protocol == "MIFARE_Classic_Weak_Defaults":
|
||||
Guard: "It's a MIFARE card. Standard issue."
|
||||
// Still easy, but sounds more secure
|
||||
}
|
||||
{card_protocol == "MIFARE_Classic_Custom_Keys":
|
||||
Guard: "MIFARE Classic with custom encryption."
|
||||
// They know a bit about security
|
||||
}
|
||||
{card_protocol == "MIFARE_DESFire":
|
||||
Guard: "DESFire EV2. Military-grade security."
|
||||
// High-security environment
|
||||
}
|
||||
-> guard_conversation
|
||||
|
||||
+ {has_rfid_cloner} [Ask to scan it]
|
||||
{card_instant_clone:
|
||||
// Easy clone
|
||||
Guard: "Sure, go ahead."
|
||||
# clone_keycard:{card_card_id}
|
||||
-> cloned
|
||||
}
|
||||
{card_needs_attack:
|
||||
// Need attack
|
||||
Guard: "I don't know... this is secure."
|
||||
-> need_persuasion
|
||||
}
|
||||
{card_uid_only:
|
||||
// UID only
|
||||
Guard: "No way. This is a DESFire card."
|
||||
-> refused
|
||||
}
|
||||
```
|
||||
|
||||
## Ink Tags for RFID Actions
|
||||
|
||||
Use these tags to trigger RFID operations from Ink:
|
||||
|
||||
| Tag | Description | Example |
|
||||
|-----|-------------|---------|
|
||||
| `# clone_keycard:{card_id}` | Clone a card instantly | `# clone_keycard:employee_badge` |
|
||||
| `# save_uid_only:{card_id}\|{uid}` | Save UID only (DESFire) | `# save_uid_only:exec_card\|A1B2C3D4E5F6` |
|
||||
| `# save_uid_and_start_attack:{card_id}\|{uid}` | Start Darkside attack | `# save_uid_and_start_attack:secure_badge\|12345678` |
|
||||
|
||||
Note: The actual implementation of these tags depends on your tag handler. These are suggested patterns.
|
||||
|
||||
## Scenario JSON Format
|
||||
|
||||
Define keycards in your scenario using the simplified `card_id` format:
|
||||
|
||||
```json
|
||||
{
|
||||
"npcId": "security_guard",
|
||||
"itemsHeld": [
|
||||
{
|
||||
"type": "keycard",
|
||||
"card_id": "employee_badge",
|
||||
"rfid_protocol": "EM4100",
|
||||
"name": "Employee Badge"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
The technical RFID data (hex, UID, etc.) is generated automatically from `card_id`.
|
||||
|
||||
## Tips for Scenario Designers
|
||||
|
||||
1. **Check security level first**: Use `card_security` for quick branching
|
||||
2. **Use boolean helpers**: `card_instant_clone`, `card_needs_attack`, `card_uid_only` are easier than checking protocol names
|
||||
3. **Provide context**: NPCs with high-security cards should acknowledge the security ("This is a DESFire card")
|
||||
4. **Realistic behavior**: Security-conscious NPCs shouldn't casually hand over DESFire cards
|
||||
5. **Time pressure**: Custom key attacks take ~30 seconds - use this for tension
|
||||
|
||||
## Complete Example Scenario
|
||||
|
||||
```ink
|
||||
VAR card_protocol = ""
|
||||
VAR card_name = ""
|
||||
VAR card_card_id = ""
|
||||
VAR card_security = ""
|
||||
VAR card_instant_clone = false
|
||||
VAR card_needs_attack = false
|
||||
VAR card_uid_only = false
|
||||
VAR card_uid = ""
|
||||
VAR has_rfid_cloner = false
|
||||
|
||||
-> hotel_reception
|
||||
|
||||
=== hotel_reception ===
|
||||
You approach the hotel reception desk.
|
||||
{has_keycard:
|
||||
The receptionist has a {card_name} on a lanyard.
|
||||
|
||||
+ [Ask about room access]
|
||||
"All our rooms use RFID cards for security."
|
||||
{card_security == "low":
|
||||
"Basic prox cards. Nothing fancy."
|
||||
}
|
||||
{card_security == "medium":
|
||||
"We use MIFARE Classic with custom keys."
|
||||
}
|
||||
{card_security == "high":
|
||||
"DESFire cards. Top of the line security."
|
||||
}
|
||||
-> reception_menu
|
||||
}
|
||||
-> reception_menu
|
||||
|
||||
=== reception_menu ===
|
||||
+ [Ask to see their card]
|
||||
{card_instant_clone:
|
||||
"Sure!" They hold it out carelessly.
|
||||
# clone_keycard:{card_card_id}
|
||||
Your Flipper Zero quickly clones it.
|
||||
-> cloned_success
|
||||
}
|
||||
{card_needs_attack:
|
||||
"I... suppose?" They seem hesitant.
|
||||
You'll need to run an attack.
|
||||
-> attempt_attack
|
||||
}
|
||||
{card_uid_only:
|
||||
"No way. These are secure."
|
||||
You can only get the UID if you steal it.
|
||||
-> refused
|
||||
}
|
||||
|
||||
+ [Leave]
|
||||
-> END
|
||||
|
||||
=== cloned_success ===
|
||||
Success! You now have a copy of {card_name}.
|
||||
-> END
|
||||
|
||||
=== attempt_attack ===
|
||||
+ [Run Darkside attack]
|
||||
# save_uid_and_start_attack:{card_card_id}|{card_uid}
|
||||
Starting attack...
|
||||
[30 seconds pass]
|
||||
Success! All keys cracked.
|
||||
-> END
|
||||
|
||||
=== refused ===
|
||||
You'll need to find another way.
|
||||
-> END
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Variables not syncing?**
|
||||
- Ensure variables are declared at the top of your Ink script
|
||||
- Check console for sync messages: `✅ Synced card: ...`
|
||||
- Verify NPC has keycards in `itemsHeld` array
|
||||
|
||||
**Protocol always shows EM4100?**
|
||||
- Check that `rfid_protocol` is set in scenario JSON
|
||||
- Default protocol is EM4100 if not specified
|
||||
|
||||
**Boolean helpers not working?**
|
||||
- Make sure to declare boolean variables: `VAR card_instant_clone = false`
|
||||
- Don't use string comparisons for booleans
|
||||
Reference in New Issue
Block a user