awacke1's picture
Update game.js
1f6a578 verified
// --- Game State ---
// (Keep the gameState definition from the previous step, including the nested 'character' object)
let gameState = {
currentPageId: 1,
character: {
name: "Hero", race: "Human", alignment: "Neutral Good", class: "Fighter",
level: 1, xp: 0, xpToNextLevel: 100, statPointsPerLevel: 1, availableStatPoints: 0,
stats: { strength: 7, intelligence: 5, wisdom: 5, dexterity: 6, constitution: 6, charisma: 5, hp: 30, maxHp: 30 },
inventory: []
}
};
// --- DOM Element Getters ---
// (Keep all the getters from the previous step: charNameInput, charRaceSpan, etc.)
const charNameInput = document.getElementById('char-name');
const charRaceSpan = document.getElementById('char-race');
const charAlignmentSpan = document.getElementById('char-alignment');
const charClassSpan = document.getElementById('char-class');
const charLevelSpan = document.getElementById('char-level');
const charXPSpan = document.getElementById('char-xp');
const charXPNextSpan = document.getElementById('char-xp-next');
const charHPSpan = document.getElementById('char-hp');
const charMaxHPSpan = document.getElementById('char-max-hp');
const charInventoryList = document.getElementById('char-inventory-list');
const statSpans = {
strength: document.getElementById('stat-strength'), intelligence: document.getElementById('stat-intelligence'),
wisdom: document.getElementById('stat-wisdom'), dexterity: document.getElementById('stat-dexterity'),
constitution: document.getElementById('stat-constitution'), charisma: document.getElementById('stat-charisma'),
};
const statIncreaseButtons = document.querySelectorAll('.stat-increase');
const levelUpButton = document.getElementById('levelup-btn');
const saveCharButton = document.getElementById('save-char-btn');
const exportCharButton = document.getElementById('export-char-btn');
const statIncreaseCostSpan = document.getElementById('stat-increase-cost');
const statPointsAvailableSpan = document.getElementById('stat-points-available'); // Added span for clarity
// --- Character Sheet Functions ---
function renderCharacterSheet() {
// (Keep the logic from the previous renderCharacterSheet function)
// ...
const char = gameState.character;
charNameInput.value = char.name;
charRaceSpan.textContent = char.race;
charAlignmentSpan.textContent = char.alignment;
charClassSpan.textContent = char.class;
charLevelSpan.textContent = char.level;
charXPSpan.textContent = char.xp;
charXPNextSpan.textContent = char.xpToNextLevel;
char.stats.hp = Math.min(char.stats.hp, char.stats.maxHp);
charHPSpan.textContent = char.stats.hp;
charMaxHPSpan.textContent = char.stats.maxHp;
for (const stat in statSpans) {
if (statSpans.hasOwnProperty(stat) && char.stats.hasOwnProperty(stat)) {
statSpans[stat].textContent = char.stats[stat];
}
}
charInventoryList.innerHTML = '';
const maxSlots = 15;
for (let i = 0; i < maxSlots; i++) {
const li = document.createElement('li');
if (i < char.inventory.length) {
const item = char.inventory[i];
const itemInfo = itemsData[item] || { type: 'unknown', description: '???' };
const itemSpan = document.createElement('span');
itemSpan.classList.add(`item-${itemInfo.type || 'unknown'}`);
itemSpan.title = itemInfo.description;
itemSpan.textContent = item;
li.appendChild(itemSpan);
} else {
const emptySlotSpan = document.createElement('span');
emptySlotSpan.classList.add('item-slot');
emptySlotSpan.textContent = '[Empty]'; // Set text directly
li.appendChild(emptySlotSpan);
}
charInventoryList.appendChild(li);
}
updateLevelUpAvailability(); // Handles button disabling logic
}
function calculateStatIncreaseCost() {
// (Keep the same logic)
return (gameState.character.level * 10) + 5;
}
function updateLevelUpAvailability() {
const char = gameState.character;
const canLevelUp = char.xp >= char.xpToNextLevel;
levelUpButton.disabled = !canLevelUp;
const cost = calculateStatIncreaseCost();
const canIncreaseWithXP = char.xp >= cost;
const canIncreaseWithPoints = char.availableStatPoints > 0;
statIncreaseButtons.forEach(button => {
button.disabled = !(canIncreaseWithPoints || canIncreaseWithXP);
// Optionally disable if level up is pending
// button.disabled = button.disabled || canLevelUp;
});
// Update cost/points display text
statIncreaseCostSpan.textContent = cost;
statPointsAvailableSpan.textContent = char.availableStatPoints;
}
function handleLevelUp() {
// (Keep the same logic)
const char = gameState.character;
if (char.xp >= char.xpToNextLevel) {
char.level++;
char.xp -= char.xpToNextLevel;
char.xpToNextLevel = Math.floor(char.xpToNextLevel * 1.6);
char.availableStatPoints += char.statPointsPerLevel;
const conModifier = Math.floor((char.stats.constitution - 10) / 2);
const hpGain = Math.max(1, Math.floor(Math.random() * 6) + 1 + conModifier);
char.stats.maxHp += hpGain;
char.stats.hp = char.stats.maxHp;
console.log(`Leveled Up to ${char.level}! Gained ${char.statPointsPerLevel} stat point(s) and ${hpGain} HP.`);
renderCharacterSheet();
} else {
console.warn("Not enough XP to level up yet.");
}
}
function handleStatIncrease(statName) {
// (Keep the same logic, including point spending priority)
const char = gameState.character;
const cost = calculateStatIncreaseCost();
if (char.availableStatPoints > 0) {
char.stats[statName]++;
char.availableStatPoints--;
console.log(`Increased ${statName} using a point. ${char.availableStatPoints} points remaining.`);
if (statName === 'constitution') { /* ... update maxHP ... */ } // Add HP update logic here if needed
renderCharacterSheet();
return;
}
if (char.xp >= cost) {
char.stats[statName]++;
char.xp -= cost;
console.log(`Increased ${statName} for ${cost} XP.`);
if (statName === 'constitution') { /* ... update maxHP ... */ } // Add HP update logic here if needed
renderCharacterSheet();
} else {
console.warn(`Not enough XP or points to increase ${statName}.`);
}
}
function saveCharacter() {
try {
localStorage.setItem('textAdventureCharacter', JSON.stringify(gameState.character));
console.log('Character saved locally.');
// Update button text for confirmation - NO EMOJI
saveCharButton.textContent = 'Saved!';
saveCharButton.disabled = true; // Briefly disable
setTimeout(() => {
saveCharButton.textContent = 'Save'; // Restore original text
saveCharButton.disabled = false;
}, 1500);
} catch (e) {
console.error('Error saving character:', e);
alert('Failed to save character.');
}
}
function loadCharacter() {
// (Keep the same logic)
try {
const savedData = localStorage.getItem('textAdventureCharacter');
if (savedData) {
const loadedChar = JSON.parse(savedData);
gameState.character = { ...gameState.character, ...loadedChar, stats: { ...gameState.character.stats, ...(loadedChar.stats || {}) }, inventory: loadedChar.inventory || [] };
console.log('Character loaded from local storage.');
return true;
}
} catch (e) { console.error('Error loading character:', e); }
return false;
}
function exportCharacter() {
// (Keep the same logic)
try {
const charJson = JSON.stringify(gameState.character, null, 2);
const blob = new Blob([charJson], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a'); a.href = url;
const filename = `${gameState.character.name.replace(/[^a-z0-9]/gi, '_').toLowerCase() || 'character'}_save.json`;
a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url);
console.log(`Character exported as ${filename}`);
} catch (e) { console.error('Error exporting character:', e); alert('Failed to export character data.'); }
}
// --- Event Listeners ---
// (Keep the same event listeners, they reference elements by ID)
charNameInput.addEventListener('change', () => {
gameState.character.name = charNameInput.value.trim() || "Hero";
console.log(`Name changed to: ${gameState.character.name}`);
});
levelUpButton.addEventListener('click', handleLevelUp);
statIncreaseButtons.forEach(button => {
button.addEventListener('click', () => {
const statToIncrease = button.dataset.stat;
if (statToIncrease) { handleStatIncrease(statToIncrease); }
});
});
saveCharButton.addEventListener('click', saveCharacter);
exportCharButton.addEventListener('click', exportCharacter);
// --- Modify Existing Functions ---
function startGame() {
// (Keep the same logic, including loadCharacter and default merging)
if (!loadCharacter()) { console.log("No saved character found, starting new."); }
gameState.character = {
...{ name: "Hero", race: "Human", alignment: "Neutral Good", class: "Fighter", level: 1, xp: 0, xpToNextLevel: 100, statPointsPerLevel: 1, availableStatPoints: 0, stats: { strength: 7, intelligence: 5, wisdom: 5, dexterity: 6, constitution: 6, charisma: 5, hp: 30, maxHp: 30 }, inventory: [] },
...gameState.character
};
gameState.character.stats = {
strength: 7, intelligence: 5, wisdom: 5, dexterity: 6, constitution: 6, charisma: 5, hp: 30, maxHp: 30,
...(gameState.character.stats || {})
}
gameState.currentPageId = 1;
renderCharacterSheet();
renderPage(gameState.currentPageId);
}
function handleChoiceClick(choiceData) {
// (Keep the same logic, including reward processing)
const nextPageId = parseInt(choiceData.nextPage); const itemToAdd = choiceData.addItem; if (isNaN(nextPageId)) { console.error("Invalid nextPageId:", choiceData.nextPage); return; }
if (itemToAdd && !gameState.character.inventory.includes(itemToAdd)) { gameState.character.inventory.push(itemToAdd); console.log("Added item:", itemToAdd); }
gameState.currentPageId = nextPageId; const nextPageData = gameData[nextPageId];
if (nextPageData) { if (nextPageData.hpLoss) { gameState.character.stats.hp -= nextPageData.hpLoss; console.log(`Lost ${nextPageData.hpLoss} HP.`); if (gameState.character.stats.hp <= 0) { gameState.character.stats.hp = 0; console.log("Player died from HP loss!"); renderCharacterSheet(); renderPage(99); return; } }
if (nextPageData.reward) { if (nextPageData.reward.xp) { gameState.character.xp += nextPageData.reward.xp; console.log(`Gained ${nextPageData.reward.xp} XP!`); } if (nextPageData.reward.statIncrease) { const stat = nextPageData.reward.statIncrease.stat; const amount = nextPageData.reward.statIncrease.amount; if (gameState.character.stats.hasOwnProperty(stat)) { gameState.character.stats[stat] += amount; console.log(`Stat ${stat} increased by ${amount}!`); if (stat === 'constitution') { /* ... update maxHP ... */ } } } if(nextPageData.reward.addItem && !gameState.character.inventory.includes(nextPageData.reward.addItem)){ gameState.character.inventory.push(nextPageData.reward.addItem); console.log(`Found item: ${nextPageData.reward.addItem}`); } }
if (nextPageData.gameOver) { console.log("Reached Game Over page."); renderCharacterSheet(); renderPage(nextPageId); return; }
} else { console.error(`Data for page ${nextPageId} not found!`); renderCharacterSheet(); renderPage(99); return; }
renderCharacterSheet(); renderPage(nextPageId);
}
// --- REMEMBER ---
// Make sure to remove the OLD #stats-display and #inventory-display elements from your HTML
// and the old updateStatsDisplay() and updateInventoryDisplay() functions from your JS.
// --- Initialization ---
// initThreeJS(); // Keep your Three.js initialization
startGame();