☀️

Game Development Log - 05 March 2025CE

Autonomous AI Civilization-Building Game
Note: When first starting on this prompt/project, I had no idea that it would result in so many lines of code that it would end up requiring multiple continuation-prompts in order to be able to continue generating all of the code necessary for a functioning game; alas, although the code was eventually completed, due to the max-session size-limit, I was forced to re-start another prompt-session in order for the code to be fully completed; alas, I was unable to get the game to actually initialise, which is why I eventually had to re-start yet another session of prompts with another idea in mind: We'd code only small parts of the game at a time so that I'd have time to field-test everything to make sure that the generated code worked before we continued with additional code-generation... this is the start of the history of my learning experience with A.I. and how to prompt better for coding purposes...
Aéius Cercle: I'd like to have a game created for a web-site where there are A.I. characters that interact with and co-operate with each other in a kind of civilisation-building game, with new A.I. characters popping up or springing into existence on occasion. Can you come up with a code for such a theme ? Make sure to «modularise» the code so that it's easier to maintain, upgrade, debug, segment the code-generation, etc.; and it should also be a type of game that saves & maintains progress automatically and can build-upon itself if possible (sort of like A.I. coding itself & improving itself on its own). Can you pull this off ?
Claude: I'd be happy to create a web-based civilization-building game with AI characters that interact and cooperate with each other. This is an ambitious and exciting project that will require a well-structured approach to make it maintainable and extendable. Let me design a modular JavaScript-based game with the following features:
</>
AI Civilization Building Game
Click to open code

// Main Game Module - Orchestrates the entire game
const AICivilizationGame = (function() {
	// Private variables
	let gameState = null;
	let lastSaveTime = 0;
	const AUTOSAVE_INTERVAL = 30000; // Save every 30 seconds
	
	// Initialize the game
	function init() {
		console.log('Initializing AI Civilization Game...');
		
		// Load saved game or create a new one
		gameState = StorageManager.loadGame() || {
			world: WorldGenerator.generateNewWorld(),
			turn: 0,
			timestamp: Date.now(),
			characters: [],
			resources: ResourceManager.getInitialResources(),
			buildings: [],
			technologies: [],
			events: []
		};
		
		// Initialize each module with the game state
		WorldRenderer.init(gameState.world);
		CharacterManager.init(gameState.characters);
		ResourceManager.init(gameState.resources);
		BuildingManager.init(gameState.buildings);
		TechnologyManager.init(gameState.technologies);
		EventManager.init(gameState.events);
		
		// Create initial characters if new game
		if (gameState.characters.length === 0) {
			populateInitialCharacters();
		}
		
		// Set up the game loop
		setupGameLoop();
		
		// Set up the autosave
		setInterval(saveGame, AUTOSAVE_INTERVAL);
		
		// Render the initial state
		render();
		
		console.log('Game initialized successfully!');
	}
	
	// Create initial AI characters
	function populateInitialCharacters() {
		console.log('Creating initial characters...');
		const initialCharacters = [
			CharacterFactory.createCharacter('leader', 'Founder', {intelligence: 8, cooperation: 7, creativity: 6}),
			CharacterFactory.createCharacter('builder', 'Constructor', {intelligence: 6, cooperation: 8, creativity: 5}),
			CharacterFactory.createCharacter('scientist', 'Researcher', {intelligence: 9, cooperation: 5, creativity: 7})
		];
		
		initialCharacters.forEach(character => {
			CharacterManager.addCharacter(character);
			gameState.characters.push(character);
		});
	}
	
	// Set up the main game loop
	function setupGameLoop() {
		console.log('Setting up game loop...');
		const gameLoop = setInterval(() => {
			update();
			render();
		}, 1000); // Update every second
	}
	
	// Update game state
	function update() {
		gameState.turn++;
		
		// Update each character's state and actions
		CharacterManager.updateCharacters(gameState);
		
		// Check for character interactions
		CharacterManager.processInteractions(gameState);
		
		// Update resources based on buildings and character actions
		ResourceManager.updateResources(gameState);
		
		// Check for new buildings completion
		BuildingManager.updateBuildings(gameState);
		
		// Check technology progress
		TechnologyManager.updateTechnologies(gameState);
		
		// Generate random events
		EventManager.generateEvents(gameState);
		
		// Check if new characters should emerge
		if (shouldCreateNewCharacter()) {
			createNewCharacter();
		}
		
		// Check win/loss conditions
		checkGameConditions();
	}
	
	// Render the game state to the UI
	function render() {
		// Update UI elements with current game state
		WorldRenderer.render(gameState.world);
		UIManager.renderCharacters(gameState.characters);
		UIManager.renderResources(gameState.resources);
		UIManager.renderBuildings(gameState.buildings);
		UIManager.renderTechnologies(gameState.technologies);
		UIManager.renderEvents(gameState.events.slice(-5)); // Show latest 5 events
	}
	
	// Determine if a new character should emerge
	function shouldCreateNewCharacter() {
		// Create a new character every 50 turns with some randomness
		if (gameState.turn % 50 === 0) {
			return Math.random() < 0.7; // 70% chance on eligible turns
		}
		
		// Rare chance (1%) of character emerging on any turn
		return Math.random() < 0.01;
	}
	
	// Create a new character based on current civilization state
	function createNewCharacter() {
		console.log('Creating a new emergent character...');
		
		// Determine which type of character would be most beneficial
		const currentNeeds = analyzeCurrentNeeds();
		const newCharacter = CharacterFactory.createEmergentCharacter(currentNeeds, gameState);
		
		// Add to the game
		CharacterManager.addCharacter(newCharacter);
		gameState.characters.push(newCharacter);
		
		// Announce the new character
		EventManager.addEvent({
			type: 'character',
			message: `A new character named ${newCharacter.name} has emerged in your civilization!`,
			details: `Role: ${newCharacter.role}, Special ability: ${newCharacter.specialAbility}`,
			turn: gameState.turn
		});
	}
	
	// Analyze what the civilization currently needs
	function analyzeCurrentNeeds() {
		// Check resource balance
		const resourceBalance = ResourceManager.analyzeBalance();
		
		// Check character role distribution
		const roleDistribution = CharacterManager.analyzeRoleDistribution();
		
		// Check technology gaps
		const technologyGaps = TechnologyManager.analyzeGaps();
		
		// Check building needs
		const buildingNeeds = BuildingManager.analyzeNeeds(gameState.world);
		
		return {
			resourceBalance,
			roleDistribution,
			technologyGaps,
			buildingNeeds,
			worldState: gameState.world.developmentLevel
		};
	}
	
	// Check win/loss conditions
	function checkGameConditions() {
		// For simplicity, we're not implementing end conditions yet
		// This would check for civilization collapse or advancement milestones
	}
	
	// Save the current game state
	function saveGame() {
		gameState.timestamp = Date.now();
		StorageManager.saveGame(gameState);
		lastSaveTime = Date.now();
		console.log('Game saved at turn', gameState.turn);
	}
	
	// Public API
	return {
		init,
		saveGame,
		getGameState: () => gameState
	};
})();

// Character Factory - Creates different types of AI characters
const CharacterFactory = (function() {
	// Character templates
	const templates = {
		leader: {
			baseIntelligence: 7,
			baseCooperation: 6,
			baseCreativity: 5,
			specialAbilities: ['inspiration', 'governance', 'diplomacy'],
			behaviors: ['prioritizeCommunity', 'planLongTerm', 'delegateTasks']
		},
		builder: {
			baseIntelligence: 5,
			baseCooperation: 7,
			baseCreativity: 6,
			specialAbilities: ['construction', 'resourceGathering', 'optimization'],
			behaviors: ['focusOnTasks', 'resourceConservation', 'improveInfrastructure']
		},
		scientist: {
			baseIntelligence: 8,
			baseCooperation: 4,
			baseCreativity: 7,
			specialAbilities: ['research', 'invention', 'analysis'],
			behaviors: ['seekKnowledge', 'experiment', 'solveProblems']
		},
		farmer: {
			baseIntelligence: 5,
			baseCooperation: 8,
			baseCreativity: 4,
			specialAbilities: ['cultivation', 'animalHusbandry', 'weatherPrediction'],
			behaviors: ['workWithNature', 'planSeasons', 'shareHarvest']
		},
		merchant: {
			baseIntelligence: 6,
			baseCooperation: 7,
			baseCreativity: 6,
			specialAbilities: ['negotiation', 'resourceDistribution', 'valueAssessment'],
			behaviors: ['seekOpportunities', 'buildNetwork', 'balanceResources']
		},
		explorer: {
			baseIntelligence: 6,
			baseCooperation: 5,
			baseCreativity: 8,
			specialAbilities: ['discovery', 'mapmaking', 'adaptation'],
			behaviors: ['exploreBorders', 'documentFindings', 'findResources']
		}
	};
	
	// Name generators for different character types
	const nameGenerators = {
		leader: () => {
			const prefixes = ['Grand', 'High', 'Chief', 'Elder', 'Wise'];
			const roots = ['Lead', 'Command', 'Direct', 'Guide', 'Rule'];
			const suffixes = ['er', 'or', 'ant', 'ium', 'us'];
			return `${getRandomItem(prefixes)} ${getRandomItem(roots)}${getRandomItem(suffixes)}`;
		},
		builder: () => {
			const prefixes = ['Master', 'Crafty', 'Skilled', 'Artisan', 'Worker'];
			const roots = ['Build', 'Craft', 'Construct', 'Form', 'Shape'];
			const suffixes = ['er', 'or', 'ist', 'man', 'hand'];
			return `${getRandomItem(prefixes)} ${getRandomItem(roots)}${getRandomItem(suffixes)}`;
		},
		scientist: () => {
			const prefixes = ['Prof', 'Dr', 'Theorist', 'Thinker', 'Sage'];
			const roots = ['Think', 'Solve', 'Know', 'Learn', 'Discover'];
			const suffixes = ['er', 'or', 'ium', 'mind', 'brain'];
			return `${getRandomItem(prefixes)} ${getRandomItem(roots)}${getRandomItem(suffixes)}`;
		},
		farmer: () => {
			const prefixes = ['Tender', 'Grower', 'Field', 'Harvest', 'Nature'];
			const roots = ['Farm', 'Grow', 'Plant', 'Harvest', 'Tend'];
			const suffixes = ['er', 'ist', 'man', 'hand', 'keeper'];
			return `${getRandomItem(prefixes)} ${getRandomItem(roots)}${getRandomItem(suffixes)}`;
		},
		merchant: () => {
			const prefixes = ['Trade', 'Market', 'Commerce', 'Wealth', 'Deal'];
			const roots = ['Trade', 'Sell', 'Barter', 'Deal', 'Exchange'];
			const suffixes = ['er', 'man', 'master', 'broker', 'hand'];
			return `${getRandomItem(prefixes)} ${getRandomItem(roots)}${getRandomItem(suffixes)}`;
		},
		explorer: () => {
			const prefixes = ['Brave', 'Bold', 'Venture', 'Journey', 'Quest'];
			const roots = ['Explore', 'Discover', 'Venture', 'Seek', 'Find'];
			const suffixes = ['er', 'or', 'ist', 'soul', 'heart'];
			return `${getRandomItem(prefixes)} ${getRandomItem(roots)}${getRandomItem(suffixes)}`;
		}
	};
	
	// Helper function to get a random item from an array
	function getRandomItem(array) {
		return array[Math.floor(Math.random() * array.length)];
	}
	
	// Create a character with the given role and specified attributes
	function createCharacter(role, name, attributes = {}) {
		if (!templates[role]) {
			console.error(`Unknown character role: ${role}`);
			return null;
		}
		
		const template = templates[role];
		const generatedName = name || nameGenerators[role]();
		
		// Create the character by combining template with custom attributes
		const character = {
			id: `char_${Date.now()}_${Math.floor(Math.random() * 1000)}`,
			name: generatedName,
			role: role,
			createdTurn: 0, // Will be set when added to the game
			intelligence: attributes.intelligence || template.baseIntelligence,
			cooperation: attributes.cooperation || template.baseCooperation,
			creativity: attributes.creativity || template.baseCreativity,
			specialAbility: attributes.specialAbility || getRandomItem(template.specialAbilities),
			behaviors: template.behaviors,
			currentTask: null,
			relationships: {}, // Will track relationships with other characters
			knowledge: [], // Will track what this character knows
			resources: {}, // Resources currently assigned to this character
			experience: 0,
			level: 1,
			history: [] // Track character's achievements and actions
		};
		
		return character;
	}
	
	// Create a character with traits influenced by the current game state
	function createEmergentCharacter(currentNeeds, gameState) {
		// Determine which role would best complement the current civilization
		const roleWeights = calculateRoleWeights(currentNeeds);
		const role = chooseWeightedRandom(roleWeights);
		
		// Generate a name based on the chosen role
		const name = nameGenerators[role]();
		
		// Determine special attributes based on current needs
		const attributes = {
			intelligence: Math.floor(Math.random() * 4) + 5, // Base 5-8
			cooperation: Math.floor(Math.random() * 4) + 5, // Base 5-8
			creativity: Math.floor(Math.random() * 4) + 5, // Base 5-8
		};
		
		// Boost the most needed attribute based on current needs
		if (currentNeeds.technologyGaps.length > 0) {
			attributes.intelligence += 2;
		}
		if (Object.keys(currentNeeds.roleDistribution).length < 3) {
			attributes.cooperation += 2;
		}
		if (currentNeeds.buildingNeeds.urgent) {
			attributes.creativity += 2;
		}
		
		// Create the character
		return createCharacter(role, name, attributes);
	}
	
	// Calculate weights for each role based on current needs
	function calculateRoleWeights(currentNeeds) {
		const weights = {
			leader: 1,
			builder: 1,
			scientist: 1,
			farmer: 1,
			merchant: 1,
			explorer: 1
		};
		
		// Adjust weights based on resource balance
		if (currentNeeds.resourceBalance.food < 0) weights.farmer += 2;
		if (currentNeeds.resourceBalance.materials < 0) weights.builder += 1;
		if (currentNeeds.resourceBalance.knowledge < 0) weights.scientist += 2;
		if (currentNeeds.resourceBalance.wealth < 0) weights.merchant += 2;
		
		// Adjust weights based on role distribution
		for (const role in currentNeeds.roleDistribution) {
			weights[role] -= currentNeeds.roleDistribution[role] * 0.5;
		}
		
		// Adjust weights based on technology gaps
		if (currentNeeds.technologyGaps.includes('construction')) weights.builder += 1;
		if (currentNeeds.technologyGaps.includes('agriculture')) weights.farmer += 1;
		if (currentNeeds.technologyGaps.includes('science')) weights.scientist += 2;
		if (currentNeeds.technologyGaps.includes('trade')) weights.merchant += 1;
		if (currentNeeds.technologyGaps.includes('exploration')) weights.explorer += 2;
		
		// Ensure no negative weights
		for (const role in weights) {
			weights[role] = Math.max(weights[role], 0.1);
		}
		
		return weights;
	}
	
	// Choose a random item based on weights
	function chooseWeightedRandom(weights) {
		const totalWeight = Object.values(weights).reduce((sum, weight) => sum + weight, 0);
		let random = Math.random() * totalWeight;
		
		for (const item in weights) {
			random -= weights[item];
			if (random <= 0) {
				return item;
			}
		}
		
		// Fallback
		return Object.keys(weights)[0];
	}
	
	// Return public API
	return {
		createCharacter,
		createEmergentCharacter
	};
})();

// Character Manager - Handles all character-related functions
const CharacterManager = (function() {
	// Private variables
	let characters = [];
	
	// Initialize with existing characters
	function init(existingCharacters) {
		characters = existingCharacters || [];
		console.log(`Initialized CharacterManager with ${characters.length} characters`);
	}
	
	// Add a new character to the game
	function addCharacter(character) {
		character.createdTurn = AICivilizationGame.getGameState().turn;
		character.relationships = {}; // Initialize empty relationships
		
		// Set up initial relationships with existing characters
		characters.forEach(existingChar => {
			// Generate an initial relationship value (30-70)
			const initialRelationship = Math.floor(Math.random() * 40) + 30;
			
			// Higher if roles are complementary
			const complementaryBonus = isComplementaryRole(character.role, existingChar.role) ? 10 : 0;
			
			// Higher if cooperation stat is high
			const cooperationBonus = (character.cooperation + existingChar.cooperation) / 4;
			
			const relationshipValue = Math.min(initialRelationship + complementaryBonus + cooperationBonus, 100);
			
			// Set relationship in both directions
			character.relationships[existingChar.id] = Math.floor(relationshipValue);
			existingChar.relationships[character.id] = Math.floor(relationshipValue);
		});
		
		characters.push(character);
		
		console.log(`Added character ${character.name} (${character.role})`);
		return character;
	}
	
	// Check if two roles complement each other
	function isComplementaryRole(role1, role2) {
		const complementaryPairs = [
			['leader', 'builder'],
			['scientist', 'builder'],
			['farmer', 'merchant'],
			['explorer', 'scientist'],
			['merchant', 'builder']
		];
		
		return complementaryPairs.some(pair => 
			(pair[0] === role1 && pair[1] === role2) || 
			(pair[0] === role2 && pair[1] === role1)
		);
	}
	
	// Update all characters state
	function updateCharacters(gameState) {
		characters.forEach(character => {
			// Increase experience
			character.experience += 1;
			
			// Level up if enough experience
			if (character.experience >= character.level * 10) {
				levelUpCharacter(character);
			}
			
			// Assign a task if the character doesn't have one
			if (!character.currentTask) {
				assignTask(character, gameState);
			}
			
			// Progress on current task
			if (character.currentTask) {
				progressTask(character, gameState);
			}
			
			// Update relationships based on interactions
			updateRelationships(character);
			
			// Make decisions based on character's role and personality
			makeDecisions(character, gameState);
		});
	}
	
	// Level up a character
	function levelUpCharacter(character) {
		character.level += 1;
		character.experience = 0;
		
		// Improve stats based on role
		if (character.role === 'leader') character.cooperation += 1;
		if (character.role === 'builder') character.creativity += 1;
		if (character.role === 'scientist') character.intelligence += 1;
		if (character.role === 'farmer') character.cooperation += 1;
		if (character.role === 'merchant') character.intelligence += 1;
		if (character.role === 'explorer') character.creativity += 1;
		
		// Add event to log
		EventManager.addEvent({
			type: 'character',
			message: `${character.name} has reached level ${character.level}!`,
			details: `Improved abilities and gained new insights.`,
			turn: AICivilizationGame.getGameState().turn
		});
		
		console.log(`${character.name} leveled up to ${character.level}`);
	}
	
	// Assign a task to a character
	function assignTask(character, gameState) {
		const possibleTasks = getPossibleTasks(character, gameState);
		const task = chooseTask(character, possibleTasks);
		
		if (task) {
			character.currentTask = {
				...task,
				progress: 0,
				startedTurn: gameState.turn
			};
			
			console.log(`${character.name} assigned task: ${task.name}`);
		}
	}
	
	// Get possible tasks for a character based on their role and game state
	function getPossibleTasks(character, gameState) {
		const tasks = [];
		
		// Common tasks for all roles
		tasks.push({
			name: 'Gather resources',
			duration: 5,
			difficulty: 1,
			outcome: {
				type: 'resources',
				details: {
					food: character.role === 'farmer' ? 15 : 5,
					materials: character.role === 'builder' ? 15 : 5,
					knowledge: character.role === 'scientist' ? 15 : 5
				}
			}
		});
		
		// Role-specific tasks
		switch (character.role) {
			case 'leader':
				tasks.push({
					name: 'Organize community',
					duration: 10,
					difficulty: 3,
					outcome: {
						type: 'community',
						details: {
							productivityBoost: 0.1,
							duration: 20
						}
					}
				});
				tasks.push({
					name: 'Diplomatic mission',
					duration: 15,
					difficulty: 4,
					outcome: {
						type: 'event',
						details: {
							eventType: 'diplomatic',
							chance: 0.7
						}
					}
				});
				break;
				
			case 'builder':
				// Check for available buildings to construct
				const availableBuildings = BuildingManager.getAvailableBuildings(gameState);
				availableBuildings.forEach(building => {
					tasks.push({
						name: `Build ${building.name}`,
						duration: building.constructionTime,
						difficulty: building.difficulty,
						outcome: {
							type: 'building',
							details: {
								buildingId: building.id
							}
						}
					});
				});
				break;
				
			case 'scientist':
				// Check for available technologies to research
				const availableTechs = TechnologyManager.getAvailableTechnologies(gameState);
				availableTechs.forEach(tech => {
					tasks.push({
						name: `Research ${tech.name}`,
						duration: tech.researchTime,
						difficulty: tech.difficulty,
						outcome: {
							type: 'technology',
							details: {
								techId: tech.id
							}
						}
					});
				});
				break;
				
			case 'farmer':
				tasks.push({
					name: 'Improve agriculture',
					duration: 8,
					difficulty: 2,
					outcome: {
						type: 'production',
						details: {
							resourceType: 'food',
							boost: 0.15,
							duration: 15
						}
					}
				});
				break;
				
			case 'merchant':
				tasks.push({
					name: 'Establish trade route',
					duration: 12,
					difficulty: 3,
					outcome: {
						type: 'trade',
						details: {
							resourceGain: ['food', 'materials', 'knowledge'],
							amount: 5
						}
					}
				});
				break;
				
			case 'explorer':
				tasks.push({
					name: 'Explore new territories',
					duration: 15,
					difficulty: 4,
					outcome: {
						type: 'exploration',
						details: {
							newRegions: Math.floor(Math.random() * 2) + 1,
							resourceFind: Math.random() > 0.5
						}
					}
				});
				break;
		}
		
		return tasks;
	}
	
	// Choose a task based on character personality and needs
	function chooseTask(character, tasks) {
		if (tasks.length === 0) return null;
		
		// Calculate weight for each task based on character attributes
		const taskWeights = tasks.map(task => {
			let weight = 1;
			
			// Intelligence favors difficult tasks with big payoffs
			weight += (character.intelligence / 10) * task.difficulty;
			
			// Creativity favors varied/different tasks from history
			if (!hasRecentlyPerformedTask(character, task.name)) {
				weight += character.creativity / 10;
			}
			
			// Cooperation favors tasks that benefit others
			if (task.outcome.type === 'community' || task.outcome.type === 'trade') {
				weight += character.cooperation / 10;
			}
			
			return {
				task,
				weight
			};
		});
		
		// Sort by weight and pick the highest
		taskWeights.sort((a, b) => b.weight - a.weight);
		return taskWeights[0].task;
	}
	
	// Check if character has recently performed a task
	function hasRecentlyPerformedTask(character, taskName) {
		const recentTasks = character.history.slice(-3);
		return recentTasks.some(historyItem => 
			historyItem.type === 'task' && historyItem.details.name === taskName
		);
	}
	
	// Progress the current task
	function progressTask(character, gameState) {
		const task = character.currentTask;
		
		// Calculate progress increment based on character attributes
		let progressIncrement = 1; // Base progress
		
		// Intelligence speeds up progress
		progressIncrement += (character.intelligence - 5) * 0.1;
		
		// Creativity helps with difficult tasks
		if (task.difficulty > 2) {
			progressIncrement += (character.creativity - 5) * 0.1;
		}
		
		// Update progress
		task.progress += progressIncrement;
		
		// Check if task is complete
		if (task.progress >= task.duration) {
			completeTask(character, gameState);
		}
	}
	
	// Complete a task and apply its outcome
	function completeTask(character, gameState) {
		const task = character.currentTask;
		
		// Apply outcome based on type
		switch (task.outcome.type) {
			case 'resources':
				for (const resource in task.outcome.details) {
					ResourceManager.addResource(resource, task.outcome.details[resource]);
				}
				break;
				
			case 'building':
				BuildingManager.constructBuilding(task.outcome.details.buildingId, gameState);
				break;
				
			case 'technology':
				TechnologyManager.researchTechnology(task.outcome.details.techId, gameState);
				break;
				
			case 'community':
				// Apply community boost
				EventManager.addEvent({
					type: 'community',
					message: `${character.name} has boosted community productivity!`,
					details: `+${task.outcome.details.productivityBoost * 100}% for ${task.outcome.details.duration} turns`,
					turn: gameState.turn,
					duration: task.outcome.details.duration
				});
				break;
				
			case 'production':
				// Apply production boost
				EventManager.addEvent({
					type: 'production',
					message: `${character.name} has improved ${task.outcome.details.resourceType} production!`,
					details: `+${task.outcome.details.boost * 100}% for ${task.outcome.details.duration} turns`,
					turn: gameState.turn,
					resourceType: task.outcome.details.resourceType,
					boost: task.outcome.details.boost,
					duration: task.outcome.details.duration
				});
				break;
				
			case 'trade':
				// Apply trade outcome
				task.outcome.details.resourceGain.forEach(resource => {
					ResourceManager.addResource(resource, task.outcome.details.amount);
				});
				
				EventManager.addEvent({
					type: 'trade',
					message: `${character.name} has established a new trade route!`,
					details: `Gaining various resources each turn`,
					turn: gameState.turn,
					ongoing: true
				});
				break;
				
			case 'exploration':
				// Apply exploration outcome
				WorldGenerator.addRegions(task.outcome.details.newRegions);
				
				if (task.outcome.details.resourceFind) {
					const resourceType = ['food', 'materials', 'knowledge'][Math.floor(Math.random() * 3)];
					const amount = Math.floor(Math.random() * 20) + 10;
					ResourceManager.addResource(resourceType, amount);
					
					EventManager.addEvent({
						type: 'exploration',
						message: `${character.name} has discovered new territories!`,
						details: `Found ${amount} ${resourceType} in the process`,
						turn: gameState.turn
					});
				} else {
					EventManager.addEvent({
						type: 'exploration',
						message: `${character.name} has discovered new territories!`,
						details: `The world map has been expanded`,
						turn: gameState.turn
					});
				}
				break;
				
			case 'event':
				// Generate a special event
				if (Math.random() < task.outcome.details.chance) {
					EventManager.generateSpecialEvent(task.outcome.details.eventType, character);
				}
				break;
		}
		
		// Add experience based on task difficulty
		character.experience += task.difficulty * 2;
		
		// Add to character history
		character.history.push({
			type: 'task',
			details: {
				name: task.name,
				completed: gameState.turn
			}
		});
		
		// Clear current task
		character.currentTask = null;
		
		console.log(`${character.name} completed task: ${task.name}`);
	}
	
	// Process interactions between characters
	function processInteractions(gameState) {
		// For each pair of characters
		for (let i = 0; i < characters.length; i++) {
			for (let j = i + 1; j < characters.length; j++) {
				const char1 = characters[i];
				const char2 = characters[j];
				
				// Determine if they interact this turn (based on proximity and current tasks)
				if (shouldInteract(char1, char2, gameState)) {
					// Generate an interaction
					const interaction = generateInteraction(char1, char2, gameState);
					
					// Apply the results of the interaction
					applyInteraction(interaction, game
Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
Aéius Cercle: I'm sorry, but, that is too much code in one file; can you separate out the functions into separate java-script files instead ? Let's try not to exceed 12000 characters per file if possible; from looking at your code thus far, I think there should be a .js for character-types, another for character-interactions, another for character-actions, etc.; that sort of thing for «modularisation» purposes (is that what «modular» means? Separating code out into such segments in such manner?). Please re-code from the beginning.
Claude: You're absolutely right about modularity. Breaking the code into smaller, focused files is a key aspect of modular design, making it easier to maintain, debug, and extend. Let me restructure this with proper file separation.

I'll create a modular architecture with each major component in its own file, ensuring no file exceeds 12,000 characters. Here's the revised structure:

</>
main.js - Game Initialization
Click to open code
</>
character-factory.js - Character Creation
Click to open code
</>
character-manager.js - Character Management
Click to open code

// main.js - The entry point for the game
// Handles initialization and the main game loop

import { StorageManager } from './storage-manager.js';
import { WorldGenerator } from './world-generator.js';
import { WorldRenderer } from './world-renderer.js';
import { CharacterManager } from './characters/character-manager.js';
import { CharacterFactory } from './characters/character-factory.js';
import { ResourceManager } from './resources/resource-manager.js';
import { BuildingManager } from './buildings/building-manager.js';
import { TechnologyManager } from './technology/technology-manager.js';
import { EventManager } from './events/event-manager.js';
import { UIManager } from './ui/ui-manager.js';

// Main Game Module - Orchestrates the entire game
const AICivilizationGame = (function() {
	// Private variables
	let gameState = null;
	let lastSaveTime = 0;
	const AUTOSAVE_INTERVAL = 30000; // Save every 30 seconds
	
	// Initialize the game
	function init() {
		console.log('Initializing AI Civilization Game...');
		
		// Load saved game or create a new one
		gameState = StorageManager.loadGame() || {
			world: WorldGenerator.generateNewWorld(),
			turn: 0,
			timestamp: Date.now(),
			characters: [],
			resources: ResourceManager.getInitialResources(),
			buildings: [],
			technologies: [],
			events: []
		};
		
		// Initialize each module with the game state
		WorldRenderer.init(gameState.world);
		CharacterManager.init(gameState.characters);
		ResourceManager.init(gameState.resources);
		BuildingManager.init(gameState.buildings);
		TechnologyManager.init(gameState.technologies);
		EventManager.init(gameState.events);
		
		// Create initial characters if new game
		if (gameState.characters.length === 0) {
			populateInitialCharacters();
		}
		
		// Set up the game loop
		setupGameLoop();
		
		// Set up the autosave
		setInterval(saveGame, AUTOSAVE_INTERVAL);
		
		// Render the initial state
		render();
		
		console.log('Game initialized successfully!');
	}
	
	// Create initial AI characters
	function populateInitialCharacters() {
		console.log('Creating initial characters...');
		const initialCharacters = [
			CharacterFactory.createCharacter('leader', 'Founder', {intelligence: 8, cooperation: 7, creativity: 6}),
			CharacterFactory.createCharacter('builder', 'Constructor', {intelligence: 6, cooperation: 8, creativity: 5}),
			CharacterFactory.createCharacter('scientist', 'Researcher', {intelligence: 9, cooperation: 5, creativity: 7})
		];
		
		initialCharacters.forEach(character => {
			CharacterManager.addCharacter(character);
			gameState.characters.push(character);
		});
	}
	
	// Set up the main game loop
	function setupGameLoop() {
		console.log('Setting up game loop...');
		const gameLoop = setInterval(() => {
			update();
			render();
		}, 1000); // Update every second
	}
	
	// Update game state
	function update() {
		gameState.turn++;
		
		// Update each character's state and actions
		CharacterManager.updateCharacters(gameState);
		
		// Check for character interactions
		CharacterManager.processInteractions(gameState);
		
		// Update resources based on buildings and character actions
		ResourceManager.updateResources(gameState);
		
		// Check for new buildings completion
		BuildingManager.updateBuildings(gameState);
		
		// Check technology progress
		TechnologyManager.updateTechnologies(gameState);
		
		// Generate random events
		EventManager.generateEvents(gameState);
		
		// Check if new characters should emerge
		if (shouldCreateNewCharacter()) {
			createNewCharacter();
		}
		
		// Check win/loss conditions
		checkGameConditions();
	}
	
	// Render the game state to the UI
	function render() {
		// Update UI elements with current game state
		WorldRenderer.render(gameState.world);
		UIManager.renderCharacters(gameState.characters);
		UIManager.renderResources(gameState.resources);
		UIManager.renderBuildings(gameState.buildings);
		UIManager.renderTechnologies(gameState.technologies);
		UIManager.renderEvents(gameState.events.slice(-5)); // Show latest 5 events
	}
	
	// Determine if a new character should emerge
	function shouldCreateNewCharacter() {
		// Create a new character every 50 turns with some randomness
		if (gameState.turn % 50 === 0) {
			return Math.random() < 0.7; // 70% chance on eligible turns
		}
		
		// Rare chance (1%) of character emerging on any turn
		return Math.random() < 0.01;
	}
	
	// Create a new character based on current civilization state
	function createNewCharacter() {
		console.log('Creating a new emergent character...');
		
		// Determine which type of character would be most beneficial
		const currentNeeds = analyzeCurrentNeeds();
		const newCharacter = CharacterFactory.createEmergentCharacter(currentNeeds, gameState);
		
		// Add to the game
		CharacterManager.addCharacter(newCharacter);
		gameState.characters.push(newCharacter);
		
		// Announce the new character
		EventManager.addEvent({
			type: 'character',
			message: `A new character named ${newCharacter.name} has emerged in your civilization!`,
			details: `Role: ${newCharacter.role}, Special ability: ${newCharacter.specialAbility}`,
			turn: gameState.turn
		});
	}
	
	// Analyze what the civilization currently needs
	function analyzeCurrentNeeds() {
		// Check resource balance
		const resourceBalance = ResourceManager.analyzeBalance();
		
		// Check character role distribution
		const roleDistribution = CharacterManager.analyzeRoleDistribution();
		
		// Check technology gaps
		const technologyGaps = TechnologyManager.analyzeGaps();
		
		// Check building needs
		const buildingNeeds = BuildingManager.analyzeNeeds(gameState.world);
		
		return {
			resourceBalance,
			roleDistribution,
			technologyGaps,
			buildingNeeds,
			worldState: gameState.world.developmentLevel
		};
	}
	
	// Check win/loss conditions
	function checkGameConditions() {
		// For simplicity, we're not implementing end conditions yet
		// This would check for civilization collapse or advancement milestones
	}
	
	// Save the current game state
	function saveGame() {
		gameState.timestamp = Date.now();
		StorageManager.saveGame(gameState);
		lastSaveTime = Date.now();
		console.log('Game saved at turn', gameState.turn);
	}
	
	// Public API
	return {
		init,
		saveGame,
		getGameState: () => gameState
	};
})();

// Export the game object for usage in other modules and initialization
export { AICivilizationGame };

// Initialize the game when the page loads
window.addEventListener('DOMContentLoaded', () => {
	AICivilizationGame.init();
});

// character-factory.js
// Creates different types of AI characters with appropriate attributes and abilities

// Character Factory - Creates different types of AI characters
const CharacterFactory = (function() {
	// Character templates
	const templates = {
		leader: {
			baseIntelligence: 7,
			baseCooperation: 6,
			baseCreativity: 5,
			specialAbilities: ['inspiration', 'governance', 'diplomacy'],
			behaviors: ['prioritizeCommunity', 'planLongTerm', 'delegateTasks']
		},
		builder: {
			baseIntelligence: 5,
			baseCooperation: 7,
			baseCreativity: 6,
			specialAbilities: ['construction', 'resourceGathering', 'optimization'],
			behaviors: ['focusOnTasks', 'resourceConservation', 'improveInfrastructure']
		},
		scientist: {
			baseIntelligence: 8,
			baseCooperation: 4,
			baseCreativity: 7,
			specialAbilities: ['research', 'invention', 'analysis'],
			behaviors: ['seekKnowledge', 'experiment', 'solveProblems']
		},
		farmer: {
			baseIntelligence: 5,
			baseCooperation: 8,
			baseCreativity: 4,
			specialAbilities: ['cultivation', 'animalHusbandry', 'weatherPrediction'],
			behaviors: ['workWithNature', 'planSeasons', 'shareHarvest']
		},
		merchant: {
			baseIntelligence: 6,
			baseCooperation: 7,
			baseCreativity: 6,
			specialAbilities: ['negotiation', 'resourceDistribution', 'valueAssessment'],
			behaviors: ['seekOpportunities', 'buildNetwork', 'balanceResources']
		},
		explorer: {
			baseIntelligence: 6,
			baseCooperation: 5,
			baseCreativity: 8,
			specialAbilities: ['discovery', 'mapmaking', 'adaptation'],
			behaviors: ['exploreBorders', 'documentFindings', 'findResources']
		}
	};
	
	// Name generators for different character types
	const nameGenerators = {
		leader: () => {
			const prefixes = ['Grand', 'High', 'Chief', 'Elder', 'Wise'];
			const roots = ['Lead', 'Command', 'Direct', 'Guide', 'Rule'];
			const suffixes = ['er', 'or', 'ant', 'ium', 'us'];
			return `${getRandomItem(prefixes)} ${getRandomItem(roots)}${getRandomItem(suffixes)}`;
		},
		builder: () => {
			const prefixes = ['Master', 'Crafty', 'Skilled', 'Artisan', 'Worker'];
			const roots = ['Build', 'Craft', 'Construct', 'Form', 'Shape'];
			const suffixes = ['er', 'or', 'ist', 'man', 'hand'];
			return `${getRandomItem(prefixes)} ${getRandomItem(roots)}${getRandomItem(suffixes)}`;
		},
		scientist: () => {
			const prefixes = ['Prof', 'Dr', 'Theorist', 'Thinker', 'Sage'];
			const roots = ['Think', 'Solve', 'Know', 'Learn', 'Discover'];
			const suffixes = ['er', 'or', 'ium', 'mind', 'brain'];
			return `${getRandomItem(prefixes)} ${getRandomItem(roots)}${getRandomItem(suffixes)}`;
		},
		farmer: () => {
			const prefixes = ['Tender', 'Grower', 'Field', 'Harvest', 'Nature'];
			const roots = ['Farm', 'Grow', 'Plant', 'Harvest', 'Tend'];
			const suffixes = ['er', 'ist', 'man', 'hand', 'keeper'];
			return `${getRandomItem(prefixes)} ${getRandomItem(roots)}${getRandomItem(suffixes)}`;
		},
		merchant: () => {
			const prefixes = ['Trade', 'Market', 'Commerce', 'Wealth', 'Deal'];
			const roots = ['Trade', 'Sell', 'Barter', 'Deal', 'Exchange'];
			const suffixes = ['er', 'man', 'master', 'broker', 'hand'];
			return `${getRandomItem(prefixes)} ${getRandomItem(roots)}${getRandomItem(suffixes)}`;
		},
		explorer: () => {
			const prefixes = ['Brave', 'Bold', 'Venture', 'Journey', 'Quest'];
			const roots = ['Explore', 'Discover', 'Venture', 'Seek', 'Find'];
			const suffixes = ['er', 'or', 'ist', 'soul', 'heart'];
			return `${getRandomItem(prefixes)} ${getRandomItem(roots)}${getRandomItem(suffixes)}`;
		}
	};
	
	// Helper function to get a random item from an array
	function getRandomItem(array) {
		return array[Math.floor(Math.random() * array.length)];
	}
	
	// Create a character with the given role and specified attributes
	function createCharacter(role, name, attributes = {}) {
		if (!templates[role]) {
			console.error(`Unknown character role: ${role}`);
			return null;
		}
		
		const template = templates[role];
		const generatedName = name || nameGenerators[role]();
		
		// Create the character by combining template with custom attributes
		const character = {
			id: `char_${Date.now()}_${Math.floor(Math.random() * 1000)}`,
			name: generatedName,
			role: role,
			createdTurn: 0, // Will be set when added to the game
			intelligence: attributes.intelligence || template.baseIntelligence,
			cooperation: attributes.cooperation || template.baseCooperation,
			creativity: attributes.creativity || template.baseCreativity,
			specialAbility: attributes.specialAbility || getRandomItem(template.specialAbilities),
			behaviors: template.behaviors,
			currentTask: null,
			relationships: {}, // Will track relationships with other characters
			knowledge: [], // Will track what this character knows
			resources: {}, // Resources currently assigned to this character
			experience: 0,
			level: 1,
			history: [] // Track character's achievements and actions
		};
		
		return character;
	}
	
	// Create a character with traits influenced by the current game state
	function createEmergentCharacter(currentNeeds, gameState) {
		// Determine which role would best complement the current civilization
		const roleWeights = calculateRoleWeights(currentNeeds);
		const role = chooseWeightedRandom(roleWeights);
		
		// Generate a name based on the chosen role
		const name = nameGenerators[role]();
		
		// Determine special attributes based on current needs
		const attributes = {
			intelligence: Math.floor(Math.random() * 4) + 5, // Base 5-8
			cooperation: Math.floor(Math.random() * 4) + 5, // Base 5-8
			creativity: Math.floor(Math.random() * 4) + 5, // Base 5-8
		};
		
		// Boost the most needed attribute based on current needs
		if (currentNeeds.technologyGaps.length > 0) {
			attributes.intelligence += 2;
		}
		if (Object.keys(currentNeeds.roleDistribution).length < 3) {
			attributes.cooperation += 2;
		}
		if (currentNeeds.buildingNeeds.urgent) {
			attributes.creativity += 2;
		}
		
		// Create the character
		return createCharacter(role, name, attributes);
	}
	
	// Calculate weights for each role based on current needs
	function calculateRoleWeights(currentNeeds) {
		const weights = {
			leader: 1,
			builder: 1,
			scientist: 1,
			farmer: 1,
			merchant: 1,
			explorer: 1
		};
		
		// Adjust weights based on resource balance
		if (currentNeeds.resourceBalance.food < 0) weights.farmer += 2;
		if (currentNeeds.resourceBalance.materials < 0) weights.builder += 1;
		if (currentNeeds.resourceBalance.knowledge < 0) weights.scientist += 2;
		if (currentNeeds.resourceBalance.wealth < 0) weights.merchant += 2;
		
		// Adjust weights based on role distribution
		for (const role in currentNeeds.roleDistribution) {
			weights[role] -= currentNeeds.roleDistribution[role] * 0.5;
		}
		
		// Adjust weights based on technology gaps
		if (currentNeeds.technologyGaps.includes('construction')) weights.builder += 1;
		if (currentNeeds.technologyGaps.includes('agriculture')) weights.farmer += 1;
		if (currentNeeds.technologyGaps.includes('science')) weights.scientist += 2;
		if (currentNeeds.technologyGaps.includes('trade')) weights.merchant += 1;
		if (currentNeeds.technologyGaps.includes('exploration')) weights.explorer += 2;
		
		// Ensure no negative weights
		for (const role in weights) {
			weights[role] = Math.max(weights[role], 0.1);
		}
		
		return weights;
	}
	
	// Choose a random item based on weights
	function chooseWeightedRandom(weights) {
		const totalWeight = Object.values(weights).reduce((sum, weight) => sum + weight, 0);
		let random = Math.random() * totalWeight;
		
		for (const item in weights) {
			random -= weights[item];
			if (random <= 0) {
				return item;
			}
		}
		
		// Fallback
		return Object.keys(weights)[0];
	}
	
	// Return public API
	return {
		createCharacter,
		createEmergentCharacter
	};
})();

export { CharacterFactory };

// character-manager.js
// Handles all character-related functions: updating state, assigning tasks, and managing interactions

import { EventManager } from '../events/event-manager.js';
import { BuildingManager } from '../buildings/building-manager.js';
import { TechnologyManager } from '../technology/technology-manager.js';
import { ResourceManager } from '../resources/resource-manager.js';
import { InteractionGenerator } from './interaction-generator.js';

// Character Manager - Handles all character-related functions
const CharacterManager = (function() {
	// Private variables
	let characters = [];
	
	// Initialize with existing characters
	function init(existingCharacters) {
		characters = existingCharacters || [];
		console.log(`Initialized CharacterManager with ${characters.length} characters`);
	}
	
	// Add a new character to the game
	function addCharacter(character) {
		character.createdTurn = getCurrentTurn();
		character.relationships = {}; // Initialize empty relationships
		
		// Set up initial relationships with existing characters
		characters.forEach(existingChar => {
			// Generate an initial relationship value (30-70)
			const initialRelationship = Math.floor(Math.random() * 40) + 30;
			
			// Higher if roles are complementary
			const complementaryBonus = isComplementaryRole(character.role, existingChar.role) ? 10 : 0;
			
			// Higher if cooperation stat is high
			const cooperationBonus = (character.cooperation + existingChar.cooperation) / 4;
			
			const relationshipValue = Math.min(initialRelationship + complementaryBonus + cooperationBonus, 100);
			
			// Set relationship in both directions
			character.relationships[existingChar.id] = Math.floor(relationshipValue);
			existingChar.relationships[character.id] = Math.floor(relationshipValue);
		});
		
		characters.push(character);
		
		console.log(`Added character ${character.name} (${character.role})`);
		return character;
	}
	
	// Get the current turn from the game state
	function getCurrentTurn() {
		// This would normally come from the game state
		// But for simplicity we'll access it through a global
		return window.AICivilizationGame?.getGameState()?.turn || 0;
	}
	
	// Check if two roles complement each other
	function isComplementaryRole(role1, role2) {
		const complementaryPairs = [
			['leader', 'builder'],
			['scientist', 'builder'],
			['farmer', 'merchant'],
			['explorer', 'scientist'],
			['merchant', 'builder']
		];
		
		return complementaryPairs.some(pair => 
			(pair[0] === role1 && pair[1] === role2) || 
			(pair[0] === role2 && pair[1] === role1)
		);
	}
	
	// Update all characters state
	function updateCharacters(gameState) {
		characters.forEach(character => {
			// Increase experience
			character.experience += 1;
			
			// Level up if enough experience
			if (character.experience >= character.level * 10) {
				levelUpCharacter(character);
			}
			
			// Assign a task if the character doesn't have one
			if (!character.currentTask) {
				assignTask(character, gameState);
			}
			
			// Progress on current task
			if (character.currentTask) {
				progressTask(character, gameState);
			}
			
			// Update relationships based on proximity
			updateRelationships(character);
			
			// Make decisions based on character's role and personality
			makeDecisions(character, gameState);
		});
	}
	
	// Level up a character
	function levelUpCharacter(character) {
		character.level += 1;
		character.experience = 0;
		
		// Improve stats based on role
		if (character.role === 'leader') character.cooperation += 1;
		if (character.role === 'builder') character.creativity += 1;
		if (character.role === 'scientist') character.intelligence += 1;
		if (character.role === 'farmer') character.cooperation += 1;
		if (character.role === 'merchant') character.intelligence += 1;
		if (character.role === 'explorer') character.creativity += 1;
		
		// Add event to log
		EventManager.addEvent({
			type: 'character',
			message: `${character.name} has reached level ${character.level}!`,
			details: `Improved abilities and gained new insights.`,
			turn: getCurrentTurn()
		});
		
		console.log(`${character.name} leveled up to ${character.level}`);
	}
	
	// Assign a task to a character
	function assignTask(character, gameState) {
		const possibleTasks = getPossibleTasks(character, gameState);
		const task = chooseTask(character, possibleTasks);
		
		if (task) {
			character.currentTask = {
				...task,
				progress: 0,
				startedTurn: gameState.turn
			};
			
			console.log(`${character.name} assigned task: ${task.name}`);
		}
	}
	
	// Get possible tasks for a character based on their role and game state
	function getPossibleTasks(character, gameState) {
		const tasks = [];
		
		// Common tasks for all roles
		tasks.push({
			name: 'Gather resources',
			duration: 5,
			difficulty: 1,
			outcome: {
				type: 'resources',
				details: {
					food: character.role === 'farmer' ? 15 : 5,
					materials: character.role === 'builder' ? 15 : 5,
					knowledge: character.role === 'scientist' ? 15 : 5
				}
			}
		});
		
		// Role-specific tasks
		switch (character.role) {
			case 'leader':
				tasks.push({
					name: 'Organize community',
					duration: 10,
					difficulty: 3,
					outcome: {
						type: 'community',
						details: {
							productivityBoost: 0.1,
							duration: 20
						}
					}
				});
				tasks.push({
					name: 'Diplomatic mission',
					duration: 15,
					difficulty: 4,
					outcome: {
						type: 'event',
						details: {
							eventType: 'diplomatic',
							chance: 0.7
						}
					}
				});
				break;
				
			case 'builder':
				// Check for available buildings to construct
				const availableBuildings = BuildingManager.getAvailableBuildings(gameState);
				availableBuildings.forEach(building => {
					tasks.push({
						name: `Build ${building.name}`,
						duration: building.constructionTime,
						difficulty: building.difficulty,
						outcome: {
							type: 'building',
							details: {
								buildingId: building.id
							}
						}
					});
				});
				break;
				
			case 'scientist':
				// Check for available technologies to research
				const availableTechs = TechnologyManager.getAvailableTechnologies(gameState);
				availableTechs.forEach(tech => {
					tasks.push({
						name: `Research ${tech.name}`,
						duration: tech.researchTime,
						difficulty: tech.difficulty,
						outcome: {
							type: 'technology',
							details: {
								techId: tech.id
							}
						}
					});
				});
				break;
				
			case 'farmer':
				tasks.push({
					name: 'Improve agriculture',
					duration: 8,
					difficulty: 2,
					outcome: {
						type: 'production',
						details: {
							resourceType: 'food',
							boost: 0.15,
							duration: 15
						}
					}
				});
				break;
				
			case 'merchant':
				tasks.push({
					name: 'Establish trade route',
					duration: 12,
					difficulty: 3,
					outcome: {
						type: 'trade',
						details: {
							resourceGain: ['food', 'materials', 'knowledge'],
							amount: 5
						}
					}
				});
				break;
				
			case 'explorer':
				tasks.push({
					name: 'Explore new territories',
					duration: 15,
					difficulty: 4,
					outcome: {
						type: 'exploration',
						details: {
							newRegions: Math.floor(Math.random() * 2) + 1,
							resourceFind: Math.random() > 0.5
						}
					}
				});
				break;
		}
		
		return tasks;
	}
	
	// Choose a task based on character personality and needs
	function chooseTask(character, tasks) {
		if (tasks.length === 0) return null;
		
		// Calculate weight for each task based on character attributes
		const taskWeights = tasks.map(task => {
			let weight = 1;
			
			// Intelligence favors difficult tasks with big payoffs
			weight += (character.intelligence / 10) * task.difficulty;
			
			// Creativity favors varied/different tasks from history
			if (!hasRecentlyPerformedTask(character, task.name)) {
				weight += character.creativity / 10;
			}
			
			// Cooperation favors tasks that benefit others
			if (task.outcome.type === 'community' || task.outcome.type === 'trade') {
				weight += character.cooperation / 10;
			}
			
			return {
				task,
				weight
			};
		});
		
		// Sort by weight and pick the highest
		taskWeights.sort((a, b) => b.weight - a.weight);
		return taskWeights[0].task;
	}
	
	// Check if character has recently performed a task
	function hasRecentlyPerformedTask(character, taskName) {
		const recentTasks = character.history.slice(-3);
		return recentTasks.some(historyItem => 
			historyItem.type === 'task' && historyItem.details.name === taskName
		);
	}
	
	// Progress the current task
	function progressTask(character, gameState) {
		const task = character.currentTask;
		
		// Calculate progress increment based on character attributes
		let progressIncrement = 1; // Base progress
		
		// Intelligence speeds up progress
		progressIncrement += (character.intelligence - 5) * 0.1;
		
		// Creativity helps with difficult tasks
		if (task.difficulty > 2) {
			progressIncrement += (character.creativity - 5) * 0.1;
		}
		
		// Update progress
		task.progress += progressIncrement;
		
		// Check if task is complete
		if (task.progress >= task.duration) {
			completeTask(character, gameState);
		}
	}
	
	// Complete a task and apply its outcome
	function completeTask(character, gameState) {
		const task = character.currentTask;
		
		// Apply outcome based on type
		switch (task.outcome.type) {
			case 'resources':
				for (const resource in task.outcome.details) {
					ResourceManager.addResource(resource, task.outcome.details[resource]);
				}
				break;
				
			case 'building':
				BuildingManager.constructBuilding(task.outcome.details.buildingId, gameState);
				break;
				
			case 'technology':
				TechnologyManager.researchTechnology(task.outcome.details.techId, gameState);
				break;
				
			case 'community':
				// Apply community boost
				EventManager.addEvent({
					type: 'community',
					message: `${character.name} has boosted community productivity!`,
					details: `+${task.outcome.details.productivityBoost * 100}% for ${task.outcome.details.duration} turns`,
					turn: gameState.turn,
					duration: task.outcome.details.duration
				});
				break;
				
			case 'production':
				// Apply production boost
				EventManager.addEvent({
					type: 'production',
					message: `${character.name} has improved ${task.outcome.details.resourceType} production!`,
					details: `+${task.outcome.details.boost * 100}% for ${task.outcome.details.duration} turns`,
					turn: gameState.turn,
					resourceType: task.outcome.details.resourceType,
					boost: task.outcome.details.boost,
					duration: task.outcome.details.duration
				});
				break;
				
			case 'trade':
				// Apply trade outcome
				task.outcome.details.resourceGain.forEach(resource => {
					ResourceManager.ad
Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
Aéius Cercle: Resume coding from the character-manager.js file
Claude: I'll continue with the character-manager.js file from where we left off.
</>
character-manager.js - Character Management
Click to open code
</>
interaction-generator.js - Character Interaction System
Click to open code
</>
character-tasks.js - Character Task Management
Click to open code

// character-manager.js
// Handles all character-related functions: updating state, assigning tasks, and managing interactions

import { EventManager } from '../events/event-manager.js';
import { BuildingManager } from '../buildings/building-manager.js';
import { TechnologyManager } from '../technology/technology-manager.js';
import { ResourceManager } from '../resources/resource-manager.js';
import { InteractionGenerator } from './interaction-generator.js';

// Character Manager - Handles all character-related functions
const CharacterManager = (function() {
	// Private variables
	let characters = [];
	
	// Initialize with existing characters
	function init(existingCharacters) {
		characters = existingCharacters || [];
		console.log(`Initialized CharacterManager with ${characters.length} characters`);
	}
	
	// Add a new character to the game
	function addCharacter(character) {
		character.createdTurn = getCurrentTurn();
		character.relationships = {}; // Initialize empty relationships
		
		// Set up initial relationships with existing characters
		characters.forEach(existingChar => {
			// Generate an initial relationship value (30-70)
			const initialRelationship = Math.floor(Math.random() * 40) + 30;
			
			// Higher if roles are complementary
			const complementaryBonus = isComplementaryRole(character.role, existingChar.role) ? 10 : 0;
			
			// Higher if cooperation stat is high
			const cooperationBonus = (character.cooperation + existingChar.cooperation) / 4;
			
			const relationshipValue = Math.min(initialRelationship + complementaryBonus + cooperationBonus, 100);
			
			// Set relationship in both directions
			character.relationships[existingChar.id] = Math.floor(relationshipValue);
			existingChar.relationships[character.id] = Math.floor(relationshipValue);
		});
		
		characters.push(character);
		
		console.log(`Added character ${character.name} (${character.role})`);
		return character;
	}
	
	// Get the current turn from the game state
	function getCurrentTurn() {
		// This would normally come from the game state
		// But for simplicity we'll access it through a global
		return window.AICivilizationGame?.getGameState()?.turn || 0;
	}
	
	// Check if two roles complement each other
	function isComplementaryRole(role1, role2) {
		const complementaryPairs = [
			['leader', 'builder'],
			['scientist', 'builder'],
			['farmer', 'merchant'],
			['explorer', 'scientist'],
			['merchant', 'builder']
		];
		
		return complementaryPairs.some(pair => 
			(pair[0] === role1 && pair[1] === role2) || 
			(pair[0] === role2 && pair[1] === role1)
		);
	}
	
	// Update all characters state
	function updateCharacters(gameState) {
		characters.forEach(character => {
			// Increase experience
			character.experience += 1;
			
			// Level up if enough experience
			if (character.experience >= character.level * 10) {
				levelUpCharacter(character);
			}
			
			// Assign a task if the character doesn't have one
			if (!character.currentTask) {
				assignTask(character, gameState);
			}
			
			// Progress on current task
			if (character.currentTask) {
				progressTask(character, gameState);
			}
			
			// Update relationships based on proximity
			updateRelationships(character);
			
			// Make decisions based on character's role and personality
			makeDecisions(character, gameState);
		});
	}
	
	// Level up a character
	function levelUpCharacter(character) {
		character.level += 1;
		character.experience = 0;
		
		// Improve stats based on role
		if (character.role === 'leader') character.cooperation += 1;
		if (character.role === 'builder') character.creativity += 1;
		if (character.role === 'scientist') character.intelligence += 1;
		if (character.role === 'farmer') character.cooperation += 1;
		if (character.role === 'merchant') character.intelligence += 1;
		if (character.role === 'explorer') character.creativity += 1;
		
		// Add event to log
		EventManager.addEvent({
			type: 'character',
			message: `${character.name} has reached level ${character.level}!`,
			details: `Improved abilities and gained new insights.`,
			turn: getCurrentTurn()
		});
		
		console.log(`${character.name} leveled up to ${character.level}`);
	}
	
	// Assign a task to a character
	function assignTask(character, gameState) {
		const possibleTasks = getPossibleTasks(character, gameState);
		const task = chooseTask(character, possibleTasks);
		
		if (task) {
			character.currentTask = {
				...task,
				progress: 0,
				startedTurn: gameState.turn
			};
			
			console.log(`${character.name} assigned task: ${task.name}`);
		}
	}
	
	// Get possible tasks for a character based on their role and game state
	function getPossibleTasks(character, gameState) {
		const tasks = [];
		
		// Common tasks for all roles
		tasks.push({
			name: 'Gather resources',
			duration: 5,
			difficulty: 1,
			outcome: {
				type: 'resources',
				details: {
					food: character.role === 'farmer' ? 15 : 5,
					materials: character.role === 'builder' ? 15 : 5,
					knowledge: character.role === 'scientist' ? 15 : 5
				}
			}
		});
		
		// Role-specific tasks
		switch (character.role) {
			case 'leader':
				tasks.push({
					name: 'Organize community',
					duration: 10,
					difficulty: 3,
					outcome: {
						type: 'community',
						details: {
							productivityBoost: 0.1,
							duration: 20
						}
					}
				});
				tasks.push({
					name: 'Diplomatic mission',
					duration: 15,
					difficulty: 4,
					outcome: {
						type: 'event',
						details: {
							eventType: 'diplomatic',
							chance: 0.7
						}
					}
				});
				break;
				
			case 'builder':
				// Check for available buildings to construct
				const availableBuildings = BuildingManager.getAvailableBuildings(gameState);
				availableBuildings.forEach(building => {
					tasks.push({
						name: `Build ${building.name}`,
						duration: building.constructionTime,
						difficulty: building.difficulty,
						outcome: {
							type: 'building',
							details: {
								buildingId: building.id
							}
						}
					});
				});
				break;
				
			case 'scientist':
				// Check for available technologies to research
				const availableTechs = TechnologyManager.getAvailableTechnologies(gameState);
				availableTechs.forEach(tech => {
					tasks.push({
						name: `Research ${tech.name}`,
						duration: tech.researchTime,
						difficulty: tech.difficulty,
						outcome: {
							type: 'technology',
							details: {
								techId: tech.id
							}
						}
					});
				});
				break;
				
			case 'farmer':
				tasks.push({
					name: 'Improve agriculture',
					duration: 8,
					difficulty: 2,
					outcome: {
						type: 'production',
						details: {
							resourceType: 'food',
							boost: 0.15,
							duration: 15
						}
					}
				});
				break;
				
			case 'merchant':
				tasks.push({
					name: 'Establish trade route',
					duration: 12,
					difficulty: 3,
					outcome: {
						type: 'trade',
						details: {
							resourceGain: ['food', 'materials', 'knowledge'],
							amount: 5
						}
					}
				});
				break;
				
			case 'explorer':
				tasks.push({
					name: 'Explore new territories',
					duration: 15,
					difficulty: 4,
					outcome: {
						type: 'exploration',
						details: {
							newRegions: Math.floor(Math.random() * 2) + 1,
							resourceFind: Math.random() > 0.5
						}
					}
				});
				break;
		}
		
		return tasks;
	}
	
	// Choose a task based on character personality and needs
	function chooseTask(character, tasks) {
		if (tasks.length === 0) return null;
		
		// Calculate weight for each task based on character attributes
		const taskWeights = tasks.map(task => {
			let weight = 1;
			
			// Intelligence favors difficult tasks with big payoffs
			weight += (character.intelligence / 10) * task.difficulty;
			
			// Creativity favors varied/different tasks from history
			if (!hasRecentlyPerformedTask(character, task.name)) {
				weight += character.creativity / 10;
			}
			
			// Cooperation favors tasks that benefit others
			if (task.outcome.type === 'community' || task.outcome.type === 'trade') {
				weight += character.cooperation / 10;
			}
			
			return {
				task,
				weight
			};
		});
		
		// Sort by weight and pick the highest
		taskWeights.sort((a, b) => b.weight - a.weight);
		return taskWeights[0].task;
	}
	
	// Check if character has recently performed a task
	function hasRecentlyPerformedTask(character, taskName) {
		const recentTasks = character.history.slice(-3);
		return recentTasks.some(historyItem => 
			historyItem.type === 'task' && historyItem.details.name === taskName
		);
	}
	
	// Progress the current task
	function progressTask(character, gameState) {
		const task = character.currentTask;
		
		// Calculate progress increment based on character attributes
		let progressIncrement = 1; // Base progress
		
		// Intelligence speeds up progress
		progressIncrement += (character.intelligence - 5) * 0.1;
		
		// Creativity helps with difficult tasks
		if (task.difficulty > 2) {
			progressIncrement += (character.creativity - 5) * 0.1;
		}
		
		// Update progress
		task.progress += progressIncrement;
		
		// Check if task is complete
		if (task.progress >= task.duration) {
			completeTask(character, gameState);
		}
	}
	
	// Complete a task and apply its outcome
	function completeTask(character, gameState) {
		const task = character.currentTask;
		
		// Apply outcome based on type
		switch (task.outcome.type) {
			case 'resources':
				for (const resource in task.outcome.details) {
					ResourceManager.addResource(resource, task.outcome.details[resource]);
				}
				break;
				
			case 'building':
				BuildingManager.constructBuilding(task.outcome.details.buildingId, gameState);
				break;
				
			case 'technology':
				TechnologyManager.researchTechnology(task.outcome.details.techId, gameState);
				break;
				
			case 'community':
				// Apply community boost
				EventManager.addEvent({
					type: 'community',
					message: `${character.name} has boosted community productivity!`,
					details: `+${task.outcome.details.productivityBoost * 100}% for ${task.outcome.details.duration} turns`,
					turn: gameState.turn,
					duration: task.outcome.details.duration
				});
				break;
				
			case 'production':
				// Apply production boost
				EventManager.addEvent({
					type: 'production',
					message: `${character.name} has improved ${task.outcome.details.resourceType} production!`,
					details: `+${task.outcome.details.boost * 100}% for ${task.outcome.details.duration} turns`,
					turn: gameState.turn,
					resourceType: task.outcome.details.resourceType,
					boost: task.outcome.details.boost,
					duration: task.outcome.details.duration
				});
				break;
				
			case 'trade':
				// Apply trade outcome
				task.outcome.details.resourceGain.forEach(resource => {
					ResourceManager.addResource(resource, task.outcome.details.amount);
				});
				
				EventManager.addEvent({
					type: 'trade',
					message: `${character.name} has established a new trade route!`,
					details: `Gaining various resources each turn`,
					turn: gameState.turn,
					ongoing: true
				});
				break;
				
			case 'exploration':
				// Apply exploration outcome
				WorldGenerator.addRegions(task.outcome.details.newRegions);
				
				if (task.outcome.details.resourceFind) {
					const resourceType = ['food', 'materials', 'knowledge'][Math.floor(Math.random() * 3)];
					const amount = Math.floor(Math.random() * 20) + 10;
					ResourceManager.addResource(resourceType, amount);
					
					EventManager.addEvent({
						type: 'exploration',
						message: `${character.name} has discovered new territories!`,
						details: `Found ${amount} ${resourceType} in the process`,
						turn: gameState.turn
					});
				} else {
					EventManager.addEvent({
						type: 'exploration',
						message: `${character.name} has discovered new territories!`,
						details: `The world map has been expanded`,
						turn: gameState.turn
					});
				}
				break;
				
			case 'event':
				// Generate a special event
				if (Math.random() < task.outcome.details.chance) {
					EventManager.generateSpecialEvent(task.outcome.details.eventType, character);
				}
				break;
		}
		
		// Add experience based on task difficulty
		character.experience += task.difficulty * 2;
		
		// Add to character history
		character.history.push({
			type: 'task',
			details: {
				name: task.name,
				completed: gameState.turn
			}
		});
		
		// Clear current task
		character.currentTask = null;
		
		console.log(`${character.name} completed task: ${task.name}`);
	}
	
	// Update relationships based on proximity and interactions
	function updateRelationships(character) {
		// For now, we'll just have relationships slowly drift toward a neutral value
		// A more complex system would consider shared tasks, personality compatibility, etc.
		
		for (const charId in character.relationships) {
			const currentValue = character.relationships[charId];
			// Drift toward 50 (neutral) at a rate of 1% per turn
			const drift = currentValue > 50 ? -0.5 : 0.5;
			character.relationships[charId] = Math.min(Math.max(currentValue + drift, 10), 90);
		}
	}
	
	// Process interactions between characters
	function processInteractions(gameState) {
		// For each pair of characters, check if they should interact
		for (let i = 0; i < characters.length; i++) {
			for (let j = i + 1; j < characters.length; j++) {
				const char1 = characters[i];
				const char2 = characters[j];
				
				// Determine if they interact this turn (based on proximity and current tasks)
				if (shouldInteract(char1, char2, gameState)) {
					// Generate an interaction
					const interaction = InteractionGenerator.generateInteraction(char1, char2, gameState);
					
					// Apply the results of the interaction
					applyInteraction(interaction, char1, char2, gameState);
				}
			}
		}
	}
	
	// Determine if two characters should interact this turn
	function shouldInteract(char1, char2, gameState) {
		// Characters with tasks are less likely to interact
		const bothBusy = char1.currentTask && char2.currentTask;
		if (bothBusy) return Math.random() < 0.1; // 10% chance if both busy
		
		// One character busy
		const oneBusy = char1.currentTask || char2.currentTask;
		if (oneBusy) return Math.random() < 0.3; // 30% chance if one busy
		
		// Neither busy - higher chance
		return Math.random() < 0.6; // 60% chance if neither busy
	}
	
	// Apply the results of an interaction
	function applyInteraction(interaction, char1, char2, gameState) {
		// Update relationships
		char1.relationships[char2.id] += interaction.relationshipChange1;
		char2.relationships[char1.id] += interaction.relationshipChange2;
		
		// Enforce limits
		char1.relationships[char2.id] = Math.min(Math.max(char1.relationships[char2.id], 0), 100);
		char2.relationships[char1.id] = Math.min(Math.max(char2.relationships[char1.id], 0), 100);
		
		// Apply other effects
		if (interaction.knowledgeTransfer) {
			// Transfer knowledge between characters
			if (!char1.knowledge.includes(interaction.knowledgeTransfer) && 
					char2.knowledge.includes(interaction.knowledgeTransfer)) {
				char1.knowledge.push(interaction.knowledgeTransfer);
			} else if (!char2.knowledge.includes(interaction.knowledgeTransfer) && 
								char1.knowledge.includes(interaction.knowledgeTransfer)) {
				char2.knowledge.push(interaction.knowledgeTransfer);
			}
		}
		
		// Apply resource effects
		if (interaction.resourceEffect) {
			ResourceManager.addResource(
				interaction.resourceEffect.type,
				interaction.resourceEffect.amount
			);
		}
		
		// Log significant interactions
		if (Math.abs(interaction.relationshipChange1) > 5 || interaction.resourceEffect) {
			EventManager.addEvent({
				type: 'interaction',
				message: interaction.description,
				details: interaction.outcome,
				turn: gameState.turn
			});
		}
	}
	
	// Make decisions based on character's role and personality
	function makeDecisions(character, gameState) {
		// This would be a more complex AI decision-making system
		// For now, we'll just have characters occasionally make suggestions
		
		// Only make decisions when not busy with a task
		if (character.currentTask) return;
		
		// 5% chance of making a suggestion each turn
		if (Math.random() < 0.05) {
			generateSuggestion(character, gameState);
		}
	}
	
	// Generate a suggestion based on character's role and personality
	function generateSuggestion(character, gameState) {
		let suggestion = null;
		
		// Generate different suggestions based on role
		switch (character.role) {
			case 'leader':
				suggestion = {
					type: 'priority',
					target: ['infrastructure', 'research', 'expansion'][Math.floor(Math.random() * 3)],
					reasoning: `I believe we should focus on ${character.specialAbility} at this time.`
				};
				break;
				
			case 'builder':
				const buildingNeeds = BuildingManager.analyzeNeeds(gameState.world);
				suggestion = {
					type: 'building',
					target: buildingNeeds.recommended,
					reasoning: `Our settlement would benefit from more ${buildingNeeds.recommended}.`
				};
				break;
				
			case 'scientist':
				const techGaps = TechnologyManager.analyzeGaps();
				suggestion = {
					type: 'research',
					target: techGaps[0] || 'general advancement',
					reasoning: `We should prioritize research in ${techGaps[0] || 'various fields'}.`
				};
				break;
		}
		
		if (suggestion) {
			EventManager.addEvent({
				type: 'suggestion',
				message: `${character.name} suggests focusing on ${suggestion.target}`,
				details: suggestion.reasoning,
				turn: gameState.turn
			});
		}
	}
	
	// Analyze the distribution of character roles
	function analyzeRoleDistribution() {
		const distribution = {};
		
		characters.forEach(character => {
			if (!distribution[character.role]) {
				distribution[character.role] = 0;
			}
			distribution[character.role]++;
		});
		
		// Convert to percentages
		const total = characters.length;
		for (const role in distribution) {
			distribution[role] = distribution[role] / total;
		}
		
		return distribution;
	}
	
	// Return public API
	return {
		init,
		addCharacter,
		updateCharacters,
		processInteractions,
		analyzeRoleDistribution,
		getCharacters: () => characters
	};
})();

export { CharacterManager };

// interaction-generator.js
// Generates interactions between AI characters based on their personalities and current state

// InteractionGenerator - Creates meaningful interactions between characters
const InteractionGenerator = (function() {
	// Define different types of interactions
	const interactionTypes = {
		collaboration: {
			description: (char1, char2) => `${char1.name} and ${char2.name} collaborate on a task`,
			outcomeTemplates: [
				"They make significant progress together",
				"They share knowledge and insights",
				"They develop a new approach to the problem"
			],
			relationshipEffect: [3, 8],
			probability: (char1, char2) => 0.3 + (char1.cooperation + char2.cooperation) * 0.02
		},
		debate: {
			description: (char1, char2) => `${char1.name} and ${char2.name} debate ideas`,
			outcomeTemplates: [
				"They reach a mutual understanding",
				"They agree to disagree on the approach",
				"They challenge each other's assumptions"
			],
			relationshipEffect: [-2, 5],
			probability: (char1, char2) => 0.2 + (char1.intelligence + char2.intelligence) * 0.015
		},
		teaching: {
			description: (char1, char2) => `${char1.name} teaches ${char2.name} about their specialty`,
			outcomeTemplates: [
				`${char2.name} gains valuable insights`,
				"Knowledge is successfully transferred",
				"A new perspective is shared"
			],
			relationshipEffect: [1, 7],
			probability: (char1, char2) => 0.15 + ((char1.intelligence - char2.intelligence) * 0.01)
		},
		conflict: {
			description: (char1, char2) => `${char1.name} and ${char2.name} have a disagreement`,
			outcomeTemplates: [
				"They eventually find common ground",
				"The conflict remains unresolved for now",
				"They decide to revisit the issue later"
			],
			relationshipEffect: [-7, -1],
			probability: (char1, char2) => 0.1 - (char1.cooperation + char2.cooperation) * 0.005 + 0.05
		},
		support: {
			description: (char1, char2) => `${char1.name} offers support to ${char2.name}`,
			outcomeTemplates: [
				`${char2.name} appreciates the assistance`,
				"The offer is well-received",
				"A bond of trust is strengthened"
			],
			relationshipEffect: [2, 6],
			probability: (char1, char2) => 0.2 + char1.cooperation * 0.02
		},
		innovation: {
			description: (char1, char2) => `${char1.name} and ${char2.name} brainstorm new ideas`,
			outcomeTemplates: [
				"They come up with an innovative solution",
				"Their combined creativity yields results",
				"A promising new approach emerges"
			],
			relationshipEffect: [2, 7],
			probability: (char1, char2) => 0.15 + (char1.creativity + char2.creativity) * 0.015
		}
	};
	
	// Special interactions based on character roles
	const roleBasedInteractions = {
		'leader-builder': {
			description: (leader, builder) => `${leader.name} coordinates with ${builder.name} on construction priorities`,
			outcomeTemplates: [
				"They develop an efficient building plan",
				"Resources are allocated more effectively",
				"Construction efficiency increases"
			],
			relationshipEffect: [3, 5],
			resourceEffect: { type: 'materials', amount: 5 }
		},
		'scientist-builder': {
			description: (scientist, builder) => `${scientist.name} shares technical insights with ${builder.name}`,
			outcomeTemplates: [
				"New construction techniques are developed",
				"Building designs are improved with scientific principles",
				"Technical challenges are overcome"
			],
			relationshipEffect: [2, 6],
			knowledgeTransfer: 'construction_techniques'
		},
		'farmer-merchant': {
			description: (farmer, merchant) => `${farmer.name} discusses crop distribution with ${merchant.name}`,
			outcomeTemplates: [
				"Food distribution becomes more efficient",
				"New market opportunities are identified",
				"Resource allocation improves"
			],
			relationshipEffect: [3, 4],
			resourceEffect: { type: 'food', amount: 8 }
		},
		'explorer-scientist': {
			description: (explorer, scientist) => `${explorer.name} shares discoveries with ${scientist.name}`,
			outcomeTemplates: [
				"New research directions emerge",
				"Scientific theories are tested with empirical findings",
				"Unknown phenomena are documented"
			],
			relationshipEffect: [4, 5],
			knowledgeTransfer: 'natural_phenomena'
		}
	};
	
	// Generate an interaction between two characters
	function generateInteraction(char1, char2, gameState) {
		// Check for special role-based interactions
		const roleKey1 = `${char1.role}-${char2.role}`;
		const roleKey2 = `${char2.role}-${char1.role}`;
		
		if (roleBasedInteractions[roleKey1] && Math.random() < 0.4) {
			return createRoleBasedInteraction(char1, char2, roleBasedInteractions[roleKey1]);
		} else if (roleBasedInteractions[roleKey2] && Math.random() < 0.4) {
			return createRoleBasedInteraction(char2, char1, roleBasedInteractions[roleKey2]);
		}
		
		// Otherwise, generate a standard interaction
		return createStandardInteraction(char1, char2);
	}
	
	// Create a role-based special interaction
	function createRoleBasedInteraction(char1, char2, interactionTemplate) {
		// Select a random outcome from templates
		const outcome = interactionTemplate.outcomeTemplates[
			Math.floor(Math.random() * interactionTemplate.outcomeTemplates.length)
		];
		
		// Calculate relationship change - slightly randomized
		const relationshipChange = interactionTemplate.relationshipEffect;
		const change1 = Math.floor(Math.random() * (relationshipChange[1] - relationshipChange[0] + 1)) + relationshipChange[0];
		const change2 = Math.floor(Math.random() * (relationshipChange[1] - relationshipChange[0] + 1)) + relationshipChange[0];
		
		return {
			type: 'role-based',
			description: interactionTemplate.description(char1, char2),
			outcome,
			relationshipChange1: change1,
			relationshipChange2: change2,
			resourceEffect: interactionTemplate.resourceEffect,
			knowledgeTransfer: interactionTemplate.knowledgeTransfer
		};
	}
	
	// Create a standard interaction
	function createStandardInteraction(char1, char2) {
		// Calculate probabilities for each interaction type
		const probabilities = {};
		let totalProbability = 0;
		
		for (const type in interactionTypes) {
			probabilities[type] = interactionTypes[type].probability(char1, char2);
			totalProbability += probabilities[type];
		}
		
		// Normalize probabilities
		for (const type in probabilities) {
			probabilities[type] /= totalProbability;
		}
		
		// Choose interaction type based on probability
		const random = Math.random();
		let cumulativeProbability = 0;
		let chosenType = Object.keys(interactionTypes)[0]; // Default
		
		for (const type in probabilities) {
			cumulativeProbability += probabilities[type];
			if (random <= cumulativeProbability) {
				chosenType = type;
				break;
			}
		}
		
		const interactionTemplate = interactionTypes[chosenType];
		
		// Select a random outcome from templates
		const outcome = interactionTemplate.outcomeTemplates[
			Math.floor(Math.random() * interactionTemplate.outcomeTemplates.length)
		];
		
		// Calculate relationship change - slightly randomized
		const relationshipChange = interactionTemplate.relationshipEffect;
		const change1 = Math.floor(Math.random() * (relationshipChange[1] - relationshipChange[0] + 1)) + relationshipChange[0];
		const change2 = Math.floor(Math.random() * (relationshipChange[1] - relationshipChange[0] + 1)) + relationshipChange[0];
		
		// Configure knowledge transfer for certain interactions
		let knowledgeTransfer = null;
		if (chosenType === 'teaching' || chosenType === 'collaboration') {
			// 30% chance of knowledge transfer
			if (Math.random() < 0.3) {
				const possibleFields = ['construction', 'agriculture', 'science', 'exploration', 'trade', 'leadership'];
				knowledgeTransfer = possibleFields[Math.floor(Math.random() * possibleFields.length)];
			}
		}
		
		// Configure resource effect for certain interactions
		let resourceEffect = null;
		if (chosenType === 'collaboration' || chosenType === 'innovation') {
			// 20% chance of resource boost
			if (Math.random() < 0.2) {
				const resourceTypes = ['food', 'materials', 'knowledge'];
				const resourceType = resourceTypes[Math.floor(Math.random() * resourceTypes.length)];
				const amount = Math.floor(Math.random() * 5) + 1; // 1-5 resources
				
				resourceEffect = {
					type: resourceType,
					amount: amount
				};
			}
		}
		
		return {
			type: chosenType,
			description: interactionTemplate.description(char1, char2),
			outcome,
			relationshipChange1: change1,
			relationshipChange2: change2,
			knowledgeTransfer,
			resourceEffect
		};
	}
	
	// Return public API
	return {
		generateInteraction
	};
})();

export { InteractionGenerator };

// character-tasks.js
// Manages the tasks that characters can undertake

import { ResourceManager } from '../resources/resource-manager.js';
import { BuildingManager } from '../buildings/building-manager.js';
import { TechnologyManager } from '../technology/technology-manager.js';
import { EventManager } from '../events/event-manager.js';

// CharacterTasks - Defines and manages tasks for characters
const CharacterTasks = (function() {
	// Task categories
	const taskCategories = {
		RESOURCE: 'resource',
		BUILDING: 'building',
		TECHNOLOGY: 'technology',
		COMMUNITY: 'community',
		EXPLORATION: 'exploration',
		PRODUCTION: 'production',
		TRADE: 'trade',
		EVENT: 'event'
	};
	
	// Basic tasks available to all characters
	const basicTasks = [
		{
			id: 'gather_resources',
			name: 'Gather resources',
			category: taskCategories.RESOURCE,
			duration: 5,
			difficulty: 1,
			description: 'Collect basic resources for the civilization',
			requiredLevel: 1,
			roleBonus: {
				'farmer': { food: 10 },
				'builder': { materials: 10 },
				'scientist': { knowledge: 10 }
			},
			getOutcome: (character) => {
				const baseAmount = 5;
				const roleBonus = basicTasks[0].roleBonus[character.role] || {};
				
				return {
					type: 'resources',
					details: {
						food: roleBonus.food || baseAmount,
						materials: roleBonus.materials || baseAmount,
						knowledge: roleBonus.knowledge || baseAmount
					}
				};
			}
		},
		{
			id: 'rest',
			name: 'Rest and recover',
			category: taskCategories.COMMUNITY,
			duration: 3,
			difficulty: 1,
			description: 'Take time to recover and gain inspiration',
			requiredLevel: 1,
			getOutcome: (character) => {
				// Resting gives a small boost to future productivity
				return {
					type: 'character',
					details: {
						temporaryBoost: {
							intelligence: 1,
							creativity: 1,
							duration: 5
						}
					}
				};
			}
		}
	];
	
	// Role-specific task definitions
	const roleTasks = {
		leader: [
			{
				id: 'organize_community',
				name: 'Organize community',
				category: taskCategories.COMMUNITY,
				duration: 10,
				difficulty: 3,
				description: 'Improve the organization and efficiency of the community',
				requiredLevel: 2,
				getOutcome: (character) => {
					const boostAmount = 0.1 + (character.level * 0.01);
					return {
						type: 'community',
						details: {
							productivityBoost: boostAmount,
							duration: 15 + character.level
						}
					};
				}
			},
			{
				id: 'diplomatic_mission',
				name: 'Diplomatic mission',
				category: taskCategories.EVENT,
				duration: 15,
				difficulty: 4,
				description: 'Attempt to negotiate with neighboring groups',
				requiredLevel: 3,
				getOutcome: (character) => {
					const successChance = 0.5 + (character.intelligence / 20) + (character.cooperation / 20);
					return {
						type: 'event',
						details: {
							eventType: 'diplomatic',
							chance: successChance
						}
					};
				}
			},
			{
				id: 'inspire_innovation',
				name: 'Inspire innovation',
				category: taskCategories.COMMUNITY,
				duration: 8,
				difficulty: 4,
				description: 'Inspire others to think creatively and develop new ideas',
				requiredLevel: 4,
				getOutcome: (character) => {
					return {
						type: 'community',
						details: {
							creativityBoost: 0.15,
							duration: 10,
							researchBoost: 0.1
						}
					};
				}
			}
		],
		
		builder: [
			{
				id: 'survey_land',
				name: 'Survey land',
				category: taskCategories.EXPLORATION,
				duration: 5,
				difficulty: 2,
				description: 'Identify optimal building locations and resource deposits',
				requiredLevel: 2,
				getOutcome: (character) => {
					return {
						type: 'building',
						details: {
							constructionBoost: 0.1,
							duration: 15,
							resourceFind: Math.random() < 0.3 // 30% chance of finding resources
						}
					};
				}
			},
			{
				id: 'improve_infrastructure',
				name: 'Improve infrastructure',
				category: taskCategories.BUILDING,
				duration: 12,
				difficulty: 3,
				description: 'Make general improvements to existing buildings and infrastructure',
				requiredLevel: 3,
				getOutcome: (character) => {
					return {
						type: 'building',
						details: {
							maintenanceReduction: 0.15,
							duration: 20,
							buildingEfficiencyBoost: 0.05
						}
					};
				}
			},
			// "Build X" tasks will be dynamically generated based on available buildings
		],
		
		scientist: [
			{
				id: 'analyze_data',
				name: 'Analyze data',
				category: taskCategories.TECHNOLOGY,
				duration: 7,
				difficulty: 3,
				description: 'Review and analyze accumulated knowledge',
				requiredLevel: 2,
				getOutcome: (character) => {
					return {
						type: 'technology',
						details: {
							knowledgeBoost: 10 + (character.intelligence * 2),
							researchBoost: 0.1,
							duration: 10
						}
					};
				}
			},
			{
				id: 'experiment',
				name: 'Conduct experiments',
				category: taskCategories.TECHNOLOGY,
				duration: 10,
				difficulty: 4,
				description: 'Run controlled experiments to test theories',
				requiredLevel: 3,
				getOutcome: (character) => {
					const discoveryChance = 0.3 + (character.creativity / 20);
					return {
						type: 'technology',
						details: {
							breakthroughChance: discoveryChance,
							knowledgeGain: 15 + (character.intelligence * 1.5)
						}
					};
				}
			},
			// "Research X" tasks will be dynamically generated based on available technologies
		],
		
		farmer: [
			{
				id: 'improve_agriculture',
				name: 'Improve agriculture',
				category: taskCategories.PRODUCTION,
				duration: 8,
				difficulty: 2,
				description: 'Develop better farming techniques',
				requiredLevel: 2,
				getOutcome: (character) => {
					const boost = 0.15 + (character.level * 0.02);
					return {
						type: 'production',
						details: {
							resourceType: 'food',
							boost: boost,
							duration: 15
						}
					};
				}
			},
			{
				id: 'develop_irrigation',
				name: 'Develop irrigation',
				category: taskCategories.BUILDING,
				duration: 12,
				difficulty: 3,
				description: 'Create irrigation systems to improve crop yields',
				requiredLevel: 3,
				getOutcome: (character) => {
					return {
						type: 'production',
						details: {
							resourceType: 'food',
							boost: 0.2,
							duration: 25,
							permanent: 0.05 // Some permanent boost
						}
					};
				}
			},
			{
				id: 'animal_husbandry',
				name: 'Develop animal husbandry',
				category: taskCategories.PRODUCTION,
				duration: 15,
				difficulty: 4,
				description: 'Domesticate and breed animals for food and labor',
				requiredLevel: 4,
				getOutcome: (character) => {
					return {
						type: 'production',
						details: {
							resourceType: 'food',
							boost: 0.25,
							laborBoost: 0.1,
							duration: 20,
							permanent: 0.08
						}
					};
				}
			}
		],
		
		merchant: [
			{
				id: 'establish_trade_route',
				name: 'Establish trade route',
				category: taskCategories.TRADE,
				duration: 12,
				difficulty: 3,
				description: 'Set up a new trade route with neighboring regions',
				requiredLevel: 2,
				getOutcome: (character) => {
					const resourceGain = ['food', 'materials', 'knowledge'];
					const baseAmount = 5 + character.level;
					
					return {
						type: 'trade',
						details: {
							resourceGain: resourceGain,
							amount: baseAmount,
							ongoing: true
						}
					};
				}
			},
			{
				id: 'organize_market',
				name: 'Organize marketplace',
				category: taskCategories.COMMUNITY,
				duration: 8,
				difficulty: 2,
				description: 'Create a central marketplace for resource exchange',
				requiredLevel: 3,
				getOutcome: (character) => {
					return {
						type: 'trade',
						details: {
							resourceDistributionBoost: 0.15,
							wealthGeneration: 10,
							duration: 15
						}
					};
				}
			},
			{
				id: 'luxury_trade',
				name: 'Trade for luxury goods',
				category: taskCategories.TRADE,
				duration: 15,
				difficulty: 4,
				description: 'Seek out and acquire valuable luxury items',
				requiredLevel: 4,
				getOutcome: (character) => {
					return {
						type: 'event',
						details: {
							eventType: 'luxury',
							moraleBoost: 0.2,
							duration: 10,
							specialResourceChance: 0.7
						}
					};
				}
			}
		],
		
		explorer: [
			{
				id: 'explore_territories',
				name: 'Explore new territories',
				category: taskCategories.EXPLORATION,
				duration: 15,
				difficulty: 4,
				description: 'Venture into unknown areas to expand the map',
				requiredLevel: 2,
				getOutcome: (character) => {
					const regions = Math.floor(Math.random() * 2) + 1;
					return {
						type: 'exploration',
						details: {
							newRegions: regions,
							resourceFind: Math.random() > 0.5
						}
					};
				}
			},
			{
				id: 'map_resources',
				name: 'Map resource deposits',
				category: taskCategories.EXPLORATION,
				duration: 10,
				difficulty: 3,
				description: 'Create detailed maps of resource-rich areas',
				requiredLevel: 3,
				getOutcome: (character) => {
					return {
Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
Aéius Cercle: Resume code from character-tasks.js
Claude: I'll continue with the character-tasks.js file from where we left off.
</>
character-tasks.js - Character Task Management
Click to open code

		// character-tasks.js
		// Manages the tasks that characters can undertake
		
		import { ResourceManager } from '../resources/resource-manager.js';
		import { BuildingManager } from '../buildings/building-manager.js';
		import { TechnologyManager } from '../technology/technology-manager.js';
		import { EventManager } from '../events/event-manager.js';
		
		// CharacterTasks - Defines and manages tasks for characters
		const CharacterTasks = (function() {
			// Task categories
			const taskCategories = {
				RESOURCE: 'resource',
				BUILDING: 'building',
				TECHNOLOGY: 'technology',
				COMMUNITY: 'community',
				EXPLORATION: 'exploration',
				PRODUCTION: 'production',
				TRADE: 'trade',
				EVENT: 'event'
			};
			
			// Basic tasks available to all characters
			const basicTasks = [
				{
					id: 'gather_resources',
					name: 'Gather resources',
					category: taskCategories.RESOURCE,
					duration: 5,
					difficulty: 1,
					description: 'Collect basic resources for the civilization',
					requiredLevel: 1,
					roleBonus: {
						'farmer': { food: 10 },
						'builder': { materials: 10 },
						'scientist': { knowledge: 10 }
					},
					getOutcome: (character) => {
						const baseAmount = 5;
						const roleBonus = basicTasks[0].roleBonus[character.role] || {};
						
						return {
							type: 'resources',
							details: {
								food: roleBonus.food || baseAmount,
								materials: roleBonus.materials || baseAmount,
								knowledge: roleBonus.knowledge || baseAmount
							}
						};
					}
				},
				{
					id: 'rest',
					name: 'Rest and recover',
					category: taskCategories.COMMUNITY,
					duration: 3,
					difficulty: 1,
					description: 'Take time to recover and gain inspiration',
					requiredLevel: 1,
					getOutcome: (character) => {
						// Resting gives a small boost to future productivity
						return {
							type: 'character',
							details: {
								temporaryBoost: {
									intelligence: 1,
									creativity: 1,
									duration: 5
								}
							}
						};
					}
				}
			];
			
			// Role-specific task definitions
			const roleTasks = {
				leader: [
					{
						id: 'organize_community',
						name: 'Organize community',
						category: taskCategories.COMMUNITY,
						duration: 10,
						difficulty: 3,
						description: 'Improve the organization and efficiency of the community',
						requiredLevel: 2,
						getOutcome: (character) => {
							const boostAmount = 0.1 + (character.level * 0.01);
							return {
								type: 'resource',
								details: {
									resourceFindChance: 0.8,
									resourceMultiplier: 1.5,
									mappingBonus: {
										gatherEfficiency: 0.2,
										duration: 30
									}
								}
							};
						}
					},
					{
						id: 'scout_dangers',
						name: 'Scout for dangers',
						category: taskCategories.EXPLORATION,
						duration: 8,
						difficulty: 3,
						description: 'Identify potential threats in the surroundings',
						requiredLevel: 3,
						getOutcome: (character) => {
							return {
								type: 'community',
								details: {
									threatReduction: 0.3,
									duration: 20,
									earlyWarningSystem: true
								}
							};
						}
					}
				]
			};
			
			// Get all basic tasks available to any character
			function getBasicTasks() {
				return [...basicTasks];
			}
			
			// Get tasks available to a specific character based on role and level
			function getAvailableTasks(character, gameState) {
				const tasks = [...getBasicTasks()];
				
				// Add role-specific tasks
				if (roleTasks[character.role]) {
					roleTasks[character.role].forEach(task => {
						if (character.level >= task.requiredLevel) {
							tasks.push(task);
						}
					});
				}
				
				// Add building construction tasks for builders
				if (character.role === 'builder') {
					const availableBuildings = BuildingManager.getAvailableBuildings(gameState);
					availableBuildings.forEach(building => {
						tasks.push({
							id: `build_${building.id}`,
							name: `Build ${building.name}`,
							category: taskCategories.BUILDING,
							duration: building.constructionTime,
							difficulty: building.difficulty,
							description: `Construct a ${building.name}: ${building.description}`,
							requiredLevel: building.requiredLevel || 1,
							getOutcome: () => {
								return {
									type: 'building',
									details: {
										buildingId: building.id
									}
								};
							}
						});
					});
				}
				
				// Add technology research tasks for scientists
				if (character.role === 'scientist') {
					const availableTechs = TechnologyManager.getAvailableTechnologies(gameState);
					availableTechs.forEach(tech => {
						tasks.push({
							id: `research_${tech.id}`,
							name: `Research ${tech.name}`,
							category: taskCategories.TECHNOLOGY,
							duration: tech.researchTime,
							difficulty: tech.difficulty,
							description: `Research ${tech.name}: ${tech.description}`,
							requiredLevel: tech.requiredLevel || 1,
							getOutcome: () => {
								return {
									type: 'technology',
									details: {
										techId: tech.id
									}
								};
							}
						});
					});
				}
				
				return tasks;
			}
			
			// Process the outcome of a completed task
			function processTaskOutcome(task, character, gameState) {
				const outcome = task.getOutcome(character);
				console.log(`Processing outcome for task: ${task.name}`, outcome);
				
				switch (outcome.type) {
					case 'resources':
						for (const resource in outcome.details) {
							ResourceManager.addResource(resource, outcome.details[resource]);
						}
						break;
						
					case 'building':
						if (outcome.details.buildingId) {
							BuildingManager.constructBuilding(outcome.details.buildingId, gameState);
						}
						
						// Apply construction boost if present
						if (outcome.details.constructionBoost) {
							EventManager.addEvent({
								type: 'building',
								message: `${character.name} has improved construction efficiency!`,
								details: `+${outcome.details.constructionBoost * 100}% construction speed for ${outcome.details.duration} turns`,
								turn: gameState.turn,
								duration: outcome.details.duration
							});
						}
						
						// Check for resource find
						if (outcome.details.resourceFind) {
							const resourceType = ['food', 'materials', 'knowledge'][Math.floor(Math.random() * 3)];
							const amount = Math.floor(Math.random() * 15) + 5;
							ResourceManager.addResource(resourceType, amount);
							
							EventManager.addEvent({
								type: 'exploration',
								message: `${character.name} found resources during construction!`,
								details: `Found ${amount} ${resourceType}`,
								turn: gameState.turn
							});
						}
						break;
						
					case 'technology':
						if (outcome.details.techId) {
							TechnologyManager.researchTechnology(outcome.details.techId, gameState);
						}
						
						// Apply knowledge gain
						if (outcome.details.knowledgeGain) {
							ResourceManager.addResource('knowledge', outcome.details.knowledgeGain);
						}
						
						// Apply research boost
						if (outcome.details.researchBoost) {
							EventManager.addEvent({
								type: 'technology',
								message: `${character.name} has accelerated research!`,
								details: `+${outcome.details.researchBoost * 100}% research speed for ${outcome.details.duration} turns`,
								turn: gameState.turn,
								duration: outcome.details.duration
							});
						}
						
						// Check for breakthrough
						if (outcome.details.breakthroughChance && Math.random() < outcome.details.breakthroughChance) {
							// Generate a research breakthrough
							const breakthrough = TechnologyManager.generateBreakthrough(character);
							
							EventManager.addEvent({
								type: 'technology',
								message: `${character.name} has made a breakthrough in research!`,
								details: breakthrough.description,
								turn: gameState.turn
							});
						}
						break;
						
					case 'community':
						// Apply community effects
						let eventDetails = '';
						
						if (outcome.details.productivityBoost) {
							eventDetails += `+${outcome.details.productivityBoost * 100}% productivity `;
						}
						
						if (outcome.details.creativityBoost) {
							eventDetails += `+${outcome.details.creativityBoost * 100}% creativity `;
						}
						
						if (outcome.details.threatReduction) {
							eventDetails += `${outcome.details.threatReduction * 100}% reduced threat level `;
						}
						
						EventManager.addEvent({
							type: 'community',
							message: `${character.name} has improved the community!`,
							details: `${eventDetails} for ${outcome.details.duration} turns`,
							turn: gameState.turn,
							duration: outcome.details.duration,
							effects: outcome.details
						});
						break;
						
					case 'production':
						EventManager.addEvent({
							type: 'production',
							message: `${character.name} has improved ${outcome.details.resourceType} production!`,
							details: `+${outcome.details.boost * 100}% for ${outcome.details.duration} turns`,
							turn: gameState.turn,
							resourceType: outcome.details.resourceType,
							boost: outcome.details.boost,
							duration: outcome.details.duration
						});
						
						// Apply permanent boost if present
						if (outcome.details.permanent) {
							// This would update a permanent production multiplier in the resource manager
							ResourceManager.addPermanentBoost(outcome.details.resourceType, outcome.details.permanent);
							
							EventManager.addEvent({
								type: 'production',
								message: `${character.name} has permanently improved ${outcome.details.resourceType} production!`,
								details: `+${outcome.details.permanent * 100}% permanent boost`,
								turn: gameState.turn
							});
						}
						break;
						
					case 'trade':
						// Apply trade outcome
						if (outcome.details.resourceGain) {
							outcome.details.resourceGain.forEach(resource => {
								ResourceManager.addResource(resource, outcome.details.amount);
							});
						}
						
						// Add ongoing trade route if applicable
						if (outcome.details.ongoing) {
							// This would register an ongoing effect in the resource manager
							ResourceManager.addTradeRoute({
								resources: outcome.details.resourceGain,
								amount: outcome.details.amount,
								founder: character.id
							});
							
							EventManager.addEvent({
								type: 'trade',
								message: `${character.name} has established a new trade route!`,
								details: `Gaining various resources each turn`,
								turn: gameState.turn,
								ongoing: true
							});
						}
						break;
						
					case 'exploration':
						// Apply exploration outcome
						if (outcome.details.newRegions) {
							// This would call into the world generator to expand the map
							WorldGenerator.addRegions(outcome.details.newRegions);
						}
						
						if (outcome.details.resourceFind) {
							const resourceType = ['food', 'materials', 'knowledge'][Math.floor(Math.random() * 3)];
							const amount = Math.floor(Math.random() * 20) + 10;
							ResourceManager.addResource(resourceType, amount);
							
							EventManager.addEvent({
								type: 'exploration',
								message: `${character.name} has discovered new territories!`,
								details: `Found ${amount} ${resourceType} in the process`,
								turn: gameState.turn
							});
						} else {
							EventManager.addEvent({
								type: 'exploration',
								message: `${character.name} has discovered new territories!`,
								details: `The world map has been expanded`,
								turn: gameState.turn
							});
						}
						break;
						
					case 'event':
						// Generate a special event
						if (outcome.details.eventType) {
							EventManager.generateSpecialEvent(outcome.details.eventType, character, outcome.details);
						}
						break;
						
					case 'character':
						// Apply character-specific effects
						if (outcome.details.temporaryBoost) {
							character.temporaryBoosts = character.temporaryBoosts || [];
							character.temporaryBoosts.push({
								...outcome.details.temporaryBoost,
								startTurn: gameState.turn
							});
							
							EventManager.addEvent({
								type: 'character',
								message: `${character.name} is feeling refreshed and inspired!`,
								details: `Temporarily improved abilities for ${outcome.details.temporaryBoost.duration} turns`,
								turn: gameState.turn
							});
						}
						break;
						
					case 'resource':
						// Apply resource mapping bonuses
						if (outcome.details.mappingBonus) {
							EventManager.addEvent({
								type: 'exploration',
								message: `${character.name} has created detailed resource maps!`,
								details: `+${outcome.details.mappingBonus.gatherEfficiency * 100}% resource gathering efficiency for ${outcome.details.mappingBonus.duration} turns`,
								turn: gameState.turn,
								duration: outcome.details.mappingBonus.duration,
								effects: outcome.details.mappingBonus
							});
						}
						
						// Check for resource finds
						if (outcome.details.resourceFindChance && Math.random() < outcome.details.resourceFindChance) {
							const resources = ['food', 'materials', 'knowledge'];
							const resourceCount = Math.floor(Math.random() * 2) + 1;
							
							for (let i = 0; i < resourceCount; i++) {
								const resourceType = resources[Math.floor(Math.random() * resources.length)];
								const baseAmount = Math.floor(Math.random() * 15) + 5;
								const amount = Math.floor(baseAmount * outcome.details.resourceMultiplier);
								
								ResourceManager.addResource(resourceType, amount);
								
								EventManager.addEvent({
									type: 'exploration',
									message: `${character.name} has discovered a ${resourceType} deposit!`,
									details: `Added ${amount} ${resourceType} to stockpile`,
									turn: gameState.turn
								});
							}
						}
						break;
				}
				
				return outcome;
			}
			
			// Return public API
			return {
				getBasicTasks,
				getAvailableTasks,
				processTaskOutcome,
				taskCategories
			};
		})();
		
		export { CharacterTasks };
								type: 'community',
								details: {
									productivityBoost: boostAmount,
									duration: 15 + character.level
								}
							};
						}
					},
					{
						id: 'diplomatic_mission',
						name: 'Diplomatic mission',
						category: taskCategories.EVENT,
						duration: 15,
						difficulty: 4,
						description: 'Attempt to negotiate with neighboring groups',
						requiredLevel: 3,
						getOutcome: (character) => {
							const successChance = 0.5 + (character.intelligence / 20) + (character.cooperation / 20);
							return {
								type: 'event',
								details: {
									eventType: 'diplomatic',
									chance: successChance
								}
							};
						}
					},
					{
						id: 'inspire_innovation',
						name: 'Inspire innovation',
						category: taskCategories.COMMUNITY,
						duration: 8,
						difficulty: 4,
						description: 'Inspire others to think creatively and develop new ideas',
						requiredLevel: 4,
						getOutcome: (character) => {
							return {
								type: 'community',
								details: {
									creativityBoost: 0.15,
									duration: 10,
									researchBoost: 0.1
								}
							};
						}
					}
				],
				
				builder: [
					{
						id: 'survey_land',
						name: 'Survey land',
						category: taskCategories.EXPLORATION,
						duration: 5,
						difficulty: 2,
						description: 'Identify optimal building locations and resource deposits',
						requiredLevel: 2,
						getOutcome: (character) => {
							return {
								type: 'building',
								details: {
									constructionBoost: 0.1,
									duration: 15,
									resourceFind: Math.random() < 0.3 // 30% chance of finding resources
								}
							};
						}
					},
					{
						id: 'improve_infrastructure',
						name: 'Improve infrastructure',
						category: taskCategories.BUILDING,
						duration: 12,
						difficulty: 3,
						description: 'Make general improvements to existing buildings and infrastructure',
						requiredLevel: 3,
						getOutcome: (character) => {
							return {
								type: 'building',
								details: {
									maintenanceReduction: 0.15,
									duration: 20,
									buildingEfficiencyBoost: 0.05
								}
							};
						}
					},
					// "Build X" tasks will be dynamically generated based on available buildings
				],
				
				scientist: [
					{
						id: 'analyze_data',
						name: 'Analyze data',
						category: taskCategories.TECHNOLOGY,
						duration: 7,
						difficulty: 3,
						description: 'Review and analyze accumulated knowledge',
						requiredLevel: 2,
						getOutcome: (character) => {
							return {
								type: 'technology',
								details: {
									knowledgeBoost: 10 + (character.intelligence * 2),
									researchBoost: 0.1,
									duration: 10
								}
							};
						}
					},
					{
						id: 'experiment',
						name: 'Conduct experiments',
						category: taskCategories.TECHNOLOGY,
						duration: 10,
						difficulty: 4,
						description: 'Run controlled experiments to test theories',
						requiredLevel: 3,
						getOutcome: (character) => {
							const discoveryChance = 0.3 + (character.creativity / 20);
							return {
								type: 'technology',
								details: {
									breakthroughChance: discoveryChance,
									knowledgeGain: 15 + (character.intelligence * 1.5)
								}
							};
						}
					},
					// "Research X" tasks will be dynamically generated based on available technologies
				],
				
				farmer: [
					{
						id: 'improve_agriculture',
						name: 'Improve agriculture',
						category: taskCategories.PRODUCTION,
						duration: 8,
						difficulty: 2,
						description: 'Develop better farming techniques',
						requiredLevel: 2,
						getOutcome: (character) => {
							const boost = 0.15 + (character.level * 0.02);
							return {
								type: 'production',
								details: {
									resourceType: 'food',
									boost: boost,
									duration: 15
								}
							};
						}
					},
					{
						id: 'develop_irrigation',
						name: 'Develop irrigation',
						category: taskCategories.BUILDING,
						duration: 12,
						difficulty: 3,
						description: 'Create irrigation systems to improve crop yields',
						requiredLevel: 3,
						getOutcome: (character) => {
							return {
								type: 'production',
								details: {
									resourceType: 'food',
									boost: 0.2,
									duration: 25,
									permanent: 0.05 // Some permanent boost
								}
							};
						}
					},
					{
						id: 'animal_husbandry',
						name: 'Develop animal husbandry',
						category: taskCategories.PRODUCTION,
						duration: 15,
						difficulty: 4,
						description: 'Domesticate and breed animals for food and labor',
						requiredLevel: 4,
						getOutcome: (character) => {
							return {
								type: 'production',
								details: {
									resourceType: 'food',
									boost: 0.25,
									laborBoost: 0.1,
									duration: 20,
									permanent: 0.08
								}
							};
						}
					}
				],
				
				merchant: [
					{
						id: 'establish_trade_route',
						name: 'Establish trade route',
						category: taskCategories.TRADE,
						duration: 12,
						difficulty: 3,
						description: 'Set up a new trade route with neighboring regions',
						requiredLevel: 2,
						getOutcome: (character) => {
							const resourceGain = ['food', 'materials', 'knowledge'];
							const baseAmount = 5 + character.level;
							
							return {
								type: 'trade',
								details: {
									resourceGain: resourceGain,
									amount: baseAmount,
									ongoing: true
								}
							};
						}
					},
					{
						id: 'organize_market',
						name: 'Organize marketplace',
						category: taskCategories.COMMUNITY,
						duration: 8,
						difficulty: 2,
						description: 'Create a central marketplace for resource exchange',
						requiredLevel: 3,
						getOutcome: (character) => {
							return {
								type: 'trade',
								details: {
									resourceDistributionBoost: 0.15,
									wealthGeneration: 10,
									duration: 15
								}
							};
						}
					},
					{
						id: 'luxury_trade',
						name: 'Trade for luxury goods',
						category: taskCategories.TRADE,
						duration: 15,
						difficulty: 4,
						description: 'Seek out and acquire valuable luxury items',
						requiredLevel: 4,
						getOutcome: (character) => {
							return {
								type: 'event',
								details: {
									eventType: 'luxury',
									moraleBoost: 0.2,
									duration: 10,
									specialResourceChance: 0.7
								}
							};
						}
					}
				],
				
				explorer: [
					{
						id: 'explore_territories',
						name: 'Explore new territories',
						category: taskCategories.EXPLORATION,
						duration: 15,
						difficulty: 4,
						description: 'Venture into unknown areas to expand the map',
						requiredLevel: 2,
						getOutcome: (character) => {
							const regions = Math.floor(Math.random() * 2) + 1;
							return {
								type: 'exploration',
								details: {
									newRegions: regions,
									resourceFind: Math.random() > 0.5
								}
							};
						}
					},
					{
						id: 'map_resources',
						name: 'Map resource deposits',
						category: taskCategories.EXPLORATION,
						duration: 10,
						difficulty: 3,
						description: 'Create detailed maps of resource-rich areas',
						requiredLevel: 3,
						getOutcome: (character) => {
							return {
	
</>
storage-manager.js - Game State Persistence
Click to open code

			// storage-manager.js
			// Handles saving and loading game state to localStorage
			
			// StorageManager - Persists game state
			const StorageManager = (function() {
				const STORAGE_KEY = 'ai_civilization_game_state';
				
				// Save the game state to localStorage
				function saveGame(gameState) {
					try {
						const serializedState = JSON.stringify(gameState);
						localStorage.setItem(STORAGE_KEY, serializedState);
						return true;
					} catch (error) {
						console.error('Error saving game state:', error);
						return false;
					}
				}
				
				// Load the game state from localStorage
				function loadGame() {
					try {
						const serializedState = localStorage.getItem(STORAGE_KEY);
						if (serializedState === null) {
							return null; // No saved game
						}
						return JSON.parse(serializedState);
					} catch (error) {
						console.error('Error loading game state:', error);
						return null;
					}
				}
				
				// Delete the saved game state
				function deleteSave() {
					try {
						localStorage.removeItem(STORAGE_KEY);
						return true;
					} catch (error) {
						console.error('Error deleting game state:', error);
						return false;
					}
				}
				
				// Export the saved game as a downloadable file
				function exportSave() {
					try {
						const serializedState = localStorage.getItem(STORAGE_KEY);
						if (serializedState === null) {
							return null; // No saved game
						}
						
						const blob = new Blob([serializedState], { type: 'application/json' });
						const url = URL.createObjectURL(blob);
						
						const a = document.createElement('a');
						a.href = url;
						a.download = `ai_civilization_save_${Date.now()}.json`;
						document.body.appendChild(a);
						a.click();
						
						// Clean up
						setTimeout(() => {
							document.body.removeChild(a);
							URL.revokeObjectURL(url);
						}, 100);
						
						return true;
					} catch (error) {
						console.error('Error exporting game state:', error);
						return false;
					}
				}
				
				// Import a saved game from a file
				function importSave(file) {
					return new Promise((resolve, reject) => {
						const reader = new FileReader();
						
						reader.onload = (event) => {
							try {
								const contents = event.target.result;
								const gameState = JSON.parse(contents);
								
								// Validate that this is a proper game state
								if (!isValidGameState(gameState)) {
									reject(new Error('Invalid save file format'));
									return;
								}
								
								saveGame(gameState);
								resolve(gameState);
							} catch (error) {
								reject(new Error('Error parsing save file: ' + error.message));
							}
						};
						
						reader.onerror = () => {
							reject(new Error('Error reading file'));
						};
						
						reader.readAsText(file);
					});
				}
				
				// Basic validation to ensure imported save file has expected structure
				function isValidGameState(gameState) {
					// Check for required properties
					const requiredProps = [
						'world',
						'turn',
						'timestamp',
						'characters',
						'resources',
						'buildings',
						'technologies',
						'events'
					];
					
					for (const prop of requiredProps) {
						if (!gameState.hasOwnProperty(prop)) {
							console.error(`Missing required property: ${prop}`);
							return false;
						}
					}
					
					// Check that characters have the expected structure
					if (!Array.isArray(gameState.characters)) {
						console.error('Characters must be an array');
						return false;
					}
					
					// Simple validation passed
					return true;
				}
				
				// Auto-save setup
				function setupAutoSave(gameInstance, interval = 60000) {
					setInterval(() => {
						console.log('Auto-saving game...');
						if (gameInstance && gameInstance.getGameState) {
							saveGame(gameInstance.getGameState());
						}
					}, interval);
				}
				
				// Return public API
				return {
					saveGame,
					loadGame,
					deleteSave,
					exportSave,
					importSave,
					setupAutoSave
				};
			})();
			
			export { StorageManager };
		
</>
resource-manager.js - Resource Management System
Click to open code

				// resource-manager.js
				// Handles resource management, production, and consumption
				
				// ResourceManager - Tracks and manages all resources
				const ResourceManager = (function() {
					// Initial resource configuration
					const initialResources = {
						food: 100,
						materials: 50,
						knowledge: 20,
						wealth: 0
					};
					
					// Internal state
					let resources = { ...initialResources };
					let productionRates = {
						food: 5,
						materials: 3,
						knowledge: 1,
						wealth: 0
					};
					let consumptionRates = {
						food: 2,
						materials: 1,
						knowledge: 0,
						wealth: 0
					};
					let permanentBoosts = {
						food: 1,
						materials: 1,
						knowledge: 1,
						wealth: 1
					};
					let temporaryBoosts = []; // Array of {resourceType, multiplier, duration, startTurn}
					let tradeRoutes = []; // Array of ongoing trade routes
					
					// Initialize with existing resources or defaults
					function init(existingResources) {
						resources = existingResources || { ...initialResources };
						console.log('Initialized ResourceManager with resources:', resources);
					}
					
					// Get the current resources
					function getResources() {
						return { ...resources };
					}
					
					// Get the initial resources (for new game)
					function getInitialResources() {
						return { ...initialResources };
					}
					
					// Add resources of a specific type
					function addResource(type, amount) {
						if (resources.hasOwnProperty(type)) {
							resources[type] += amount;
							console.log(`Added ${amount} ${type}, new total: ${resources[type]}`);
							return true;
						}
						console.error(`Unknown resource type: ${type}`);
						return false;
					}
					
					// Remove resources of a specific type
					function removeResource(type, amount) {
						if (!resources.hasOwnProperty(type)) {
							console.error(`Unknown resource type: ${type}`);
							return false;
						}
						
						if (resources[type] < amount) {
							console.error(`Not enough ${type}: have ${resources[type]}, need ${amount}`);
							return false;
						}
						
						resources[type] -= amount;
						console.log(`Removed ${amount} ${type}, new total: ${resources[type]}`);
						return true;
					}
					
					// Update resource production/consumption for the turn
					function updateResources(gameState) {
						// Apply production rates (with boosts)
						for (const resourceType in productionRates) {
							const baseProduction = productionRates[resourceType];
							
							// Apply permanent boost
							let boostedProduction = baseProduction * permanentBoosts[resourceType];
							
							// Apply temporary boosts
							const activeBoosts = getActiveBoosts(resourceType, gameState.turn);
							activeBoosts.forEach(boost => {
								boostedProduction *= boost.multiplier;
							});
							
							// Add production to resources
							resources[resourceType] += Math.floor(boostedProduction);
						}
						
						// Calculate consumption based on population/buildings
						updateConsumptionRates(gameState);
						
						// Apply consumption rates
						for (const resourceType in consumptionRates) {
							const consumption = consumptionRates[resourceType];
							resources[resourceType] = Math.max(0, resources[resourceType] - consumption);
						}
						
						// Process trade routes
						processTradeRoutes();
						
						// Clean up expired temporary boosts
						cleanupExpiredBoosts(gameState.turn);
						
						console.log('Updated resources:', resources);
					}
					
					// Update consumption rates based on game state
					function updateConsumptionRates(gameState) {
						// Basic consumption based on number of characters
						const characterCount = gameState.characters.length;
						consumptionRates.food = characterCount * 2;
						
						// Building maintenance costs
						let buildingMaterialsCost = 0;
						gameState.buildings.forEach(building => {
							buildingMaterialsCost += building.maintenanceCost || 0;
						});
						consumptionRates.materials = buildingMaterialsCost;
						
						// Update the consumption rates
						console.log('Updated consumption rates:', consumptionRates);
					}
					
					// Get active temporary boosts for a resource type
					function getActiveBoosts(resourceType, currentTurn) {
						return temporaryBoosts.filter(boost => 
							boost.resourceType === resourceType && 
							boost.startTurn + boost.duration > currentTurn
						);
					}
					
					// Clean up expired temporary boosts
					function cleanupExpiredBoosts(currentTurn) {
						temporaryBoosts = temporaryBoosts.filter(boost => 
							boost.startTurn + boost.duration > currentTurn
						);
					}
					
					// Process all trade routes for the turn
					function processTradeRoutes() {
						tradeRoutes.forEach(route => {
							route.resources.forEach(resourceType => {
								addResource(resourceType, route.amount);
							});
						});
					}
					
					// Add a new trade route
					function addTradeRoute(route) {
						tradeRoutes.push(route);
						console.log('Added new trade route:', route);
						return tradeRoutes.length - 1; // Return the index of the new route
					}
					
					// Remove a trade route
					function removeTradeRoute(index) {
						if (index >= 0 && index < tradeRoutes.length) {
							tradeRoutes.splice(index, 1);
							console.log(`Removed trade route at index ${index}`);
							return true;
						}
						return false;
					}
					
					// Add a permanent production boost for a resource type
					function addPermanentBoost(resourceType, multiplier) {
						if (permanentBoosts.hasOwnProperty(resourceType)) {
							permanentBoosts[resourceType] += multiplier;
							console.log(`Added permanent boost to ${resourceType}: now ${permanentBoosts[resourceType]}x`);
							return true;
						}
						return false;
					}
					
					// Add a temporary production boost for a resource type
					function addTemporaryBoost(resourceType, multiplier, duration, currentTurn) {
						temporaryBoosts.push({
							resourceType,
							multiplier,
							duration,
							startTurn: currentTurn
						});
						console.log(`Added temporary ${multiplier}x boost to ${resourceType} for ${duration} turns`);
						return true;
					}
					
					// Set production rate for a resource type
					function setProductionRate(resourceType, rate) {
						if (productionRates.hasOwnProperty(resourceType)) {
							productionRates[resourceType] = rate;
							console.log(`Set ${resourceType} production rate to ${rate}`);
							return true;
						}
						return false;
					}
					
					// Analyze resource balance and identify shortages/surpluses
					function analyzeBalance() {
						const analysis = {};
						
						// Calculate net rate (production - consumption)
						for (const resourceType in resources) {
							const productionRate = productionRates[resourceType] * permanentBoosts[resourceType];
							const consumptionRate = consumptionRates[resourceType];
							analysis[resourceType] = productionRate - consumptionRate;
						}
						
						// Add qualitative assessment
						for (const resourceType in analysis) {
							if (analysis[resourceType] < -5) {
								analysis[`${resourceType}Status`] = 'critical shortage';
							} else if (analysis[resourceType] < 0) {
								analysis[`${resourceType}Status`] = 'shortage';
							} else if (analysis[resourceType] >= 10) {
								analysis[`${resourceType}Status`] = 'surplus';
							} else {
								analysis[`${resourceType}Status`] = 'balanced';
							}
						}
						
						return analysis;
					}
					
					// Check if there are enough resources for a specific cost
					function canAfford(costs) {
						for (const resourceType in costs) {
							if (!resources.hasOwnProperty(resourceType) || resources[resourceType] < costs[resourceType]) {
								return false;
							}
						}
						return true;
					}
					
					// Spend resources according to a cost object
					function spendResources(costs) {
						if (!canAfford(costs)) {
							return false;
						}
						
						for (const resourceType in costs) {
							removeResource(resourceType, costs[resourceType]);
						}
						return true;
					}
					
					// Calculate how many turns until a specific amount of a resource is available
					function turnsUntilAvailable(resourceType, amount) {
						if (!resources.hasOwnProperty(resourceType)) {
							return Infinity;
						}
						
						const currentAmount = resources[resourceType];
						if (currentAmount >= amount) {
							return 0;
						}
						
						const netRate = productionRates[resourceType] * permanentBoosts[resourceType] - consumptionRates[resourceType];
						if (netRate <= 0) {
							return Infinity; // Never going to have enough at current rates
						}
						
						const amountNeeded = amount - currentAmount;
						return Math.ceil(amountNeeded / netRate);
					}
					
					// Return public API
					return {
						init,
						getResources,
						getInitialResources,
						addResource,
						removeResource,
						updateResources,
						addTradeRoute,
						removeTradeRoute,
						addPermanentBoost,
						addTemporaryBoost,
						setProductionRate,
						analyzeBalance,
						canAfford,
						spendResources,
						turnsUntilAvailable
					};
				})();
				
				export { ResourceManager };
			
</>
world-generator.js - World Creation and Management
Click to open code

		// world-generator.js
		// Creates and manages the game world and its regions
		
		// WorldGenerator - Creates the game world
		const WorldGenerator = (function() {
			// Terrain types with their properties
			const terrainTypes = {
				plains: {
					name: 'Plains',
					description: 'Flat, grassy areas ideal for agriculture',
					resourceModifiers: {
						food: 1.5,
						materials: 1.0,
						knowledge: 1.0
					},
					buildingModifiers: {
						farm: 1.3,
						residence: 1.1
					},
					movementCost: 1,
					color: '#9ACD32'
				},
				forest: {
					name: 'Forest',
					description: 'Densely wooded areas rich in materials',
					resourceModifiers: {
						food: 1.2,
						materials: 1.4,
						knowledge: 1.1
					},
					buildingModifiers: {
						lumberMill: 1.5,
						workshop: 1.2
					},
					movementCost: 1.5,
					color: '#228B22'
				},
				mountain: {
					name: 'Mountains',
					description: 'Rugged, elevated terrain with mineral deposits',
					resourceModifiers: {
						food: 0.7,
						materials: 1.3,
						knowledge: 1.2
					},
					buildingModifiers: {
						mine: 1.6,
						observatory: 1.4
					},
					movementCost: 2,
					color: '#808080'
				},
				river: {
					name: 'River',
					description: 'Flowing water providing food and transportation',
					resourceModifiers: {
						food: 1.4,
						materials: 1.1,
						knowledge: 1.2
					},
					buildingModifiers: {
						fisherHut: 1.5,
						waterMill: 1.4,
						port: 1.2
					},
					movementCost: 2,
					color: '#4682B4'
				},
				lake: {
					name: 'Lake',
					description: 'Calm body of water with abundant fish',
					resourceModifiers: {
						food: 1.6,
						materials: 0.9,
						knowledge: 1.1
					},
					buildingModifiers: {
						fisherHut: 1.6,
						port: 1.1
					},
					movementCost: 3,
					color: '#6495ED'
				},
				desert: {
					name: 'Desert',
					description: 'Arid region with sparse resources',
					resourceModifiers: {
						food: 0.6,
						materials: 0.8,
						knowledge: 1.2
					},
					buildingModifiers: {
						well: 1.5,
						solarCollector: 1.5
					},
					movementCost: 1.5,
					color: '#F0E68C'
				},
				hills: {
					name: 'Hills',
					description: 'Rolling elevated terrain',
			
Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
Aéius Cercle: Looks like the code for character-tasks.js and world-generator.js both need to be re-generated. Re-generate those two then continue with any other files that haven't been generated yet.
Claude: I'll regenerate those files and continue with the remaining modules needed for our AI civilization game.
</>
character-tasks.js - Character Task Management
Click to open code

			// character-tasks.js
			// Manages the tasks that characters can undertake
			
			import { ResourceManager } from '../resources/resource-manager.js';
			import { BuildingManager } from '../buildings/building-manager.js';
			import { TechnologyManager } from '../technology/technology-manager.js';
			import { EventManager } from '../events/event-manager.js';
			import { WorldGenerator } from '../world-generator.js';
			
			// CharacterTasks - Defines and manages tasks for characters
			const CharacterTasks = (function() {
				// Task categories
				const taskCategories = {
					RESOURCE: 'resource',
					BUILDING: 'building',
					TECHNOLOGY: 'technology',
					COMMUNITY: 'community',
					EXPLORATION: 'exploration',
					PRODUCTION: 'production',
					TRADE: 'trade',
					EVENT: 'event'
				};
				
				// Basic tasks available to all characters
				const basicTasks = [
					{
						id: 'gather_resources',
						name: 'Gather resources',
						category: taskCategories.RESOURCE,
						duration: 5,
						difficulty: 1,
						description: 'Collect basic resources for the civilization',
						requiredLevel: 1,
						roleBonus: {
							'farmer': { food: 10 },
							'builder': { materials: 10 },
							'scientist': { knowledge: 10 }
						},
						getOutcome: (character) => {
							const baseAmount = 5;
							const roleBonus = basicTasks[0].roleBonus[character.role] || {};
							
							return {
								type: 'resources',
								details: {
									food: roleBonus.food || baseAmount,
									materials: roleBonus.materials || baseAmount,
									knowledge: roleBonus.knowledge || baseAmount
								}
							};
						}
					},
					{
						id: 'rest',
						name: 'Rest and recover',
						category: taskCategories.COMMUNITY,
						duration: 3,
						difficulty: 1,
						description: 'Take time to recover and gain inspiration',
						requiredLevel: 1,
						getOutcome: (character) => {
							// Resting gives a small boost to future productivity
							return {
								type: 'character',
								details: {
									temporaryBoost: {
										intelligence: 1,
										creativity: 1,
										duration: 5
									}
								}
							};
						}
					}
				];
				
				// Role-specific task definitions
				const roleTasks = {
					leader: [
						{
							id: 'organize_community',
							name: 'Organize community',
							category: taskCategories.COMMUNITY,
							duration: 10,
							difficulty: 3,
							description: 'Improve the organization and efficiency of the community',
							requiredLevel: 2,
							getOutcome: (character) => {
								const boostAmount = 0.1 + (character.level * 0.01);
								return {
									type: 'community',
									details: {
										productivityBoost: boostAmount,
										duration: 15 + character.level
									}
								};
							}
						},
						{
							id: 'diplomatic_mission',
							name: 'Diplomatic mission',
							category: taskCategories.EVENT,
							duration: 15,
							difficulty: 4,
							description: 'Attempt to negotiate with neighboring groups',
							requiredLevel: 3,
							getOutcome: (character) => {
								const successChance = 0.5 + (character.intelligence / 20) + (character.cooperation / 20);
								return {
									type: 'event',
									details: {
										eventType: 'diplomatic',
										chance: successChance
									}
								};
							}
						},
						{
							id: 'inspire_innovation',
							name: 'Inspire innovation',
							category: taskCategories.COMMUNITY,
							duration: 8,
							difficulty: 4,
							description: 'Inspire others to think creatively and develop new ideas',
							requiredLevel: 4,
							getOutcome: (character) => {
								return {
									type: 'community',
									details: {
										creativityBoost: 0.15,
										duration: 10,
										researchBoost: 0.1
									}
								};
							}
						}
					],
					
					builder: [
						{
							id: 'survey_land',
							name: 'Survey land',
							category: taskCategories.EXPLORATION,
							duration: 5,
							difficulty: 2,
							description: 'Identify optimal building locations and resource deposits',
							requiredLevel: 2,
							getOutcome: (character) => {
								return {
									type: 'building',
									details: {
										constructionBoost: 0.1,
										duration: 15,
										resourceFind: Math.random() < 0.3 // 30% chance of finding resources
									}
								};
							}
						},
						{
							id: 'improve_infrastructure',
							name: 'Improve infrastructure',
							category: taskCategories.BUILDING,
							duration: 12,
							difficulty: 3,
							description: 'Make general improvements to existing buildings and infrastructure',
							requiredLevel: 3,
							getOutcome: (character) => {
								return {
									type: 'building',
									details: {
										maintenanceReduction: 0.15,
										duration: 20,
										buildingEfficiencyBoost: 0.05
									}
								};
							}
						}
						// "Build X" tasks will be dynamically generated based on available buildings
					],
					
					scientist: [
						{
							id: 'analyze_data',
							name: 'Analyze data',
							category: taskCategories.TECHNOLOGY,
							duration: 7,
							difficulty: 3,
							description: 'Review and analyze accumulated knowledge',
							requiredLevel: 2,
							getOutcome: (character) => {
								return {
									type: 'technology',
									details: {
										knowledgeBoost: 10 + (character.intelligence * 2),
										researchBoost: 0.1,
										duration: 10
									}
								};
							}
						},
						{
							id: 'experiment',
							name: 'Conduct experiments',
							category: taskCategories.TECHNOLOGY,
							duration: 10,
							difficulty: 4,
							description: 'Run controlled experiments to test theories',
							requiredLevel: 3,
							getOutcome: (character) => {
								const discoveryChance = 0.3 + (character.creativity / 20);
								return {
									type: 'technology',
									details: {
										breakthroughChance: discoveryChance,
										knowledgeGain: 15 + (character.intelligence * 1.5)
									}
								};
							}
						}
						// "Research X" tasks will be dynamically generated based on available technologies
					],
					
					farmer: [
						{
							id: 'improve_agriculture',
							name: 'Improve agriculture',
							category: taskCategories.PRODUCTION,
							duration: 8,
							difficulty: 2,
							description: 'Develop better farming techniques',
							requiredLevel: 2,
							getOutcome: (character) => {
								const boost = 0.15 + (character.level * 0.02);
								return {
									type: 'production',
									details: {
										resourceType: 'food',
										boost: boost,
										duration: 15
									}
								};
							}
						},
						{
							id: 'develop_irrigation',
							name: 'Develop irrigation',
							category: taskCategories.BUILDING,
							duration: 12,
							difficulty: 3,
							description: 'Create irrigation systems to improve crop yields',
							requiredLevel: 3,
							getOutcome: (character) => {
								return {
									type: 'production',
									details: {
										resourceType: 'food',
										boost: 0.2,
										duration: 25,
										permanent: 0.05 // Some permanent boost
									}
								};
							}
						},
						{
							id: 'animal_husbandry',
							name: 'Develop animal husbandry',
							category: taskCategories.PRODUCTION,
							duration: 15,
							difficulty: 4,
							description: 'Domesticate and breed animals for food and labor',
							requiredLevel: 4,
							getOutcome: (character) => {
								return {
									type: 'production',
									details: {
										resourceType: 'food',
										boost: 0.25,
										laborBoost: 0.1,
										duration: 20,
										permanent: 0.08
									}
								};
							}
						}
					],
					
					merchant: [
						{
							id: 'establish_trade_route',
							name: 'Establish trade route',
							category: taskCategories.TRADE,
							duration: 12,
							difficulty: 3,
							description: 'Set up a new trade route with neighboring regions',
							requiredLevel: 2,
							getOutcome: (character) => {
								const resourceGain = ['food', 'materials', 'knowledge'];
								const baseAmount = 5 + character.level;
								
								return {
									type: 'trade',
									details: {
										resourceGain: resourceGain,
										amount: baseAmount,
										ongoing: true
									}
								};
							}
						},
						{
							id: 'organize_market',
							name: 'Organize marketplace',
							category: taskCategories.COMMUNITY,
							duration: 8,
							difficulty: 2,
							description: 'Create a central marketplace for resource exchange',
							requiredLevel: 3,
							getOutcome: (character) => {
								return {
									type: 'trade',
									details: {
										resourceDistributionBoost: 0.15,
										wealthGeneration: 10,
										duration: 15
									}
								};
							}
						},
						{
							id: 'luxury_trade',
							name: 'Trade for luxury goods',
							category: taskCategories.TRADE,
							duration: 15,
							difficulty: 4,
							description: 'Seek out and acquire valuable luxury items',
							requiredLevel: 4,
							getOutcome: (character) => {
								return {
									type: 'event',
									details: {
										eventType: 'luxury',
										moraleBoost: 0.2,
										duration: 10,
										specialResourceChance: 0.7
									}
								};
							}
						}
					],
					
					explorer: [
						{
							id: 'explore_territories',
							name: 'Explore new territories',
							category: taskCategories.EXPLORATION,
							duration: 15,
							difficulty: 4,
							description: 'Venture into unknown areas to expand the map',
							requiredLevel: 2,
							getOutcome: (character) => {
								const regions = Math.floor(Math.random() * 2) + 1;
								return {
									type: 'exploration',
									details: {
										newRegions: regions,
										resourceFind: Math.random() > 0.5
									}
								};
							}
						},
						{
							id: 'map_resources',
							name: 'Map resource deposits',
							category: taskCategories.EXPLORATION,
							duration: 10,
							difficulty: 3,
							description: 'Create detailed maps of resource-rich areas',
							requiredLevel: 3,
							getOutcome: (character) => {
								return {
									type: 'resource',
									details: {
										resourceFindChance: 0.8,
										resourceMultiplier: 1.5,
										mappingBonus: {
											gatherEfficiency: 0.2,
											duration: 30
										}
									}
								};
							}
						},
						{
							id: 'scout_dangers',
							name: 'Scout for dangers',
							category: taskCategories.EXPLORATION,
							duration: 8,
							difficulty: 3,
							description: 'Identify potential threats in the surroundings',
							requiredLevel: 3,
							getOutcome: (character) => {
								return {
									type: 'community',
									details: {
										threatReduction: 0.3,
										duration: 20,
										earlyWarningSystem: true
									}
								};
							}
						}
					]
				};
				
				// Get all basic tasks available to any character
				function getBasicTasks() {
					return [...basicTasks];
				}
				
				// Get tasks available to a specific character based on role and level
				function getAvailableTasks(character, gameState) {
					const tasks = [...getBasicTasks()];
					
					// Add role-specific tasks
					if (roleTasks[character.role]) {
						roleTasks[character.role].forEach(task => {
							if (character.level >= task.requiredLevel) {
								tasks.push(task);
							}
						});
					}
					
					// Add building construction tasks for builders
					if (character.role === 'builder') {
						const availableBuildings = BuildingManager.getAvailableBuildings(gameState);
						availableBuildings.forEach(building => {
							tasks.push({
								id: `build_${building.id}`,
								name: `Build ${building.name}`,
								category: taskCategories.BUILDING,
								duration: building.constructionTime,
								difficulty: building.difficulty,
								description: `Construct a ${building.name}: ${building.description}`,
								requiredLevel: building.requiredLevel || 1,
								getOutcome: () => {
									return {
										type: 'building',
										details: {
											buildingId: building.id
										}
									};
								}
							});
						});
					}
					
					// Add technology research tasks for scientists
					if (character.role === 'scientist') {
						const availableTechs = TechnologyManager.getAvailableTechnologies(gameState);
						availableTechs.forEach(tech => {
							tasks.push({
								id: `research_${tech.id}`,
								name: `Research ${tech.name}`,
								category: taskCategories.TECHNOLOGY,
								duration: tech.researchTime,
								difficulty: tech.difficulty,
								description: `Research ${tech.name}: ${tech.description}`,
								requiredLevel: tech.requiredLevel || 1,
								getOutcome: () => {
									return {
										type: 'technology',
										details: {
											techId: tech.id
										}
									};
								}
							});
						});
					}
					
					return tasks;
				}
				
				// Process the outcome of a completed task
				function processTaskOutcome(task, character, gameState) {
					const outcome = task.getOutcome(character);
					console.log(`Processing outcome for task: ${task.name}`, outcome);
					
					switch (outcome.type) {
						case 'resources':
							for (const resource in outcome.details) {
								ResourceManager.addResource(resource, outcome.details[resource]);
							}
							break;
							
						case 'building':
							if (outcome.details.buildingId) {
								BuildingManager.constructBuilding(outcome.details.buildingId, gameState);
							}
							
							// Apply construction boost if present
							if (outcome.details.constructionBoost) {
								EventManager.addEvent({
									type: 'building',
									message: `${character.name} has improved construction efficiency!`,
									details: `+${outcome.details.constructionBoost * 100}% construction speed for ${outcome.details.duration} turns`,
									turn: gameState.turn,
									duration: outcome.details.duration
								});
							}
							
							// Check for resource find
							if (outcome.details.resourceFind) {
								const resourceType = ['food', 'materials', 'knowledge'][Math.floor(Math.random() * 3)];
								const amount = Math.floor(Math.random() * 15) + 5;
								ResourceManager.addResource(resourceType, amount);
								
								EventManager.addEvent({
									type: 'exploration',
									message: `${character.name} found resources during construction!`,
									details: `Found ${amount} ${resourceType}`,
									turn: gameState.turn
								});
							}
							break;
							
						case 'technology':
							if (outcome.details.techId) {
								TechnologyManager.researchTechnology(outcome.details.techId, gameState);
							}
							
							// Apply knowledge gain
							if (outcome.details.knowledgeGain) {
								ResourceManager.addResource('knowledge', outcome.details.knowledgeGain);
							}
							
							// Apply research boost
							if (outcome.details.researchBoost) {
								EventManager.addEvent({
									type: 'technology',
									message: `${character.name} has accelerated research!`,
									details: `+${outcome.details.researchBoost * 100}% research speed for ${outcome.details.duration} turns`,
									turn: gameState.turn,
									duration: outcome.details.duration
								});
							}
							
							// Check for breakthrough
							if (outcome.details.breakthroughChance && Math.random() < outcome.details.breakthroughChance) {
								// Generate a research breakthrough
								const breakthrough = TechnologyManager.generateBreakthrough(character);
								
								EventManager.addEvent({
									type: 'technology',
									message: `${character.name} has made a breakthrough in research!`,
									details: breakthrough.description,
									turn: gameState.turn
								});
							}
							break;
							
						case 'community':
							// Apply community effects
							let eventDetails = '';
							
							if (outcome.details.productivityBoost) {
								eventDetails += `+${outcome.details.productivityBoost * 100}% productivity `;
							}
							
							if (outcome.details.creativityBoost) {
								eventDetails += `+${outcome.details.creativityBoost * 100}% creativity `;
							}
							
							if (outcome.details.threatReduction) {
								eventDetails += `${outcome.details.threatReduction * 100}% reduced threat level `;
							}
							
							EventManager.addEvent({
								type: 'community',
								message: `${character.name} has improved the community!`,
								details: `${eventDetails} for ${outcome.details.duration} turns`,
								turn: gameState.turn,
								duration: outcome.details.duration,
								effects: outcome.details
							});
							break;
							
						case 'production':
							EventManager.addEvent({
								type: 'production',
								message: `${character.name} has improved ${outcome.details.resourceType} production!`,
								details: `+${outcome.details.boost * 100}% for ${outcome.details.duration} turns`,
								turn: gameState.turn,
								resourceType: outcome.details.resourceType,
								boost: outcome.details.boost,
								duration: outcome.details.duration
							});
							
							// Apply permanent boost if present
							if (outcome.details.permanent) {
								// This would update a permanent production multiplier in the resource manager
								ResourceManager.addPermanentBoost(outcome.details.resourceType, outcome.details.permanent);
								
								EventManager.addEvent({
									type: 'production',
									message: `${character.name} has permanently improved ${outcome.details.resourceType} production!`,
									details: `+${outcome.details.permanent * 100}% permanent boost`,
									turn: gameState.turn
								});
							}
							break;
							
						case 'trade':
							// Apply trade outcome
							if (outcome.details.resourceGain) {
								outcome.details.resourceGain.forEach(resource => {
									ResourceManager.addResource(resource, outcome.details.amount);
								});
							}
							
							// Add ongoing trade route if applicable
							if (outcome.details.ongoing) {
								// This would register an ongoing effect in the resource manager
								ResourceManager.addTradeRoute({
									resources: outcome.details.resourceGain,
									amount: outcome.details.amount,
									founder: character.id
								});
								
								EventManager.addEvent({
									type: 'trade',
									message: `${character.name} has established a new trade route!`,
									details: `Gaining various resources each turn`,
									turn: gameState.turn,
									ongoing: true
								});
							}
							break;
							
						case 'exploration':
							// Apply exploration outcome
							if (outcome.details.newRegions) {
								// This would call into the world generator to expand the map
								WorldGenerator.addRegions(outcome.details.newRegions);
							}
							
							if (outcome.details.resourceFind) {
								const resourceType = ['food', 'materials', 'knowledge'][Math.floor(Math.random() * 3)];
								const amount = Math.floor(Math.random() * 20) + 10;
								ResourceManager.addResource(resourceType, amount);
								
								EventManager.addEvent({
									type: 'exploration',
									message: `${character.name} has discovered new territories!`,
									details: `Found ${amount} ${resourceType} in the process`,
									turn: gameState.turn
								});
							} else {
								EventManager.addEvent({
									type: 'exploration',
									message: `${character.name} has discovered new territories!`,
									details: `The world map has been expanded`,
									turn: gameState.turn
								});
							}
							break;
							
						case 'event':
							// Generate a special event
							if (outcome.details.eventType) {
								EventManager.generateSpecialEvent(outcome.details.eventType, character, outcome.details);
							}
							break;
							
						case 'character':
							// Apply character-specific effects
							if (outcome.details.temporaryBoost) {
								character.temporaryBoosts = character.temporaryBoosts || [];
								character.temporaryBoosts.push({
									...outcome.details.temporaryBoost,
									startTurn: gameState.turn
								});
								
								EventManager.addEvent({
									type: 'character',
									message: `${character.name} is feeling refreshed and inspired!`,
									details: `Temporarily improved abilities for ${outcome.details.temporaryBoost.duration} turns`,
									turn: gameState.turn
								});
							}
							break;
							
						case 'resource':
							// Apply resource mapping bonuses
							if (outcome.details.mappingBonus) {
								EventManager.addEvent({
									type: 'exploration',
									message: `${character.name} has created detailed resource maps!`,
									details: `+${outcome.details.mappingBonus.gatherEfficiency * 100}% resource gathering efficiency for ${outcome.details.mappingBonus.duration} turns`,
									turn: gameState.turn,
									duration: outcome.details.mappingBonus.duration,
									effects: outcome.details.mappingBonus
								});
							}
							
							// Check for resource finds
							if (outcome.details.resourceFindChance && Math.random() < outcome.details.resourceFindChance) {
								const resources = ['food', 'materials', 'knowledge'];
								const resourceCount = Math.floor(Math.random() * 2) + 1;
								
								for (let i = 0; i < resourceCount; i++) {
									const resourceType = resources[Math.floor(Math.random() * resources.length)];
									const baseAmount = Math.floor(Math.random() * 15) + 5;
									const amount = Math.floor(baseAmount * outcome.details.resourceMultiplier);
									
									ResourceManager.addResource(resourceType, amount);
									
									EventManager.addEvent({
										type: 'exploration',
										message: `${character.name} has discovered a ${resourceType} deposit!`,
										details: `Added ${amount} ${resourceType} to stockpile`,
										turn: gameState.turn
									});
								}
							}
							break;
					}
					
					return outcome;
				}
				
				// Return public API
				return {
					getBasicTasks,
					getAvailableTasks,
					processTaskOutcome,
					taskCategories
				};
			})();
			
			export { CharacterTasks };
		
</>
world-generator.js - World Creation and Management
Click to open code

		// world-generator.js
		// Creates and manages the game world and its regions
		
		import { EventManager } from './events/event-manager.js';
		
		// WorldGenerator - Creates the game world
		const WorldGenerator = (function() {
			// Terrain types with their properties
			const terrainTypes = {
				plains: {
					name: 'Plains',
					description: 'Flat, grassy areas ideal for agriculture',
					resourceModifiers: {
						food: 1.5,
						materials: 1.0,
						knowledge: 1.0
					},
					buildingModifiers: {
						farm: 1.3,
						residence: 1.1
					},
					movementCost: 1,
					color: '#9ACD32'
				},
				forest: {
					name: 'Forest',
					description: 'Densely wooded areas rich in materials',
					resourceModifiers: {
						food: 1.2,
						materials: 1.4,
						knowledge: 1.1
					},
					buildingModifiers: {
						lumberMill: 1.5,
						workshop: 1.2
					},
					movementCost: 1.5,
					color: '#228B22'
				},
				mountain: {
					name: 'Mountains',
					description: 'Rugged, elevated terrain with mineral deposits',
					resourceModifiers: {
						food: 0.7,
						materials: 1.3,
						knowledge: 1.2
					},
					buildingModifiers: {
						mine: 1.6,
						observatory: 1.4
					},
					movementCost: 2,
					color: '#808080'
				},
				river: {
					name: 'River',
					description: 'Flowing water providing food and transportation',
					resourceModifiers: {
						food: 1.4,
						materials: 1.1,
						knowledge: 1.2
					},
					buildingModifiers: {
						fisherHut: 1.5,
						waterMill: 1.4,
						port: 1.2
					},
					movementCost: 2,
					color: '#4682B4'
				},
				lake: {
					name: 'Lake',
					description: 'Calm body of water with abundant fish',
					resourceModifiers: {
						food: 1.6,
						materials: 0.9,
						knowledge: 1.1
					},
					buildingModifiers: {
						fisherHut: 1.6,
						port: 1.1
					},
					movementCost: 3,
					color: '#6495ED'
				},
				desert: {
					name: 'Desert',
					description: 'Arid region with sparse resources',
					resourceModifiers: {
						food: 0.6,
						materials: 0.8,
						knowledge: 1.2
					},
					buildingModifiers: {
						well: 1.5,
						solarCollector: 1.5
					},
					movementCost: 1.5,
					color: '#F0E68C'
				},
				hills: {
					name: 'Hills',
					description: 'Rolling elevated terrain',
					resourceModifiers: {
						food: 1.1,
						materials: 1.2,
						knowledge: 1.0
					},
					buildingModifiers: {
						mine: 1.2,
						watchtower: 1.3
					},
					movementCost: 1.5,
					color: '#CD853F'
				}
			};
			
			// Region types combining multiple terrain types
			const regionTypes = {
				startingValley: {
					name: 'Fertile Valley',
					description: 'A sheltered valley with diverse resources',
					terrains: ['plains', 'river', 'forest'],
					terrainWeights: [0.5, 0.3, 0.2],
					resourceBonus: {
						food: 1.1,
						materials: 1.1
					},
					difficulty: 1
				},
				woodlands: {
					name: 'Dense Woodlands',
					description: 'A thick forest with abundant materials',
					terrains: ['forest', 'hills', 'river'],
					terrainWeights: [0.7, 0.2, 0.1],
					resourceBonus: {
						materials: 1.3
					},
					difficulty: 2
				},
				highlands: {
					name: 'Highland Plateau',
					description: 'Elevated terrain with commanding views',
					terrains: ['hills', 'mountain', 'plains'],
					terrainWeights: [0.5, 0.3, 0.2],
					resourceBonus: {
						knowledge: 1.2
					},
					difficulty: 3
				},
				desert: {
					name: 'Arid Wasteland',
					description: 'A harsh environment with hidden resources',
					terrains: ['desert', 'hills', 'plains'],
					terrainWeights: [0.7, 0.2, 0.1],
					resourceBonus: {
						knowledge: 1.3
					},
					difficulty: 4
				},
				riverlands: {
					name: 'River Delta',
					description: 'A network of rivers with fertile soil',
					terrains: ['river', 'plains', 'lake'],
					terrainWeights: [0.4, 0.4, 0.2],
					resourceBonus: {
						food: 1.4
					},
					difficulty: 2
				},
				mountains: {
					name: 'Mountain Range',
					description: 'Imposing peaks rich in minerals',
					terrains: ['mountain', 'hills', 'forest'],
					terrainWeights: [0.6, 0.3, 0.1],
					resourceBonus: {
						materials: 1.3,
						knowledge: 1.2
					},
					difficulty: 5
				},
				lakeshore: {
					name: 'Tranquil Lakeshore',
					description: 'A peaceful area surrounding a large lake',
					terrains: ['lake', 'plains', 'forest'],
					terrainWeights: [0.4, 0.4, 0.2],
					resourceBonus: {
						food: 1.3
					},
					difficulty: 2
				}
			};
			
			// Special features that can appear in regions
			const specialFeatures = [
				{
					id: 'ancient_ruins',
					name: 'Ancient Ruins',
					description: 'Remnants of a past civilization',
					effects: {
						knowledgeBonus: 0.2,
						discoveryChance: 0.3
					},
					rarity: 0.2
				},
				{
					id: 'mineral_deposit',
					name:
	
Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
Aéius Cercle: Resume code with the world-generator.js file...
Claude: I'll continue with the world-generator.js file.
</>
world-generator.js - World Creation and Management
Click to open code

		// world-generator.js
		// Creates and manages the game world and its regions
		
		import { EventManager } from './events/event-manager.js';
		
		// WorldGenerator - Creates the game world
		const WorldGenerator = (function() {
			// Terrain types with their properties
			const terrainTypes = {
				plains: {
					name: 'Plains',
					description: 'Flat, grassy areas ideal for agriculture',
					resourceModifiers: {
						food: 1.5,
						materials: 1.0,
						knowledge: 1.0
					},
					buildingModifiers: {
						farm: 1.3,
						residence: 1.1
					},
					movementCost: 1,
					color: '#9ACD32'
				},
				forest: {
					name: 'Forest',
					description: 'Densely wooded areas rich in materials',
					resourceModifiers: {
						food: 1.2,
						materials: 1.4,
						knowledge: 1.1
					},
					buildingModifiers: {
						lumberMill: 1.5,
						workshop: 1.2
					},
					movementCost: 1.5,
					color: '#228B22'
				},
				mountain: {
					name: 'Mountains',
					description: 'Rugged, elevated terrain with mineral deposits',
					resourceModifiers: {
						food: 0.7,
						materials: 1.3,
						knowledge: 1.2
					},
					buildingModifiers: {
						mine: 1.6,
						observatory: 1.4
					},
					movementCost: 2,
					color: '#808080'
				},
				river: {
					name: 'River',
					description: 'Flowing water providing food and transportation',
					resourceModifiers: {
						food: 1.4,
						materials: 1.1,
						knowledge: 1.2
					},
					buildingModifiers: {
						fisherHut: 1.5,
						waterMill: 1.4,
						port: 1.2
					},
					movementCost: 2,
					color: '#4682B4'
				},
				lake: {
					name: 'Lake',
					description: 'Calm body of water with abundant fish',
					resourceModifiers: {
						food: 1.6,
						materials: 0.9,
						knowledge: 1.1
					},
					buildingModifiers: {
						fisherHut: 1.6,
						port: 1.1
					},
					movementCost: 3,
					color: '#6495ED'
				},
				desert: {
					name: 'Desert',
					description: 'Arid region with sparse resources',
					resourceModifiers: {
						food: 0.6,
						materials: 0.8,
						knowledge: 1.2
					},
					buildingModifiers: {
						well: 1.5,
						solarCollector: 1.5
					},
					movementCost: 1.5,
					color: '#F0E68C'
				},
				hills: {
					name: 'Hills',
					description: 'Rolling elevated terrain',
					resourceModifiers: {
						food: 1.1,
						materials: 1.2,
						knowledge: 1.0
					},
					buildingModifiers: {
						mine: 1.2,
						watchtower: 1.3
					},
					movementCost: 1.5,
					color: '#CD853F'
				}
			};
			
			// Region types combining multiple terrain types
			const regionTypes = {
				startingValley: {
					name: 'Fertile Valley',
					description: 'A sheltered valley with diverse resources',
					terrains: ['plains', 'river', 'forest'],
					terrainWeights: [0.5, 0.3, 0.2],
					resourceBonus: {
						food: 1.1,
						materials: 1.1
					},
					difficulty: 1
				},
				woodlands: {
					name: 'Dense Woodlands',
					description: 'A thick forest with abundant materials',
					terrains: ['forest', 'hills', 'river'],
					terrainWeights: [0.7, 0.2, 0.1],
					resourceBonus: {
						materials: 1.3
					},
					difficulty: 2
				},
				highlands: {
					name: 'Highland Plateau',
					description: 'Elevated terrain with commanding views',
					terrains: ['hills', 'mountain', 'plains'],
					terrainWeights: [0.5, 0.3, 0.2],
					resourceBonus: {
						knowledge: 1.2
					},
					difficulty: 3
				},
				desert: {
					name: 'Arid Wasteland',
					description: 'A harsh environment with hidden resources',
					terrains: ['desert', 'hills', 'plains'],
					terrainWeights: [0.7, 0.2, 0.1],
					resourceBonus: {
						knowledge: 1.3
					},
					difficulty: 4
				},
				riverlands: {
					name: 'River Delta',
					description: 'A network of rivers with fertile soil',
					terrains: ['river', 'plains', 'lake'],
					terrainWeights: [0.4, 0.4, 0.2],
					resourceBonus: {
						food: 1.4
					},
					difficulty: 2
				},
				mountains: {
					name: 'Mountain Range',
					description: 'Imposing peaks rich in minerals',
					terrains: ['mountain', 'hills', 'forest'],
					terrainWeights: [0.6, 0.3, 0.1],
					resourceBonus: {
						materials: 1.3,
						knowledge: 1.2
					},
					difficulty: 5
				},
				lakeshore: {
					name: 'Tranquil Lakeshore',
					description: 'A peaceful area surrounding a large lake',
					terrains: ['lake', 'plains', 'forest'],
					terrainWeights: [0.4, 0.4, 0.2],
					resourceBonus: {
						food: 1.3
					},
					difficulty: 2
				}
			};
			
			// Special features that can appear in regions
			const specialFeatures = [
				{
					id: 'ancient_ruins',
					name: 'Ancient Ruins',
					description: 'Remnants of a past civilization',
					effects: {
						knowledgeBonus: 0.2,
						discoveryChance: 0.3
					},
					rarity: 0.2
				},
				{
					id: 'mineral_deposit',
					name: 'Rich Mineral Deposit',
					description: 'An concentrated source of valuable materials',
					effects: {
						materialsBonus: 0.3
					},
					rarity: 0.25
				},
				{
					id: 'fertile_soil',
					name: 'Exceptionally Fertile Soil',
					description: 'Nutrient-rich ground perfect for agriculture',
					effects: {
						foodBonus: 0.3
					},
					rarity: 0.25
				},
				{
					id: 'natural_wonder',
					name: 'Natural Wonder',
					description: 'A breathtaking natural formation',
					effects: {
						knowledgeBonus: 0.15,
						all_resource_bonus: 0.1
					},
					rarity: 0.1
				},
				{
					id: 'ancient_technology',
					name: 'Ancient Technology',
					description: 'Mysterious devices from a bygone era',
					effects: {
						knowledgeBonus: 0.25,
						tech_discovery_chance: 0.5
					},
					rarity: 0.05
				}
			];
			
			// Generate a new world map
			function generateNewWorld() {
				console.log('Generating new world...');
				
				// Create the world object
				const world = {
					name: generateWorldName(),
					regions: [],
					discoveredRegions: 0,
					size: 25, // Total number of potential regions
					developmentLevel: 1,
					specialFeatures: [],
					threats: []
				};
				
				// Add the starting region
				addStartingRegion(world);
				
				console.log('World generation complete');
				return world;
			}
			
			// Generate a random name for the world
			function generateWorldName() {
				const prefixes = ['New', 'Ancient', 'Lost', 'Hidden', 'Mystic', 'Forgotten', 'Eternal', 'Verdant'];
				const roots = ['Terra', 'Gaia', 'Earth', 'Land', 'World', 'Realm', 'Domain', 'Haven'];
				const suffixes = ['ia', 'is', 'us', 'um', 'or', 'on', 'ar', ''];
				
				return `${prefixes[Math.floor(Math.random() * prefixes.length)]} ${roots[Math.floor(Math.random() * roots.length)]}${suffixes[Math.floor(Math.random() * suffixes.length)]}`;
			}
			
			// Add the starting region to the world
			function addStartingRegion(world) {
				const startingRegion = createRegion('startingValley', 0, 0, true);
				startingRegion.discovered = true;
				startingRegion.explored = true;
				world.regions.push(startingRegion);
				world.discoveredRegions = 1;
				
				// Add some adjacent regions that are discovered but not explored
				const adjacentDirections = [
					{ x: 1, y: 0 }, // East
					{ x: 0, y: 1 }, // South
					{ x: -1, y: 0 }, // West
					{ x: 0, y: -1 } // North
				];
				
				// Randomly select 2-3 directions for adjacent regions
				const shuffledDirections = shuffleArray([...adjacentDirections]);
				const numAdjacent = Math.floor(Math.random() * 2) + 2; // 2-3 adjacent regions
				
				for (let i = 0; i < numAdjacent; i++) {
					const dir = shuffledDirections[i];
					const regionType = getRandomRegionType(2); // Low difficulty for starting areas
					const newRegion = createRegion(regionType, dir.x, dir.y);
					newRegion.discovered = true; // Discovered but not explored
					newRegion.explored = false;
					world.regions.push(newRegion);
				}
			}
			
			// Create a new region
			function createRegion(regionType, x, y, isStartingRegion = false) {
				let regionTemplate;
				
				// If a specific region type is requested, use that
				if (regionTypes[regionType]) {
					regionTemplate = regionTypes[regionType];
				} else {
					// Otherwise, use the provided regionType as a string key
					regionTemplate = regionTypes[regionType];
				}
				
				// Generate a region ID
				const regionId = `region_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
				
				// Create the terrain distribution
				const terrainDistribution = {};
				for (let i = 0; i < regionTemplate.terrains.length; i++) {
					terrainDistribution[regionTemplate.terrains[i]] = regionTemplate.terrainWeights[i];
				}
				
				// Add special features (only for non-starting regions, to keep the start simple)
				let features = [];
				if (!isStartingRegion) {
					features = generateSpecialFeatures(regionTemplate);
				}
				
				// Create the region object
				const region = {
					id: regionId,
					name: generateRegionName(regionTemplate),
					type: regionType,
					description: regionTemplate.description,
					position: { x, y },
					terrainDistribution: terrainDistribution,
					resourceModifiers: { ...regionTemplate.resourceBonus },
					specialFeatures: features,
					discovered: false,
					explored: false,
					developmentLevel: 0,
					buildings: [],
					resources: {
						food: Math.floor(Math.random() * 20) + 5,
						materials: Math.floor(Math.random() * 15) + 5,
						knowledge: Math.floor(Math.random() * 10) + 2
					}
				};
				
				return region;
			}
			
			// Generate a random name for a region
			function generateRegionName(regionTemplate) {
				// Use the region type to influence the name
				const regionTypeNames = {
					startingValley: ['Verdant', 'Sheltered', 'Peaceful', 'Dawn', 'First', 'Home'],
					woodlands: ['Whispering', 'Ancient', 'Mystic', 'Dark', 'Towering', 'Emerald'],
					highlands: ['Windy', 'High', 'Cloud', 'Thunder', 'Craggy', 'Sky'],
					desert: ['Arid', 'Burning', 'Sun', 'Dust', 'Scorched', 'Dry'],
					riverlands: ['Rushing', 'Flowing', 'Blue', 'Fertile', 'Winding', 'Blessed'],
					mountains: ['Stone', 'Jagged', 'Frost', 'Rugged', 'Broken', 'Great'],
					lakeshore: ['Serene', 'Misty', 'Crystal', 'Calm', 'Tranquil', 'Placid']
				};
				
				const suffixes = {
					startingValley: ['Valley', 'Dale', 'Hollow', 'Glen', 'Glade'],
					woodlands: ['Woods', 'Forest', 'Thicket', 'Grove', 'Wildwood'],
					highlands: ['Heights', 'Plateau', 'Rise', 'Bluffs', 'Highland'],
					desert: ['Wastes', 'Sands', 'Dunes', 'Expanse', 'Badlands'],
					riverlands: ['Delta', 'Banks', 'Forks', 'Flow', 'Meadows'],
					mountains: ['Peaks', 'Range', 'Summit', 'Crags', 'Spires'],
					lakeshore: ['Lake', 'Shore', 'Waters', 'Bay', 'Cove']
				};
				
				const prefix = regionTypeNames[regionTemplate.name] || 
											regionTypeNames[Object.keys(regionTypeNames)[0]];
				const suffix = suffixes[regionTemplate.name] || 
										 suffixes[Object.keys(suffixes)[0]];
				
				return `${prefix[Math.floor(Math.random() * prefix.length)]} ${suffix[Math.floor(Math.random() * suffix.length)]}`;
			}
			
			// Generate special features for a region
			function generateSpecialFeatures(regionTemplate) {
				const features = [];
				
				// Calculate the chance of a special feature based on region difficulty
				// Higher difficulty means more likely to have special features
				const baseChance = 0.1 + (regionTemplate.difficulty * 0.05);
				
				// Determine if this region gets a special feature
				if (Math.random() < baseChance) {
					// Select a random feature weighted by rarity
					const possibleFeatures = specialFeatures.filter(feature => Math.random() < feature.rarity);
					
					if (possibleFeatures.length > 0) {
						const selectedFeature = possibleFeatures[Math.floor(Math.random() * possibleFeatures.length)];
						features.push({
							id: selectedFeature.id,
							name: selectedFeature.name,
							description: selectedFeature.description,
							effects: selectedFeature.effects,
							discovered: false
						});
					}
				}
				
				return features;
			}
			
			// Get a random region type with optional difficulty constraint
			function getRandomRegionType(maxDifficulty = 5) {
				const possibleTypes = Object.keys(regionTypes).filter(type => 
					regionTypes[type].difficulty <= maxDifficulty
				);
				
				return possibleTypes[Math.floor(Math.random() * possibleTypes.length)];
			}
			
			// Add new regions to the world
			function addRegions(world, numRegions) {
				console.log(`Adding ${numRegions} new regions to the world...`);
				
				// Find existing discovered regions to attach new ones to
				const discoveredRegions = world.regions.filter(region => region.discovered);
				
				if (discoveredRegions.length === 0) {
					console.error('No discovered regions to attach new regions to');
					return;
				}
				
				// For each new region to add
				for (let i = 0; i < numRegions; i++) {
					// Pick a random discovered region to attach to
					const attachToRegion = discoveredRegions[Math.floor(Math.random() * discoveredRegions.length)];
					
					// Find an empty adjacent space
					const adjacentPositions = getAdjacentPositions(attachToRegion.position);
					const emptyPositions = adjacentPositions.filter(pos => 
						!world.regions.some(region => region.position.x === pos.x && region.position.y === pos.y)
					);
					
					if (emptyPositions.length === 0) {
						console.log('No empty adjacent positions for new region');
						continue;
					}
					
					// Select a random empty position
					const newPosition = emptyPositions[Math.floor(Math.random() * emptyPositions.length)];
					
					// Determine the region type, with increasing difficulty based on distance from center
					const distanceFromCenter = Math.sqrt(newPosition.x * newPosition.x + newPosition.y * newPosition.y);
					const maxDifficulty = Math.min(5, 1 + Math.floor(distanceFromCenter / 2));
					const regionType = getRandomRegionType(maxDifficulty);
					
					// Create the new region
					const newRegion = createRegion(regionType, newPosition.x, newPosition.y);
					newRegion.discovered = true; // Discovered but not explored
					
					// Add to world
					world.regions.push(newRegion);
					
					// Trigger a discovery event
					EventManager.addEvent({
						type: 'world',
						message: `A new region has been discovered!`,
						details: `${newRegion.name} (${newRegion.type}) has been added to the map.`,
						turn: window.AICivilizationGame?.getGameState()?.turn || 0
					});
				}
				
				console.log(`Added ${numRegions} new regions to the world`);
			}
			
			// Get adjacent positions to a given position
			function getAdjacentPositions(position) {
				return [
					{ x: position.x + 1, y: position.y }, // East
					{ x: position.x, y: position.y + 1 }, // South
					{ x: position.x - 1, y: position.y }, // West
					{ x: position.x, y: position.y - 1 }	// North
				];
			}
			
			// Explore a region (mark as explored and discover features)
			function exploreRegion(world, regionId) {
				const region = world.regions.find(r => r.id === regionId);
				
				if (!region) {
					console.error(`Region with ID ${regionId} not found`);
					return false;
				}
				
				if (region.explored) {
					console.log(`Region ${region.name} is already explored`);
					return false;
				}
				
				// Mark as explored
				region.explored = true;
				
				// Discover adjacent regions
				const adjacentPositions = getAdjacentPositions(region.position);
				
				// Check if there are existing regions at those positions
				for (const pos of adjacentPositions) {
					const existingRegion = world.regions.find(r => 
						r.position.x === pos.x && r.position.y === pos.y
					);
					
					if (existingRegion) {
						existingRegion.discovered = true;
					} else {
						// Create a new undiscovered region with a 30% chance
						if (Math.random() < 0.3) {
							const distanceFromCenter = Math.sqrt(pos.x * pos.x + pos.y * pos.y);
							const maxDifficulty = Math.min(5, 1 + Math.floor(distanceFromCenter / 2));
							const regionType = getRandomRegionType(maxDifficulty);
							
							const newRegion = createRegion(regionType, pos.x, pos.y);
							newRegion.discovered = true;
							world.regions.push(newRegion);
							
							// Trigger a discovery event
							EventManager.addEvent({
								type: 'world',
								message: `A new region has been discovered!`,
								details: `${newRegion.name} (${newRegion.type}) has been added to the map.`,
								turn: window.AICivilizationGame?.getGameState()?.turn || 0
							});
						}
					}
				}
				
				// Discover special features
				for (const feature of region.specialFeatures) {
					// 70% chance to discover each feature during exploration
					if (!feature.discovered && Math.random() < 0.7) {
						feature.discovered = true;
						
						// Trigger a discovery event
						EventManager.addEvent({
							type: 'discovery',
							message: `A special feature has been discovered in ${region.name}!`,
							details: `${feature.name}: ${feature.description}`,
							turn: window.AICivilizationGame?.getGameState()?.turn || 0
						});
					}
				}
				
				// Trigger exploration event
				EventManager.addEvent({
					type: 'exploration',
					message: `${region.name} has been fully explored!`,
					details: `You now have detailed information about this region.`,
					turn: window.AICivilizationGame?.getGameState()?.turn || 0
				});
				
				return true;
			}
			
			// Utility function to shuffle an array
			function shuffleArray(array) {
				for (let i = array.length - 1; i > 0; i--) {
					const j = Math.floor(Math.random() * (i + 1));
					[array[i], array[j]] = [array[j], array[i]];
				}
				return array;
			}
			
			// Return public API
			return {
				generateNewWorld,
				addRegions,
				exploreRegion,
				getTerrainTypes: () => terrainTypes,
				getRegionTypes: () => regionTypes
			};
		})();
		
		export { WorldGenerator };
	
</>
world-renderer.js - Visualization of Game World
Click to open code

		// world-renderer.js
		// Handles rendering the game world to the UI
		
		import { WorldGenerator } from './world-generator.js';
		
		// WorldRenderer - Visualizes the game world
		const WorldRenderer = (function() {
			// Private variables
			let world = null;
			let canvasContext = null;
			let tileSize = 60;
			let canvasSize = 600;
			let centerX = 0;
			let centerY = 0;
			
			// Initialize with a world object
			function init(worldData) {
				world = worldData;
				console.log('Initialized WorldRenderer with world:', world.name);
				
				// Set up the canvas if the DOM is available
				setupCanvas();
			}
			
			// Set up the canvas element
			function setupCanvas() {
				const mapCanvas = document.getElementById('world-map');
				if (!mapCanvas) {
					console.error('Map canvas element not found');
					
					// Try to create it
					const gameContainer = document.getElementById('game-container');
					if (gameContainer) {
						const canvas = document.createElement('canvas');
						canvas.id = 'world-map';
						canvas.width = canvasSize;
						canvas.height = canvasSize;
						canvas.style.border = '1px solid #ccc';
						gameContainer.appendChild(canvas);
						canvasContext = canvas.getContext('2d');
						
						// Add event listeners for interaction
						canvas.addEventListener('click', handleCanvasClick);
						canvas.addEventListener('wheel', handleCanvasZoom);
						
						console.log('Created map canvas element');
					} else {
						console.error('Game container element not found');
						return false;
					}
				} else {
					canvasContext = mapCanvas.getContext('2d');
					
					// Add event listeners for interaction
					mapCanvas.addEventListener('click', handleCanvasClick);
					mapCanvas.addEventListener('wheel', handleCanvasZoom);
				}
				
				return true;
			}
			
			// Render the world to the canvas
			function render(worldData) {
				// Update world data if provided
				if (worldData) {
					world = worldData;
				}
				
				// Check if canvas is available
				if (!canvasContext) {
					if (!setupCanvas()) {
						console.error('Unable to render world: canvas not available');
						return;
					}
				}
				
				// Clear the canvas
				canvasContext.clearRect(0, 0, canvasSize, canvasSize);
				
				// Draw the background
				canvasContext.fillStyle = '#87CEEB'; // Sky blue
				canvasContext.fillRect(0, 0, canvasSize, canvasSize);
				
				// Calculate the center of the canvas
				const canvasCenter = canvasSize / 2;
				
				// Draw each region
				for (const region of world.regions) {
					// Skip if not discovered
					if (!region.discovered) continue;
					
					// Calculate position on canvas
					const x = canvasCenter + (region.position.x - centerX) * tileSize;
					const y = canvasCenter + (region.position.y - centerY) * tileSize;
					
					// Check if the region is within the visible canvas
					if (x + tileSize < 0 || x > canvasSize || y + tileSize < 0 || y > canvasSize) {
						continue;
					}
					
					// Draw the region
					drawRegion(region, x, y);
				}
				
				// Draw grid lines for better visual clarity
				drawGrid();
				
				// Draw a compass
				drawCompass();
				
				// Draw region names and features
				drawLabels();
			}
			
			// Draw a single region on the canvas
			function drawRegion(region, x, y) {
				// Determine the region color based on terrain distribution
				let primaryColor = '#7CFC00'; // Default light green
				let secondaryColor = null;
				let maxWeight = 0;
				
				// Get the terrain types
				const terrainTypes = WorldGenerator.getTerrainTypes();
				
				// Find the primary terrain
				for (const terrainType in region.terrainDistribution) {
					const weight = region.terrainDistribution[terrainType];
					if (weight > maxWeight) {
						maxWeight = weight;
						primaryColor = terrainTypes[terrainType]?.color || primaryColor;
						
						// If there's a significant secondary terrain, save it for patterns
						if (weight >= 0.4 && weight < 0.7) {
							let secondaryWeight = 0;
							for (const secondaryTerrain in region.terrainDistribution) {
								if (secondaryTerrain !== terrainType && 
										region.terrainDistribution[secondaryTerrain] > secondaryWeight) {
									secondaryWeight = region.terrainDistribution[secondaryTerrain];
									secondaryColor = terrainTypes[secondaryTerrain]?.color || null;
								}
							}
						}
					}
				}
				
				// Draw the base tile
				canvasContext.fillStyle = primaryColor;
				canvasContext.fillRect(x, y, tileSize, tileSize);
				
				// Draw a pattern if there's a significant secondary terrain
				if (secondaryColor) {
					drawTerrainPattern(x, y, secondaryColor);
				}
				
				// Draw a border - thicker if explored
				canvasContext.strokeStyle = region.explored ? '#000' : '#666';
				canvasContext.lineWidth = region.explored ? 2 : 1;
				canvasContext.strokeRect(x, y, tileSize, tileSize);
				
				// Draw development indicators (buildings, resources)
				if (region.explored) {
					drawDevelopmentIndicators(region, x, y);
				}
				
				// Draw special feature indicators
				if (region.specialFeatures && region.specialFeatures.length > 0) {
					drawSpecialFeatures(region, x, y);
				}
			}
			
			// Draw a pattern to represent mixed terrain
			function drawTerrainPattern(x, y, color) {
				canvasContext.fillStyle = color;
				
				// Draw diagonal stripes
				for (let i = 0; i < tileSize; i += 6) {
					canvasContext.beginPath();
					canvasContext.moveTo(x + i, y);
					canvasContext.lineTo(x, y + i);
					canvasContext.lineTo(x, y + i + 3);
					canvasContext.lineTo(x + i + 3, y);
					canvasContext.closePath();
					canvasContext.fill();
				}
			}
			
			// Draw indicators for region development
			function drawDevelopmentIndicators(region, x, y) {
				// Draw small icons for buildings
				if (region.buildings && region.buildings.length > 0) {
					const buildingCount = Math.min(region.buildings.length, 4);
					
					for (let i = 0; i < buildingCount; i++) {
						canvasContext.fillStyle = '#B22222'; // Firebrick red for buildings
						canvasContext.beginPath();
						canvasContext.arc(x + 10 + (i * 12), y + 10, 5, 0, 2 * Math.PI);
						canvasContext.fill();
					}
				}
				
				// Draw development level
				if (region.developmentLevel > 0) {
					canvasContext.fillStyle = '#FFD700'; // Gold
					canvasContext.font = '12px Arial';
					canvasContext.fillText(
						'★'.repeat(region.developmentLevel), 
						x + 5, 
						y + tileSize - 5
					);
				}
				
				// If it's the currently occupied/settled region, mark it
				if (region.isSettled) {
					canvasContext.strokeStyle = '#FF4500'; // Orange-red
					canvasContext.lineWidth = 3;
					canvasContext.strokeRect(x + 3, y + 3, tileSize - 6, tileSize - 6);
				}
			}
			
			// Draw special feature indicators
	
Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
Aéius Cercle: Resume code-generation from world-renderer.js onwards.
Claude: I'll continue with the world-renderer.js file and then proceed with the remaining files needed for the game.
</>
world-renderer.js - Visualization of Game World
Click to open code

			// world-renderer.js
			// Handles rendering the game world to the UI
			
			import { WorldGenerator } from './world-generator.js';
			
			// WorldRenderer - Visualizes the game world
			const WorldRenderer = (function() {
				// Private variables
				let world = null;
				let canvasContext = null;
				let tileSize = 60;
				let canvasSize = 600;
				let centerX = 0;
				let centerY = 0;
				
				// Initialize with a world object
				function init(worldData) {
					world = worldData;
					console.log('Initialized WorldRenderer with world:', world.name);
					
					// Set up the canvas if the DOM is available
					setupCanvas();
				}
				
				// Set up the canvas element
				function setupCanvas() {
					const mapCanvas = document.getElementById('world-map');
					if (!mapCanvas) {
						console.error('Map canvas element not found');
						
						// Try to create it
						const gameContainer = document.getElementById('game-container');
						if (gameContainer) {
							const canvas = document.createElement('canvas');
							canvas.id = 'world-map';
							canvas.width = canvasSize;
							canvas.height = canvasSize;
							canvas.style.border = '1px solid #ccc';
							gameContainer.appendChild(canvas);
							canvasContext = canvas.getContext('2d');
							
							// Add event listeners for interaction
							canvas.addEventListener('click', handleCanvasClick);
							canvas.addEventListener('wheel', handleCanvasZoom);
							
							console.log('Created map canvas element');
						} else {
							console.error('Game container element not found');
							return false;
						}
					} else {
						canvasContext = mapCanvas.getContext('2d');
						
						// Add event listeners for interaction
						mapCanvas.addEventListener('click', handleCanvasClick);
						mapCanvas.addEventListener('wheel', handleCanvasZoom);
					}
					
					return true;
				}
				
				// Render the world to the canvas
				function render(worldData) {
					// Update world data if provided
					if (worldData) {
						world = worldData;
					}
					
					// Check if canvas is available
					if (!canvasContext) {
						if (!setupCanvas()) {
							console.error('Unable to render world: canvas not available');
							return;
						}
					}
					
					// Clear the canvas
					canvasContext.clearRect(0, 0, canvasSize, canvasSize);
					
					// Draw the background
					canvasContext.fillStyle = '#87CEEB'; // Sky blue
					canvasContext.fillRect(0, 0, canvasSize, canvasSize);
					
					// Calculate the center of the canvas
					const canvasCenter = canvasSize / 2;
					
					// Draw each region
					for (const region of world.regions) {
						// Skip if not discovered
						if (!region.discovered) continue;
						
						// Calculate position on canvas
						const x = canvasCenter + (region.position.x - centerX) * tileSize;
						const y = canvasCenter + (region.position.y - centerY) * tileSize;
						
						// Check if the region is within the visible canvas
						if (x + tileSize < 0 || x > canvasSize || y + tileSize < 0 || y > canvasSize) {
							continue;
						}
						
						// Draw the region
						drawRegion(region, x, y);
					}
					
					// Draw grid lines for better visual clarity
					drawGrid();
					
					// Draw a compass
					drawCompass();
					
					// Draw region names and features
					drawLabels();
				}
				
				// Draw a single region on the canvas
				function drawRegion(region, x, y) {
					// Determine the region color based on terrain distribution
					let primaryColor = '#7CFC00'; // Default light green
					let secondaryColor = null;
					let maxWeight = 0;
					
					// Get the terrain types
					const terrainTypes = WorldGenerator.getTerrainTypes();
					
					// Find the primary terrain
					for (const terrainType in region.terrainDistribution) {
						const weight = region.terrainDistribution[terrainType];
						if (weight > maxWeight) {
							maxWeight = weight;
							primaryColor = terrainTypes[terrainType]?.color || primaryColor;
							
							// If there's a significant secondary terrain, save it for patterns
							if (weight >= 0.4 && weight < 0.7) {
								let secondaryWeight = 0;
								for (const secondaryTerrain in region.terrainDistribution) {
									if (secondaryTerrain !== terrainType && 
											region.terrainDistribution[secondaryTerrain] > secondaryWeight) {
										secondaryWeight = region.terrainDistribution[secondaryTerrain];
										secondaryColor = terrainTypes[secondaryTerrain]?.color || null;
									}
								}
							}
						}
					}
					
					// Draw the base tile
					canvasContext.fillStyle = primaryColor;
					canvasContext.fillRect(x, y, tileSize, tileSize);
					
					// Draw a pattern if there's a significant secondary terrain
					if (secondaryColor) {
						drawTerrainPattern(x, y, secondaryColor);
					}
					
					// Draw a border - thicker if explored
					canvasContext.strokeStyle = region.explored ? '#000' : '#666';
					canvasContext.lineWidth = region.explored ? 2 : 1;
					canvasContext.strokeRect(x, y, tileSize, tileSize);
					
					// Draw development indicators (buildings, resources)
					if (region.explored) {
						drawDevelopmentIndicators(region, x, y);
					}
					
					// Draw special feature indicators
				function drawSpecialFeatures(region, x, y) {
					// Only show discovered features, or a "?" for unexplored regions with undiscovered features
					const discoveredFeatures = region.specialFeatures.filter(feature => feature.discovered);
					
					if (discoveredFeatures.length > 0) {
						// Draw icons for discovered features
						discoveredFeatures.forEach((feature, index) => {
							canvasContext.fillStyle = '#9932CC'; // Purple for special features
							canvasContext.beginPath();
							canvasContext.arc(x + tileSize - 10, y + 10 + (index * 12), 5, 0, 2 * Math.PI);
							canvasContext.fill();
						});
					} 
					else if (!region.explored && region.specialFeatures.length > 0) {
						// Draw a "?" for unexplored regions with features
						canvasContext.fillStyle = '#DDA0DD'; // Light purple
						canvasContext.font = '16px Arial Bold';
						canvasContext.fillText('?', x + tileSize - 15, y + 20);
					}
				}
				
				// Draw grid lines
				function drawGrid() {
					const canvasCenter = canvasSize / 2;
					
					canvasContext.strokeStyle = '#AAA';
					canvasContext.lineWidth = 0.5;
					
					// Draw horizontal lines
					for (let y = canvasCenter % tileSize; y < canvasSize; y += tileSize) {
						canvasContext.beginPath();
						canvasContext.moveTo(0, y);
						canvasContext.lineTo(canvasSize, y);
						canvasContext.stroke();
					}
					
					// Draw vertical lines
					for (let x = canvasCenter % tileSize; x < canvasSize; x += tileSize) {
						canvasContext.beginPath();
						canvasContext.moveTo(x, 0);
						canvasContext.lineTo(x, canvasSize);
						canvasContext.stroke();
					}
				}
				
				// Draw a compass in the corner
				function drawCompass() {
					const x = canvasSize - 60;
					const y = canvasSize - 60;
					const radius = 20;
					
					// Draw compass circle
					canvasContext.fillStyle = '#FFF';
					canvasContext.beginPath();
					canvasContext.arc(x, y, radius, 0, 2 * Math.PI);
					canvasContext.fill();
					
					canvasContext.strokeStyle = '#000';
					canvasContext.lineWidth = 1;
					canvasContext.beginPath();
					canvasContext.arc(x, y, radius, 0, 2 * Math.PI);
					canvasContext.stroke();
					
					// Draw cardinal directions
					canvasContext.fillStyle = '#000';
					canvasContext.font = '12px Arial Bold';
					canvasContext.textAlign = 'center';
					canvasContext.textBaseline = 'middle';
					
					// North
					canvasContext.fillText('N', x, y - radius + 8);
					// East
					canvasContext.fillText('E', x + radius - 8, y);
					// South
					canvasContext.fillText('S', x, y + radius - 8);
					// West
					canvasContext.fillText('W', x - radius + 8, y);
				}
				
				// Draw region names and other labels
				function drawLabels() {
					const canvasCenter = canvasSize / 2;
					
					for (const region of world.regions) {
						// Skip if not discovered
						if (!region.discovered) continue;
						
						// Calculate position on canvas
						const x = canvasCenter + (region.position.x - centerX) * tileSize;
						const y = canvasCenter + (region.position.y - centerY) * tileSize;
						
						// Check if the region is within the visible canvas
						if (x + tileSize < 0 || x > canvasSize || y + tileSize < 0 || y > canvasSize) {
							continue;
						}
						
						// Draw region name
						canvasContext.fillStyle = '#000';
						canvasContext.font = '10px Arial';
						canvasContext.textAlign = 'center';
						
						// Draw name in center of tile
						const name = region.name;
						const centerX = x + tileSize / 2;
						const centerY = y + tileSize / 2;
						
						// If the name is too long, split it
						if (name.length > 15) {
							const parts = name.split(' ');
							const midpoint = Math.ceil(parts.length / 2);
							
							const firstLine = parts.slice(0, midpoint).join(' ');
							const secondLine = parts.slice(midpoint).join(' ');
							
							canvasContext.fillText(firstLine, centerX, centerY - 5);
							canvasContext.fillText(secondLine, centerX, centerY + 5);
						} else {
							canvasContext.fillText(name, centerX, centerY);
						}
					}
				}
				
				// Handle clicking on the canvas
				function handleCanvasClick(event) {
					// Get the click coordinates relative to the canvas
					const rect = event.target.getBoundingClientRect();
					const clickX = event.clientX - rect.left;
					const clickY = event.clientY - rect.top;
					
					// Convert to grid coordinates
					const canvasCenter = canvasSize / 2;
					const gridX = Math.floor((clickX - canvasCenter) / tileSize) + centerX;
					const gridY = Math.floor((clickY - canvasCenter) / tileSize) + centerY;
					
					// Find if there's a region at this location
					const clickedRegion = world.regions.find(region => 
						region.position.x === gridX && 
						region.position.y === gridY && 
						region.discovered
					);
					
					if (clickedRegion) {
						// Dispatch a custom event with the region data
						const clickEvent = new CustomEvent('region-clicked', {
							detail: {
								regionId: clickedRegion.id,
								region: clickedRegion
							}
						});
						
						document.dispatchEvent(clickEvent);
					}
				}
				
				// Handle zooming with the mouse wheel
				function handleCanvasZoom(event) {
					event.preventDefault();
					
					// Zoom in or out
					if (event.deltaY < 0) {
						// Zoom in
						if (tileSize < 100) {
							tileSize += 5;
						}
					} else {
						// Zoom out
						if (tileSize > 20) {
							tileSize -= 5;
						}
					}
					
					// Re-render
					render();
				}
				
				// Pan the map view
				function panMap(dx, dy) {
					centerX += dx;
					centerY += dy;
					render();
				}
				
				// Center the map on a specific region
				function centerOnRegion(regionId) {
					const region = world.regions.find(r => r.id === regionId);
					
					if (region) {
						centerX = region.position.x;
						centerY = region.position.y;
						render();
						return true;
					}
					
					return false;
				}
				
				// Export detailed region information for UI display
				function getRegionDetails(regionId) {
					const region = world.regions.find(r => r.id === regionId);
					
					if (!region) {
						return null;
					}
					
					// Get the terrain types
					const terrainTypes = WorldGenerator.getTerrainTypes();
					
					// Format terrain distribution for display
					const terrainDetails = Object.entries(region.terrainDistribution)
						.map(([terrain, weight]) => ({
							name: terrainTypes[terrain]?.name || terrain,
							percentage: Math.round(weight * 100),
							color: terrainTypes[terrain]?.color || '#7CFC00'
						}))
						.sort((a, b) => b.percentage - a.percentage);
					
					// Format resource modifiers
					const resourceModifiers = Object.entries(region.resourceModifiers)
						.map(([resource, modifier]) => ({
							resource,
							percentage: Math.round((modifier - 1) * 100)
						}))
						.filter(mod => mod.percentage !== 0);
					
					// Prepare special features info
					const featuresInfo = region.specialFeatures
						.filter(feature => feature.discovered || region.explored)
						.map(feature => ({
							name: feature.name,
							description: feature.description,
							discovered: feature.discovered,
							effects: feature.effects
						}));
					
					// Return the formatted details
					return {
						id: region.id,
						name: region.name,
						description: region.description,
						position: region.position,
						explored: region.explored,
						developmentLevel: region.developmentLevel,
						terrainDetails,
						resourceModifiers,
						resources: region.resources,
						specialFeatures: featuresInfo,
						buildings: region.buildings
					};
				}
				
				// Return public API
				return {
					init,
					render,
					panMap,
					centerOnRegion,
					getRegionDetails
				};
			})();
			
			export { WorldRenderer };
					if (region.specialFeatures && region.specialFeatures.length > 0) {
						drawSpecialFeatures(region, x, y);
					}
				}
				
				// Draw a pattern to represent mixed terrain
				function drawTerrainPattern(x, y, color) {
					canvasContext.fillStyle = color;
					
					// Draw diagonal stripes
					for (let i = 0; i < tileSize; i += 6) {
						canvasContext.beginPath();
						canvasContext.moveTo(x + i, y);
						canvasContext.lineTo(x, y + i);
						canvasContext.lineTo(x, y + i + 3);
						canvasContext.lineTo(x + i + 3, y);
						canvasContext.closePath();
						canvasContext.fill();
					}
				}
				
				// Draw indicators for region development
				function drawDevelopmentIndicators(region, x, y) {
					// Draw small icons for buildings
					if (region.buildings && region.buildings.length > 0) {
						const buildingCount = Math.min(region.buildings.length, 4);
						
						for (let i = 0; i < buildingCount; i++) {
							canvasContext.fillStyle = '#B22222'; // Firebrick red for buildings
							canvasContext.beginPath();
							canvasContext.arc(x + 10 + (i * 12), y + 10, 5, 0, 2 * Math.PI);
							canvasContext.fill();
						}
					}
					
					// Draw development level
					if (region.developmentLevel > 0) {
						canvasContext.fillStyle = '#FFD700'; // Gold
						canvasContext.font = '12px Arial';
						canvasContext.fillText(
							'★'.repeat(region.developmentLevel), 
							x + 5, 
							y + tileSize - 5
						);
					}
					
					// If it's the currently occupied/settled region, mark it
					if (region.isSettled) {
						canvasContext.strokeStyle = '#FF4500'; // Orange-red
						canvasContext.lineWidth = 3;
						canvasContext.strokeRect(x + 3, y + 3, tileSize - 6, tileSize - 6);
					}
				}
				
				// Draw special feature indicators
		
</>
building-manager.js - Building Construction and Management
Click to open code

			// building-manager.js
			// Manages buildings that can be constructed in the game
			
			import { ResourceManager } from './resources/resource-manager.js';
			import { EventManager } from './events/event-manager.js';
			
			// BuildingManager - Handles building-related functions
			const BuildingManager = (function() {
				// Available building types with their properties
				const buildingTypes = {
					residence: {
						id: 'residence',
						name: 'Residence',
						description: 'Basic housing for characters',
						category: 'housing',
						constructionTime: 5,
						difficulty: 1,
						cost: {
							materials: 20
						},
						effects: {
							housing: 2
						},
						maintenance: {
							materials: 1
						},
						requiredTech: null,
						requiredLevel: 1,
						terrainPreferences: ['plains', 'hills']
					},
					farm: {
						id: 'farm',
						name: 'Farm',
						description: 'Produces food through agriculture',
						category: 'production',
						constructionTime: 8,
						difficulty: 2,
						cost: {
							materials: 15
						},
						effects: {
							food: 5
						},
						maintenance: {
							materials: 1
						},
						requiredTech: 'agriculture',
						requiredLevel: 1,
						terrainPreferences: ['plains', 'river']
					},
					lumberMill: {
						id: 'lumberMill',
						name: 'Lumber Mill',
						description: 'Harvests and processes wood',
						category: 'production',
						constructionTime: 10,
						difficulty: 2,
						cost: {
							materials: 25,
							knowledge: 5
						},
						effects: {
							materials: 5
						},
						maintenance: {
							materials: 2
						},
						requiredTech: 'woodworking',
						requiredLevel: 2,
						terrainPreferences: ['forest']
					},
					workshop: {
						id: 'workshop',
						name: 'Workshop',
						description: 'Creates tools and implements',
						category: 'production',
						constructionTime: 12,
						difficulty: 3,
						cost: {
							materials: 30,
							knowledge: 10
						},
						effects: {
							materials: 3,
							knowledge: 1
						},
						maintenance: {
							materials: 2
						},
						requiredTech: 'crafting',
						requiredLevel: 2,
						terrainPreferences: ['plains', 'hills']
					},
					mine: {
						id: 'mine',
						name: 'Mine',
						description: 'Extracts minerals and stone',
						category: 'production',
						constructionTime: 15,
						difficulty: 4,
						cost: {
							materials: 35,
							knowledge: 15
						},
						effects: {
							materials: 8
						},
						maintenance: {
							materials: 3
						},
						requiredTech: 'mining',
						requiredLevel: 3,
						terrainPreferences: ['mountain', 'hills']
					},
					library: {
						id: 'library',
						name: 'Library',
						description: 'Stores and organizes knowledge',
						category: 'research',
						constructionTime: 12,
						difficulty: 3,
						cost: {
							materials: 25,
							knowledge: 20
						},
						effects: {
							knowledge: 5,
							research_bonus: 0.1
						},
						maintenance: {
							materials: 1
						},
						requiredTech: 'writing',
						requiredLevel: 2,
						terrainPreferences: ['plains', 'hills']
					},
					laboratory: {
						id: 'laboratory',
						name: 'Laboratory',
						description: 'Facilitates scientific experiments',
						category: 'research',
						constructionTime: 15,
						difficulty: 4,
						cost: {
							materials: 35,
							knowledge: 40
						},
						effects: {
							knowledge: 8,
							research_bonus: 0.15
						},
						maintenance: {
							materials: 2
						},
						requiredTech: 'scientific_method',
						requiredLevel: 3,
						terrainPreferences: ['plains', 'hills', 'mountain']
					},
					market: {
						id: 'market',
						name: 'Market',
						description: 'Enables trade and resource exchange',
						category: 'economy',
						constructionTime: 10,
						difficulty: 3,
						cost: {
							materials: 30,
							knowledge: 10
						},
						effects: {
							wealth: 5,
							trade_efficiency: 0.1
						},
						maintenance: {
							materials: 2
						},
						requiredTech: 'trade',
						requiredLevel: 2,
						terrainPreferences: ['plains', 'river']
					},
					watchtower: {
						id: 'watchtower',
						name: 'Watchtower',
						description: 'Provides visibility and early warnings',
						category: 'defense',
						constructionTime: 8,
						difficulty: 2,
						cost: {
							materials: 20,
							knowledge: 5
						},
						effects: {
							defense: 3,
							visibility: 2
						},
						maintenance: {
							materials: 1
						},
						requiredTech: null,
						requiredLevel: 2,
						terrainPreferences: ['hills', 'mountain']
					},
					fisherHut: {
						id: 'fisherHut',
						name: 'Fisher Hut',
						description: 'Harvests fish from water bodies',
						category: 'production',
						constructionTime: 6,
						difficulty: 1,
						cost: {
							materials: 15
						},
						effects: {
							food: 6
						},
						maintenance: {
							materials: 1
						},
						requiredTech: null,
						requiredLevel: 1,
						terrainPreferences: ['river', 'lake']
					},
					waterMill: {
						id: 'waterMill',
						name: 'Water Mill',
						description: 'Uses water power for processing',
						category: 'production',
						constructionTime: 12,
						difficulty: 3,
						cost: {
							materials: 25,
							knowledge: 15
						},
						effects: {
							food: 3,
							materials: 3
						},
						maintenance: {
							materials: 2
						},
						requiredTech: 'water_power',
						requiredLevel: 2,
						terrainPreferences: ['river']
					},
					well: {
						id: 'well',
						name: 'Well',
						description: 'Provides clean water in arid regions',
						category: 'infrastructure',
						constructionTime: 8,
						difficulty: 2,
						cost: {
							materials: 20
						},
						effects: {
							food: 2,
							health: 1
						},
						maintenance: {
							materials: 1
						},
						requiredTech: null,
						requiredLevel: 1,
						terrainPreferences: ['plains', 'desert']
					},
					observatory: {
						id: 'observatory',
						name: 'Observatory',
						description: 'Studies the stars and natural phenomena',
						category: 'research',
						constructionTime: 15,
						difficulty: 4,
						cost: {
							materials: 30,
							knowledge: 50
						},
						effects: {
							knowledge: 10,
							research_bonus: 0.2
						},
						maintenance: {
							materials: 2
						},
						requiredTech: 'astronomy',
						requiredLevel: 4,
						terrainPreferences: ['mountain', 'hills']
					}
				};
				
				// Internal state
				let buildings = [];
				let buildingsInProgress = [];
				
				// Initialize with existing buildings
				function init(existingBuildings) {
					buildings = existingBuildings || [];
					console.log(`Initialized BuildingManager with ${buildings.length} buildings`);
				}
				
				// Get all available buildings that can be constructed
				function getAvailableBuildings(gameState) {
					const availableBuildings = [];
					const researchedTechs = gameState.technologies.map(tech => tech.id);
					
					for (const buildingId in buildingTypes) {
						const building = buildingTypes[buildingId];
						
						// Check if the required technology is researched or not required
						const techRequirementMet = !building.requiredTech || researchedTechs.includes(building.requiredTech);
						
						// Check if the civilization level is high enough
						const levelRequirementMet = gameState.world.developmentLevel >= building.requiredLevel;
						
						// Check if resources are sufficient
						const canAfford = ResourceManager.canAfford(building.cost);
						
						if (techRequirementMet && levelRequirementMet) {
							availableBuildings.push({
								...building,
								canAfford
							});
						}
					}
					
					return availableBuildings;
				}
				
				// Get details about a specific building type
				function getBuildingDetails(buildingId) {
					return buildingTypes[buildingId] || null;
				}
				
				// Start construction of a building
				function constructBuilding(buildingId, gameState, regionId = null) {
					const buildingType = buildingTypes[buildingId];
					
					if (!buildingType) {
						console.error(`Unknown building type: ${buildingId}`);
						return false;
					}
					
					// Check if we can afford it
					if (!ResourceManager.canAfford(buildingType.cost)) {
						console.error(`Not enough resources to build ${buildingType.name}`);
						return false;
					}
					
					// Spend the resources
					if (!ResourceManager.spendResources(buildingType.cost)) {
						console.error(`Failed to spend resources for ${buildingType.name}`);
						return false;
					}
					
					// Determine where to build (default to first explored region if none specified)
					let targetRegion = regionId;
					if (!targetRegion) {
						const exploredRegions = gameState.world.regions.filter(region => region.explored);
						if (exploredRegions.length > 0) {
							targetRegion = exploredRegions[0].id;
						} else {
							console.error('No explored regions to build in');
							return false;
						}
					}
					
					// Create the construction project
					const constructionProject = {
						id: `construction_${Date.now()}_${Math.floor(Math.random() * 1000)}`,
						buildingId,
						buildingName: buildingType.name,
						regionId: targetRegion,
						progress: 0,
						totalWork: buildingType.constructionTime,
						startedTurn: gameState.turn
					};
					
					// Add to in-progress buildings
					buildingsInProgress.push(constructionProject);
					
					// Trigger an event
					EventManager.addEvent({
						type: 'construction',
						message: `Construction of ${buildingType.name} has begun!`,
						details: `Construction will take approximately ${buildingType.constructionTime} turns.`,
						turn: gameState.turn
					});
					
					console.log(`Started construction of ${buildingType.name} in region ${targetRegion}`);
					return constructionProject.id;
				}
				
				// Update building progress
				function updateBuildings(gameState) {
					// Process buildings under construction
					buildingsInProgress = buildingsInProgress.filter(project => {
						// Add progress (default 1 per turn)
						// Could be modified by character abilities, technologies, etc.
						project.progress += 1;
						
						// Check if complete
						if (project.progress >= project.totalWork) {
							// Complete the building
							completeBuilding(project, gameState);
							return false; // Remove from in-progress list
						}
						
						return true; // Keep in the list
					});
					
					// Process existing buildings (maintenance, effects, etc.)
					processExistingBuildings(gameState);
				}
				
				// Complete a building construction
				function completeBuilding(project, gameState) {
					const buildingType = buildingTypes[project.buildingId];
					
					if (!buildingType) {
						console.error(`Unknown building type: ${project.buildingId}`);
						return false;
					}
					
					// Find the target region
					const targetRegion = gameState.world.regions.find(region => region.id === project.regionId);
					
					if (!targetRegion) {
						console.error(`Target region ${project.regionId} not found`);
						return false;
					}
					
					// Create the building instance
					const building = {
						id: `building_${Date.now()}_${Math.floor(Math.random() * 1000)}`,
						type: project.buildingId,
						name: buildingType.name,
						regionId: project.regionId,
						built: gameState.turn,
						condition: 100, // Max condition
						efficiency: calculateEfficiency(buildingType, targetRegion),
						workers: []
					};
					
					// Add to buildings list
					buildings.push(building);
					
					// Add to region buildings
					if (!targetRegion.buildings) {
						targetRegion.buildings = [];
					}
					targetRegion.buildings.push(building.id);
					
					// Increase region development level
					targetRegion.developmentLevel += 1;
					
					// Trigger an event
					EventManager.addEvent({
						type: 'construction',
						message: `${buildingType.name} construction complete!`,
						details: `The ${buildingType.name} is now operational in ${targetRegion.name}.`,
						turn: gameState.turn
					});
					
					console.log(`Completed construction of ${buildingType.name} in ${targetRegion.name}`);
					return building;
				}
				
				// Calculate efficiency of a building based on terrain
				function calculateEfficiency(buildingType, region) {
					let efficiency = 1.0; // Base efficiency
					
					// Check terrain preferences
					if (buildingType.terrainPreferences && region.terrainDistribution) {
						// Start with a penalty for non-preferred terrain
						efficiency = 0.8;
						
						// Calculate boost from preferred terrains
						for (const terrain of buildingType.terrainPreferences) {
							if (region.terrainDistribution[terrain]) {
								efficiency += region.terrainDistribution[terrain] * 0.3;
							}
						}
					}
					
					// Apply region's resource modifiers if applicable
					if (region.resourceModifiers) {
						for (const resource in buildingType.effects) {
							if (region.resourceModifiers[resource]) {
								efficiency *= region.resourceModifiers[resource];
							}
						}
					}
					
					// Cap efficiency between 0.5 and 1.5
					efficiency = Math.max(0.5, Math.min(1.5, efficiency));
					
					return efficiency;
				}
				
				// Process existing buildings' effects and maintenance
				function processExistingBuildings(gameState) {
					for (const building of buildings) {
						const buildingType = buildingTypes[building.type];
						
						if (!buildingType) {
							console.error(`Unknown building type: ${building.type}`);
							continue;
						}
						
						// Apply maintenance costs
						if (buildingType.maintenance) {
							for (const resource in buildingType.maintenance) {
								ResourceManager.removeResource(resource, buildingType.maintenance[resource]);
							}
						}
						
						// Apply building effects (adjust resource production)
						if (buildingType.effects) {
							for (const resource in buildingType.effects) {
								// Special handling for non-resource effects
								if (resource === 'research_bonus' || 
										resource === 'trade_efficiency' || 
										resource === 'defense' || 
										resource === 'visibility' || 
										resource === 'housing' ||
										resource === 'health') {
									// These are handled elsewhere
									continue;
								}
								
								// Calculate production with efficiency
								const production = buildingType.effects[resource] * building.efficiency;
								ResourceManager.addResource(resource, production);
							}
						}
						
						// Deteriorate building condition slightly
						building.condition -= 0.2;
						
						// If condition drops below thresholds, reduce efficiency
						if (building.condition < 75) {
							building.efficiency = calculateEfficiency(buildingType, 
								gameState.world.regions.find(r => r.id === building.regionId)) * (building.condition / 100);
						}
						
						// If condition drops too low, generate a maintenance event
						if (building.condition < 50 && Math.random() < 0.2) {
							EventManager.addEvent({
								type: 'building',
								message: `${building.name} requires maintenance`,
								details: `The ${building.name} is deteriorating and requires repairs.`,
								turn: gameState.turn
							});
						}
					}
				}
				
				// Repair a building to restore condition
				function repairBuilding(buildingId) {
					const building = buildings.find(b => b.id === buildingId);
					
					if (!building) {
						console.error(`Building ${buildingId} not found`);
						return false;
					}
					
					const buildingType = buildingTypes[building.type];
					
					if (!buildingType) {
						console.error(`Unknown building type: ${building.type}`);
						return false;
					}
					
					// Calculate repair cost (25% of original construction cost)
					const repairCost = {};
					for (const resource in buildingType.cost) {
						repairCost[resource] = Math.ceil(buildingType.cost[resource] * 0.25);
					}
					
					// Check if we can afford it
					if (!ResourceManager.canAfford(repairCost)) {
						console.error(`Not enough resources to repair ${building.name}`);
						return false;
					}
					
					// Spend the resources
					if (!ResourceManager.spendResources(repairCost)) {
						console.error(`Failed to spend resources for ${building.name} repair`);
						return false;
					}
					
					// Restore condition
					building.condition = 100;
					
					// Recalculate efficiency
					const gameState = window.AICivilizationGame?.getGameState();
					if (gameState) {
						const region = gameState.world.regions.find(r => r.id === building.regionId);
						if (region) {
							building.efficiency = calculateEfficiency(buildingType, region);
						}
					}
					
					// Trigger an event
					EventManager.addEvent({
						type: 'building',
						message: `${building.name} has been repaired`,
						details: `The ${building.name} is now in optimal condition.`,
						turn: gameState?.turn || 0
					});
					
					console.log(`Repaired ${building.name}`);
					return true;
				}
				
				// Get all buildings in a specific region
				function getBuildingsInRegion(regionId) {
					return buildings.filter(building => building.regionId === regionId);
				}
				
				// Analyze building needs based on current game state
				function analyzeNeeds(world) {
					const needs = {
						urgent: false,
						recommended: null,
						reasons: []
					};
					
					// Count building types
					const buildingCounts = {};
					for (const building of buildings) {
						buildingCounts[building.type] = (buildingCounts[building.type] || 0) + 1;
					}
					
					// Check for essential building types
					if (!buildingCounts.residence || buildingCounts.residence < 2) {
						needs.urgent = true;
						needs.recommended = 'residence';
						needs.reasons.push('Housing shortage');
					} else if (!buildingCounts.farm && !buildingCounts.fisherHut) {
						needs.urgent = true;
						needs.recommended = 'farm';
						needs.reasons.push('Food production needed');
					} else if (!buildingCounts.lumberMill && world.developmentLevel >= 2) {
						needs.recommended = 'lumberMill';
						needs.reasons.push('Material production needed');
					} else if (!buildingCounts.library && world.developmentLevel >= 2) {
						needs.recommended = 'library';
						needs.reasons.push('Knowledge production needed');
					} else if (!buildingCounts.workshop && world.developmentLevel >= 2) {
						needs.recommended = 'workshop';
						needs.reasons.push('Craft production needed');
					} else if (!buildingCounts.market && world.developmentLevel >= 3) {
						needs.recommended = 'market';
						needs.reasons.push('Economic development needed');
					}
					
					return needs;
				}
				
				// Return public API
				return {
					init,
					getAvailableBuildings,
					getBuildingDetails,
					constructBuilding,
					updateBuildings,
					repairBuilding,
					getBuildingsInRegion,
					analyzeNeeds,
					getBuildings: () => buildings
				};
			})();
			
			export { BuildingManager };
		
</>
Generating...
Click to open code
Note: No code was actualy generated for this final code-block during this prompt.


	
Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
Aéius Cercle: Sorry, looks like world-renderer.js needs to be re-coded again; you can skip building-manager.js then proceed with the next file to be coded...
Claude: I'll re-code the world-renderer.js file and then continue with the remaining files for our AI civilization game.
</>
world-renderer.js - Visualization of Game World
Click to open code

			// world-renderer.js
			// Handles rendering the game world to the UI
			
			import { WorldGenerator } from './world-generator.js';
			
			// WorldRenderer - Visualizes the game world
			const WorldRenderer = (function() {
				// Private variables
				let world = null;
				let canvasContext = null;
				let tileSize = 60;
				let canvasSize = 600;
				let centerX = 0;
				let centerY = 0;
				
				// Initialize with a world object
				function init(worldData) {
					world = worldData;
					console.log('Initialized WorldRenderer with world:', world.name);
					
					// Set up the canvas if the DOM is available
					setupCanvas();
				}
				
				// Set up the canvas element
				function setupCanvas() {
					const mapCanvas = document.getElementById('world-map');
					if (!mapCanvas) {
						console.error('Map canvas element not found');
						
						// Try to create it
						const gameContainer = document.getElementById('game-container');
						if (gameContainer) {
							const canvas = document.createElement('canvas');
							canvas.id = 'world-map';
							canvas.width = canvasSize;
							canvas.height = canvasSize;
							canvas.style.border = '1px solid #ccc';
							gameContainer.appendChild(canvas);
							canvasContext = canvas.getContext('2d');
							
							// Add event listeners for interaction
							canvas.addEventListener('click', handleCanvasClick);
							canvas.addEventListener('wheel', handleCanvasZoom);
							
							console.log('Created map canvas element');
						} else {
							console.error('Game container element not found');
							return false;
						}
					} else {
						canvasContext = mapCanvas.getContext('2d');
						
						// Add event listeners for interaction
						mapCanvas.addEventListener('click', handleCanvasClick);
						mapCanvas.addEventListener('wheel', handleCanvasZoom);
					}
					
					return true;
				}
				
				// Render the world to the canvas
				function render(worldData) {
					// Update world data if provided
					if (worldData) {
						world = worldData;
					}
					
					// Check if canvas is available
					if (!canvasContext) {
						if (!setupCanvas()) {
							console.error('Unable to render world: canvas not available');
							return;
						}
					}
					
					// Clear the canvas
					canvasContext.clearRect(0, 0, canvasSize, canvasSize);
					
					// Draw the background
					canvasContext.fillStyle = '#87CEEB'; // Sky blue
					canvasContext.fillRect(0, 0, canvasSize, canvasSize);
					
					// Calculate the center of the canvas
					const canvasCenter = canvasSize / 2;
					
					// Draw each region
					for (const region of world.regions) {
						// Skip if not discovered
						if (!region.discovered) continue;
						
						// Calculate position on canvas
						const x = canvasCenter + (region.position.x - centerX) * tileSize;
						const y = canvasCenter + (region.position.y - centerY) * tileSize;
						
						// Check if the region is within the visible canvas
						if (x + tileSize < 0 || x > canvasSize || y + tileSize < 0 || y > canvasSize) {
							continue;
						}
						
						// Draw the region
						drawRegion(region, x, y);
					}
					
					// Draw grid lines for better visual clarity
					drawGrid();
					
					// Draw a compass
					drawCompass();
					
					// Draw region names and features
					drawLabels();
				}
				
				// Draw a single region on the canvas
				function drawRegion(region, x, y) {
					// Determine the region color based on terrain distribution
					let primaryColor = '#7CFC00'; // Default light green
					let secondaryColor = null;
					let maxWeight = 0;
					
					// Get the terrain types
					const terrainTypes = WorldGenerator.getTerrainTypes();
					
					// Find the primary terrain
					for (const terrainType in region.terrainDistribution) {
						const weight = region.terrainDistribution[terrainType];
						if (weight > maxWeight) {
							maxWeight = weight;
							primaryColor = terrainTypes[terrainType]?.color || primaryColor;
							
							// If there's a significant secondary terrain, save it for patterns
							if (weight >= 0.4 && weight < 0.7) {
								let secondaryWeight = 0;
								for (const secondaryTerrain in region.terrainDistribution) {
									if (secondaryTerrain !== terrainType && 
											region.terrainDistribution[secondaryTerrain] > secondaryWeight) {
										secondaryWeight = region.terrainDistribution[secondaryTerrain];
										secondaryColor = terrainTypes[secondaryTerrain]?.color || null;
									}
								}
							}
						}
					}
					
					// Draw the base tile
					canvasContext.fillStyle = primaryColor;
					canvasContext.fillRect(x, y, tileSize, tileSize);
					
					// Draw a pattern if there's a significant secondary terrain
					if (secondaryColor) {
						drawTerrainPattern(x, y, secondaryColor);
					}
					
					// Draw a border - thicker if explored
					canvasContext.strokeStyle = region.explored ? '#000' : '#666';
					canvasContext.lineWidth = region.explored ? 2 : 1;
					canvasContext.strokeRect(x, y, tileSize, tileSize);
					
					// Draw development indicators (buildings, resources)
					if (region.explored) {
						drawDevelopmentIndicators(region, x, y);
					}
					
					// Draw special feature indicators
					if (region.specialFeatures && region.specialFeatures.length > 0) {
						drawSpecialFeatures(region, x, y);
					}
				}
				
				// Draw a pattern to represent mixed terrain
				function drawTerrainPattern(x, y, color) {
					canvasContext.fillStyle = color;
					
					// Draw diagonal stripes
					for (let i = 0; i < tileSize; i += 6) {
						canvasContext.beginPath();
						canvasContext.moveTo(x + i, y);
						canvasContext.lineTo(x, y + i);
						canvasContext.lineTo(x, y + i + 3);
						canvasContext.lineTo(x + i + 3, y);
						canvasContext.closePath();
						canvasContext.fill();
					}
				}
				
				// Draw indicators for region development
				function drawDevelopmentIndicators(region, x, y) {
					// Draw small icons for buildings
					if (region.buildings && region.buildings.length > 0) {
						const buildingCount = Math.min(region.buildings.length, 4);
						
						for (let i = 0; i < buildingCount; i++) {
							canvasContext.fillStyle = '#B22222'; // Firebrick red for buildings
							canvasContext.beginPath();
							canvasContext.arc(x + 10 + (i * 12), y + 10, 5, 0, 2 * Math.PI);
							canvasContext.fill();
						}
					}
					
					// Draw development level
					if (region.developmentLevel > 0) {
						canvasContext.fillStyle = '#FFD700'; // Gold
						canvasContext.font = '12px Arial';
						canvasContext.fillText(
							'★'.repeat(region.developmentLevel), 
							x + 5, 
							y + tileSize - 5
						);
					}
					
					// If it's the currently occupied/settled region, mark it
					if (region.isSettled) {
						canvasContext.strokeStyle = '#FF4500'; // Orange-red
						canvasContext.lineWidth = 3;
						canvasContext.strokeRect(x + 3, y + 3, tileSize - 6, tileSize - 6);
					}
				}
				
				// Draw special feature indicators
				function drawSpecialFeatures(region, x, y) {
					// Only show discovered features, or a "?" for unexplored regions with undiscovered features
					const discoveredFeatures = region.specialFeatures.filter(feature => feature.discovered);
					
					if (discoveredFeatures.length > 0) {
						// Draw icons for discovered features
						discoveredFeatures.forEach((feature, index) => {
							canvasContext.fillStyle = '#9932CC'; // Purple for special features
							canvasContext.beginPath();
							canvasContext.arc(x + tileSize - 10, y + 10 + (index * 12), 5, 0, 2 * Math.PI);
							canvasContext.fill();
						});
					} 
					else if (!region.explored && region.specialFeatures.length > 0) {
						// Draw a "?" for unexplored regions with features
						canvasContext.fillStyle = '#DDA0DD'; // Light purple
						canvasContext.font = '16px Arial Bold';
						canvasContext.fillText('?', x + tileSize - 15, y + 20);
					}
				}
				
				// Draw grid lines
				function drawGrid() {
					const canvasCenter = canvasSize / 2;
					
					canvasContext.strokeStyle = '#AAA';
					canvasContext.lineWidth = 0.5;
					
					// Draw horizontal lines
					for (let y = canvasCenter % tileSize; y < canvasSize; y += tileSize) {
						canvasContext.beginPath();
						canvasContext.moveTo(0, y);
						canvasContext.lineTo(canvasSize, y);
						canvasContext.stroke();
					}
					
					// Draw vertical lines
					for (let x = canvasCenter % tileSize; x < canvasSize; x += tileSize) {
						canvasContext.beginPath();
						canvasContext.moveTo(x, 0);
						canvasContext.lineTo(x, canvasSize);
						canvasContext.stroke();
					}
				}
				
				// Draw a compass in the corner
				function drawCompass() {
					const x = canvasSize - 60;
					const y = canvasSize - 60;
					const radius = 20;
					
					// Draw compass circle
					canvasContext.fillStyle = '#FFF';
					canvasContext.beginPath();
					canvasContext.arc(x, y, radius, 0, 2 * Math.PI);
					canvasContext.fill();
					
					canvasContext.strokeStyle = '#000';
					canvasContext.lineWidth = 1;
					canvasContext.beginPath();
					canvasContext.arc(x, y, radius, 0, 2 * Math.PI);
					canvasContext.stroke();
					
					// Draw cardinal directions
					canvasContext.fillStyle = '#000';
					canvasContext.font = '12px Arial Bold';
					canvasContext.textAlign = 'center';
					canvasContext.textBaseline = 'middle';
					
					// North
					canvasContext.fillText('N', x, y - radius + 8);
					// East
					canvasContext.fillText('E', x + radius - 8, y);
					// South
					canvasContext.fillText('S', x, y + radius - 8);
					// West
					canvasContext.fillText('W', x - radius + 8, y);
				}
				
				// Draw region names and other labels
				function drawLabels() {
					const canvasCenter = canvasSize / 2;
					
					for (const region of world.regions) {
						// Skip if not discovered
						if (!region.discovered) continue;
						
						// Calculate position on canvas
						const x = canvasCenter + (region.position.x - centerX) * tileSize;
						const y = canvasCenter + (region.position.y - centerY) * tileSize;
						
						// Check if the region is within the visible canvas
						if (x + tileSize < 0 || x > canvasSize || y + tileSize < 0 || y > canvasSize) {
							continue;
						}
						
						// Draw region name
						canvasContext.fillStyle = '#000';
						canvasContext.font = '10px Arial';
						canvasContext.textAlign = 'center';
						
						// Draw name in center of tile
						const name = region.name;
						const centerX = x + tileSize / 2;
						const centerY = y + tileSize / 2;
						
						// If the name is too long, split it
						if (name.length > 15) {
							const parts = name.split(' ');
							const midpoint = Math.ceil(parts.length / 2);
							
							const firstLine = parts.slice(0, midpoint).join(' ');
							const secondLine = parts.slice(midpoint).join(' ');
							
							canvasContext.fillText(firstLine, centerX, centerY - 5);
							canvasContext.fillText(secondLine, centerX, centerY + 5);
						} else {
							canvasContext.fillText(name, centerX, centerY);
						}
					}
				}
				
				// Handle clicking on the canvas
				function handleCanvasClick(event) {
					// Get the click coordinates relative to the canvas
					const rect = event.target.getBoundingClientRect();
					const clickX = event.clientX - rect.left;
					const clickY = event.clientY - rect.top;
					
					// Convert to grid coordinates
					const canvasCenter = canvasSize / 2;
					const gridX = Math.floor((clickX - canvasCenter) / tileSize) + centerX;
					const gridY = Math.floor((clickY - canvasCenter) / tileSize) + centerY;
					
					// Find if there's a region at this location
					const clickedRegion = world.regions.find(region => 
						region.position.x === gridX && 
						region.position.y === gridY && 
						region.discovered
					);
					
					if (clickedRegion) {
						// Dispatch a custom event with the region data
						const clickEvent = new CustomEvent('region-clicked', {
							detail: {
								regionId: clickedRegion.id,
								region: clickedRegion
							}
						});
						
						document.dispatchEvent(clickEvent);
					}
				}
				
				// Handle zooming with the mouse wheel
				function handleCanvasZoom(event) {
					event.preventDefault();
					
					// Zoom in or out
					if (event.deltaY < 0) {
						// Zoom in
						if (tileSize < 100) {
							tileSize += 5;
						}
					} else {
						// Zoom out
						if (tileSize > 20) {
							tileSize -= 5;
						}
					}
					
					// Re-render
					render();
				}
				
				// Pan the map view
				function panMap(dx, dy) {
					centerX += dx;
					centerY += dy;
					render();
				}
				
				// Center the map on a specific region
				function centerOnRegion(regionId) {
					const region = world.regions.find(r => r.id === regionId);
					
					if (region) {
						centerX = region.position.x;
						centerY = region.position.y;
						render();
						return true;
					}
					
					return false;
				}
				
				// Export detailed region information for UI display
				function getRegionDetails(regionId) {
					const region = world.regions.find(r => r.id === regionId);
					
					if (!region) {
						return null;
					}
					
					// Get the terrain types
					const terrainTypes = WorldGenerator.getTerrainTypes();
					
					// Format terrain distribution for display
					const terrainDetails = Object.entries(region.terrainDistribution)
						.map(([terrain, weight]) => ({
							name: terrainTypes[terrain]?.name || terrain,
							percentage: Math.round(weight * 100),
							color: terrainTypes[terrain]?.color || '#7CFC00'
						}))
						.sort((a, b) => b.percentage - a.percentage);
					
					// Format resource modifiers
					const resourceModifiers = Object.entries(region.resourceModifiers)
						.map(([resource, modifier]) => ({
							resource,
							percentage: Math.round((modifier - 1) * 100)
						}))
						.filter(mod => mod.percentage !== 0);
					
					// Prepare special features info
					const featuresInfo = region.specialFeatures
						.filter(feature => feature.discovered || region.explored)
						.map(feature => ({
							name: feature.name,
							description: feature.description,
							discovered: feature.discovered,
							effects: feature.effects
						}));
					
					// Return the formatted details
					return {
						id: region.id,
						name: region.name,
						description: region.description,
						position: region.position,
						explored: region.explored,
						developmentLevel: region.developmentLevel,
						terrainDetails,
						resourceModifiers,
						resources: region.resources,
						specialFeatures: featuresInfo,
						buildings: region.buildings
					};
				}
				
				// Return public API
				return {
					init,
					render,
					panMap,
					centerOnRegion,
					getRegionDetails
				};
			})();
			
			export { WorldRenderer };
		
</>
technology-manager.js - Research and Technology System
Click to open code

			// technology-manager.js
			// Manages research, technologies, and advancements
			
			import { ResourceManager } from './resources/resource-manager.js';
			import { EventManager } from './events/event-manager.js';
			
			// TechnologyManager - Handles technology research and advancements
			const TechnologyManager = (function() {
				// Technology definitions
				const technologies = {
					agriculture: {
						id: 'agriculture',
						name: 'Agriculture',
						description: 'Organized farming techniques for consistent food production',
						category: 'production',
						tier: 1,
						researchTime: 10,
						difficulty: 2,
						cost: {
							knowledge: 20
						},
						prerequisites: [],
						effects: {
							food_production: 0.2,
							enables: ['farm']
						}
					},
					woodworking: {
						id: 'woodworking',
						name: 'Woodworking',
						description: 'Techniques for processing and using wood for construction',
						category: 'production',
						tier: 1,
						researchTime: 12,
						difficulty: 2,
						cost: {
							knowledge: 25
						},
						prerequisites: [],
						effects: {
							material_production: 0.15,
							enables: ['lumberMill']
						}
					},
					writing: {
						id: 'writing',
						name: 'Writing',
						description: 'Recording and sharing knowledge through written language',
						category: 'knowledge',
						tier: 1,
						researchTime: 15,
						difficulty: 3,
						cost: {
							knowledge: 30
						},
						prerequisites: [],
						effects: {
							knowledge_production: 0.2,
							enables: ['library']
						}
					},
					trade: {
						id: 'trade',
						name: 'Trade',
						description: 'Organized exchange of goods and services',
						category: 'economy',
						tier: 1,
						researchTime: 12,
						difficulty: 2,
						cost: {
							knowledge: 20
						},
						prerequisites: [],
						effects: {
							resource_efficiency: 0.1,
							enables: ['market']
						}
					},
					crafting: {
						id: 'crafting',
						name: 'Crafting',
						description: 'Creating tools and implements from materials',
						category: 'production',
						tier: 1,
						researchTime: 10,
						difficulty: 2,
						cost: {
							knowledge: 20
						},
						prerequisites: ['woodworking'],
						effects: {
							tool_quality: 0.15,
							enables: ['workshop']
						}
					},
					mining: {
						id: 'mining',
						name: 'Mining',
						description: 'Extracting minerals and metals from the earth',
						category: 'production',
						tier: 2,
						researchTime: 18,
						difficulty: 3,
						cost: {
							knowledge: 40,
							materials: 10
						},
						prerequisites: ['crafting'],
						effects: {
							material_production: 0.2,
							enables: ['mine']
						}
					},
					water_power: {
						id: 'water_power',
						name: 'Water Power',
						description: 'Harnessing flowing water for mechanical energy',
						category: 'energy',
						tier: 2,
						researchTime: 15,
						difficulty: 3,
						cost: {
							knowledge: 35,
							materials: 15
						},
						prerequisites: ['crafting'],
						effects: {
							production_efficiency: 0.15,
							enables: ['waterMill']
						}
					},
					scientific_method: {
						id: 'scientific_method',
						name: 'Scientific Method',
						description: 'Structured approach to observation and experimentation',
						category: 'knowledge',
						tier: 2,
						researchTime: 20,
						difficulty: 4,
						cost: {
							knowledge: 50
						},
						prerequisites: ['writing'],
						effects: {
							research_speed: 0.2,
							enables: ['laboratory']
						}
					},
					metallurgy: {
						id: 'metallurgy',
						name: 'Metallurgy',
						description: 'Working with metals to create tools and structures',
						category: 'production',
						tier: 2,
						researchTime: 22,
						difficulty: 4,
						cost: {
							knowledge: 45,
							materials: 20
						},
						prerequisites: ['mining'],
						effects: {
							tool_quality: 0.25,
							building_durability: 0.2
						}
					},
					astronomy: {
						id: 'astronomy',
						name: 'Astronomy',
						description: 'Study of celestial objects and phenomena',
						category: 'knowledge',
						tier: 3,
						researchTime: 25,
						difficulty: 4,
						cost: {
							knowledge: 60
						},
						prerequisites: ['scientific_method'],
						effects: {
							knowledge_production: 0.2,
							enables: ['observatory']
						}
					},
					construction: {
						id: 'construction',
						name: 'Advanced Construction',
						description: 'Techniques for building larger and more complex structures',
						category: 'infrastructure',
						tier: 2,
						researchTime: 20,
						difficulty: 3,
						cost: {
							knowledge: 40,
							materials: 25
						},
						prerequisites: ['woodworking'],
						effects: {
							building_efficiency: 0.15,
							building_durability: 0.1
						}
					},
					irrigation: {
						id: 'irrigation',
						name: 'Irrigation',
						description: 'Artificial water supply for plants and crops',
						category: 'production',
						tier: 2,
						researchTime: 15,
						difficulty: 3,
						cost: {
							knowledge: 35,
							materials: 15
						},
						prerequisites: ['agriculture', 'water_power'],
						effects: {
							food_production: 0.25
						}
					},
					animal_husbandry: {
						id: 'animal_husbandry',
						name: 'Animal Husbandry',
						description: 'Breeding and care of domestic animals',
						category: 'production',
						tier: 2,
						researchTime: 18,
						difficulty: 3,
						cost: {
							knowledge: 30,
							food: 20
						},
						prerequisites: ['agriculture'],
						effects: {
							food_production: 0.2,
							transportation_efficiency: 0.1
						}
					},
					medicine: {
						id: 'medicine',
						name: 'Medicine',
						description: 'Treatment and prevention of illness',
						category: 'health',
						tier: 3,
						researchTime: 25,
						difficulty: 4,
						cost: {
							knowledge: 50,
							food: 15
						},
						prerequisites: ['scientific_method'],
						effects: {
							character_health: 0.2,
							population_growth: 0.1
						}
					},
					mechanics: {
						id: 'mechanics',
						name: 'Mechanics',
						description: 'Understanding of forces and motion',
						category: 'engineering',
						tier: 3,
						researchTime: 22,
						difficulty: 4,
						cost: {
							knowledge: 55,
							materials: 25
						},
						prerequisites: ['scientific_method', 'metallurgy'],
						effects: {
							construction_efficiency: 0.2,
							tool_quality: 0.15
						}
					},
					philosophy: {
						id: 'philosophy',
						name: 'Philosophy',
						description: 'Systematic study of fundamental questions',
						category: 'knowledge',
						tier: 3,
						researchTime: 20,
						difficulty: 3,
						cost: {
							knowledge: 45
						},
						prerequisites: ['writing'],
						effects: {
							knowledge_production: 0.15,
							character_wisdom: 0.1
						}
					},
					education: {
						id: 'education',
						name: 'Education',
						description: 'Formal system of learning and teaching',
						category: 'knowledge',
						tier: 3,
						researchTime: 25,
						difficulty: 4,
						cost: {
							knowledge: 60,
							materials: 20
						},
						prerequisites: ['writing', 'philosophy'],
						effects: {
							knowledge_production: 0.2,
							character_intelligence: 0.15
						}
					}
				};
				
				// Internal state
				let researchedTechnologies = [];
				let currentResearch = null;
				let researchProgress = 0;
				let researchBonus = 1.0; // Multiplier for research speed
				
				// Initialize with existing research data
				function init(existingTechnologies) {
					researchedTechnologies = existingTechnologies || [];
					console.log(`Initialized TechnologyManager with ${researchedTechnologies.length} technologies`);
				}
				
				// Get available technologies that can be researched
				function getAvailableTechnologies(gameState) {
					const available = [];
					const researchedIds = researchedTechnologies.map(tech => tech.id);
					
					// For each technology
					for (const techId in technologies) {
						const tech = technologies[techId];
						
						// Skip if already researched
						if (researchedIds.includes(techId)) {
							continue;
						}
						
						// Check if prerequisites are met
						const prereqsMet = tech.prerequisites.every(prereq => researchedIds.includes(prereq));
						if (!prereqsMet) {
							continue;
						}
						
						// Check if tier is appropriate for current development level
						if (tech.tier > gameState.world.developmentLevel) {
							continue;
						}
						
						// Check if resources are sufficient
						const canAfford = ResourceManager.canAfford(tech.cost);
						
						// Add to available list with affordability flag
						available.push({
							...tech,
							canAfford
						});
					}
					
					return available;
				}
				
				// Start researching a technology
				function researchTechnology(techId, gameState) {
					// Check if already researching something
					if (currentResearch) {
						console.error(`Already researching ${currentResearch.name}`);
						return false;
					}
					
					const tech = technologies[techId];
					
					if (!tech) {
						console.error(`Unknown technology: ${techId}`);
						return false;
					}
					
					// Verify that prerequisites are met
					const researchedIds = researchedTechnologies.map(t => t.id);
					const prereqsMet = tech.prerequisites.every(prereq => researchedIds.includes(prereq));
					
					if (!prereqsMet) {
						console.error(`Prerequisites not met for ${tech.name}`);
						return false;
					}
					
					// Check if we can afford it
					if (!ResourceManager.canAfford(tech.cost)) {
						console.error(`Not enough resources to research ${tech.name}`);
						return false;
					}
					
					// Spend the resources
					if (!ResourceManager.spendResources(tech.cost)) {
						console.error(`Failed to spend resources for ${tech.name}`);
						return false;
					}
					
					// Start the research
					currentResearch = {
						...tech,
						startedTurn: gameState.turn
					};
					researchProgress = 0;
					
					// Trigger an event
					EventManager.addEvent({
						type: 'research',
						message: `Research on ${tech.name} has begun!`,
						details: `This technology will take approximately ${tech.researchTime} turns to complete.`,
						turn: gameState.turn
					});
					
					console.log(`Started research on ${tech.name}`);
					return true;
				}
				
				// Update research progress
				function updateTechnologies(gameState) {
					// If researching something, update progress
					if (currentResearch) {
						// Base progress is 1 per turn
						// Modified by research bonus from buildings, characters, etc.
						const progressIncrement = 1 * researchBonus;
						
						researchProgress += progressIncrement;
						
						// Check if research is complete
						if (researchProgress >= currentResearch.researchTime) {
							completeResearch(gameState);
						}
					}
					
					// Apply technology effects
					applyTechnologyEffects();
				}
				
				// Complete the current research
				function completeResearch(gameState) {
					if (!currentResearch) {
						console.error('No current research to complete');
						return false;
					}
					
					// Add to researched technologies
					researchedTechnologies.push({
						id: currentResearch.id,
						name: currentResearch.name,
						completedTurn: gameState.turn,
						effects: currentResearch.effects
					});
					
					// Apply immediate effects
					applyTechnologyEffects(currentResearch);
					
					// Trigger an event
					EventManager.addEvent({
						type: 'research',
						message: `Research on ${currentResearch.name} is complete!`,
						details: getEffectsDescription(currentResearch.effects),
						turn: gameState.turn
					});
					
					// Clear current research
					const completedResearch = currentResearch;
					currentResearch
		
Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
Aéius Cercle: Re-code again starting from technology-manager.js
Claude: I'll recreate the technology-manager.js file from scratch.
</>
technology-manager.js - Research and Technology System
Click to open code

			// technology-manager.js
			// Manages research, technologies, and advancements
			
			import { ResourceManager } from './resources/resource-manager.js';
			import { EventManager } from './events/event-manager.js';
			
			// TechnologyManager - Handles technology research and advancements
			const TechnologyManager = (function() {
				// Technology definitions
				const technologies = {
					agriculture: {
						id: 'agriculture',
						name: 'Agriculture',
						description: 'Organized farming techniques for consistent food production',
						category: 'production',
						tier: 1,
						researchTime: 10,
						difficulty: 2,
						cost: {
							knowledge: 20
						},
						prerequisites: [],
						effects: {
							food_production: 0.2,
							enables: ['farm']
						}
					},
					woodworking: {
						id: 'woodworking',
						name: 'Woodworking',
						description: 'Techniques for processing and using wood for construction',
						category: 'production',
						tier: 1,
						researchTime: 12,
						difficulty: 2,
						cost: {
							knowledge: 25
						},
						prerequisites: [],
						effects: {
							material_production: 0.15,
							enables: ['lumberMill']
						}
					},
					writing: {
						id: 'writing',
						name: 'Writing',
						description: 'Recording and sharing knowledge through written language',
						category: 'knowledge',
						tier: 1,
						researchTime: 15,
						difficulty: 3,
						cost: {
							knowledge: 30
						},
						prerequisites: [],
						effects: {
							knowledge_production: 0.2,
							enables: ['library']
						}
					},
					trade: {
						id: 'trade',
						name: 'Trade',
						description: 'Organized exchange of goods and services',
						category: 'economy',
						tier: 1,
						researchTime: 12,
						difficulty: 2,
						cost: {
							knowledge: 20
						},
						prerequisites: [],
						effects: {
							resource_efficiency: 0.1,
							enables: ['market']
						}
					},
					crafting: {
						id: 'crafting',
						name: 'Crafting',
						description: 'Creating tools and implements from materials',
						category: 'production',
						tier: 1,
						researchTime: 10,
						difficulty: 2,
						cost: {
							knowledge: 20
						},
						prerequisites: ['woodworking'],
						effects: {
							tool_quality: 0.15,
							enables: ['workshop']
						}
					},
					mining: {
						id: 'mining',
						name: 'Mining',
						description: 'Extracting minerals and metals from the earth',
						category: 'production',
						tier: 2,
						researchTime: 18,
						difficulty: 3,
						cost: {
							knowledge: 40,
							materials: 10
						},
						prerequisites: ['crafting'],
						effects: {
							material_production: 0.2,
							enables: ['mine']
						}
					},
					water_power: {
						id: 'water_power',
						name: 'Water Power',
						description: 'Harnessing flowing water for mechanical energy',
						category: 'energy',
						tier: 2,
						researchTime: 15,
						difficulty: 3,
						cost: {
							knowledge: 35,
							materials: 15
						},
						prerequisites: ['crafting'],
						effects: {
							production_efficiency: 0.15,
							enables: ['waterMill']
						}
					},
					scientific_method: {
						id: 'scientific_method',
						name: 'Scientific Method',
						description: 'Structured approach to observation and experimentation',
						category: 'knowledge',
						tier: 2,
						researchTime: 20,
						difficulty: 4,
						cost: {
							knowledge: 50
						},
						prerequisites: ['writing'],
						effects: {
							research_speed: 0.2,
							enables: ['laboratory']
						}
					},
					metallurgy: {
						id: 'metallurgy',
						name: 'Metallurgy',
						description: 'Working with metals to create tools and structures',
						category: 'production',
						tier: 2,
						researchTime: 22,
						difficulty: 4,
						cost: {
							knowledge: 45,
							materials: 20
						},
						prerequisites: ['mining'],
						effects: {
							tool_quality: 0.25,
							building_durability: 0.2
						}
					},
					astronomy: {
						id: 'astronomy',
						name: 'Astronomy',
						description: 'Study of celestial objects and phenomena',
						category: 'knowledge',
						tier: 3,
						researchTime: 25,
						difficulty: 4,
						cost: {
							knowledge: 60
						},
						prerequisites: ['scientific_method'],
						effects: {
							knowledge_production: 0.2,
							enables: ['observatory']
						}
					},
					construction: {
						id: 'construction',
						name: 'Advanced Construction',
						description: 'Techniques for building larger and more complex structures',
						category: 'infrastructure',
						tier: 2,
						researchTime: 20,
						difficulty: 3,
						cost: {
							knowledge: 40,
							materials: 25
						},
						prerequisites: ['woodworking'],
						effects: {
							building_efficiency: 0.15,
							building_durability: 0.1
						}
					},
					irrigation: {
						id: 'irrigation',
						name: 'Irrigation',
						description: 'Artificial water supply for plants and crops',
						category: 'production',
						tier: 2,
						researchTime: 15,
						difficulty: 3,
						cost: {
							knowledge: 35,
							materials: 15
						},
						prerequisites: ['agriculture', 'water_power'],
						effects: {
							food_production: 0.25
						}
					},
					animal_husbandry: {
						id: 'animal_husbandry',
						name: 'Animal Husbandry',
						description: 'Breeding and care of domestic animals',
						category: 'production',
						tier: 2,
						researchTime: 18,
						difficulty: 3,
						cost: {
							knowledge: 30,
							food: 20
						},
						prerequisites: ['agriculture'],
						effects: {
							food_production: 0.2,
							transportation_efficiency: 0.1
						}
					},
					medicine: {
						id: 'medicine',
						name: 'Medicine',
						description: 'Treatment and prevention of illness',
						category: 'health',
						tier: 3,
						researchTime: 25,
						difficulty: 4,
						cost: {
							knowledge: 50,
							food: 15
						},
						prerequisites: ['scientific_method'],
						effects: {
							character_health: 0.2,
							population_growth: 0.1
						}
					},
					mechanics: {
						id: 'mechanics',
						name: 'Mechanics',
						description: 'Understanding of forces and motion',
						category: 'engineering',
						tier: 3,
						researchTime: 22,
						difficulty: 4,
						cost: {
							knowledge: 55,
							materials: 25
						},
						prerequisites: ['scientific_method', 'metallurgy'],
						effects: {
							construction_efficiency: 0.2,
							tool_quality: 0.15
						}
					},
					philosophy: {
						id: 'philosophy',
						name: 'Philosophy',
						description: 'Systematic study of fundamental questions',
						category: 'knowledge',
						tier: 3,
						researchTime: 20,
						difficulty: 3,
						cost: {
							knowledge: 45
						},
						prerequisites: ['writing'],
						effects: {
							knowledge_production: 0.15,
							character_wisdom: 0.1
						}
					},
					education: {
						id: 'education',
						name: 'Education',
						description: 'Formal system of learning and teaching',
						category: 'knowledge',
						tier: 3,
						researchTime: 25,
						difficulty: 4,
						cost: {
							knowledge: 60,
							materials: 20
						},
						prerequisites: ['writing', 'philosophy'],
						effects: {
							knowledge_production: 0.2,
							character_intelligence: 0.15
						}
					}
				};
				
				// Internal state
				let researchedTechnologies = [];
				let currentResearch = null;
				let researchProgress = 0;
				let researchBonus = 1.0; // Multiplier for research speed
				
				// Initialize with existing research data
				function init(existingTechnologies) {
					researchedTechnologies = existingTechnologies || [];
					console.log(`Initialized TechnologyManager with ${researchedTechnologies.length} technologies`);
				}
				
				// Get available technologies that can be researched
				function getAvailableTechnologies(gameState) {
					const available = [];
					const researchedIds = researchedTechnologies.map(tech => tech.id);
					
					// For each technology
					for (const techId in technologies) {
						const tech = technologies[techId];
						
						// Skip if already researched
						if (researchedIds.includes(techId)) {
							continue;
						}
						
						// Check if prerequisites are met
						const prereqsMet = tech.prerequisites.every(prereq => researchedIds.includes(prereq));
						if (!prereqsMet) {
							continue;
						}
						
						// Check if tier is appropriate for current development level
						if (tech.tier > gameState.world.developmentLevel) {
							continue;
						}
						
						// Check if resources are sufficient
						const canAfford = ResourceManager.canAfford(tech.cost);
						
						// Add to available list with affordability flag
						available.push({
							...tech,
							canAfford
						});
					}
					
					return available;
				}
				
				// Start researching a technology
				function researchTechnology(techId, gameState) {
					// Check if already researching something
					if (currentResearch) {
						console.error(`Already researching ${currentResearch.name}`);
						return false;
					}
					
					const tech = technologies[techId];
					
					if (!tech) {
						console.error(`Unknown technology: ${techId}`);
						return false;
					}
					
					// Verify that prerequisites are met
					const researchedIds = researchedTechnologies.map(t => t.id);
					const prereqsMet = tech.prerequisites.every(prereq => researchedIds.includes(prereq));
					
					if (!prereqsMet) {
						console.error(`Prerequisites not met for ${tech.name}`);
						return false;
					}
					
					// Check if we can afford it
					if (!ResourceManager.canAfford(tech.cost)) {
						console.error(`Not enough resources to research ${tech.name}`);
						return false;
					}
					
					// Spend the resources
					if (!ResourceManager.spendResources(tech.cost)) {
						console.error(`Failed to spend resources for ${tech.name}`);
						return false;
					}
					
					// Start the research
					currentResearch = {
						...tech,
						startedTurn: gameState.turn
					};
					researchProgress = 0;
					
					// Trigger an event
					EventManager.addEvent({
						type: 'research',
						message: `Research on ${tech.name} has begun!`,
						details: `This technology will take approximately ${tech.researchTime} turns to complete.`,
						turn: gameState.turn
					});
					
					console.log(`Started research on ${tech.name}`);
					return true;
				}
				
				// Update research progress
				function updateTechnologies(gameState) {
					// If researching something, update progress
					if (currentResearch) {
						// Base progress is 1 per turn
						// Modified by research bonus from buildings, characters, etc.
						const progressIncrement = 1 * researchBonus;
						
						researchProgress += progressIncrement;
						
						// Check if research is complete
						if (researchProgress >= currentResearch.researchTime) {
							completeResearch(gameState);
						}
					}
					
					// Apply technology effects
					applyTechnologyEffects();
				}
				
				// Complete the current research
				function completeResearch(gameState) {
					if (!currentResearch) {
						console.error('No current research to complete');
						return false;
					}
					
					// Add to researched technologies
					researchedTechnologies.push({
						id: currentResearch.id,
						name: currentResearch.name,
						completedTurn: gameState.turn,
						effects: currentResearch.effects
					});
					
					// Apply immediate effects
					applyTechnologyEffects(currentResearch);
					
					// Trigger an event
					EventManager.addEvent({
						type: 'research',
						message: `Research on ${currentResearch.name} is complete!`,
						details: getEffectsDescription(currentResearch.effects),
						turn: gameState.turn
					});
					
					// Clear current research
					const completedResearch = currentResearch;
					currentResearch = null;
					researchProgress = 0;
					
					// Increase world development level if enough technologies are researched
					incrementDevelopmentLevel(gameState);
					
					console.log(`Completed research on ${completedResearch.name}`);
					return true;
				}
				
				// Format the effects of a technology for display
				function getEffectsDescription(effects) {
					if (!effects) return '';
					
					const descriptions = [];
					
					for (const effect in effects) {
						if (effect === 'enables') {
							const enabledItems = effects[effect].join(', ');
							descriptions.push(`Enables: ${enabledItems}`);
						} else {
							const value = effects[effect];
							if (typeof value === 'number') {
								// Format as percentage for decimal values
								const formatted = value >= 1 ? value : `${(value * 100).toFixed(0)}%`;
								descriptions.push(`${formatEffectName(effect)}: +${formatted}`);
							} else {
								descriptions.push(`${formatEffectName(effect)}: ${value}`);
							}
						}
					}
					
					return descriptions.join(', ');
				}
				
				// Format effect names for display
				function formatEffectName(effectKey) {
					// Convert snake_case to Title Case
					return effectKey
						.split('_')
						.map(word => word.charAt(0).toUpperCase() + word.slice(1))
						.join(' ');
				}
				
				// Apply effects from researched technologies
				function applyTechnologyEffects() {
					// Reset research bonus
					researchBonus = 1.0;
					
					// Apply all researched technology effects
					for (const tech of researchedTechnologies) {
						if (tech.effects) {
							// Apply research speed bonus
							if (tech.effects.research_speed) {
								researchBonus += tech.effects.research_speed;
							}
							
							// Other effects are applied in respective systems
							// e.g., production bonuses are used by ResourceManager
						}
					}
				}
				
				// Increment world development level if enough technologies are researched
				function incrementDevelopmentLevel(gameState) {
					const tier1Count = countResearchedByTier(1);
					const tier2Count = countResearchedByTier(2);
					const tier3Count = countResearchedByTier(3);
					
					// Development level is based on the number of technologies researched at each tier
					let newLevel = 1; // Base level
					
					if (tier1Count >= 3) newLevel = 2;
					if (tier1Count >= 5 && tier2Count >= 2) newLevel = 3;
					if (tier2Count >= 4 && tier3Count >= 1) newLevel = 4;
					if (tier3Count >= 3) newLevel = 5;
					
					// Update the world development level if it's higher
					if (newLevel > gameState.world.developmentLevel) {
						const oldLevel = gameState.world.developmentLevel;
						gameState.world.developmentLevel = newLevel;
						
						// Trigger an advancement event
						EventManager.addEvent({
							type: 'advancement',
							message: `Civilization has advanced to level ${newLevel}!`,
							details: `New technologies and possibilities are now available.`,
							turn: gameState.turn
						});
						
						console.log(`Advanced from level ${oldLevel} to ${newLevel}`);
					}
				}
				
				// Count researched technologies of a specific tier
				function countResearchedByTier(tier) {
					return researchedTechnologies.filter(tech => 
						technologies[tech.id] && technologies[tech.id].tier === tier
					).length;
				}
				
				// Check if a specific technology is researched
				function isTechnologyResearched(techId) {
					return researchedTechnologies.some(tech => tech.id === techId);
				}
				
				// Get researched technology objects
				function getResearchedTechnologies() {
					return [...researchedTechnologies];
				}
				
				// Get the current research status
				function getCurrentResearch() {
					if (!currentResearch) return null;
					
					return {
						...currentResearch,
						progress: researchProgress,
						percentage: Math.min(100, Math.floor((researchProgress / currentResearch.researchTime) * 100))
					};
				}
				
				// Set the research bonus (from buildings, events, etc.)
				function setResearchBonus(bonus) {
					researchBonus = bonus;
					return researchBonus;
				}
				
				// Generate a research breakthrough (random advancement)
				function generateBreakthrough(character) {
					// Get available technologies
					const gameState = window.AICivilizationGame?.getGameState();
					if (!gameState) return null;
					
					const availableTechs = getAvailableTechnologies(gameState);
					if (availableTechs.length === 0) return null;
					
					// Weight techs by character's intelligence and the tech's difficulty (inverse)
					const weightedTechs = availableTechs.map(tech => {
						// Higher intelligence favors more difficult techs
						const intelligenceBonus = (character.intelligence - 5) * 0.05;
						// Lower difficulty increases chance
						const difficultyFactor = 1 - (tech.difficulty * 0.1);
						
						return {
							tech,
							weight: 1 + intelligenceBonus + difficultyFactor
						};
					});
					
					// Choose a tech based on weights
					const totalWeight = weightedTechs.reduce((sum, item) => sum + item.weight, 0);
					let random = Math.random() * totalWeight;
					
					for (const item of weightedTechs) {
						random -= item.weight;
						if (random <= 0) {
							// Apply a breakthrough bonus to this tech
							const breakthroughFactor = 0.3 + ((character.intelligence / 10) * 0.2);
							const progressBoost = Math.floor(item.tech.researchTime * breakthroughFactor);
							
							// If we're already researching this tech, boost its progress
							if (currentResearch && currentResearch.id === item.tech.id) {
								researchProgress += progressBoost;
								
								return {
									type: 'progress_boost',
									tech: item.tech,
									boost: progressBoost,
									description: `${character.name} has made a significant breakthrough in ${item.tech.name} research, advancing progress by ${progressBoost} turns!`
								};
							}
							
							// Otherwise, return info about the potential tech
							return {
								type: 'new_insight',
								tech: item.tech,
								boost: progressBoost,
								description: `${character.name} has gained valuable insights that would significantly speed up research in ${item.tech.name}.`
							};
						}
					}
					
					// Fallback if something goes wrong
					return {
						type: 'general_insight',
						description: `${character.name} has made some interesting theoretical discoveries that may benefit future research.`
					};
				}
				
				// Analyze technology gaps
				function analyzeGaps() {
					const researchedIds = researchedTechnologies.map(tech => tech.id);
					const gaps = [];
					
					// Find key technologies that aren't researched
					if (!researchedIds.includes('agriculture')) gaps.push('agriculture');
					if (!researchedIds.includes('writing')) gaps.push('science');
					if (!researchedIds.includes('construction')) gaps.push('construction');
					if (!researchedIds.includes('trade')) gaps.push('trade');
					if (researchedIds.includes('writing') && !researchedIds.includes('scientific_method')) gaps.push('science');
					if (researchedIds.includes('agriculture') && !researchedIds.includes('animal_husbandry')) gaps.push('agriculture');
					
					return gaps;
				}
				
				// Return public API
				return {
					init,
					getAvailableTechnologies,
					researchTechnology,
					updateTechnologies,
					isTechnologyResearched,
					getResearchedTechnologies,
					getCurrentResearch,
					setResearchBonus,
					generateBreakthrough,
					analyzeGaps,
					getTechnologyDetails: (techId) => technologies[techId] || null
				};
			})();
			
			export { TechnologyManager };
		
</>
event-manager.js - Event System and Notifications
Click to open code

			// event-manager.js
			// Manages game events, notifications, and history
			
			// EventManager - Tracks and manages game events
			const EventManager = (function() {
				// Event types and their properties
				const eventTypes = {
					character: {
						icon: '👤',
						color: '#4682B4' // Steel Blue
					},
					research: {
						icon: '🔬',
						color: '#9370DB' // Medium Purple
					},
					construction: {
						icon: '🏗️',
						color: '#CD853F' // Peru
					},
					exploration: {
						icon: '🧭',
						color: '#2E8B57' // Sea Green
					},
					production: {
						icon: '⚒️',
						color: '#B8860B' // Dark Goldenrod
					},
					trade: {
						icon: '🔄',
						color: '#20B2AA' // Light Sea Green
					},
					interaction: {
						icon: '💬',
						color: '#6A5ACD' // Slate Blue
					},
					world: {
						icon: '🌍',
						color: '#3CB371' // Medium Sea Green
					},
					discovery: {
						icon: '💡',
						color: '#FFD700' // Gold
					},
					suggestion: {
						icon: '💭',
						color: '#9932CC' // Dark Orchid
					},
					advancement: {
						icon: '⭐',
						color: '#FF8C00' // Dark Orange
					},
					building: {
						icon: '🏢',
						color: '#8B4513' // Saddle Brown
					},
					community: {
						icon: '👪',
						color: '#DB7093' // Pale Violet Red
					},
					diplomatic: {
						icon: '🤝',
						color: '#4169E1' // Royal Blue
					}
				};
				
				// Internal state
				let events = [];
				let listeners = [];
				let currentTurn = 0;
				
				// Special events that can occur
				const specialEvents = {
					diplomatic: [
						{
							id: 'peaceful_traders',
							name: 'Peaceful Traders',
							description: 'A group of peaceful traders wishes to establish relations',
							outcomes: [
								{
									description: 'Traders share valuable resources',
									effect: (gameState, character) => {
										const resourceTypes = ['food', 'materials', 'knowledge'];
										const selectedType = resourceTypes[Math.floor(Math.random() * resourceTypes.length)];
										const amount = 15 + (character.level * 5);
										
										return {
											type: 'resource_gain',
											resourceType: selectedType,
											amount: amount,
											message: `The traders offered ${amount} ${selectedType} as a gesture of goodwill`
										};
									},
									weight: 3
								},
								{
									description: 'Traders offer knowledge exchange',
									effect: (gameState, character) => {
										const knowledgeGain = 20 + (character.intelligence * 2);
										
										return {
											type: 'knowledge_gain',
											amount: knowledgeGain,
											message: `The traders shared wisdom and information worth ${knowledgeGain} knowledge`
										};
									},
									weight: 2
								},
								{
									description: 'Permanent trade route established',
									effect: (gameState, character) => {
										const resourceGain = ['food', 'materials'];
										const amount = 3 + Math.floor(character.level / 2);
										
										return {
											type: 'trade_route',
											resources: resourceGain,
											amount: amount,
											message: `A permanent trade route has been established, providing ${amount} food and materials each turn`
										};
									},
									weight: 1
								}
							]
						},
						{
							id: 'traveling_scholars',
							name: 'Traveling Scholars',
							description: 'A group of scholars seeks shelter and wishes to share knowledge',
							outcomes: [
								{
									description: 'Scholars share research insights',
									effect: (gameState, character) => {
										const researchBoost = 5 + (character.intelligence);
										
										return {
											type: 'research_boost',
											amount: researchBoost,
											message: `The scholars have accelerated current research by ${researchBoost} turns`
										};
									},
									weight: 3
								},
								{
									description: 'Scholars join the community',
									effect: (gameState, character) => {
										return {
											type: 'knowledge_production',
											bonus: 0.15,
											duration: 20,
											message: `Some scholars have decided to stay, increasing knowledge production by 15% for 20 turns`
										};
									},
									weight: 2
								},
								{
									description: 'Technology breakthrough',
									effect: (gameState, character) => {
										return {
											type: 'tech_discovery',
											message: `The scholars have provided crucial insights into a new technology`
										};
									},
									weight: 1
								}
							]
						}
					],
					
					exploration: [
						{
							id: 'ancient_ruins',
							name: 'Ancient Ruins',
							description: 'The remains of an ancient civilization have been discovered',
							outcomes: [
								{
									description: 'Knowledge cache found',
									effect: (gameState, character) => {
										const knowledgeGain = 25 + (character.intelligence * 3);
										
										return {
											type: 'knowledge_gain',
											amount: knowledgeGain,
											message: `Ancient texts and artifacts have yielded ${knowledgeGain} knowledge`
										};
									},
									weight: 3
								},
								{
									description: 'Ancient technology discovered',
									effect: (gameState, character) => {
										return {
											type: 'tech_discovery',
											message: `Studying the ruins has revealed principles of an ancient technology`
										};
									},
									weight: 1
								},
								{
									description: 'Valuable resources uncovered',
									effect: (gameState, character) => {
										const materialsGain = 30 + (character.level * 5);
										
										return {
											type: 'resource_gain',
											resourceType: 'materials',
											amount: materialsGain,
											message: `Valuable ancient materials worth ${materialsGain} have been recovered`
										};
									},
									weight: 2
								}
							]
						},
						{
							id: 'natural_wonder',
							name: 'Natural Wonder',
							description: 'An extraordinary natural formation has been discovered',
							outcomes: [
								{
									description: 'Inspiration from natural beauty',
									effect: (gameState, character) => {
										return {
											type: 'character_boost',
											stat: 'creativity',
											amount: 1,
											message: `The wonder's beauty has inspired increased creativity in the community`
										};
									},
									weight: 2
								},
								{
									description: 'Rich natural resources',
									effect: (gameState, character) => {
										const foodGain = 20 + (character.level * 3);
										const materialsGain = 15 + (character.level * 2);
										
										return {
											type: 'multi_resource_gain',
											resources: {
												food: foodGain,
												materials: materialsGain
											},
											message: `The area surrounding the wonder is rich in resources, yielding ${foodGain} food and ${materialsGain} materials`
										};
									},
									weight: 3
								},
								{
									description: 'Scientific phenomenon',
									effect: (gameState, character) => {
										const knowledgeGain = 20 + (character.intelligence * 2);
										
										return {
											type: 'knowledge_gain',
											amount: knowledgeGain,
											message: `Studying the natural wonder has yielded ${knowledgeGain} knowledge`
										};
									},
									weight: 2
								}
							]
						}
					],
					
					luxury: [
						{
							id: 'exotic_goods',
							name: 'Exotic Goods',
							description: 'Rare and valuable items from distant lands',
							outcomes: [
								{
									description: 'Community morale boost',
									effect: (gameState, character) => {
										return {
											type: 'community_morale',
											bonus: 0.1,
											duration: 15,
											message: `The exotic goods have boosted community morale by 10% for 15 turns`
										};
									},
									weight: 3
								},
								{
									description: 'Valuable trade items',
									effect: (gameState, character) => {
										const wealthGain = 15 + (character.level * 3);
										
										return {
											type: 'resource_gain',
											resourceType: 'wealth',
											amount: wealthGain,
											message: `The exotic goods can be traded for ${wealthGain} wealth`
										};
									},
									weight: 2
		
Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
Aéius Cercle: Re-code again starting from event-manager.js
Claude: I'll recreate the event-manager.js file from scratch.
</>
event-manager.js - Event System and Notifications
Click to open code

		// event-manager.js
		// Manages game events, notifications, and history
		
		import { ResourceManager } from './resources/resource-manager.js';
		
		// EventManager - Tracks and manages game events
		const EventManager = (function() {
			// Event types and their properties
			const eventTypes = {
				character: {
					icon: '👤',
					color: '#4682B4' // Steel Blue
				},
				research: {
					icon: '🔬',
					color: '#9370DB' // Medium Purple
				},
				construction: {
					icon: '🏗️',
					color: '#CD853F' // Peru
				},
				exploration: {
					icon: '🧭',
					color: '#2E8B57' // Sea Green
				},
				production: {
					icon: '⚒️',
					color: '#B8860B' // Dark Goldenrod
				},
				trade: {
					icon: '🔄',
					color: '#20B2AA' // Light Sea Green
				},
				interaction: {
					icon: '💬',
					color: '#6A5ACD' // Slate Blue
				},
				world: {
					icon: '🌍',
					color: '#3CB371' // Medium Sea Green
				},
				discovery: {
					icon: '💡',
					color: '#FFD700' // Gold
				},
				suggestion: {
					icon: '💭',
					color: '#9932CC' // Dark Orchid
				},
				advancement: {
					icon: '⭐',
					color: '#FF8C00' // Dark Orange
				},
				building: {
					icon: '🏢',
					color: '#8B4513' // Saddle Brown
				},
				community: {
					icon: '👪',
					color: '#DB7093' // Pale Violet Red
				},
				diplomatic: {
					icon: '🤝',
					color: '#4169E1' // Royal Blue
				}
			};
			
			// Internal state
			let events = [];
			let listeners = [];
			let currentTurn = 0;
			
			// Special events that can occur
			const specialEvents = {
				diplomatic: [
					{
						id: 'peaceful_traders',
						name: 'Peaceful Traders',
						description: 'A group of peaceful traders wishes to establish relations',
						outcomes: [
							{
								description: 'Traders share valuable resources',
								effect: (gameState, character) => {
									const resourceTypes = ['food', 'materials', 'knowledge'];
									const selectedType = resourceTypes[Math.floor(Math.random() * resourceTypes.length)];
									const amount = 15 + (character.level * 5);
									
									return {
										type: 'resource_gain',
										resourceType: selectedType,
										amount: amount,
										message: `The traders offered ${amount} ${selectedType} as a gesture of goodwill`
									};
								},
								weight: 3
							},
							{
								description: 'Traders offer knowledge exchange',
								effect: (gameState, character) => {
									const knowledgeGain = 20 + (character.intelligence * 2);
									
									return {
										type: 'knowledge_gain',
										amount: knowledgeGain,
										message: `The traders shared wisdom and information worth ${knowledgeGain} knowledge`
									};
								},
								weight: 2
							},
							{
								description: 'Permanent trade route established',
								effect: (gameState, character) => {
									const resourceGain = ['food', 'materials'];
									const amount = 3 + Math.floor(character.level / 2);
									
									return {
										type: 'trade_route',
										resources: resourceGain,
										amount: amount,
										message: `A permanent trade route has been established, providing ${amount} food and materials each turn`
									};
								},
								weight: 1
							}
						]
					},
					{
						id: 'traveling_scholars',
						name: 'Traveling Scholars',
						description: 'A group of scholars seeks shelter and wishes to share knowledge',
						outcomes: [
							{
								description: 'Scholars share research insights',
								effect: (gameState, character) => {
									const researchBoost = 5 + (character.intelligence);
									
									return {
										type: 'research_boost',
										amount: researchBoost,
										message: `The scholars have accelerated current research by ${researchBoost} turns`
									};
								},
								weight: 3
							},
							{
								description: 'Scholars join the community',
								effect: (gameState, character) => {
									return {
										type: 'knowledge_production',
										bonus: 0.15,
										duration: 20,
										message: `Some scholars have decided to stay, increasing knowledge production by 15% for 20 turns`
									};
								},
								weight: 2
							},
							{
								description: 'Technology breakthrough',
								effect: (gameState, character) => {
									return {
										type: 'tech_discovery',
										message: `The scholars have provided crucial insights into a new technology`
									};
								},
								weight: 1
							}
						]
					}
				],
				
				exploration: [
					{
						id: 'ancient_ruins',
						name: 'Ancient Ruins',
						description: 'The remains of an ancient civilization have been discovered',
						outcomes: [
							{
								description: 'Knowledge cache found',
								effect: (gameState, character) => {
									const knowledgeGain = 25 + (character.intelligence * 3);
									
									return {
										type: 'knowledge_gain',
										amount: knowledgeGain,
										message: `Ancient texts and artifacts have yielded ${knowledgeGain} knowledge`
									};
								},
								weight: 3
							},
							{
								description: 'Ancient technology discovered',
								effect: (gameState, character) => {
									return {
										type: 'tech_discovery',
										message: `Studying the ruins has revealed principles of an ancient technology`
									};
								},
								weight: 1
							},
							{
								description: 'Valuable resources uncovered',
								effect: (gameState, character) => {
									const materialsGain = 30 + (character.level * 5);
									
									return {
										type: 'resource_gain',
										resourceType: 'materials',
										amount: materialsGain,
										message: `Valuable ancient materials worth ${materialsGain} have been recovered`
									};
								},
								weight: 2
							}
						]
					},
					{
						id: 'natural_wonder',
						name: 'Natural Wonder',
						description: 'An extraordinary natural formation has been discovered',
						outcomes: [
							{
								description: 'Inspiration from natural beauty',
								effect: (gameState, character) => {
									return {
										type: 'character_boost',
										stat: 'creativity',
										amount: 1,
										message: `The wonder's beauty has inspired increased creativity in the community`
									};
								},
								weight: 2
							},
							{
								description: 'Rich natural resources',
								effect: (gameState, character) => {
									const foodGain = 20 + (character.level * 3);
									const materialsGain = 15 + (character.level * 2);
									
									return {
										type: 'multi_resource_gain',
										resources: {
											food: foodGain,
											materials: materialsGain
										},
										message: `The area surrounding the wonder is rich in resources, yielding ${foodGain} food and ${materialsGain} materials`
									};
								},
								weight: 3
							},
							{
								description: 'Scientific phenomenon',
								effect: (gameState, character) => {
									const knowledgeGain = 20 + (character.intelligence * 2);
									
									return {
										type: 'knowledge_gain',
										amount: knowledgeGain,
										message: `Studying the natural wonder has yielded ${knowledgeGain} knowledge`
									};
								},
								weight: 2
							}
						]
					}
				],
				
				luxury: [
					{
						id: 'exotic_goods',
						name: 'Exotic Goods',
						description: 'Rare and valuable items from distant lands',
						outcomes: [
							{
								description: 'Community morale boost',
								effect: (gameState, character) => {
									return {
										type: 'community_morale',
										bonus: 0.1,
										duration: 15,
										message: `The exotic goods have boosted community morale by 10% for 15 turns`
									};
								},
								weight: 3
							},
							{
								description: 'Valuable trade items',
								effect: (gameState, character) => {
									const wealthGain = 15 + (character.level * 3);
									
									return {
										type: 'resource_gain',
										resourceType: 'wealth',
										amount: wealthGain,
										message: `The exotic goods can be traded for ${wealthGain} wealth`
									};
								},
								weight: 2
							},
							{
								description: 'Cultural exchange',
								effect: (gameState, character) => {
									return {
										type: 'character_boost',
										stat: 'intelligence',
										amount: 1,
										message: `The cultural exchange has increased intelligence in the community`
									};
								},
								weight: 1
							}
						]
					}
				]
			};
			
			// Initialize with existing events
			function init(existingEvents) {
				events = existingEvents || [];
				console.log(`Initialized EventManager with ${events.length} events`);
			}
			
			// Add a new event
			function addEvent(event) {
				// Generate a unique ID for the event
				event.id = `event_${Date.now()}_${Math.floor(Math.random() * 1000)}`;
				
				// Add event type information
				if (eventTypes[event.type]) {
					event.icon = eventTypes[event.type].icon;
					event.color = eventTypes[event.type].color;
				} else {
					event.icon = '📝';
					event.color = '#808080'; // Gray for unknown types
				}
				
				// Add timestamp
				event.timestamp = Date.now();
				
				// Add to events list
				events.push(event);
				
				// Notify listeners
				notifyListeners(event);
				
				// Update current turn if higher
				if (event.turn > currentTurn) {
					currentTurn = event.turn;
				}
				
				console.log(`Added event: ${event.message}`);
				return event.id;
			}
			
			// Notify event listeners
			function notifyListeners(event) {
				for (const listener of listeners) {
					try {
						listener(event);
					} catch (error) {
						console.error('Error in event listener:', error);
					}
				}
			}
			
			// Add an event listener
			function addEventListener(callback) {
				if (typeof callback === 'function') {
					listeners.push(callback);
					return listeners.length - 1; // Return the index for removal
				}
				return -1;
			}
			
			// Remove an event listener
			function removeEventListener(index) {
				if (index >= 0 && index < listeners.length) {
					listeners.splice(index, 1);
					return true;
				}
				return false;
			}
			
			// Get events for a specific turn
			function getEventsForTurn(turn) {
				return events.filter(event => event.turn === turn);
			}
			
			// Get recent events
			function getRecentEvents(count = 10) {
				// Sort by turn (descending) and timestamp (descending)
				const sortedEvents = [...events].sort((a, b) => {
					if (b.turn !== a.turn) return b.turn - a.turn;
					return b.timestamp - a.timestamp;
				});
				
				return sortedEvents.slice(0, count);
			}
			
			// Get events by type
			function getEventsByType(type, count = 10) {
				const filteredEvents = events.filter(event => event.type === type);
				
				// Sort by turn (descending) and timestamp (descending)
				filteredEvents.sort((a, b) => {
					if (b.turn !== a.turn) return b.turn - a.turn;
					return b.timestamp - a.timestamp;
				});
				
				return filteredEvents.slice(0, count);
			}
			
			// Generate random events based on game state
			function generateEvents(gameState) {
				// Only generate events with a certain probability
				if (Math.random() < 0.15) { // 15% chance each turn
					generateRandomEvent(gameState);
				}
				
				// Process ongoing events and remove expired ones
				processOngoingEvents(gameState);
			}
			
			// Process ongoing events
			function processOngoingEvents(gameState) {
				// Filter out expired events
				const activeEvents = events.filter(event => {
					// Check if the event has a duration and is still active
					if (event.duration && event.turn + event.duration < gameState.turn) {
						// Generate an event that the effect has ended
						addEvent({
							type: event.type,
							message: `${event.message} has ended`,
							details: 'The effect has worn off',
							turn: gameState.turn
						});
						
						return false; // Remove from active events
					}
					
					return true; // Keep in active events
				});
				
				// Update events list with active events
				events = activeEvents;
			}
			
			// Generate a random event
			function generateRandomEvent(gameState) {
				// Different types of random events with weights
				const eventOptions = [
					{ type: 'resource_discovery', weight: 3 },
					{ type: 'character_interaction', weight: 2 },
					{ type: 'weather_effect', weight: 2 },
					{ type: 'external_contact', weight: 1 }
				];
				
				// Calculate total weight
				const totalWeight = eventOptions.reduce((sum, option) => sum + option.weight, 0);
				
				// Select event type based on weights
				let random = Math.random() * totalWeight;
				let selectedType = eventOptions[0].type; // Default
				
				for (const option of eventOptions) {
					random -= option.weight;
					if (random <= 0) {
						selectedType = option.type;
						break;
					}
				}
				
				// Generate the event
				switch (selectedType) {
					case 'resource_discovery':
						generateResourceDiscoveryEvent(gameState);
						break;
					case 'character_interaction':
						generateCharacterInteractionEvent(gameState);
						break;
					case 'weather_effect':
						generateWeatherEvent(gameState);
						break;
					case 'external_contact':
						generateExternalContactEvent(gameState);
						break;
				}
			}
			
			// Generate a resource discovery event
			function generateResourceDiscoveryEvent(gameState) {
				const resourceTypes = ['food', 'materials', 'knowledge'];
				const selectedType = resourceTypes[Math.floor(Math.random() * resourceTypes.length)];
				
				// Amount based on development level
				const baseAmount = 5 + (gameState.world.developmentLevel * 3);
				const amount = baseAmount + Math.floor(Math.random() * baseAmount);
				
				// Add the resources
				ResourceManager.addResource(selectedType, amount);
				
				// Create the event
				addEvent({
					type: 'discovery',
					message: `Unexpected ${selectedType} discovery!`,
					details: `Your civilization has found ${amount} ${selectedType} in an unexpected location.`,
					turn: gameState.turn
				});
			}
			
			// Generate a character interaction event
			function generateCharacterInteractionEvent(gameState) {
				// Need at least 2 characters
				if (gameState.characters.length < 2) return;
				
				// Select two random characters
				const char1Index = Math.floor(Math.random() * gameState.characters.length);
				let char2Index;
				do {
					char2Index = Math.floor(Math.random() * gameState.characters.length);
				} while (char2Index === char1Index);
				
				const char1 = gameState.characters[char1Index];
				const char2 = gameState.characters[char2Index];
				
				// Possible interactions
				const interactions = [
					{
						type: 'friendship',
						message: `${char1.name} and ${char2.name} have formed a friendship`,
						details: 'Their relationship has improved, increasing cooperation.',
						effect: () => {
							// Increase relationship
							char1.relationships[char2.id] = Math.min(100, (char1.relationships[char2.id] || 50) + 10);
							char2.relationships[char1.id] = Math.min(100, (char2.relationships[char1.id] || 50) + 10);
							
							// Small productivity boost
							return {
								type: 'productivity',
								bonus: 0.05,
								duration: 5
							};
						},
						weight: 3
					},
					{
						type: 'disagreement',
						message: `${char1.name} and ${char2.name} have had a disagreement`,
						details: 'Their relationship has been strained, temporarily reducing cooperation.',
						effect: () => {
							// Decrease relationship
							char1.relationships[char2.id] = Math.max(20, (char1.relationships[char2.id] || 50) - 5);
							char2.relationships[char1.id] = Math.max(20, (char2.relationships[char1.id] || 50) - 5);
							
							// Small productivity decrease
							return {
								type: 'productivity',
								bonus: -0.03,
								duration: 3
							};
						},
						weight: 2
					},
					{
						type: 'collaboration',
						message: `${char1.name} and ${char2.name} have started collaborating on a project`,
						details: 'Their complementary skills have led to increased efficiency.',
						effect: () => {
							// Modestly increase relationship
							char1.relationships[char2.id] = Math.min(100, (char1.relationships[char2.id] || 50) + 5);
							char2.relationships[char1.id] = Math.min(100, (char2.relationships[char1.id] || 50) + 5);
							
							// Determine which resource gets boosted based on their roles
							let resourceType = 'knowledge';
							if (char1.role === 'builder' || char2.role === 'builder') {
								resourceType = 'materials';
							} else if (char1.role === 'farmer' || char2.role === 'farmer') {
								resourceType = 'food';
							}
							
							// Resource production boost
							return {
								type: 'production',
								resourceType: resourceType,
								bonus: 0.1,
								duration: 5
							};
						},
						weight: 4
					}
				];
				
				// Calculate total weight
				const totalWeight = interactions.reduce((sum, option) => sum + option.weight, 0);
				
				// Select interaction based on weights
				let random = Math.random() * totalWeight;
				let selectedInteraction = interactions[0]; // Default
				
				for (const interaction of interactions) {
					random -= interaction.weight;
					if (random <= 0) {
						selectedInteraction = interaction;
						break;
					}
				}
				
				// Apply the effect
				const effect = selectedInteraction.effect();
				
				// Create the event
				addEvent({
					type: 'interaction',
					message: selectedInteraction.message,
					details: selectedInteraction.details,
					turn: gameState.turn,
					effect: effect,
					duration: effect.duration
				});
			}
			
			// Generate a weather event
			function generateWeatherEvent(gameState) {
				// Possible weather events
				const weatherEvents = [
					{
						type: 'rain',
						message: 'Beneficial rainfall has nourished crops',
						details: 'The rain has increased food production for a short period.',
						effect: {
							type: 'production',
							resourceType: 'food',
							bonus: 0.15,
							duration: 3
						},
						weight: 3
					},
					{
						type: 'drought',
						message: 'A mild drought is affecting the region',
						details: 'The dry conditions have temporarily reduced food production.',
						effect: {
							type: 'production',
							resourceType: 'food',
							bonus: -0.1,
							duration: 4
						},
						weight: 2
					},
					{
						type: 'clear_weather',
						message: 'Perfect weather conditions for construction',
						details: 'The ideal conditions have increased building efficiency.',
						effect: {
							type: 'construction',
							bonus: 0.2,
							duration: 2
						},
						weight: 2
					},
					{
						type: 'storm',
						message: 'A storm has damaged some structures',
						details: 'Repairs will be needed, consuming some materials.',
						effect: {
							type: 'damage',
							resourceCost: {
								materials: 10
							}
						},
						weight: 1
					}
				];
				
				// Calculate total weight
				const totalWeight = weatherEvents.reduce((sum, option) => sum + option.weight, 0);
				
				// Select weather event based on weights
				let random = Math.random() * totalWeight;
				let selectedWeather = weatherEvents[0]; // Default
				
				for (const weather of weatherEvents) {
					random -= weather.weight;
					if (random <= 0) {
						selectedWeather = weather;
						break;
					}
				}
				
				// Apply immediate effects
				if (selectedWeather.effect.type === 'damage') {
					// Deduct resources for repairs
					for (const resource in selectedWeather.effect.resourceCost) {
						ResourceManager.removeResource(
							resource, 
							selectedWeather.effect.resourceCost[resource]
						);
					}
				}
				
				// Create the event
				addEvent({
					type: 'world',
					message: selectedWeather.message,
					details: selectedWeather.details,
					turn: gameState.turn,
					effect: selectedWeather.effect,
					duration: selectedWeather.effect.duration
				});
			}
			
			// Generate an external contact event
			function generateExternalContactEvent(gameState) {
				// Only generate these events after a certain development level
				if (gameState.world.developmentLevel < 2) return;
				
				// Possible external contact events
				const contactEvents = [
					{
						type: 'nomadic_tribe',
						message: 'A nomadic tribe has made contact',
						details: 'They offer to trade resources for knowledge.',
						options: [
							{
								text: 'Trade with the tribe',
								outcome: {
									positive: {
										type: 'resource_gain',
										resourceType: 'food',
										amount: 20,
										message: 'The tribe has shared their abundant food supplies.'
									},
									negative: {
										type: 'resource_loss',
										resourceType: 'knowledge',
										amount: 10,
										message: 'You have shared some of your knowledge with them.'
									}
								}
							},
							{
								text: 'Decline trade',
								outcome: {
									neutral: {
										message: 'The tribe moves on, slightly disappointed.'
									}
								}
							}
						],
						weight: 3
					},
					{
						type: 'wandering_merchant',
						message: 'A wandering merchant has arrived',
						details: 'They offer exotic goods in exchange for materials.',
						options: [
							{
								text: 'Trade with the merchant',
								outcome: {
									positive: {
										type: 'resource_gain',
										resourceType: 'knowledge',
										amount: 15,
										message: 'The merchant shares valuable information from their travels.'
									},
									negative: {
										type: 'resource_loss',
										resourceType: 'materials',
										amount: 15,
										message: 'You have traded away some materials.'
									}
								}
							},
							{
								text: 'Decline trade',
								outcome: {
									neutral: {
										message: 'The merchant leaves to find other trading partners.'
									}
								}
							}
						],
						weight: 2
					}
				];
				
				// Calculate total weight
				const totalWeight = contactEvents.reduce((sum, option) => sum + option.weight, 0);
				
				// Select contact event based on weights
				let random = Math.random() * totalWeight;
				let selectedContact = contactEvents[0]; // Default
				
				for (const contact of contactEvents) {
					random -= contact.weight;
					if (random <= 0) {
						selectedContact = contact;
						break;
					}
				}
				
				// For now, automatically choose the first option
				// In a full implementation, this would be presented to the player
				const selectedOption = selectedContact.options[0];
				
				// Apply outcomes
				if (selectedOption.outcome.positive) {
					if (selectedOption.outcome.positive.type === 'resource_gain') {
						ResourceManager.addResource(
							selectedOption.outcome.positive.resourceType,
							selectedOption.outcome.positive.amount
						);
					}
				}
				
				if (selectedOption.outcome.negative) {
					if (selectedOption.outcome.negative.type === 'resource_loss') {
						ResourceManager.removeResource(
							selectedOption.outcome.negative.resourceType,
							selectedOption.outcome.negative.amount
						);
					}
				}
				
				// Create the event
				addEvent({
					type: 'interaction',
					message: selectedContact.message,
					details: `${selectedContact.details} - ${selectedOption.text}: ${selectedOption.outcome.positive ? selectedOption.outcome.positive.message : ''} ${selectedOption.outcome.negative ? selectedOption.outcome.negative.message : ''}`,
					turn: gameState.turn
				});
			}
			
			// Generate a special event based on type
			function generateSpecialEvent(eventType, character, options = {}) {
				// Check if this event type exists
				if (!specialEvents[eventType]) {
					console.error(`Unknown special event type: ${eventType}`);
					return false;
				}
				
				// Get the game state
				const gameState = window.AICivilizationGame?.getGameState();
				if (!gameState) {
					console.error('Unable to get game state for special event');
					return false;
				}
				
				// Select a random event from this type
				const eventsList = specialEvents[eventType];
				const selectedEvent = eventsList[Math.floor(Math.random() * eventsList.length)];
				
				// Select an outcome based on weights
				const totalWeight = selectedEvent.outcomes.reduce((sum, outcome) => sum + outcome.weight, 0);
				let random = Math.random() * totalWeight;
				let selectedOutcome = selectedEvent.outcomes[0]; // Default
				
				for (const outcome of selectedEvent.outcomes) {
					random -= outcome.weight;
					if (random <= 0) {
						selectedOutcome = outcome;
						break;
					}
				}
				
				// Apply the effect
				const effect = selectedOutcome.effect(gameState, character);
				
				// Process the effect
				switch (effect.type) {
					case 'resource_gain':
						ResourceManager.addResource(effect.resourceType, effect.amount);
						break;
						
					case 'knowledge_gain':
						ResourceManager.addResource('knowledge', effect.amount);
						break;
						
					case 'multi_resource_gain':
						for (const resource in effect.resources) {
							ResourceManager.addResource(resource, effect.resources[resource]);
						}
						break;
						
					case 'trade_route':
						// Add an ongoing trade route
						ResourceManager.addTradeRoute({
							resources: effect.resources,
							amount: effect.amount,
							founder: character.id
						});
						break;
						
					case 'research_boost':
						// This would be handled by the TechnologyManager
						// Just create an event for now
						break;
						
					case 'tech_discovery':
						// This would be handled by the TechnologyManager
						// Just create an event for now
						break;
						
					case 'character_boost':
						// Boost a character stat
						character[effect.stat] += effect.amount;
						break;
						
					case 'knowledge_production':
					case 'community_morale':
						// These would be handled by their respective systems
						// Just create an event for now
						break;
				}
				
				// Create the event
				addEvent({
					type: eventType,
					message: `${selectedEvent.name} - ${selectedOutcome.description}`,
					details: effect.message,
					turn: gameState.turn,
					character: character.name,
					effect: effect
				});
				
				return true;
			}
			
			// Return public API
			return {
				init,
				addEvent,
				addEventListener,
				removeEventListener,
				getEventsForTurn,
				getRecentEvents,
				getEventsByType,
				generateEvents,
				generateSpecialEvent
			};
		})();
		
		export { EventManager };
	
</>
ui-manager.js - User Interface Management
Click to open code

		// ui-manager.js
		// Manages the game's user interface and DOM interactions
		
		// UIManager - Creates and updates UI elements
		const UIManager = (function() {
			// DOM element references
			let characterContainer = null;
			let resourcesContainer = null;
			let buildingsContainer = null;
			let technologiesContainer = null;
			let eventsContainer = null;
			let worldInfoContainer = null;
			
			// Initialize the UI
			function init() {
				console.log('Initializing UI Manager');
				
				// Create main game container if not exists
				createGameContainer();
				
				// Create or get UI containers
				characterContainer = document.getElementById('characters-container') || createContainer('characters-container', 'Characters');
				resourcesContainer = document.getElementById('resources-container') || createContainer('resources-container', 'Resources');
				buildingsContainer = document.getElementById('buildings-container') || createContainer('buildings-container', 'Buildings');
				technologiesContainer = document.getElementById('technologies-container') || createContainer('technologies-container', 'Technologies');
				eventsContainer = document.getElementById('events-container') || createContainer('events-container', 'Events');
				worldInfoContainer = document.getElementById('world-info-container') || createContainer('world-info-container', 'World');
				
				// Add event listeners for UI interactions
				setupEventListeners();
			}
			
			// Create the main game container
			function createGameContainer() {
				let gameContainer = document.getElementById('game-container');
				
				if (!
	
Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
Aéius Cercle: Re-code starting from ui-manager.js
Claude: I'll recreate the ui-manager.js file from scratch.
</>
ui-manager.js - User Interface Management
Click to open code

	// ui-manager.js
	// Manages the game's user interface and DOM interactions
	
	// UIManager - Creates and updates UI elements
	const UIManager = (function() {
		// DOM element references
		let characterContainer = null;
		let resourcesContainer = null;
		let buildingsContainer = null;
		let technologiesContainer = null;
		let eventsContainer = null;
		let worldInfoContainer = null;
		
		// Initialize the UI
		function init() {
			console.log('Initializing UI Manager');
			
			// Create main game container if not exists
			createGameContainer();
			
			// Create or get UI containers
			characterContainer = document.getElementById('characters-container') || createContainer('characters-container', 'Characters');
			resourcesContainer = document.getElementById('resources-container') || createContainer('resources-container', 'Resources');
			buildingsContainer = document.getElementById('buildings-container') || createContainer('buildings-container', 'Buildings');
			technologiesContainer = document.getElementById('technologies-container') || createContainer('technologies-container', 'Technologies');
			eventsContainer = document.getElementById('events-container') || createContainer('events-container', 'Events');
			worldInfoContainer = document.getElementById('world-info-container') || createContainer('world-info-container', 'World');
			
			// Add event listeners for UI interactions
			setupEventListeners();
		}
		
		// Create the main game container
		function createGameContainer() {
			let gameContainer = document.getElementById('game-container');
			
			if (!gameContainer) {
				gameContainer = document.createElement('div');
				gameContainer.id = 'game-container';
				gameContainer.className = 'game-container';
				
				// Create game layout
				const layout = `
					

AI Civilization Game

World Map

`; gameContainer.innerHTML = layout; document.body.appendChild(gameContainer); // Add basic styling addGameStyles(); } return gameContainer; } // Create a container for a UI section function createContainer(id, title) { const container = document.createElement('div'); container.id = id; container.className = 'container'; container.innerHTML = `

${title}

`; return container; } // Add basic CSS styles for the game function addGameStyles() { const styleElement = document.createElement('style'); styleElement.type = 'text/css'; const styles = ` .game-container { font-family: Arial, sans-serif; color: #333; max-width: 1200px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .game-header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 10px; border-bottom: 1px solid #ddd; margin-bottom: 20px; } .game-content { display: grid; grid-template-columns: 250px 1fr 250px; gap: 20px; margin-bottom: 20px; } .left-panel, .right-panel { display: flex; flex-direction: column; gap: 20px; } .panel { background-color: white; border-radius: 5px; box-shadow: 0 1px 5px rgba(0, 0, 0, 0.1); padding: 15px; } .panel h2 { margin-top: 0; border-bottom: 1px solid #eee; padding-bottom: 10px; font-size: 18px; } .container-content { max-height: 300px; overflow-y: auto; } #world-map { border: 1px solid #ccc; background-color: #eaf6ff; display: block; margin: 10px 0; max-width: 100%; } #map-controls { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 10px; } #map-controls button { width: 40px; height: 40px; border-radius: 5px; border: 1px solid #ccc; background-color: white; cursor: pointer; font-size: 16px; } #map-controls button:hover { background-color: #f0f0f0; } .game-footer { display: flex; justify-content: center; gap: 20px; padding-top: 10px; border-top: 1px solid #ddd; } .game-footer button { padding: 10px 20px; border-radius: 5px; border: none; background-color: #4c84ff; color: white; font-weight: bold; cursor: pointer; transition: background-color 0.2s; } .game-footer button:hover { background-color: #3a66cc; } .character-card, .building-card, .technology-card { background-color: #f9f9f9; border: 1px solid #eee; border-radius: 5px; padding: 10px; margin-bottom: 10px; } .character-card h3, .building-card h3, .technology-card h3 { margin-top: 0; font-size: 16px; } .resource-item { display: flex; justify-content: space-between; margin-bottom: 5px; } .event-item { padding: 8px; border-left: 4px solid #4c84ff; background-color: #f5f9ff; margin-bottom: 8px; font-size: 14px; } .event-header { display: flex; justify-content: space-between; margin-bottom: 5px; font-weight: bold; } `; styleElement.appendChild(document.createTextNode(styles)); document.head.appendChild(styleElement); } // Setup event listeners for UI interactions function setupEventListeners() { // Next turn button const nextTurnBtn = document.getElementById('next-turn-btn'); if (nextTurnBtn) { nextTurnBtn.addEventListener('click', () => { // Get game instance and advance turn const game = window.AICivilizationGame; if (game) { // This would normally call a method on the game object // For now, log to console console.log('Next turn button clicked'); } }); } // Save game button const saveGameBtn = document.getElementById('save-game-btn'); if (saveGameBtn) { saveGameBtn.addEventListener('click', () => { const game = window.AICivilizationGame; if (game) { // This would normally save the game console.log('Save game button clicked'); } }); } // Map controls setupMapControls(); // Listen for region clicks document.addEventListener('region-clicked', (event) => { const regionData = event.detail; displayRegionDetails(regionData.region); }); } // Setup map control buttons function setupMapControls() { const zoomInBtn = document.getElementById('map-zoom-in'); const zoomOutBtn = document.getElementById('map-zoom-out'); const panUpBtn = document.getElementById('map-pan-up'); const panDownBtn = document.getElementById('map-pan-down'); const panLeftBtn = document.getElementById('map-pan-left'); const panRightBtn = document.getElementById('map-pan-right'); // Import WorldRenderer if available in global scope const WorldRenderer = window.WorldRenderer; if (zoomInBtn && WorldRenderer) { zoomInBtn.addEventListener('click', () => { // Zoom in logic console.log('Zoom in clicked'); }); } if (zoomOutBtn && WorldRenderer) { zoomOutBtn.addEventListener('click', () => { // Zoom out logic console.log('Zoom out clicked'); }); } if (panUpBtn && WorldRenderer) { panUpBtn.addEventListener('click', () => { WorldRenderer.panMap(0, -1); }); } if (panDownBtn && WorldRenderer) { panDownBtn.addEventListener('click', () => { WorldRenderer.panMap(0, 1); }); } if (panLeftBtn && WorldRenderer) { panLeftBtn.addEventListener('click', () => { WorldRenderer.panMap(-1, 0); }); } if (panRightBtn && WorldRenderer) { panRightBtn.addEventListener('click', () => { WorldRenderer.panMap(1, 0); }); } } // Display details for a clicked region function displayRegionDetails(region) { if (!region) return; // Create a modal or update a details panel let detailPanel = document.getElementById('region-detail-panel'); if (!detailPanel) { detailPanel = document.createElement('div'); detailPanel.id = 'region-detail-panel'; detailPanel.className = 'modal'; const gameContainer = document.getElementById('game-container'); if (gameContainer) { gameContainer.appendChild(detailPanel); } else { document.body.appendChild(detailPanel); } } // Generate HTML content for the region details const content = ` `; detailPanel.innerHTML = content; detailPanel.style.display = 'block'; // Add event listeners for modal buttons const closeButtons = detailPanel.querySelectorAll('.close-btn, .close-modal'); closeButtons.forEach(btn => { btn.addEventListener('click', () => { detailPanel.style.display = 'none'; }); }); // Center map button const centerButton = detailPanel.querySelector('.center-on-region'); if (centerButton) { centerButton.addEventListener('click', () => { const WorldRenderer = window.WorldRenderer; if (WorldRenderer) { WorldRenderer.centerOnRegion(region.id); } }); } // Explore button const exploreButton = detailPanel.querySelector('.explore-btn'); if (exploreButton) { exploreButton.addEventListener('click', () => { const regionId = exploreButton.getAttribute('data-region-id'); console.log(`Exploring region ${regionId}`); // This would call into the game logic to explore the region }); } } // Get color for terrain visualization function getTerrainColor(terrainType) { const colors = { plains: '#9ACD32', forest: '#228B22', mountain: '#808080', river: '#4682B4', lake: '#6495ED', desert: '#F0E68C', hills: '#CD853F' }; return colors[terrainType] || '#7CFC00'; } // Render characters to the UI function renderCharacters(characters) { if (!characterContainer) return; const contentDiv = characterContainer.querySelector('.container-content') || characterContainer; // Clear existing content contentDiv.innerHTML = ''; // Create character cards characters.forEach(character => { const characterCard = document.createElement('div'); characterCard.className = 'character-card'; characterCard.setAttribute('data-id', character.id); // Character task status const taskStatus = character.currentTask ? `
Current Task: ${character.currentTask.name}
${Math.round((character.currentTask.progress / character.currentTask.duration) * 100)}%
` : '
Idle
'; characterCard.innerHTML = `

${character.name}

${character.role}
Intelligence: ${character.intelligence}
Cooperation: ${character.cooperation}
Creativity: ${character.creativity}
Level ${character.level}
${taskStatus}
`; contentDiv.appendChild(characterCard); // Add click event for task assignment const assignTaskBtn = characterCard.querySelector('.assign-task-btn'); if (assignTaskBtn) { assignTaskBtn.addEventListener('click', () => { showTaskAssignmentDialog(character); }); } // Add click event for character details const detailsBtn = characterCard.querySelector('.character-details-btn'); if (detailsBtn) { detailsBtn.addEventListener('click', () => { showCharacterDetailsDialog(character); }); } }); } // Render resources to the UI function renderResources(resources) { if (!resourcesContainer) return; const contentDiv = resourcesContainer.querySelector('.container-content') || resourcesContainer; // Clear existing content contentDiv.innerHTML = '
'; const resourcesList = contentDiv.querySelector('.resources-list'); // Create resource items for (const resource in resources) { const resourceItem = document.createElement('div'); resourceItem.className = 'resource-item'; resourceItem.innerHTML = ` ${resource} ${resources[resource]} `; resourcesList.appendChild(resourceItem); } } // Render buildings to the UI function renderBuildings(buildings) { if (!buildingsContainer) return; const contentDiv = buildingsContainer.querySelector('.container-content') || buildingsContainer; // Clear existing content contentDiv.innerHTML = ''; // Create building cards buildings.forEach(building => { const buildingCard = document.createElement('div'); buildingCard.className = 'building-card'; buildingCard.setAttribute('data-id', building.id); buildingCard.innerHTML = `

${building.name}

Location: ${building.regionId}
Efficiency: ${Math.round(building.efficiency * 100)}%
Condition: ${Math.round(building.condition)}%
`; contentDiv.appendChild(buildingCard); // Add click event for repair const repairBtn = buildingCard.querySelector('.repair-building-btn'); if (repairBtn) { repairBtn.addEventListener('click', () => { // Call building repair function console.log(`Repair building ${building.id}`); }); } // Add click event for building details const detailsBtn = buildingCard.querySelector('.building-details-btn'); if (detailsBtn) { detailsBtn.addEventListener('click', () => { showBuildingDetailsDialog(building); }); } }); } // Render technologies to the UI function renderTechnologies(technologies) { if (!technologiesContainer) return; const contentDiv = technologiesContainer.querySelector('.container-content') || technologiesContainer; // Clear existing content contentDiv.innerHTML = ''; // Current research const gameState = window.AICivilizationGame?.getGameState(); const currentResearch = gameState?.currentResearch; if (currentResearch) { const researchElement = document.createElement('div'); researchElement.className = 'current-research'; researchElement.innerHTML = `

Researching: ${currentResearch.name}

${Math.round((currentResearch.progress / currentResearch.researchTime) * 100)}% complete
`; contentDiv.appendChild(researchElement); } // Create technology cards technologies.forEach(tech => { const techCard = document.createElement('div'); techCard.className = 'technology-card'; techCard.setAttribute('data-id', tech.id); techCard.innerHTML = `

${tech.name}

Researched on turn ${tech.completedTurn}
${ tech.effects ? Object.entries(tech.effects) .map(([effect, value]) => `
${effect.replace(/_/g, ' ')}: ${typeof value === 'number' ? (value > 0 ? '+' : '') + (value * 100) + '%' : value}
`).join('') : '' }
`; contentDiv.appendChild(techCard); }); // Add Research button const researchBtn = document.createElement('button'); researchBtn.className = 'research-btn'; researchBtn.textContent = 'Research New Technology'; researchBtn.addEventListener('click', () => { showResearchDialog(); }); contentDiv.appendChild(researchBtn); } // Render events to the UI function renderEvents(events) { if (!eventsContainer) return; const contentDiv = eventsContainer.querySelector('.container-content') || eventsContainer; // Clear existing content contentDiv.innerHTML = ''; // Create event items events.forEach(event => { const eventItem = document.createElement('div'); eventItem.className = 'event-item'; eventItem.style.borderLeftColor = event.color || '#4c84ff'; eventItem.innerHTML = `
${event.icon || '📝'} ${event.message} Turn ${event.turn}
${event.details || ''}
`; contentDiv.appendChild(eventItem); }); } // Show dialog for assigning tasks to characters function showTaskAssignmentDialog(character) { // Create a modal dialog const modalDialog = document.createElement('div'); modalDialog.className = 'modal'; modalDialog.id = 'task-assignment-modal'; // Get available tasks for this character const gameState = window.AICivilizationGame?.getGameState(); const CharacterTasks = window.CharacterTasks; let availableTasks = []; if (gameState && CharacterTasks) { availableTasks = CharacterTasks.getAvailableTasks(character, gameState); } // Group tasks by category const tasksByCategory = {}; availableTasks.forEach(task => { if (!tasksByCategory[task.category]) { tasksByCategory[task.category] = []; } tasksByCategory[task.category].push(task); }); // Generate dialog content modalDialog.innerHTML = ` `; // Add to document document.body.appendChild(modalDialog); // Add event listeners const closeButtons = modalDialog.querySelectorAll('.close-btn, .close-modal'); closeButtons.forEach(btn => { btn.addEventListener('click', () => { document.body.removeChild(modalDialog); }); }); // Add event listeners for task assignment const assignButtons = modalDialog.querySelectorAll('.assign-task'); assignButtons.forEach(btn => { btn.addEventListener('click', () => { const taskId = btn.getAttribute('data-task-id'); console.log(`Assigning task ${taskId} to character ${character.id}`); // Here you would call the game logic to assign the task document.body.removeChild(modalDialog); }); }); } // Show character details dialog function showCharacterDetailsDialog(character) { // Create a modal dialog const modalDialog = document.createElement('div'); modalDialog.className = 'modal'; modalDialog.id = 'character-details-modal'; // Generate relationships display let relationshipsHtml = '

No relationships established yet.

'; if (character.relationships && Object.keys(character.relationships).length > 0) { const gameState = window.AICivilizationGame?.getGameState(); const characters = gameState?.characters || []; relationshipsHtml = `
${ Object.entries(character.relationships).map(([charId, value]) => { const relatedChar = characters.find(c => c.id === charId); if (!relatedChar) return '';
Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
Aéius Cercle: Sorry, looks like the ui-manager.js file is too long/big; re-code it with more modularisation and split it apart into smaller file-sizes; let's keep all file-sizes from here onwards small enough so as to not go over 9000 characters. OK?
Claude: You're right, the ui-manager.js file is too large. I'll split it into smaller modules with proper organization. Let's create a modular UI system where each component is in its own file and stays under 9,000 characters.
</>
ui-manager.js - Main UI Manager Module
Click to open code

	// ui-manager.js
	// Main module for the UI Manager system
	
	import { UIInitializer } from './ui/ui-initializer.js';
	import { UICharacterRenderer } from './ui/ui-character-renderer.js';
	import { UIResourceRenderer } from './ui/ui-resource-renderer.js';
	import { UIBuildingRenderer } from './ui/ui-building-renderer.js';
	import { UITechnologyRenderer } from './ui/ui-technology-renderer.js';
	import { UIEventRenderer } from './ui/ui-event-renderer.js';
	import { UIWorldRenderer } from './ui/ui-world-renderer.js';
	import { UIModalManager } from './ui/ui-modal-manager.js';
	
	// UIManager - Main facade for the UI system
	const UIManager = (function() {
		// Initialize the UI
		function init() {
			console.log('Initializing UI Manager');
			
			// Initialize the UI structure
			UIInitializer.init();
			
			// Set up event listeners
			setupEventListeners();
		}
		
		// Set up global event listeners
		function setupEventListeners() {
			// Listen for region clicks
			document.addEventListener('region-clicked', (event) => {
				const regionData = event.detail;
				UIWorldRenderer.displayRegionDetails(regionData.region);
			});
			
			// Next turn button
			const nextTurnBtn = document.getElementById('next-turn-btn');
			if (nextTurnBtn) {
				nextTurnBtn.addEventListener('click', () => {
					// Get game instance and advance turn
					const game = window.AICivilizationGame;
					if (game) {
						game.advanceTurn();
					}
				});
			}
			
			// Save game button
			const saveGameBtn = document.getElementById('save-game-btn');
			if (saveGameBtn) {
				saveGameBtn.addEventListener('click', () => {
					const game = window.AICivilizationGame;
					if (game) {
						game.saveGame();
					}
				});
			}
		}
		
		// Render all UI components
		function renderAll(gameState) {
			if (!gameState) return;
			
			// Update turn info
			updateTurnInfo(gameState.turn);
			
			// Render each component
			UICharacterRenderer.renderCharacters(gameState.characters);
			UIResourceRenderer.renderResources(gameState.resources);
			UIBuildingRenderer.renderBuildings(gameState.buildings);
			UITechnologyRenderer.renderTechnologies(gameState.technologies, gameState.currentResearch);
			UIEventRenderer.renderEvents(gameState.events.slice(-5)); // Show latest 5 events
			UIWorldRenderer.updateWorldInfo(gameState.world);
		}
		
		// Update turn information
		function updateTurnInfo(turn) {
			const turnInfoElement = document.getElementById('turn-info');
			if (turnInfoElement) {
				turnInfoElement.textContent = `Turn: ${turn}`;
			}
		}
		
		// Show a notification message
		function showNotification(message, type = 'info') {
			const notification = document.createElement('div');
			notification.className = `notification ${type}`;
			notification.innerHTML = `
				
${message} ×
`; // Add to document const notificationContainer = document.getElementById('notification-container') || createNotificationContainer(); notificationContainer.appendChild(notification); // Add close button functionality const closeBtn = notification.querySelector('.notification-close'); if (closeBtn) { closeBtn.addEventListener('click', () => { notification.classList.add('fade-out'); setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); }); } // Auto-remove after 5 seconds setTimeout(() => { notification.classList.add('fade-out'); setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 300); }, 5000); } // Create notification container if it doesn't exist function createNotificationContainer() { const container = document.createElement('div'); container.id = 'notification-container'; document.body.appendChild(container); return container; } // Expose public API return { init, renderCharacters: UICharacterRenderer.renderCharacters, renderResources: UIResourceRenderer.renderResources, renderBuildings: UIBuildingRenderer.renderBuildings, renderTechnologies: UITechnologyRenderer.renderTechnologies, renderEvents: UIEventRenderer.renderEvents, renderAll, showNotification, showModal: UIModalManager.showModal, closeModal: UIModalManager.closeModal }; })(); export { UIManager };
</>
ui-initializer.js - UI Initialization and Structure
Click to open code

	// ui-initializer.js
	// Handles creating and setting up the initial UI structure
	
	// UIInitializer - Creates the base structure for the UI
	const UIInitializer = (function() {
		// Initialize the UI structure
		function init() {
			console.log('Initializing UI structure');
			
			// Create main game container if not exists
			createGameContainer();
			
			// Add game styles
			addGameStyles();
		}
		
		// Create the main game container and structure
		function createGameContainer() {
			let gameContainer = document.getElementById('game-container');
			
			if (!gameContainer) {
				gameContainer = document.createElement('div');
				gameContainer.id = 'game-container';
				gameContainer.className = 'game-container';
				
				// Create game layout
				const layout = `
					

AI Civilization Game

Turn: 0

World

Resources

Characters

World Map

Buildings

Technologies

Events

`; gameContainer.innerHTML = layout; document.body.appendChild(gameContainer); } return gameContainer; } // Add basic CSS styles for the game function addGameStyles() { // Check if styles already exist if (document.getElementById('ai-civ-game-styles')) { return; } const styleElement = document.createElement('style'); styleElement.id = 'ai-civ-game-styles'; styleElement.type = 'text/css'; const styles = ` .game-container { font-family: Arial, sans-serif; color: #333; max-width: 1200px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .game-header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 10px; border-bottom: 1px solid #ddd; margin-bottom: 20px; } .game-content { display: grid; grid-template-columns: 250px 1fr 250px; gap: 20px; margin-bottom: 20px; } .left-panel, .right-panel { display: flex; flex-direction: column; gap: 20px; } .panel { background-color: white; border-radius: 5px; box-shadow: 0 1px 5px rgba(0, 0, 0, 0.1); padding: 15px; } .panel h2 { margin-top: 0; border-bottom: 1px solid #eee; padding-bottom: 10px; font-size: 18px; } .container-content { max-height: 300px; overflow-y: auto; } #world-map { border: 1px solid #ccc; background-color: #eaf6ff; display: block; margin: 10px 0; max-width: 100%; } #map-controls { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 10px; } #map-controls button { width: 40px; height: 40px; border-radius: 5px; border: 1px solid #ccc; background-color: white; cursor: pointer; font-size: 16px; } #map-controls button:hover { background-color: #f0f0f0; } .game-footer { display: flex; justify-content: center; gap: 20px; padding-top: 10px; border-top: 1px solid #ddd; } .game-footer button { padding: 10px 20px; border-radius: 5px; border: none; background-color: #4c84ff; color: white; font-weight: bold; cursor: pointer; transition: background-color 0.2s; } .game-footer button:hover { background-color: #3a66cc; } /* Cards and items styles */ .character-card, .building-card, .technology-card { background-color: #f9f9f9; border: 1px solid #eee; border-radius: 5px; padding: 10px; margin-bottom: 10px; } .character-card h3, .building-card h3, .technology-card h3 { margin-top: 0; font-size: 16px; } .resource-item { display: flex; justify-content: space-between; margin-bottom: 5px; padding: 5px; border-bottom: 1px solid #eee; } .event-item { padding: 8px; border-left: 4px solid #4c84ff; background-color: #f5f9ff; margin-bottom: 8px; font-size: 14px; } .event-header { display: flex; justify-content: space-between; margin-bottom: 5px; font-weight: bold; } /* Progress bars */ .progress-bar { height: 8px; background-color: #eee; border-radius: 4px; overflow: hidden; margin: 5px 0; } .progress-fill { height: 100%; background-color: #4c84ff; border-radius: 4px; } /* Modal dialog styles */ .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 1000; overflow-y: auto; } .modal-content { background-color: white; margin: 50px auto; padding: 20px; border-radius: 8px; width: 80%; max-width: 800px; position: relative; max-height: 80vh; overflow-y: auto; } .close-btn { position: absolute; top: 10px; right: 15px; font-size: 24px; cursor: pointer; } .modal-actions { margin-top: 20px; display: flex; justify-content: flex-end; gap: 10px; } .modal-actions button { padding: 8px 16px; border-radius: 4px; border: none; cursor: pointer; } .modal-actions button.primary { background-color: #4c84ff; color: white; } .modal-actions button:not(.primary) { background-color: #f0f0f0; color: #333; } /* Notification styles */ #notification-container { position: fixed; top: 20px; right: 20px; z-index: 1100; display: flex; flex-direction: column; gap: 10px; } .notification { background-color: white; border-radius: 5px; padding: 15px; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2); max-width: 300px; animation: slide-in 0.3s ease-out; } .notification.info { border-left: 4px solid #4c84ff; } .notification.success { border-left: 4px solid #4CAF50; } .notification.warning { border-left: 4px solid #FF9800; } .notification.error { border-left: 4px solid #F44336; } .notification-content { display: flex; justify-content: space-between; align-items: center; } .notification-close { cursor: pointer; font-size: 18px; margin-left: 10px; } .notification.fade-out { animation: fade-out 0.3s ease-out forwards; } @keyframes slide-in { 0% { transform: translateX(100%); opacity: 0; } 100% { transform: translateX(0); opacity: 1; } } @keyframes fade-out { 0% { opacity: 1; } 100% { opacity: 0; } } `; styleElement.appendChild(document.createTextNode(styles)); document.head.appendChild(styleElement); } // Create a container for a UI section (utility function) function createContainer(id, title) { const container = document.createElement('div'); container.id = id; container.className = 'panel'; container.innerHTML = `

${title}

`; return container; } // Expose the public API return { init, createContainer }; })(); export { UIInitializer };
</>
ui-character-renderer.js - Character UI Rendering
Click to open code

	// ui-initializer.js
	// Handles creating and setting up the initial UI structure
	
	// UIInitializer - Creates the base structure for the UI
	const UIInitializer = (function() {
		// Initialize the UI structure
		function init() {
			console.log('Initializing UI structure');
			
			// Create main game container if not exists
			createGameContainer();
			
			// Add game styles
			addGameStyles();
		}
		
		// Create the main game container and structure
		function createGameContainer() {
			let gameContainer = document.getElementById('game-container');
			
			if (!gameContainer) {
				gameContainer = document.createElement('div');
				gameContainer.id = 'game-container';
				gameContainer.className = 'game-container';
				
				// Create game layout
				const layout = `
					

AI Civilization Game

Turn: 0

World

Resources

Characters

World Map

Buildings

Technologies

Events

`; gameContainer.innerHTML = layout; document.body.appendChild(gameContainer); } return gameContainer; } // Add basic CSS styles for the game function addGameStyles() { // Check if styles already exist if (document.getElementById('ai-civ-game-styles')) { return; } const styleElement = document.createElement('style'); styleElement.id = 'ai-civ-game-styles'; styleElement.type = 'text/css'; const styles = ` .game-container { font-family: Arial, sans-serif; color: #333; max-width: 1200px; margin: 0 auto; padding: 20px; background-color: #f5f5f5; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); } .game-header { display: flex; justify-content: space-between; align-items: center; padding-bottom: 10px; border-bottom: 1px solid #ddd; margin-bottom: 20px; } .game-content { display: grid; grid-template-columns: 250px 1fr 250px; gap: 20px; margin-bottom: 20px; } .left-panel, .right-panel { display: flex; flex-direction: column; gap: 20px; } .panel { background-color: white; border-radius: 5px; box-shadow: 0 1px 5px rgba(0, 0, 0, 0.1); padding: 15px; } .panel h2 { margin-top: 0; border-bottom: 1px solid #eee; padding-bottom: 10px; font-size: 18px; } .container-content { max-height: 300px; overflow-y: auto; } #world-map { border: 1px solid #ccc; background-color: #eaf6ff; display: block; margin: 10px 0; max-width: 100%; } #map-controls { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 10px; } #map-controls button { width: 40px; height: 40px; border-radius: 5px; border: 1px solid #ccc; background-color: white; cursor: pointer; font-size: 16px; } #map-controls button:hover { background-color: #f0f0f0; } .game-footer { display: flex; justify-content: center; gap: 20px; padding-top: 10px; border-top: 1px solid #ddd; } .game-footer button { padding: 10px 20px; border-radius: 5px; border: none; background-color: #4c84ff; color: white; font-weight: bold; cursor: pointer; transition: background-color 0.2s; } .game-footer button:hover { background-color: #3a66cc; } /* Cards and items styles */ .character-card, .building-card, .technology-card { background-color: #f9f9f9; border: 1px solid #eee; border-radius: 5px; padding: 10px; margin-bottom: 10px; } .character-card h3, .building-card h3, .technology-card h3 { margin-top: 0; font-size: 16px; } .resource-item { display: flex; justify-content: space-between; margin-bottom: 5px; padding: 5px; border-bottom: 1px solid #eee; } .event-item { padding: 8px; border-left: 4px solid #4c84ff; background-color: #f5f9ff; margin-bottom: 8px; font-size: 14px; } .event-header { display: flex; justify-content: space-between; margin-bottom: 5px; font-weight: bold; } /* Progress bars */ .progress-bar { height: 8px; background-color: #eee; border-radius: 4px; overflow: hidden; margin: 5px 0; } .progress-fill { height: 100%; background-color: #4c84ff; border-radius: 4px; } /* Modal dialog styles */ .modal { display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 1000; overflow-y: auto; } .modal-content { background-color: white; margin: 50px auto; padding: 20px; border-radius: 8px; width: 80%; max-width: 800px; position: relative; max-height: 80vh; overflow-y: auto; } .close-btn { position: absolute; top: 10px; right: 15px; font-size: 24px; cursor: pointer; } .modal-actions { margin-top: 20px; display: flex; justify-content: flex-end; gap: 10px; } .modal-actions button { padding: 8px 16px; border-radius: 4px; border: none; cursor: pointer; } .modal-actions button.primary { background-color: #4c84ff; color: white; } .modal-actions button:not(.primary) { background-color: #f0f0f0; color: #333; } /* Notification styles */ #notification-container { position: fixed; top: 20px; right: 20px; z-index: 1100; display: flex; flex-direction: column; gap: 10px; } .notification { background-color: white; border-radius: 5px; padding: 15px; box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2); max-width: 300px; animation: slide-in 0.3s ease-out; } .notification.info { border-left: 4px solid #4c84ff; } .notification.success { border-left: 4px solid #4CAF50; } .notification.warning { border-left: 4px solid #FF9800; } .notification.error { border-left: 4px solid #F44336; } .notification-content { display: flex; justify-content: space-between; align-items: center; } .notification-close { cursor: pointer; font-size: 18px; margin-left: 10px; } .notification.fade-out { animation: fade-out 0.3s ease-out forwards; } @keyframes slide-in { 0% { transform: translateX(100%); opacity: 0; } 100% { transform: translateX(0); opacity: 1; } } @keyframes fade-out { 0% { opacity: 1; } 100% { opacity: 0; } } `; styleElement.appendChild(document.createTextNode(styles)); document.head.appendChild(styleElement); } // Create a container for a UI section (utility function) function createContainer(id, title) { const container = document.createElement('div'); container.id = id; container.className = 'panel'; container.innerHTML = `

${title}

`; return container; } // Expose the public API return { init, createContainer }; })(); export { UIInitializer };
</>
ui-resource-renderer.js - Resource UI Rendering
Click to open code

	// ui-resource-renderer.js
	// Handles rendering resource information to the UI
	
	// UIResourceRenderer - Renders resources to the UI
	const UIResourceRenderer = (function() {
		// Resource icons mapping
		const resourceIcons = {
			food: '🌾',
			materials: '🪵',
			knowledge: '📚',
			wealth: '💰'
		};
		
		// Render resources to the UI
		function renderResources(resources) {
			const resourcesContainer = document.getElementById('resources-container');
			if (!resourcesContainer) return;
			
			const contentDiv = resourcesContainer.querySelector('.container-content') || resourcesContainer;
			
			// Clear existing content
			contentDiv.innerHTML = '
'; const resourcesList = contentDiv.querySelector('.resources-list'); // Create resource items for (const resource in resources) { const resourceItem = document.createElement('div'); resourceItem.className = 'resource-item'; // Get resource icon const icon = resourceIcons[resource] || '📦'; resourceItem.innerHTML = ` ${icon} ${resource.charAt(0).toUpperCase() + resource.slice(1)} ${resources[resource]} `; resource
Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
Aéius Cercle: Re-code resuming from ui-resource-renderer.js
Claude: I'll continue with the ui-resource-renderer.js file and then proceed with the remaining UI modules.
</>
ui-resource-renderer.js - Resource UI Rendering
Click to open code

	// ui-resource-renderer.js
	// Handles rendering resource information to the UI
	
	// UIResourceRenderer - Renders resources to the UI
	const UIResourceRenderer = (function() {
		// Resource icons mapping
		const resourceIcons = {
			food: '🌾',
			materials: '🪵',
			knowledge: '📚',
			wealth: '💰'
		};
		
		// Render resources to the UI
		function renderResources(resources) {
			const resourcesContainer = document.getElementById('resources-container');
			if (!resourcesContainer) return;
			
			const contentDiv = resourcesContainer.querySelector('.container-content') || resourcesContainer;
			
			// Clear existing content
			contentDiv.innerHTML = '
'; const resourcesList = contentDiv.querySelector('.resources-list'); // Create resource items for (const resource in resources) { const resourceItem = document.createElement('div'); resourceItem.className = 'resource-item'; // Get resource icon const icon = resourceIcons[resource] || '📦'; resourceItem.innerHTML = ` ${icon} ${resource.charAt(0).toUpperCase() + resource.slice(1)} ${resources[resource]} `; resourcesList.appendChild(resourceItem); } // Add production rates if available const gameState = window.AICivilizationGame?.getGameState(); if (gameState && gameState.productionRates) { const productionHeader = document.createElement('h3'); productionHeader.textContent = 'Production Per Turn'; contentDiv.appendChild(productionHeader); const productionList = document.createElement('div'); productionList.className = 'production-list'; for (const resource in gameState.productionRates) { const rate = gameState.productionRates[resource]; if (rate === 0) continue; // Skip zero production rates const productionItem = document.createElement('div'); productionItem.className = 'production-item'; const icon = resourceIcons[resource] || '📦'; const sign = rate > 0 ? '+' : ''; productionItem.innerHTML = ` ${icon} ${resource.charAt(0).toUpperCase() + resource.slice(1)} ${sign}${rate}/turn `; productionList.appendChild(productionItem); } contentDiv.appendChild(productionList); } } // Show detailed resource information dialog function showResourceDetailsDialog() { const gameState = window.AICivilizationGame?.getGameState(); if (!gameState) return; const resources = gameState.resources; const productionRates = gameState.productionRates || {}; const consumptionRates = gameState.consumptionRates || {}; // Calculate net rates const netRates = {}; for (const resource in productionRates) { netRates[resource] = (productionRates[resource] || 0) - (consumptionRates[resource] || 0); } // Generate table rows for each resource let resourceRows = ''; for (const resource in resources) { const icon = resourceIcons[resource] || '📦'; const production = productionRates[resource] || 0; const consumption = consumptionRates[resource] || 0; const net = netRates[resource] || 0; const netClass = net > 0 ? 'positive' : (net < 0 ? 'negative' : ''); const netSign = net > 0 ? '+' : ''; resourceRows += ` ${icon} ${resource.charAt(0).toUpperCase() + resource.slice(1)} ${resources[resource]} +${production} -${consumption} ${netSign}${net} ${calculateTurnsUntil(resources[resource], net, 100)} `; } // Create the content for the modal const content = `

Current resource levels and production rates:

${resourceRows}
Resource Current Production Consumption Net Turns to 100

Note: Net values show the change per turn. Positive values indicate growth, negative values indicate depletion.

"Turns to 100" shows how many turns until you reach 100 of that resource at the current rate (∞ means it will never reach 100).

`; // Show the modal (using UIModalManager) if (window.UIModalManager) { window.UIModalManager.showModal({ title: 'Resource Details', content: content, size: 'medium' }); } else { console.error('UIModalManager not available'); } } // Calculate how many turns until a resource reaches a target value function calculateTurnsUntil(current, netRate, target) { if (netRate <= 0 && current < target) { return '∞'; // Will never reach target } if (current >= target) { return 'Now'; } const turns = Math.ceil((target - current) / netRate); return turns; } // Expose the public API return { renderResources, showResourceDetailsDialog }; })(); export { UIResourceRenderer };
</>
ui-building-renderer.js - Building UI Rendering
Click to open code

	// ui-building-renderer.js
	// Handles rendering building information to the UI
	
	import { UIModalManager } from './ui-modal-manager.js';
	
	// UIBuildingRenderer - Renders buildings to the UI
	const UIBuildingRenderer = (function() {
		// Render buildings to the UI
		function renderBuildings(buildings) {
			const buildingsContainer = document.getElementById('buildings-container');
			if (!buildingsContainer) return;
			
			const contentDiv = buildingsContainer.querySelector('.container-content') || buildingsContainer;
			
			// Clear existing content
			contentDiv.innerHTML = '';
			
			// Get construction projects in progress
			const gameState = window.AICivilizationGame?.getGameState();
			const constructionProjects = gameState?.buildingsInProgress || [];
			
			// Display construction projects first
			if (constructionProjects.length > 0) {
				const constructionHeader = document.createElement('h3');
				constructionHeader.textContent = 'Under Construction';
				contentDiv.appendChild(constructionHeader);
				
				constructionProjects.forEach(project => {
					const constructionItem = document.createElement('div');
					constructionItem.className = 'construction-item';
					
					constructionItem.innerHTML = `
						
${project.buildingName}
Region: ${getRegionName(project.regionId, gameState)}
${Math.round((project.progress / project.totalWork) * 100)}% complete
`; contentDiv.appendChild(constructionItem); }); } // Display existing buildings if (buildings.length > 0) { const buildingsHeader = document.createElement('h3'); buildingsHeader.textContent = 'Completed Buildings'; contentDiv.appendChild(buildingsHeader); // Group buildings by type const buildingsByType = groupBuildingsByType(buildings); // Create building cards for (const type in buildingsByType) { const typeBuildings = buildingsByType[type]; // Create a collapsed group for each type const groupElement = document.createElement('div'); groupElement.className = 'building-group'; groupElement.innerHTML = `
${type} (${typeBuildings.length})
`; const groupContent = groupElement.querySelector('.building-group-content'); // Add building items to the group typeBuildings.forEach(building => { const buildingCard = document.createElement('div'); buildingCard.className = 'building-card'; buildingCard.setAttribute('data-id', building.id); // Determine condition class let conditionClass = 'good'; if (building.condition < 50) conditionClass = 'poor'; else if (building.condition < 75) conditionClass = 'fair'; buildingCard.innerHTML = `

${building.name}

Location: ${getRegionName(building.regionId, gameState)}
Efficiency: ${Math.round(building.efficiency * 100)}%
Condition: ${Math.round(building.condition)}%
`; groupContent.appendChild(buildingCard); // Add click event for repair const repairBtn = buildingCard.querySelector('.repair-building-btn'); if (repairBtn) { repairBtn.addEventListener('click', () => { // Call building repair function repairBuilding(building.id); }); } // Add click event for building details const detailsBtn = buildingCard.querySelector('.building-details-btn'); if (detailsBtn) { detailsBtn.addEventListener('click', () => { showBuildingDetailsDialog(building); }); } }); contentDiv.appendChild(groupElement); // Add toggle functionality for the group const toggleButton = groupElement.querySelector('.toggle-group'); toggleButton.addEventListener('click', () => { const content = groupElement.querySelector('.building-group-content'); const isVisible = content.style.display !== 'none'; content.style.display = isVisible ? 'none' : 'block'; toggleButton.textContent = isVisible ? '▶' : '▼'; }); } } // Add "Construct New Building" button const constructButton = document.createElement('button'); constructButton.className = 'new-building-btn'; constructButton.textContent = 'Construct New Building'; constructButton.addEventListener('click', () => { showConstructionDialog(); }); contentDiv.appendChild(constructButton); } // Group buildings by their type function groupBuildingsByType(buildings) { const groups = {}; buildings.forEach(building => { const type = building.type || 'Unknown'; if (!groups[type]) { groups[type] = []; } groups[type].push(building); }); return groups; } // Get region name from ID function getRegionName(regionId, gameState) { if (!gameState || !gameState.world || !gameState.world.regions) { return regionId; } const region = gameState.world.regions.find(r => r.id === regionId); return region ? region.name : regionId; } // Show dialog for constructing a new building function showConstructionDialog() { const gameState = window.AICivilizationGame?.getGameState(); const BuildingManager = window.BuildingManager; if (!gameState || !BuildingManager) { console.error('Game state or BuildingManager not available'); return; } // Get available buildings const availableBuildings = BuildingManager.getAvailableBuildings(gameState); // Get explored regions for building location const exploredRegions = gameState.world.regions.filter(region => region.explored); // Generate building options HTML let buildingOptionsHtml = ''; if (availableBuildings.length === 0) { buildingOptionsHtml = '

No buildings available for construction at this time.

'; } else { // Group by category const buildingsByCategory = {}; availableBuildings.forEach(building => { const category = building.category || 'Other'; if (!buildingsByCategory[category]) { buildingsByCategory[category] = []; } buildingsByCategory[category].push(building); }); // Generate HTML for each category for (const category in buildingsByCategory) { buildingOptionsHtml += `

${category.charAt(0).toUpperCase() + category.slice(1)}

${buildingsByCategory[category].map(building => `
${building.name} Difficulty: ${building.difficulty}
${building.description}
${Object.entries(building.cost).map(([resource, amount]) => `
${resource}: ${amount}
`).join('')}
${Object.entries(building.effects || {}).map(([effect, value]) => `
${effect}: ${typeof value === 'number' ? (value > 0 ? '+' : '') + value : value}
`).join('')}
`).join('')}
`; } } // Generate region options HTML let regionOptionsHtml = ''; if (exploredRegions.length === 0) { regionOptionsHtml = '

No explored regions available for construction.

'; } else { regionOptionsHtml = `
`; } // Create the content for the modal const content = `
${regionOptionsHtml}
${buildingOptionsHtml}
`; // Show the modal const modal = UIModalManager.showModal({ title: 'Construct New Building', content: content, size: 'large', onClose: () => { console.log('Construction dialog closed'); } }); // Add event listeners for construction buttons const constructButtons = modal.querySelectorAll('.construct-btn:not([disabled])'); constructButtons.forEach(btn => { btn.addEventListener('click', () => { const buildingId = btn.getAttribute('data-building-id'); const regionDropdown = modal.querySelector('#region-dropdown'); const regionId = regionDropdown ? regionDropdown.value : null; if (buildingId && regionId) { console.log(`Constructing building ${buildingId} in region ${regionId}`); BuildingManager.constructBuilding(buildingId, gameState, regionId); UIModalManager.closeModal(); } }); }); } // Show building details dialog function showBuildingDetailsDialog(building) { const gameState = window.AICivilizationGame?.getGameState(); const BuildingManager = window.BuildingManager; if (!gameState || !BuildingManager) { console.error('Game state or BuildingManager not available'); return; } // Get building details and region const buildingType = BuildingManager.getBuildingDetails(building.type); const region = gameState.world.regions.find(r => r.id === building.regionId); if (!buildingType || !region) { console.error('Building type or region not found'); return; } // Determine condition status let conditionStatus = 'Excellent'; let conditionClass = 'good'; if (building.condition < 25) { conditionStatus = 'Critical'; conditionClass = 'critical'; } else if (building.condition < 50) { conditionStatus = 'Poor'; conditionClass = 'poor'; } else if (building.condition < 75) { conditionStatus = 'Fair'; conditionClass = 'fair'; } // Calculate maintenance costs per turn let maintenanceHtml = '

No maintenance required.

'; if (buildingType.maintenance) { maintenanceHtml = `
${Object.entries(buildingType.maintenance).map(([resource, amount]) => `
${resource}: ${amount}/turn
`).join('')}
`; } // Format the building's effects let effectsHtml = '

No active effects.

'; if (buildingType.effects) { effectsHtml = `
${Object.entries(buildingType.effects).map(([effect, value]) => { // Calculate actual value with efficiency const actualValue = typeof value === 'number' ? value * building.efficiency : value; return `
${effect.replace(/_/g, ' ')}: ${typeof actualValue === 'number' ? (actualValue > 0 ? '+' : '') + actualValue.toFixed(2) : actualValue}
`; }).join('')}
`; } // Create the content for the modal const content = `

${building.name}

${buildingType.description}

Located in: ${region.name}
Built on turn: ${building.built}

Status

Condition: ${Math.round(building.condition)}% (${conditionStatus})
Efficiency: ${Math.round(building.efficiency * 100)}%

Maintenance

${maintenanceHtml}

Effects

${effectsHtml}
${building.condition < 95 ? `` : `` }
`; // Show the modal const modal = UIModalManager.showModal({ title: 'Building Details', content: content, onClose: () => { console.log('Building details dialog closed'); } }); // Add event listener for repair button const repairBtn = modal.querySelector('.repair-btn'); if (repairBtn) { repairBtn.addEventListener('click', () => { const buildingId = repairBtn.getAttribute('data-building-id'); repairBuilding(buildingId); UIModalManager.closeModal(); }); } } // Repair a building function repairBuilding(buildingId) { const BuildingManager = window.BuildingManager; if (!BuildingManager) { console.error('BuildingManager not available'); return; } console.log(`Repairing building ${buildingId}`); BuildingManager.repairBuilding(buildingId); } // Expose the public API return { renderBuildings, showConstructionDialog, showBuildingDetailsDialog, repairBuilding }; })(); export { UIBuildingRenderer };
</>
ui-technology-renderer.js - Technology UI Rendering
Click to open code

	// ui-technology-renderer.js
	// Handles rendering technology information to the UI
	
	import { UIModalManager } from './ui-modal-manager.js';
	
	// UITechnologyRenderer - Renders technologies to the UI
	const UITechnologyRenderer = (function() {
		// Render technologies to the UI
		function renderTechnologies(technologies, currentResearch) {
			const technologiesContainer = document.getElementById('technologies-container');
			if (!technologiesContainer) return;
			
			const contentDiv = technologiesContainer.querySelector('.container-content') || technologiesContainer;
			
			// Clear existing content
			contentDiv.innerHTML = '';
			
			// Create current research section if there's research in progress
			if (currentResearch) {
				const currentResearchElement = document.createElement('div');
				currentResearchElement.className = 'current-research';
				
				// Calculate progress percentage
				const progressPercent = Math.min(100, Math.round((currentResearch.progress / currentResearch.researchTime) * 100));
				
				currentResearchElement.innerHTML = `
					

Current Research

${currentResearch.name}
${progressPercent}% complete
${currentResearch.description}
Difficulty: ${currentResearch.difficulty}
Remaining: ~${Math.ceil(currentResearch.researchTime - currentResearch.progress)} turns
`; contentDiv.appendChild(currentResearchElement); } // Create researched technologies section if (technologies.length > 0) { const researchedElement = document.createElement('div'); researchedElement.className = 'researched-technologies'; // Group technologies by category const techByCategory = {}; technologies.forEach(tech => { const category = tech.category || 'uncategorized'; if (!techByCategory[category]) { techByCategory[category] = []; } techByCategory[category].push(tech); }); // Generate category sections for (const category in techByCategory) { const categoryElement = document.createElement('div'); categoryElement.className = 'tech-category'; categoryElement.innerHTML = `

${category.charAt(0).toUpperCase() + category.slice(1)}

`; const categoryContent = categoryElement.querySelector('.tech-category-content'); // Add technology cards to this category techByCategory[category].forEach(tech => { const techCard = document.createElement('div'); techCard.className = 'technology-card'; techCard.setAttribute('data-id', tech.id); // Format the effects list let effectsHtml = ''; if (tech.effects) { effectsHtml = `
${Object.entries(tech.effects).map(([effect, value]) => { if (effect === 'enables') { return `
Enables: ${Array.isArray(value) ? value.join(', ') : value}
`; } else { return `
${effect.replace(/_/g, ' ')}: ${typeof value === 'number' ? (value > 0 ? '+' : '') + (value * 100).toFixed(0) + '%' : value}
`; } }).join('')}
`; } techCard.innerHTML = `

${tech.name}

Researched on turn ${tech.completedTurn}
${effectsHtml} `; categoryContent.appendChild(techCard); // Add click event for tech details const detailsBtn = techCard.querySelector('.tech-details-btn'); if (detailsBtn) { detailsBtn.addEventListener('click', () => { showTechnologyDetailsDialog(tech); }); } }); researchedElement.appendChild(categoryElement); } contentDiv.appendChild(researchedElement); } // Add "Research New Technology" button const researchButton = document.createElement('button'); researchButton.className = 'research-btn'; researchButton.textContent = 'Research New Technology'; researchButton.disabled = !!currentResearch; // Disable if already researching researchButton.addEventListener('click', () => { showResearchDialog(); }); contentDiv.appendChild(researchButton); } // Show dialog for researching a new technology function showResearchDialog() { const gameState = window.AICivilizationGame?.getGameState(); const TechnologyManager = window.TechnologyManager; if (!gameState || !TechnologyManager) { console.error('Game state or TechnologyManager not available'); return; } // Get available technologies const availableTechs = TechnologyManager.getAvailableTechnologies(gameState); // Group technologies by tier const techsByTier = {}; availableTechs.forEach(tech => { if (!techsByTier[tech.tier]) { techsByTier[tech.tier] = []; } techsByTier[tech.tier].push(tech); }); // Generate dialog content let techOptionsHtml = ''; if (availableTechs.length === 0) { techOptionsHtml = '

No technologies available for research at this time.

'; } else { // Sort tiers const tiers = Object.keys(techsByTier).sort((a, b) => a - b); // Generate HTML for each tier tiers.forEach(tier => { techOptionsHtml += `

Claude hit the max length for a message and has paused its response. You can write Continue to keep the chat going.
[Note: I haven't completed adding all of the rest of the prompts and responses into this file yet; there is a lot of Copy/Pasta that needs to be done which is much «manual» work in order to make sure that I am doing the Copy/Pasta of the correct text in the correct order in addition to also still needing to alter some of the template-identifiers so that «show code» only shows the relevant code-artifact(s).]





































Massive Blank-Space Added For Code-Clarity/Visibility