mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-20 13:50:46 +00:00
Refactor RFID handling and enhance game logic
- Updated `GamesController` and `Game` model to include RFID lock types in the filtering logic for 'requires' attributes, ensuring proper handling of biometric, bluetooth, and RFID types. - Improved `RFIDMinigame` to standardize card ID retrieval, supporting both `card_id` and `key_id`. - Enhanced `unlock-system.js` to prioritize physical keycard checks and streamline the unlocking process with detailed logging. - Adjusted scenario JSON files to replace `keyId` with `card_id` for consistency and added new lock requirements for various rooms.
This commit is contained in:
@@ -494,10 +494,13 @@ module BreakEscape
|
||||
def filter_requires_recursive(obj)
|
||||
case obj
|
||||
when Hash
|
||||
# Remove 'requires' for exploitable lock types (key/pin/password/rfid)
|
||||
# Keep it for biometric/bluetooth since they reference collectible items, not answers
|
||||
# Remove 'requires' for exploitable lock types (key/pin/password)
|
||||
# Keep it for biometric/bluetooth/rfid since they reference collectible items, not answers
|
||||
# - biometric: requires fingerprint owner name (e.g., "Mrs Moo")
|
||||
# - bluetooth: requires device MAC/name (e.g., "00:11:22:33:44:55")
|
||||
# - rfid: requires card IDs (e.g., ["master_keycard"])
|
||||
lock_type = obj['lockType']
|
||||
if lock_type && !%w[biometric bluetooth].include?(lock_type)
|
||||
if lock_type && !%w[biometric bluetooth rfid].include?(lock_type)
|
||||
obj.delete('requires')
|
||||
end
|
||||
|
||||
|
||||
@@ -519,10 +519,13 @@ module BreakEscape
|
||||
def filter_requires_and_contents_recursive(obj)
|
||||
case obj
|
||||
when Hash
|
||||
# Remove 'requires' for exploitable lock types (key/pin/password/rfid)
|
||||
# Keep it for biometric/bluetooth since they reference collectible items, not answers
|
||||
# Remove 'requires' for exploitable lock types (key/pin/password)
|
||||
# Keep it for biometric/bluetooth/rfid since they reference collectible items, not answers
|
||||
# - biometric: requires fingerprint owner name (e.g., "Mrs Moo")
|
||||
# - bluetooth: requires device MAC/name (e.g., "00:11:22:33:44:55")
|
||||
# - rfid: requires card IDs (e.g., ["master_keycard"])
|
||||
lock_type = obj['lockType']
|
||||
if lock_type && !%w[biometric bluetooth].include?(lock_type)
|
||||
if lock_type && !%w[biometric bluetooth rfid].include?(lock_type)
|
||||
obj.delete('requires')
|
||||
end
|
||||
|
||||
|
||||
@@ -98,8 +98,8 @@ export class RFIDMinigame extends MinigameScene {
|
||||
handleCardTap(card) {
|
||||
console.log('📡 Card tapped:', card.scenarioData?.name);
|
||||
|
||||
// Support both card_id (new) and key_id (legacy)
|
||||
const cardId = card.scenarioData?.card_id || card.scenarioData?.key_id || card.key_id;
|
||||
// Get card ID (standard: card_id, legacy: key_id)
|
||||
const cardId = card.scenarioData?.card_id || card.scenarioData?.key_id;
|
||||
const isCorrect = !this.isLockingAttempt || this.requiredCardIds.includes(cardId);
|
||||
|
||||
if (isCorrect) {
|
||||
@@ -127,7 +127,7 @@ export class RFIDMinigame extends MinigameScene {
|
||||
handleEmulate(savedCard) {
|
||||
console.log('📡 Emulating card:', savedCard.name);
|
||||
|
||||
// Support both card_id (new) and key_id (legacy)
|
||||
// Get card ID (standard: card_id, legacy: key_id)
|
||||
const cardId = savedCard.card_id || savedCard.key_id;
|
||||
const isCorrect = !this.isLockingAttempt || this.requiredCardIds.includes(cardId);
|
||||
|
||||
|
||||
@@ -392,10 +392,14 @@ export function handleUnlock(lockable, type) {
|
||||
item.scenarioData.type === 'keycard'
|
||||
);
|
||||
|
||||
// Check if any physical card matches
|
||||
const hasValidCard = keycards.some(card =>
|
||||
requiredCardIds.includes(card.scenarioData.card_id || card.scenarioData.key_id)
|
||||
// Helper to get card ID (standard: card_id, legacy: key_id)
|
||||
const getCardId = (cardData) => cardData.card_id || cardData.key_id;
|
||||
|
||||
// Find a matching physical keycard
|
||||
const matchingKeycard = keycards.find(card =>
|
||||
requiredCardIds.includes(getCardId(card.scenarioData))
|
||||
);
|
||||
const hasValidCard = !!matchingKeycard;
|
||||
|
||||
// Check for RFID cloner with saved cards
|
||||
const cloner = window.inventory.items.find(item =>
|
||||
@@ -408,7 +412,7 @@ export function handleUnlock(lockable, type) {
|
||||
|
||||
// Check if any saved card matches
|
||||
const hasValidClone = savedCards.some(card =>
|
||||
requiredCardIds.includes(card.card_id || card.key_id)
|
||||
requiredCardIds.includes(getCardId(card))
|
||||
);
|
||||
|
||||
console.log('RFID CHECK', {
|
||||
@@ -418,16 +422,32 @@ export function handleUnlock(lockable, type) {
|
||||
keycardsCount: keycards.length,
|
||||
savedCardsCount: savedCards.length,
|
||||
hasValidCard,
|
||||
hasValidClone
|
||||
hasValidClone,
|
||||
matchingKeycard: matchingKeycard?.scenarioData?.name
|
||||
});
|
||||
|
||||
if (keycards.length > 0 || savedCards.length > 0) {
|
||||
// PRIORITY 1: If player has a matching physical keycard, open door directly
|
||||
if (hasValidCard) {
|
||||
const cardName = matchingKeycard.scenarioData.name || 'Keycard';
|
||||
console.log(`RFID DIRECT UNLOCK with physical keycard: ${cardName}`);
|
||||
|
||||
// Notify server and unlock
|
||||
notifyServerUnlock(lockable, type, 'rfid').then(serverResponse => {
|
||||
unlockTarget(lockable, type, lockable.layer, serverResponse);
|
||||
window.gameAlert(`Door opened with ${cardName}`, 'success', 'Access Granted', 3000);
|
||||
}).catch(error => {
|
||||
console.error('RFID unlock failed:', error);
|
||||
window.gameAlert('Access denied', 'error', 'Error', 3000);
|
||||
});
|
||||
}
|
||||
// PRIORITY 2: If player has RFID cloner, open minigame to emulate or read cards
|
||||
else if (hasCloner) {
|
||||
// Start RFID minigame in unlock mode
|
||||
window.startRFIDMinigame(lockable, type, {
|
||||
mode: 'unlock',
|
||||
requiredCardIds: requiredCardIds, // Pass array
|
||||
requiredCardIds: requiredCardIds,
|
||||
acceptsUIDOnly: acceptsUIDOnly,
|
||||
availableCards: keycards,
|
||||
availableCards: keycards, // Cards they can read in the minigame
|
||||
hasCloner: hasCloner,
|
||||
onComplete: async (success) => {
|
||||
if (success) {
|
||||
@@ -440,7 +460,9 @@ export function handleUnlock(lockable, type) {
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
}
|
||||
// PRIORITY 3: No valid keycard and no cloner
|
||||
else {
|
||||
console.log('NO RFID CARDS OR CLONER AVAILABLE');
|
||||
window.gameAlert('Requires RFID keycard', 'error', 'Access Denied', 4000);
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
{
|
||||
"type": "keycard",
|
||||
"name": "Server Room Keycard",
|
||||
"keyId": "server_keycard",
|
||||
"card_id": "server_keycard",
|
||||
"takeable": true,
|
||||
"observations": "A keycard with 'SERVER ROOM' printed on it"
|
||||
}
|
||||
@@ -122,7 +122,7 @@
|
||||
]
|
||||
},
|
||||
"server_room": {
|
||||
"type": "room_server",
|
||||
"type": "room_servers",
|
||||
"connections": {
|
||||
"south": "flag_room"
|
||||
},
|
||||
|
||||
@@ -85,28 +85,6 @@
|
||||
"takeable": true,
|
||||
"observations": "A portable multi-tool for RFID scanning and emulation. Essential for this test."
|
||||
}
|
||||
],
|
||||
"doors": [
|
||||
{
|
||||
"roomId": "lobby",
|
||||
"connectedRoom": "em4100_room",
|
||||
"direction": "north",
|
||||
"x": 200,
|
||||
"y": 100,
|
||||
"locked": true,
|
||||
"lockType": "rfid",
|
||||
"requires": ["employee_badge"]
|
||||
},
|
||||
{
|
||||
"roomId": "lobby",
|
||||
"connectedRoom": "mifare_weak_room",
|
||||
"direction": "north",
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"locked": true,
|
||||
"lockType": "rfid",
|
||||
"requires": ["hotel_keycard"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"em4100_room": {
|
||||
@@ -116,6 +94,9 @@
|
||||
"south": "lobby",
|
||||
"north": "mifare_custom_room"
|
||||
},
|
||||
"locked": true,
|
||||
"lockType": "rfid",
|
||||
"requires": ["employee_badge"],
|
||||
"npcs": [],
|
||||
"objects": [
|
||||
{
|
||||
@@ -129,24 +110,6 @@
|
||||
"note_content": "You successfully cloned an EM4100 card!\n\nThis protocol:\n• 125kHz frequency\n• No encryption whatsoever\n• Instant clone - no attack needed\n• Used in: Old hotels, parking garages\n\nProceed north to test MIFARE Classic weak defaults.",
|
||||
"observations": "EM4100 test passed"
|
||||
}
|
||||
],
|
||||
"doors": [
|
||||
{
|
||||
"roomId": "em4100_room",
|
||||
"connectedRoom": "lobby",
|
||||
"direction": "south",
|
||||
"x": 200,
|
||||
"y": 500,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"roomId": "em4100_room",
|
||||
"connectedRoom": "mifare_custom_room",
|
||||
"direction": "north",
|
||||
"x": 300,
|
||||
"y": 100,
|
||||
"locked": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"mifare_weak_room": {
|
||||
@@ -156,6 +119,9 @@
|
||||
"south": "lobby",
|
||||
"north": "mifare_custom_room"
|
||||
},
|
||||
"locked": true,
|
||||
"lockType": "rfid",
|
||||
"requires": ["hotel_keycard"],
|
||||
"npcs": [],
|
||||
"objects": [
|
||||
{
|
||||
@@ -169,24 +135,6 @@
|
||||
"note_content": "You cloned a MIFARE Classic with default keys!\n\nThis protocol:\n• 13.56MHz NFC frequency\n• Uses encryption but keeps factory defaults (FFFFFFFFFFFF)\n• Dictionary attack succeeds instantly (~95% success)\n• Used in: Cheap hotels, old transit cards\n\nProceed north to test MIFARE Custom Keys (requires attack).",
|
||||
"observations": "MIFARE weak defaults test passed"
|
||||
}
|
||||
],
|
||||
"doors": [
|
||||
{
|
||||
"roomId": "mifare_weak_room",
|
||||
"connectedRoom": "lobby",
|
||||
"direction": "south",
|
||||
"x": 400,
|
||||
"y": 500,
|
||||
"locked": false
|
||||
},
|
||||
{
|
||||
"roomId": "mifare_weak_room",
|
||||
"connectedRoom": "mifare_custom_room",
|
||||
"direction": "north",
|
||||
"x": 300,
|
||||
"y": 100,
|
||||
"locked": false
|
||||
}
|
||||
]
|
||||
},
|
||||
"mifare_custom_room": {
|
||||
@@ -238,18 +186,6 @@
|
||||
"takeable": false,
|
||||
"observations": "A guard's corporate badge - can be scanned with RFID cloner"
|
||||
}
|
||||
],
|
||||
"doors": [
|
||||
{
|
||||
"roomId": "mifare_custom_room",
|
||||
"connectedRoom": "desfire_room",
|
||||
"direction": "north",
|
||||
"x": 300,
|
||||
"y": 100,
|
||||
"locked": true,
|
||||
"lockType": "rfid",
|
||||
"requires": ["corporate_badge"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"desfire_room": {
|
||||
@@ -258,6 +194,9 @@
|
||||
"connections": {
|
||||
"south": "mifare_custom_room"
|
||||
},
|
||||
"locked": true,
|
||||
"lockType": "rfid",
|
||||
"requires": ["corporate_badge"],
|
||||
"npcs": [
|
||||
{
|
||||
"id": "guard_high",
|
||||
@@ -311,16 +250,6 @@
|
||||
"takeable": true,
|
||||
"observations": "A master override card - universal access"
|
||||
}
|
||||
],
|
||||
"doors": [
|
||||
{
|
||||
"roomId": "desfire_room",
|
||||
"connectedRoom": "mifare_custom_room",
|
||||
"direction": "south",
|
||||
"x": 300,
|
||||
"y": 500,
|
||||
"locked": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,18 +59,6 @@
|
||||
"note_content": "All high-security areas require RFID badge access. Master keycards (FF prefix) have universal access. Standard employee badges (01 prefix) have limited access.\n\nThe guard in this room has the master keycard. If you have an RFID cloner, you can scan their badge to clone it.",
|
||||
"observations": "Instructions about the RFID security system"
|
||||
}
|
||||
],
|
||||
"doors": [
|
||||
{
|
||||
"roomId": "test_lobby",
|
||||
"connectedRoom": "test_secure",
|
||||
"direction": "north",
|
||||
"x": 300,
|
||||
"y": 100,
|
||||
"locked": true,
|
||||
"lockType": "rfid",
|
||||
"requires": ["master_keycard"]
|
||||
}
|
||||
]
|
||||
},
|
||||
"test_secure": {
|
||||
@@ -79,6 +67,9 @@
|
||||
"connections": {
|
||||
"south": "test_lobby"
|
||||
},
|
||||
"locked": true,
|
||||
"lockType": "rfid",
|
||||
"requires": ["master_keycard"],
|
||||
"npcs": [],
|
||||
"objects": [
|
||||
{
|
||||
@@ -106,16 +97,6 @@
|
||||
"takeable": true,
|
||||
"observations": "The CEO's personal keycard. Ultimate access level."
|
||||
}
|
||||
],
|
||||
"doors": [
|
||||
{
|
||||
"roomId": "test_secure",
|
||||
"connectedRoom": "test_lobby",
|
||||
"direction": "south",
|
||||
"x": 300,
|
||||
"y": 500,
|
||||
"locked": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user