kimhyunwoo commited on
Commit
9815e43
Β·
verified Β·
1 Parent(s): f855f39

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +74 -39
index.html CHANGED
@@ -338,13 +338,11 @@
338
  const achievementModal = document.getElementById('achievement-modal');
339
  const achievementModalText = document.getElementById('achievement-modal-text');
340
  const achievementModalCloseButton = achievementModal.querySelector('.modal-close-button');
341
-
342
  document.getElementById('start-button').addEventListener('click', () => startGame(false));
343
  loadButton.addEventListener('click', () => startGame(true));
344
  document.getElementById('restart-button').addEventListener('click', () => startGame(false));
345
  saveButton.addEventListener('click', saveGame);
346
  achievementModalCloseButton.addEventListener('click', () => achievementModal.style.display = 'none');
347
-
348
  function initGameDataEvents() {
349
  gameData.events = [
350
  { id: 'birth', text: "당신은 νƒœμ–΄λ‚¬μŠ΅λ‹ˆλ‹€! 🍼 세상에 온 것을 ν™˜μ˜ν•΄μš”. 무엇을 λ¨Όμ € ν•΄λ³ΌκΉŒμš”?", ageMin: 0, ageMax: 0, choices: [ { text: "응애! 울기 😭", effects: { happiness: -2, health: +1 }, nextEvent: 'infant_cry', ageUp: 1 }, { text: "κ°€λ§Œνžˆ 있기 πŸ€”", effects: { intelligence: +1 }, nextEvent: 'infant_observe', ageUp: 1 } ] },
@@ -483,7 +481,6 @@
483
  ];
484
  }
485
  initGameDataEvents();
