Skip to content

Commit 9b5a907

Browse files
authored
Merge pull request #60 from alihussain6692/mg-06-clean
MG-06: Network Architecture Diagram minigame for sis02_energy
2 parents 8ac9ce2 + 5bd3116 commit 9b5a907

11 files changed

Lines changed: 1542 additions & 6 deletions

File tree

app/views/break_escape/games/show.html.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
<link rel="stylesheet" href="/break_escape/css/esd-pushbutton-minigame.css">
4646
<link rel="stylesheet" href="/break_escape/css/command-board-minigame.css">
4747
<link rel="stylesheet" href="/break_escape/css/infusion-pump-minigame.css">
48+
<link rel="stylesheet" href="/break_escape/css/network-architecture-minigame.css">
4849
<link rel="stylesheet" href="/break_escape/css/text-file-minigame.css">
4950
<link rel="stylesheet" href="/break_escape/css/npc-barks.css">
5051
<link rel="stylesheet" href="/break_escape/css/objectives.css">
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
/* =========================================================
2+
NETWORK ARCHITECTURE DIAGRAM — MG-06
3+
Purdue Model visualization. Dark industrial aesthetic.
4+
Pixel-art style: no border-radius on UI chrome.
5+
SVG nodes use rx:2 for diagram readability only.
6+
========================================================= */
7+
8+
/* ── Container ─────────────────────────────────────────── */
9+
.nad-minigame-container {
10+
background: #070b14;
11+
height: 100%;
12+
box-sizing: border-box;
13+
}
14+
15+
.nad-game-container {
16+
height: 100%;
17+
box-sizing: border-box;
18+
}
19+
20+
.nad-wrap {
21+
display: flex;
22+
flex-direction: column;
23+
height: 100%;
24+
box-sizing: border-box;
25+
background: #070b14;
26+
}
27+
28+
/* ── Header ────────────────────────────────────────────── */
29+
.nad-header {
30+
display: flex;
31+
align-items: center;
32+
justify-content: space-between;
33+
gap: 16px;
34+
padding: 8px 14px;
35+
background: #0a0f1e;
36+
border-bottom: 2px solid #1e3a5a;
37+
flex-shrink: 0;
38+
flex-wrap: wrap;
39+
}
40+
41+
.nad-title {
42+
font-family: 'Press Start 2P', monospace;
43+
font-size: 8px;
44+
color: #e2e8f0;
45+
letter-spacing: 0.5px;
46+
line-height: 1.4;
47+
}
48+
49+
/* ── Legend ────────────────────────────────────────────── */
50+
.nad-legend {
51+
display: flex;
52+
align-items: center;
53+
gap: 14px;
54+
flex-wrap: wrap;
55+
flex-shrink: 0;
56+
}
57+
58+
.nad-leg-item {
59+
display: flex;
60+
align-items: center;
61+
gap: 5px;
62+
font-family: monospace;
63+
font-size: 10px;
64+
color: #94a3b8;
65+
}
66+
67+
.nad-leg-line {
68+
display: inline-block;
69+
width: 28px;
70+
height: 2px;
71+
flex-shrink: 0;
72+
}
73+
74+
.nad-leg-normal { background: #94a3b8; }
75+
.nad-leg-boundary { background: #f59e0b; }
76+
.nad-leg-legacy { background: #f97316;
77+
background: repeating-linear-gradient(90deg, #f97316 0, #f97316 6px, transparent 6px, transparent 10px); }
78+
.nad-leg-hardwired { background: #22c55e; }
79+
.nad-leg-attack { background: #ef4444; }
80+
81+
/* ── Body ──────────────────────────────────────────────── */
82+
.nad-body {
83+
display: flex;
84+
flex-direction: column;
85+
flex: 1;
86+
min-height: 0;
87+
overflow: hidden;
88+
}
89+
90+
.nad-svg-wrap {
91+
flex: 1;
92+
min-height: 0;
93+
overflow: auto;
94+
padding: 6px;
95+
box-sizing: border-box;
96+
}
97+
98+
.nad-svg {
99+
display: block;
100+
width: 100%;
101+
height: auto;
102+
min-width: 700px;
103+
}
104+
105+
/* ── SVG: Zones ────────────────────────────────────────── */
106+
.nad-zone {
107+
/* fill and stroke set inline per zone */
108+
}
109+
110+
.nad-zone-label {
111+
font-family: 'Press Start 2P', monospace;
112+
font-size: 6px;
113+
fill: #64748b;
114+
letter-spacing: 0.3px;
115+
pointer-events: none;
116+
}
117+
118+
/* ── SVG: Connection lines ─────────────────────────────── */
119+
.nad-line {
120+
fill: none;
121+
stroke-width: 1.5;
122+
transition: opacity 0.2s;
123+
}
124+
125+
.nad-line-normal { stroke: #475569; }
126+
.nad-line-boundary { stroke: #f59e0b; stroke-width: 2; }
127+
.nad-line-legacy { stroke: #f97316; stroke-width: 2; }
128+
.nad-line-hardwired { stroke: #22c55e; stroke-width: 2; }
129+
130+
/* Dim base lines when a path is active and they're not in it */
131+
.nad-line:not(.nad-line-in-path) {
132+
opacity: 1;
133+
transition: opacity 0.25s;
134+
}
135+
136+
.nad-line.nad-line-in-path {
137+
opacity: 1;
138+
}
139+
140+
.nad-line-label {
141+
font-family: monospace;
142+
font-size: 7px;
143+
fill: #64748b;
144+
text-anchor: middle;
145+
pointer-events: none;
146+
}
147+
148+
/* ── SVG: Attack path overlay lines ────────────────────── */
149+
.nad-line-attack {
150+
fill: none;
151+
stroke: #ef4444;
152+
stroke-width: 2.5;
153+
stroke-dasharray: 10 6;
154+
marker-end: url(#nad-arrow);
155+
opacity: 0;
156+
pointer-events: none;
157+
transition: opacity 0.3s;
158+
}
159+
160+
.nad-line-attack.nad-path-active {
161+
opacity: 1;
162+
animation: nad-march 0.6s linear infinite;
163+
}
164+
165+
@keyframes nad-march {
166+
from { stroke-dashoffset: 0; }
167+
to { stroke-dashoffset: -16; }
168+
}
169+
170+
/* ── SVG: Node groups ──────────────────────────────────── */
171+
.nad-node {
172+
transition: opacity 0.25s;
173+
}
174+
175+
.nad-node.nad-node-dim {
176+
opacity: 0.3;
177+
}
178+
179+
.nad-node-rect {
180+
stroke-width: 1.5;
181+
transition: stroke 0.2s, stroke-width 0.2s;
182+
}
183+
184+
/* Zone colouring */
185+
.nad-node-it .nad-node-rect { fill: #2a1515; stroke: #7f1d1d; }
186+
.nad-node-ot .nad-node-rect { fill: #1a1a08; stroke: #78650a; }
187+
.nad-node-safe .nad-node-rect{ fill: #0a1a0a; stroke: #15803d; }
188+
189+
/* Hover */
190+
.nad-node:hover .nad-node-rect {
191+
stroke: #e2e8f0;
192+
stroke-width: 2;
193+
}
194+
195+
/* Selected */
196+
.nad-node.nad-node-selected .nad-node-rect {
197+
stroke: #f59e0b;
198+
stroke-width: 2.5;
199+
}
200+
201+
/* Node text */
202+
.nad-node-label {
203+
font-family: monospace;
204+
font-size: 9px;
205+
fill: #e2e8f0;
206+
text-anchor: middle;
207+
pointer-events: none;
208+
}
209+
210+
.nad-node-sub {
211+
font-family: monospace;
212+
font-size: 7px;
213+
fill: #94a3b8;
214+
text-anchor: middle;
215+
pointer-events: none;
216+
}
217+
218+
.nad-node-warn {
219+
font-size: 9px;
220+
fill: #f59e0b;
221+
pointer-events: none;
222+
}
223+
224+
/* ── Detail panel ──────────────────────────────────────── */
225+
.nad-detail {
226+
background: #0a0f1e;
227+
border-top: 2px solid #1e3a5a;
228+
padding: 10px 14px;
229+
min-height: 110px;
230+
max-height: 170px;
231+
overflow-y: auto;
232+
flex-shrink: 0;
233+
box-sizing: border-box;
234+
font-family: monospace;
235+
font-size: 11px;
236+
color: #94a3b8;
237+
line-height: 1.5;
238+
}
239+
240+
.nad-detail-hint {
241+
color: #334155;
242+
font-style: italic;
243+
font-size: 11px;
244+
padding: 8px 0;
245+
}
246+
247+
.nad-detail-name {
248+
font-family: 'Press Start 2P', monospace;
249+
font-size: 8px;
250+
color: #e2e8f0;
251+
margin-bottom: 6px;
252+
line-height: 1.5;
253+
}
254+
255+
.nad-detail-desc {
256+
color: #94a3b8;
257+
margin-bottom: 8px;
258+
}
259+
260+
.nad-detail-vuln {
261+
background: #1c0a00;
262+
border: 1px solid #92400e;
263+
padding: 6px 8px;
264+
margin-bottom: 8px;
265+
}
266+
267+
.nad-detail-vuln-badge {
268+
font-family: 'Press Start 2P', monospace;
269+
font-size: 7px;
270+
color: #f59e0b;
271+
display: block;
272+
margin-bottom: 4px;
273+
}
274+
275+
.nad-detail-vuln-text {
276+
color: #fbbf24;
277+
font-size: 11px;
278+
line-height: 1.5;
279+
}
280+
281+
.nad-detail-paths-title {
282+
font-family: 'Press Start 2P', monospace;
283+
font-size: 7px;
284+
color: #64748b;
285+
margin-bottom: 6px;
286+
letter-spacing: 0.3px;
287+
}
288+
289+
.nad-detail-no-paths {
290+
color: #334155;
291+
font-style: italic;
292+
}
293+
294+
.nad-detail-path {
295+
border-left: 2px solid #ef4444;
296+
padding-left: 8px;
297+
margin-bottom: 6px;
298+
}
299+
300+
.nad-path-label {
301+
color: #ef4444;
302+
font-weight: bold;
303+
}
304+
305+
.nad-path-claim {
306+
color: #f59e0b;
307+
font-size: 10px;
308+
}
309+
310+
.nad-path-desc {
311+
color: #94a3b8;
312+
font-size: 10px;
313+
margin-top: 2px;
314+
}

public/break_escape/js/minigames/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export { BackupRecoveryMinigame } from './backup-recovery/backup-recovery-miniga
2626
export { CommandBoardMinigame } from './command-board/command-board-minigame.js';
2727
export { EsdPushbuttonMinigame } from './esd-pushbutton/esd-pushbutton-minigame.js';
2828
export { InfusionPumpMinigame } from './infusion-pump/infusion-pump-minigame.js';
29+
export { NetworkArchitectureMinigame } from './network-architecture/network-architecture-minigame.js';
2930

3031
// Initialize the global minigame framework for backward compatibility
3132
import { MinigameFramework } from './framework/minigame-manager.js';
@@ -96,6 +97,7 @@ import { SiemDashboardMinigame } from './siem/siem-dashboard-minigame.js';
9697
import { EhrTerminalMinigame } from './ehr-terminal/ehr-terminal-minigame.js';
9798
import { CommandBoardMinigame } from './command-board/command-board-minigame.js';
9899
import { InfusionPumpMinigame } from './infusion-pump/infusion-pump-minigame.js';
100+
import { NetworkArchitectureMinigame } from './network-architecture/network-architecture-minigame.js';
99101

100102
// Import ransomware display minigame
101103
import { RansomwareDisplayMinigame } from './ransomware-display/ransomware-display-minigame.js';
@@ -130,6 +132,7 @@ MinigameFramework.registerScene('backup-recovery', BackupRecoveryMinigame);
130132
MinigameFramework.registerScene('command-board', CommandBoardMinigame);
131133
MinigameFramework.registerScene('esd-pushbutton', EsdPushbuttonMinigame);
132134
MinigameFramework.registerScene('infusion-pump', InfusionPumpMinigame);
135+
MinigameFramework.registerScene('network-architecture', NetworkArchitectureMinigame);
133136

134137
// Make minigame functions available globally
135138
window.startNotesMinigame = startNotesMinigame;

0 commit comments

Comments
 (0)