1/** 2 * tutorial-steps.js 3 * Manages tutorial step definitions and validation logic 4 * SIMPLIFIED VERSION: Remove reliance on capture events 5 */ 6class TutorialStepManager { 7 constructor(tutorialService) { 8 this.tutorialService = tutorialService; 9 this.game = tutorialService.game; 10 this.board = tutorialService.board; 11 this.events = tutorialService.events; 12 13 // Current expected action 14 this.expectedActionIndex = 0; 15 this.stepCompleted = false; 16 this.isRetrying = false; 17 18 // Track if a move has been made 19 this.movePerformed = false; 20 21 // Step definitions 22 this.steps = this.defineSteps(); 23 } 24 25 /** 26 * Define all tutorial steps 27 */ 28 defineSteps() { 29 return [ 30 // Step 1: Basic Movement 31 { 32 id: "basic-movement", 33 title: "Basic Movement", 34 instructions: "You can move tokens in two ways: Click to select and click again to move, or drag and drop. Try moving your white token to an adjacent space.", 35 boardSetup: [ 36 { row: 2, col: 2, color: 'white' } 37 ], 38 initialHighlights: [{ row: 2, col: 2 }], 39 expectedActions: [ 40 { 41 type: 'select', 42 position: { row: 2, col: 2 }, 43 nextInstructions: "Now click on a highlighted cell to move your token.", 44 highlightPositions: [ 45 { row: 1, col: 2 }, // Up 46 { row: 3, col: 2 }, // Down 47 { row: 2, col: 1 }, // Left 48 { row: 2, col: 3 } // Right 49 ] 50 }, 51 { 52 type: 'move', 53 validDestinations: [ 54 { row: 1, col: 2 }, { row: 3, col: 2 }, 55 { row: 2, col: 1 }, { row: 2, col: 3 } 56 ], 57 nextInstructions: "Great! You can move to any adjacent empty space." 58 // Note: completesStep removed - handled by tutorialService 59 } 60 ] 61 }, 62 63 // Step 2: Turn Alternation 64 { 65 id: "turn-alternation", 66 title: "Taking Turns", 67 instructions: "In Pressure, players take turns. Make a move with your white token.", 68 boardSetup: [ 69 { row: 2, col: 1, color: 'white' }, 70 { row: 4, col: 3, color: 'black' } 71 ], 72 initialHighlights: [{ row: 2, col: 1 }], 73 expectedActions: [ 74 { 75 type: 'select', 76 position: { row: 2, col: 1 }, 77 nextInstructions: "Select a destination for your token." 78 }, 79 { 80 type: 'move', 81 nextInstructions: "Now the black player (AI) will take their turn." 82 // onComplete removed - handled by tutorialService 83 } 84 ] 85 }, 86 87 // Step 3: Basic Pushing 88 { 89 id: "basic-pushing", 90 title: "Basic Pushing", 91 instructions: "You can push tokens if there's an empty space at the end of the line. Try pushing the black token by moving right.", 92 boardSetup: [ 93 { row: 2, col: 1, color: 'white' }, 94 { row: 2, col: 2, color: 'black' } 95 // Empty space at 2,3 96 ], 97 initialHighlights: [{ row: 2, col: 1 }], 98 expectedActions: [ 99 { 100 type: 'select', 101 position: { row: 2, col: 1 }, 102 nextInstructions: "To push the black token, move your white token to the right into the black token's position.", 103 highlightPositions: [{ row: 2, col: 2 }] 104 }, 105 { 106 type: 'move', 107 validDestinations: [{ row: 2, col: 2 }], 108 errorMessage: "Please push the black token to the right. Try again.", 109 nextInstructions: "Pushed opponent tokens become inactive for their next turn (marked with red dot)." 110 // onComplete removed - handled by tutorialService 111 } 112 ] 113 }, 114 115 // Step 4: Multi-Token Pushing 116 { 117 id: "multi-token-pushing", 118 title: "Multi-Token Pushing", 119 instructions: "You can push multiple tokens at once. Try pushing this line of tokens to the right.", 120 boardSetup: [ 121 { row: 2, col: 0, color: 'white' }, 122 { row: 2, col: 1, color: 'black' }, 123 { row: 2, col: 2, color: 'white' }, 124 { row: 2, col: 3, color: 'black' } 125 // Empty space at 2,4 126 ], 127 initialHighlights: [{ row: 2, col: 0 }], 128 expectedActions: [ 129 { 130 type: 'select', 131 position: { row: 2, col: 0 }, 132 nextInstructions: "Now push the line of tokens by moving to the right into the black token's position.", 133 highlightPositions: [{ row: 2, col: 1 }] 134 }, 135 { 136 type: 'move', 137 validDestinations: [{ row: 2, col: 1 }], 138 errorMessage: "Please push the tokens to the right. Try again.", 139 nextInstructions: "Pushed opponent tokens become inactive after being pushed. Opponent cannot move them the next turn." 140 // onComplete removed - handled by tutorialService 141 } 142 ] 143 }, 144 145 // Step 5: Token Capture 146 { 147 id: "token-capture", 148 title: "Token Capture", 149 instructions: "When a token is surrounded on all four sides, it's captured. Move your token to
149surround the black token, to capture it.", 150 boardSetup: [ 151 { row: 2, col: 1, color: 'white' }, // Left of target 152 { row: 1, col: 2, color: 'white' }, // Above target 153 { row: 3, col: 2, color: 'white' }, // Below target 154 { row: 2, col: 2, color: 'black' }, // Target for capture 155 { row: 2, col: 4, color: 'white' } // Player's token to move 156 ], 157 initialHighlights: [{ row: 2, col: 4 }], 158 expectedActions: [ 159 { 160 type: 'select', 161 position: { row: 2, col: 4 }, 162 nextInstructions: "Move your token left to capture the black token.", 163 highlightPositions: [{ row: 2, col: 3 }] 164 }, 165 { 166 type: 'move', 167 validDestinations: [{ row: 2, col: 3 }], 168 errorMessage: "Please move your token left to complete the capture. Try again.", 169 nextInstructions: "Captured tokens turn blue and cannot be moved directly, but they can still be pushed."
170 // No onComplete or validateCapture - handled by tutorialService 171 } 172 ] 173 }, 174 175 // Step 6: Edge Capture Strategy 176 { 177 id: "edge-capture", 178 title: "Edge Capture Strategy", 179 instructions: "Board edges count as surroundings too! Position your token to complete the capture using fewer pieces.", 180 boardSetup: [ 181 { row: 4, col: 1, color: 'black' }, // Target for edge capture (moved to bottom) 182 { row: 4, col: 0, color: 'white' }, // Left of target 183 { row: 3, col: 1, color: 'white' }, // Above target 184 { row: 4, col: 3, color: 'white' } // Player's token to move 185 ], 186 initialHighlights: [{ row: 4, col: 3 }], 187 expectedActions: [ 188 { 189 type: 'select', 190 position: { row: 4, col: 3 }, 191 nextInstructions: "Move your token left to capture the black token using the board edge.", 192 highlightPositions: [{ row: 4, col: 2 }] 193 }, 194 { 195 type: 'move', 196 validDestinations: [{ row: 4, col: 2 }], 197 errorMessage: "Please move your token left to complete the edge capture. Try again.", 198 nextInstructions: "Using the board edges makes capturing easier - only 3 tokens needed at an edge, and just 2 in a corner!"
199 // No onComplete or validateCapture - handled by tutorialService 200 } 201 ] 202 }, 203 204 // Step 7: Victory Conditions - updated for pushing multiple tokens upward 205 { 206 id: "victory-conditions", 207 title: "Victory Conditions", 208 instructions: "To capture token you may also surround it with opponent tokens.", 209 boardSetup: [ 210 // New setup for pushing tokens upward 211 { row: 4, col: 2, color: 'white' }, // Player's token at bottom 212 { row: 3, col: 2, color: 'black' }, // Token above player 213 { row: 2, col: 2, color: 'black' }, // One more above 214 { row: 1, col: 1, color: 'black' }, // Token to capture 215 { row: 0, col: 1, color: 'white' }, // Above black 216 { row: 1, col: 0, color: 'white' }, // Left of black 217 { row: 2, col: 1, color: 'white' }, // Under of black 218 ], 219 initialHighlights: [{ row: 4, col: 2 }], 220 expectedActions: [ 221 { 222 type: 'select', 223 position: { row: 4, col: 2 }, 224 nextInstructions: "Try pushing multiple tokens upward to make the final capture.", 225 highlightPositions: [{ row: 3, col: 2 }] 226 }, 227 { 228 type: 'move', 229 validDestinations: [{ row: 3, col: 2 }], 230 errorMessage: "Please push upward. Try again.", 231 nextInstructions: "Great!"
232 // No onComplete or validateCapture - handled by tutorialService 233 } 234 ] 235 } 236 ]; 237 } 238 239 /** 240 * Get a specific step by index 241 */ 242 getStep(index) { 243 if (index < 0 || index >= this.steps.length) { 244 return null; 245 } 246 return this.steps[index]; 247 } 248 249 /** 250 * Get the total number of steps 251 */ 252 getTotalSteps() { 253 return this.steps.length; 254 } 255 256 /** 257 * Handle token selection 258 */ 259 handleTokenSelected(data) { 260 // Don't allow actions if move has already been performed 261 if (this.movePerformed) return; 262 263 if (!data || !data.position) return; 264 265 const step = this.getStep(this.tutorialService.currentStepIndex); 266 if (!step) return; 267 268 const expectedAction = step.expectedActions[this.expectedActionIndex]; 269 270 // If we're not expecting a selection, ignore this event 271 if (expectedAction.type !== 'select') { 272 return; 273 } 274 275 // Get the selected token 276 const token = this.board.getTokenAt(data.position.row, data.position.col); 277 if (!token || token.color !== 'white' || token.isCaptured) { 278 // Invalid selection 279 return; 280 } 281 282 // Check if this is the expected token 283 if (expectedAction.position && 284 (data.position.row !== expectedAction.position.row || 285 data.position.col !== expectedAction.position.col)) { 286 // Wrong token, highlight the correct one 287 this.tutorialService.applyHighlights([expectedAction.position]); 288 return; 289 } 290 291 // Valid selection - update instructions 292 if (expectedAction.nextInstructions) { 293 this.tutorialService.uiManager.showInstructions( 294 step.title, 295 expectedAction.nextInstructions, 296 this.tutorialService.currentStepIndex + 1, 297 this.getTotalSteps() 298 ); 299 } 300 301 // Apply new highlights if specified 302 if (expectedAction.highlightPositions) { 303 this.tutorialService.applyHighlights(expectedAction.highlightPositions); 304 } 305 306 // Advance to next expected action 307 this.expectedActionIndex++; 308 } 309 310 /** 311 * Reset the current step to try again 312 */ 313 retryCurrentStep() { 314 // Reset flags 315 this.isRetrying = false; 316 this.movePerformed = false; 317 318 // Reset to the beginning of the current step 319 this.tutorialService.loadStep(this.tutorialService.currentStepIndex); 320 } 321 322 /** 323 * Reset the expected action index 324 */ 325 resetExpectedAction() { 326 this.expectedActionIndex = 0; 327 this.isRetrying = false; 328 this.movePerformed = false; 329 this.stepCompleted = false; 330 } 331}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.