486
-
487
  function startGame(loadSave) {
488
  if (loadSave && localStorage.getItem(GAME_SAVE_KEY)) {
489
  const savedData = JSON.parse(localStorage.getItem(GAME_SAVE_KEY));
@@ -512,7 +509,6 @@
512
  checkAllAchievements();
513
  loadEvent(gameData.currentEventId || 'birth');
514
  }
515
-
516
  function saveGame() {
517
  const saveData = {
518
  stats: currentStats, flags: currentFlags, lifeLog: lifeEventsLog,
@@ -521,7 +517,6 @@
521
  localStorage.setItem(GAME_SAVE_KEY, JSON.stringify(saveData));
522
  alert("πŸ’Ύ 인생이 μ €μž₯λ˜μ—ˆμŠ΅λ‹ˆλ‹€!");
523
  }
524
-
525
  function checkLoadButtonVisibility() {
526
  if (localStorage.getItem(GAME_SAVE_KEY)) {
527
  loadButton.classList.remove('hidden');
@@ -530,7 +525,6 @@
530
  }
531
  }
532
  checkLoadButtonVisibility();
533
-
534
  function updateStatsDisplay() {
535
  const statsToUpdate = {
536
  'age-stat': currentStats.age, 'health-stat': Math.max(0, currentStats.health),
@@ -561,7 +555,6 @@
561
  ageBar.style.width = agePercentage + '%';
562
  ageBar.textContent = currentStats.age > 0 ? `${currentStats.age}μ„Έ` : "";
563
  }
564
-
565
  function applyEffects(effects) {
566
  for (const stat in effects) {
567
  if (typeof effects[stat] === 'function') {
@@ -576,7 +569,6 @@
576
  currentStats.relationshipWithSpouse = Math.min(100, Math.max(-50, currentStats.relationshipWithSpouse));
577
  }
578
  }
579
-
580
  function setEventFlags(flagsToSet) {
581
  if (flagsToSet) {
582
  for (const flag in flagsToSet) { currentFlags[flag] = flagsToSet[flag]; }
@@ -587,12 +579,15 @@
587
  setFlagFunction(currentStats, currentFlags);
588
  }
589
  }
590
-
591
  function loadEvent(eventId) {
592
  let event = gameData.events.find(e => e.id === eventId);
593
  if (!event) {
594
  event = findRandomAvailableEvent();
595
- if (!event) { loadEvent('death_accident'); return; }
 
 
 
 
596
  eventId = event.id;
597
  }
598
  gameData.currentEventId = eventId;
@@ -601,19 +596,40 @@
601
  if (lifeEventsLog.length === 0 || lifeEventsLog[lifeEventsLog.length -1].event !== eventTextDisplay.textContent) {
602
  lifeEventsLog.push({age: currentStats.age, event: eventTextDisplay.textContent, choice: null });
603
  }
 
604
  choicesContainer.innerHTML = '';
605
- event.choices.forEach((choice, index) => {
606
- if (choice.condition && !choice.condition(currentStats, currentFlags)) { return; }
607
- const button = document.createElement('button');
608
- button.innerHTML = typeof choice.text === 'function' ? choice.text(currentStats, currentFlags) : choice.text;
609
- button.addEventListener('click', () => processChoice(choice));
610
- choicesContainer.appendChild(button);
611
- });
 
 
 
 
 
 
612
  updateStatsDisplay();
613
  checkAllAchievements();
614
- if (event.isEnding) { endGame(event); }
615
- }
616
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
617
  function processChoice(choice) {
618
  if (choice.effects) { applyEffects(choice.effects); }
619
  if (choice.setFlag) { setEventFlags(choice.setFlag); }
@@ -633,61 +649,82 @@
633
  currentFlags.childTraits.forEach(child => { child.age = (child.age || 0) + ageIncrement; });
634
  }
635
  }
636
- updateStatsDisplay();
 
637
  if (choice.checkHealthDeath && currentStats.health <= 0) { loadEvent('death_health'); return; }
638
- if (currentStats.health <= 0) { loadEvent('death_health'); return; }
639
  if (currentStats.age >= gameData.maxAge) {
640
  if (currentStats.happiness > 70 && currentStats.health > 50) loadEvent('death_peaceful');
641
  else if (currentStats.happiness < 20 && currentStats.health < 30) loadEvent('death_lonely');
642
- else loadEvent('reflect_on_life');
643
  return;
644
  }
645
  const povertyEnding = gameData.events.find(e => e.id === 'death_wealth_extreme_poverty');
646
  if (povertyEnding && povertyEnding.condition && povertyEnding.condition(currentStats, currentFlags)) { loadEvent('death_wealth_extreme_poverty'); return; }
647
  const brokenHeartEnding = gameData.events.find(e => e.id === 'death_broken_heart');
648
  if (brokenHeartEnding && brokenHeartEnding.condition && brokenHeartEnding.condition(currentStats, currentFlags)) { loadEvent('death_broken_heart'); return; }
649
-
 
650
  let nextEventId;
651
  if (typeof choice.nextEvent === 'function') { nextEventId = choice.nextEvent(currentStats, currentFlags); }
652
  else { nextEventId = choice.nextEvent; }
653
- if (choice.nextEventRandom) { nextEventId = findRandomAvailableEvent(true)?.id; } // nextEventRandomμ‹œ isStrictlyRandom true둜 전달
 
 
 
 
654
 
655
- if (nextEventId) { loadEvent(nextEventId); }
656
- else {
 
657
  const randomEvent = findRandomAvailableEvent();
658
- if (randomEvent) { loadEvent(randomEvent.id); }
659
- else {
 
 
 
660
  if(currentStats.age > 75 && gameData.currentEventId !== 'reflect_on_life') loadEvent('reflect_on_life');
661
- else if(currentStats.age > 58 && !currentFlags.retired && gameData.currentEventId !== 'retirement_decision') loadEvent('retirement_decision');
662
  else if(currentStats.age > 40 && gameData.currentEventId !== 'generic_mid_life' && (currentFlags.employed || currentFlags.isMarried)) loadEvent('generic_mid_life');
663
- else if(currentStats.age > 25 && currentFlags.employed && !currentFlags.isMarried && gameData.currentEventId !== 'generic_early_career') loadEvent('generic_early_career');
664
- else { currentStats.age +=1; loadEvent(findRandomAvailableEvent()?.id || 'death_accident'); }
 
 
 
 
665
  }
666
  }
667
  }
668
-
669
  function findRandomAvailableEvent(isStrictlyRandom = false) {
670
  let potentialEvents = gameData.events.filter(event => {
671
- if (event.isEnding) return false;
672
  if (event.id === gameData.currentEventId && !event.isRandomEvent) return false;
673
  const ageOk = (!event.ageMin || currentStats.age >= event.ageMin) && (!event.ageMax || currentStats.age <= event.ageMax);
674
  const conditionOk = !event.condition || event.condition(currentStats, currentFlags);
675
  let flagDependencyOk = true;
676
  if (event.requiresFlag) { for (const flag in event.requiresFlag) { if (currentFlags[flag] !== event.requiresFlag[flag]) { flagDependencyOk = false; break; } } }
677
  if (event.avoidsFlag) { for (const flag in event.avoidsFlag) { if (currentFlags[flag] === event.avoidsFlag[flag]) { flagDependencyOk = false; break; } } }
678
- return (isStrictlyRandom ? event.isRandomEvent : true) && ageOk && conditionOk && flagDependencyOk;
 
 
679
  });
 
680
  let randomEventsWithProbability = potentialEvents.filter(e => e.isRandomEvent && Math.random() < (e.probability || 0.05));
681
  if (randomEventsWithProbability.length > 0) { return randomEventsWithProbability[Math.floor(Math.random() * randomEventsWithProbability.length)]; }
 
682
  if (!isStrictlyRandom) {
683
- let generalEvents = potentialEvents.filter(e => !e.isRandomEvent);
 
 
 
684
  if (generalEvents.length > 0) { return generalEvents[Math.floor(Math.random() * generalEvents.length)]; }
685
  }
 
686
  let fallbackRandomEvents = potentialEvents.filter(e => e.isRandomEvent);
687
  if (fallbackRandomEvents.length > 0) { return fallbackRandomEvents[Math.floor(Math.random() * fallbackRandomEvents.length)]; }
 
688
  return null;
689
  }
690
-
691
  function checkAllAchievements() {
692
  for (const key in currentAchievements) {
693
  const achievement = currentAchievements[key];
@@ -697,12 +734,10 @@
697
  }
698
  }
699
  }
700
-
701
  function showAchievementModal(name, description) {
702
  achievementModalText.innerHTML = `<strong>${name}</strong><br>${description}`;
703
  achievementModal.style.display = 'flex';
704
  }
705
-
706
  function endGame(endingEvent) {
707
  gameContent.classList.add('hidden');
708
  endScreen.classList.remove('hidden');
@@ -713,7 +748,7 @@
713
  lifeEventsLog.forEach(logEntry => {
714
  const p = document.createElement('p');
715
  let entryText = `<strong>[${logEntry.age}μ„Έ]</strong> ${logEntry.event}`;
716
- if(logEntry.choice && logEntry.choice !== "κ²°κ³Ό") { entryText += ` <small>(선택: ${logEntry.choice})</small>`; }
717
  p.innerHTML = entryText;
718
  lifeSummaryDisplay.appendChild(p);
719
  });
 
338
  const achievementModal = document.getElementById('achievement-modal');
339
  const achievementModalText = document.getElementById('achievement-modal-text');
340
  const achievementModalCloseButton = achievementModal.querySelector('.modal-close-button');
 
341
  document.getElementById('start-button').addEventListener('click', () => startGame(false));
342
  loadButton.addEventListener('click', () => startGame(true));
343
  document.getElementById('restart-button').addEventListener('click', () => startGame(false));
344
  saveButton.addEventListener('click', saveGame);
345
  achievementModalCloseButton.addEventListener('click', () => achievementModal.style.display = 'none');
 
346
  function initGameDataEvents() {
347
  gameData.events = [
348
  { id: 'birth', text: "당신은 νƒœμ–΄λ‚¬μŠ΅λ‹ˆλ‹€! 🍼 세상에 온 것을 ν™˜μ˜ν•΄μš”. 무엇을 λ¨Όμ € ν•΄λ³ΌκΉŒμš”?", ageMin: 0, ageMax: 0, choices: [ { text: "응애! 울기 😭", effects: { happiness: -2, health: +1 }, nextEvent: 'infant_cry', ageUp: 1 }, { text: "κ°€λ§Œνžˆ 있기 πŸ€”", effects: { intelligence: +1 }, nextEvent: 'infant_observe', ageUp: 1 } ] },
 
481
  ];
482
  }
483
  initGameDataEvents();
 
484
  function startGame(loadSave) {
485
  if (loadSave && localStorage.getItem(GAME_SAVE_KEY)) {
486
  const savedData = JSON.parse(localStorage.getItem(GAME_SAVE_KEY));
 
509
  checkAllAchievements();
510
  loadEvent(gameData.currentEventId || 'birth');
511
  }
 
512
  function saveGame() {
513
  const saveData = {
514
  stats: currentStats, flags: currentFlags, lifeLog: lifeEventsLog,
 
517
  localStorage.setItem(GAME_SAVE_KEY, JSON.stringify(saveData));
518
  alert("πŸ’Ύ 인생이 μ €μž₯λ˜μ—ˆμŠ΅λ‹ˆλ‹€!");
519
  }
 
520
  function checkLoadButtonVisibility() {
521
  if (localStorage.getItem(GAME_SAVE_KEY)) {
522
  loadButton.classList.remove('hidden');
 
525
  }
526
  }
527
  checkLoadButtonVisibility();
 
528
  function updateStatsDisplay() {
529
  const statsToUpdate = {
530
  'age-stat': currentStats.age, 'health-stat': Math.max(0, currentStats.health),
 
555
  ageBar.style.width = agePercentage + '%';
556
  ageBar.textContent = currentStats.age > 0 ? `${currentStats.age}μ„Έ` : "";
557
  }
 
558
  function applyEffects(effects) {
559
  for (const stat in effects) {
560
  if (typeof effects[stat] === 'function') {
 
569
  currentStats.relationshipWithSpouse = Math.min(100, Math.max(-50, currentStats.relationshipWithSpouse));
570
  }
571
  }
 
572
  function setEventFlags(flagsToSet) {
573
  if (flagsToSet) {
574
  for (const flag in flagsToSet) { currentFlags[flag] = flagsToSet[flag]; }
 
579
  setFlagFunction(currentStats, currentFlags);
580
  }
581
  }
 
582
  function loadEvent(eventId) {
583
  let event = gameData.events.find(e => e.id === eventId);
584
  if (!event) {
585
  event = findRandomAvailableEvent();
586
+ if (!event) {
587
+ console.error("No valid event found, defaulting to death_accident."); // Added for debugging
588
+ loadEvent('death_accident');
589
+ return;
590
+ }
591
  eventId = event.id;
592
  }
593
  gameData.currentEventId = eventId;
 
596
  if (lifeEventsLog.length === 0 || lifeEventsLog[lifeEventsLog.length -1].event !== eventTextDisplay.textContent) {
597
  lifeEventsLog.push({age: currentStats.age, event: eventTextDisplay.textContent, choice: null });
598
  }
599
+
600
  choicesContainer.innerHTML = '';
601
+ let choicesAvailable = false;
602
+ // MODIFIED PART: Check if event.choices exists and is an array before iterating
603
+ if (event.choices && Array.isArray(event.choices)) {
604
+ event.choices.forEach((choice, index) => {
605
+ if (choice.condition && !choice.condition(currentStats, currentFlags)) { return; }
606
+ const button = document.createElement('button');
607
+ button.innerHTML = typeof choice.text === 'function' ? choice.text(currentStats, currentFlags) : choice.text;
608
+ button.addEventListener('click', () => processChoice(choice));
609
+ choicesContainer.appendChild(button);
610
+ choicesAvailable = true;
611
+ });
612
+ }
613
+
614
  updateStatsDisplay();
615
  checkAllAchievements();
 
 
616
 
617
+ if (event.isEnding) {
618
+ endGame(event);
619
+ } else if (!choicesAvailable && !event.isEndingSegment) { // isEndingSegment events might have choices but lead to ending
620
+ // If it's not an ending event and no choices were rendered (e.g., all conditions false, or empty choices array)
621
+ // This indicates a potential dead-end in the game logic for this event.
622
+ console.warn(`Event "${eventId}" is non-ending but rendered no choices. Attempting to auto-advance.`);
623
+ lifeEventsLog.push({age: currentStats.age, event: `(μ‹œμŠ€ν…œ: '${eventId}' μ΄λ²€νŠΈμ—μ„œ 선택지가 μ—†μ–΄ μžλ™μœΌλ‘œ μ§„ν–‰ν•©λ‹ˆλ‹€.)`, choice: "μžλ™ μ§„ν–‰"});
624
+ currentStats.age += 1; // Slightly advance age to help break potential loops
625
+ const nextEventAfterStuck = findRandomAvailableEvent();
626
+ if (nextEventAfterStuck) {
627
+ loadEvent(nextEventAfterStuck.id);
628
+ } else {
629
+ loadEvent('death_accident'); // Fallback if still no event can be found
630
+ }
631
+ }
632
+ }
633
  function processChoice(choice) {
634
  if (choice.effects) { applyEffects(choice.effects); }
635
  if (choice.setFlag) { setEventFlags(choice.setFlag); }
 
649
  currentFlags.childTraits.forEach(child => { child.age = (child.age || 0) + ageIncrement; });
650
  }
651
  }
652
+ updateStatsDisplay(); // Update stats after age increment and effects
653
+ // Check for game-ending conditions first
654
  if (choice.checkHealthDeath && currentStats.health <= 0) { loadEvent('death_health'); return; }
655
+ if (currentStats.health <= 0) { loadEvent('death_health'); return; } // General health check
656
  if (currentStats.age >= gameData.maxAge) {
657
  if (currentStats.happiness > 70 && currentStats.health > 50) loadEvent('death_peaceful');
658
  else if (currentStats.happiness < 20 && currentStats.health < 30) loadEvent('death_lonely');
659
+ else loadEvent('reflect_on_life'); // reflect_on_life has choices leading to final ending
660
  return;
661
  }
662
  const povertyEnding = gameData.events.find(e => e.id === 'death_wealth_extreme_poverty');
663
  if (povertyEnding && povertyEnding.condition && povertyEnding.condition(currentStats, currentFlags)) { loadEvent('death_wealth_extreme_poverty'); return; }
664
  const brokenHeartEnding = gameData.events.find(e => e.id === 'death_broken_heart');
665
  if (brokenHeartEnding && brokenHeartEnding.condition && brokenHeartEnding.condition(currentStats, currentFlags)) { loadEvent('death_broken_heart'); return; }
666
+
667
+ // Determine next event
668
  let nextEventId;
669
  if (typeof choice.nextEvent === 'function') { nextEventId = choice.nextEvent(currentStats, currentFlags); }
670
  else { nextEventId = choice.nextEvent; }
671
+
672
+ if (choice.nextEventRandom) {
673
+ const randomEv = findRandomAvailableEvent(true);
674
+ nextEventId = randomEv ? randomEv.id : null;
675
+ }
676
 
677
+ if (nextEventId) {
678
+ loadEvent(nextEventId);
679
+ } else {
680
  const randomEvent = findRandomAvailableEvent();
681
+ if (randomEvent) {
682
+ loadEvent(randomEvent.id);
683
+ } else {
684
+ // Fallback logic if no specific next event and no random event found
685
+ console.warn("No specific or random event found, attempting fallback progression.");
686
  if(currentStats.age > 75 && gameData.currentEventId !== 'reflect_on_life') loadEvent('reflect_on_life');
687
+ else if(currentStats.age > 58 && !currentFlags.retired && currentFlags.employed && gameData.currentEventId !== 'retirement_decision') loadEvent('retirement_decision');
688
  else if(currentStats.age > 40 && gameData.currentEventId !== 'generic_mid_life' && (currentFlags.employed || currentFlags.isMarried)) loadEvent('generic_mid_life');
689
+ else if(currentStats.age > 23 && currentFlags.employed && !currentFlags.isMarried && gameData.currentEventId !== 'generic_early_career') loadEvent('generic_early_career');
690
+ else {
691
+ currentStats.age +=1;
692
+ const finalAttemptEvent = findRandomAvailableEvent();
693
+ loadEvent(finalAttemptEvent ? finalAttemptEvent.id : 'death_accident');
694
+ }
695
  }
696
  }
697
  }
 
698
  function findRandomAvailableEvent(isStrictlyRandom = false) {
699
  let potentialEvents = gameData.events.filter(event => {
700
+ if (event.isEnding && !event.isEndingSegment) return false; // Allow ending segments that might have choices
701
  if (event.id === gameData.currentEventId && !event.isRandomEvent) return false;
702
  const ageOk = (!event.ageMin || currentStats.age >= event.ageMin) && (!event.ageMax || currentStats.age <= event.ageMax);
703
  const conditionOk = !event.condition || event.condition(currentStats, currentFlags);
704
  let flagDependencyOk = true;
705
  if (event.requiresFlag) { for (const flag in event.requiresFlag) { if (currentFlags[flag] !== event.requiresFlag[flag]) { flagDependencyOk = false; break; } } }
706
  if (event.avoidsFlag) { for (const flag in event.avoidsFlag) { if (currentFlags[flag] === event.avoidsFlag[flag]) { flagDependencyOk = false; break; } } }
707
+
708
+ if (isStrictlyRandom) return event.isRandomEvent && ageOk && conditionOk && flagDependencyOk;
709
+ return ageOk && conditionOk && flagDependencyOk;
710
  });
711
+
712
  let randomEventsWithProbability = potentialEvents.filter(e => e.isRandomEvent && Math.random() < (e.probability || 0.05));
713
  if (randomEventsWithProbability.length > 0) { return randomEventsWithProbability[Math.floor(Math.random() * randomEventsWithProbability.length)]; }
714
+
715
  if (!isStrictlyRandom) {
716
+ let generalEvents = potentialEvents.filter(e => !e.isRandomEvent && !e.isEndingSegment); // Prefer non-ending general events
717
+ if (generalEvents.length === 0) { // If no general non-ending, consider ending segments too
718
+ generalEvents = potentialEvents.filter(e => !e.isRandomEvent);
719
+ }
720
  if (generalEvents.length > 0) { return generalEvents[Math.floor(Math.random() * generalEvents.length)]; }
721
  }
722
+
723
  let fallbackRandomEvents = potentialEvents.filter(e => e.isRandomEvent);
724
  if (fallbackRandomEvents.length > 0) { return fallbackRandomEvents[Math.floor(Math.random() * fallbackRandomEvents.length)]; }
725
+
726
  return null;
727
  }
 
728
  function checkAllAchievements() {
729
  for (const key in currentAchievements) {
730
  const achievement = currentAchievements[key];
 
734
  }
735
  }
736
  }
 
737
  function showAchievementModal(name, description) {
738
  achievementModalText.innerHTML = `<strong>${name}</strong><br>${description}`;
739
  achievementModal.style.display = 'flex';
740
  }
 
741
  function endGame(endingEvent) {
742
  gameContent.classList.add('hidden');
743
  endScreen.classList.remove('hidden');
 
748
  lifeEventsLog.forEach(logEntry => {
749
  const p = document.createElement('p');
750
  let entryText = `<strong>[${logEntry.age}μ„Έ]</strong> ${logEntry.event}`;
751
+ if(logEntry.choice && logEntry.choice !== "κ²°κ³Ό" && logEntry.choice !== "μžλ™ μ§„ν–‰") { entryText += ` <small>(선택: ${logEntry.choice})</small>`; }
752
  p.innerHTML = entryText;
753
  lifeSummaryDisplay.appendChild(p);
754
  });