Lashtw commited on
Commit
69e4fbd
·
verified ·
1 Parent(s): fb00883

Upload 9 files

Browse files
src/services/classroom.js CHANGED
@@ -175,13 +175,14 @@ export async function startChallenge(userId, roomCode, challengeId) {
175
  // Let's update timestamp to reflect "last worked on"
176
  await updateDoc(snapshot.docs[0].ref, {
177
  status: 'started',
 
178
  timestamp: serverTimestamp()
179
  });
180
  } else {
181
  // Create new progress entry with 'started' status
182
  await addDoc(progressRef, {
183
  userId,
184
- roomCode,
185
  challengeId,
186
  status: 'started',
187
  startedAt: serverTimestamp(), // Keep original start time if we want to track duration
@@ -488,28 +489,6 @@ export async function getUser(userId) {
488
  return snap.exists() ? snap.data() : null;
489
  }
490
 
491
- /**
492
- * Subscribes to a single user's progress for real-time updates
493
- * @param {string} userId
494
- * @param {Function} callback (progressMap) => void
495
- * @returns {Function} unsubscribe
496
- */
497
- export function subscribeToUserProgress(userId, callback) {
498
- const q = query(
499
- collection(db, PROGRESS_COLLECTION),
500
- where("userId", "==", userId)
501
- );
502
-
503
- return onSnapshot(q, (snapshot) => {
504
- const progressMap = {};
505
- snapshot.forEach(doc => {
506
- const data = doc.data();
507
- progressMap[data.challengeId] = data;
508
- });
509
- callback(progressMap);
510
- });
511
- }
512
-
513
  /**
514
  * Removes a user from the classroom (Kick)
515
  * @param {string} userId
 
175
  // Let's update timestamp to reflect "last worked on"
176
  await updateDoc(snapshot.docs[0].ref, {
177
  status: 'started',
178
+ roomCode: String(roomCode), // Ensure roomCode is updated
179
  timestamp: serverTimestamp()
180
  });
181
  } else {
182
  // Create new progress entry with 'started' status
183
  await addDoc(progressRef, {
184
  userId,
185
+ roomCode: String(roomCode),
186
  challengeId,
187
  status: 'started',
188
  startedAt: serverTimestamp(), // Keep original start time if we want to track duration
 
489
  return snap.exists() ? snap.data() : null;
490
  }
491
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
492
  /**
493
  * Removes a user from the classroom (Kick)
494
  * @param {string} userId
src/utils/monsterUtils.js CHANGED
@@ -441,18 +441,48 @@ export function getNextMonster(currentStage, likes, classSize, currentMonsterId
441
  // Filter potential monsters for this Stage
442
  let candidates = MONSTER_DEFS.filter(m => m.stage === currentStage);
443
 
444
- // Enforce Lineage (Family)
445
- if (currentFam && currentFam !== 'Origin') {
446
- // Filter candidates that belong to this Family tree
447
- // We need to know which families map to which.
448
- // Stage 1 -> Stage 2 Mapping:
449
- // Dust -> Dust (Trash/Slime/Tech)
450
- // Beast -> Beast (Wolf/Cat/Mech)
451
- // Spirit -> Spirit (Fire/Angel/Dragon)
452
-
453
- // In MONSTER_DEFS, 'fam' for Stage 2 is 'Dust', 'Beast', 'Spirit'.
454
- // So we just match strict equality.
455
- candidates = candidates.filter(m => m.fam === currentFam || m.fam.includes(currentFam));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456
  }
457
 
458
  // Filter by Tier (Suffix A/B/C)
 
441
  // Filter potential monsters for this Stage
442
  let candidates = MONSTER_DEFS.filter(m => m.stage === currentStage);
443
 
444
+ // 1. Strict Lineage by ID Pattern (L{stage}_{path})
445
+ // e.g. L1_A -> L2_A... / L2_AB -> L3_AB...
446
+ // This allows exact branching control as per design doc.
447
+ const isStandardId = currentMonsterId?.match(/^L\d+_[ABC]+$/);
448
+
449
+ if (isStandardId) {
450
+ // Extract path suffix (e.g. "A" from "L1_A", "AB" from "L2_AB")
451
+ const currentPath = currentMonsterId.split('_')[1];
452
+ // Filter candidates: Must start with L{nextStage}_{currentPath}
453
+ const strictMatches = candidates.filter(m => m.id.startsWith(`L${currentStage + 1}_${currentPath}`));
454
+
455
+ if (strictMatches.length > 0) {
456
+ candidates = strictMatches;
457
+ }
458
+ }
459
+ // 2. Fallback: Family Tree Logic (for custom IDs or Egg)
460
+ else if (currentFam && currentFam !== 'Origin') {
461
+ // Define Family Tree Mapping
462
+ const FAMILY_TREE = {
463
+ // Stage 1 Root: Low Tier (Red Path)
464
+ 'Dust': ['Dust', 'Glitch', 'Trash', 'Slime', 'Tech', 'Hacker', 'Virus'],
465
+ // Stage 1 Root: Mid Tier (Yellow Path)
466
+ 'Beast': ['Beast', 'Wolf', 'Cat', 'Mech', 'Mech Lion', 'Animal', 'Grunge', 'Royal', 'Warrior', 'Undead', 'Elemental'],
467
+ // Stage 1 Root: High Tier (Blue Path)
468
+ 'Spirit': ['Spirit', 'Ghost', 'Holy', 'Cosmos', 'Angel', 'Divin', 'Void', 'Undead']
469
+ };
470
+
471
+ // Find which root family the current family belongs to
472
+ let rootFamily = null;
473
+ for (const [root, children] of Object.entries(FAMILY_TREE)) {
474
+ if (root === currentFam || children.includes(currentFam) || children.some(c => currentFam.includes(c))) {
475
+ rootFamily = root;
476
+ break;
477
+ }
478
+ }
479
+
480
+ if (rootFamily) {
481
+ const allowedFamilies = FAMILY_TREE[rootFamily];
482
+ candidates = candidates.filter(m => {
483
+ return allowedFamilies.some(fam => m.fam.includes(fam) || fam.includes(m.fam));
484
+ });
485
+ }
486
  }
487
 
488
  // Filter by Tier (Suffix A/B/C)
src/views/StudentView.js CHANGED
@@ -698,10 +698,17 @@ window.triggerEvolution = async (currentStage, nextStage, likes, classSize, curr
698
  const { getNextMonster, generateMonsterSVG, MONSTER_STAGES } = await import("../utils/monsterUtils.js");
699
  const { updateUserMonster } = await import("../services/classroom.js");
700
 
 
701
  // Calculate Next Monster with Lineage
 
702
  const currentMonster = getNextMonster(currentStage, likes, classSize, currentMonsterId);
 
 
 
703
  const nextMonster = getNextMonster(nextStage, likes, classSize, currentMonsterId);
704
 
 
 
705
  const container = document.querySelector('#monster-container-fixed .pixel-monster');
706
  const containerWrapper = document.querySelector('#monster-container-fixed .pixel-art-container');
707
 
@@ -739,6 +746,7 @@ window.triggerEvolution = async (currentStage, nextStage, likes, classSize, curr
739
  containerWrapper.style.transition = 'filter 0.8s ease-out';
740
  containerWrapper.style.filter = 'drop-shadow(0 0 30px #ffffff) brightness(1.5)';
741
 
 
742
  setFrame(svgNext, false);
743
 
744
  setTimeout(async () => {
 
698
  const { getNextMonster, generateMonsterSVG, MONSTER_STAGES } = await import("../utils/monsterUtils.js");
699
  const { updateUserMonster } = await import("../services/classroom.js");
700
 
701
+
702
  // Calculate Next Monster with Lineage
703
+ // IMPORTANT: Ensure we pass currentMonsterId to enforce lineage!
704
  const currentMonster = getNextMonster(currentStage, likes, classSize, currentMonsterId);
705
+
706
+ // Next Stage: If evolving, we need to find what this specific monster turns into.
707
+ // We pass 'currentMonsterId' to the next stage calculation too, so it knows the family 'Origin'.
708
  const nextMonster = getNextMonster(nextStage, likes, classSize, currentMonsterId);
709
 
710
+ console.log("Evolving from:", currentMonster.name, "to:", nextMonster.name);
711
+
712
  const container = document.querySelector('#monster-container-fixed .pixel-monster');
713
  const containerWrapper = document.querySelector('#monster-container-fixed .pixel-art-container');
714
 
 
746
  containerWrapper.style.transition = 'filter 0.8s ease-out';
747
  containerWrapper.style.filter = 'drop-shadow(0 0 30px #ffffff) brightness(1.5)';
748
 
749
+ // Force SVG update to next monster for final state
750
  setFrame(svgNext, false);
751
 
752
  setTimeout(async () => {