'use strict';

/* ────────────────────────────────────────────────────────────────
   EPIC E8 — World Building
   
   Open world streaming, procedural terrain, weather system,
   day/night cycle, NPC schedules, quest system
──────────────────────────────────────────────────────────────── */
const DEFAULT_STREAMING = {
    chunkSize: 256,
    loadRadius: 3,
    unloadRadius: 5,
    preloadRadius: 4,
    maxLoadedChunks: 100,
    loadPriority: 'combined',
    transitionBlend: 32,
};
function getChunkCoord(worldPos, chunkSize) {
    return {
        cx: Math.floor(worldPos.x / chunkSize),
        cy: Math.floor(worldPos.y / chunkSize),
    };
}
function getChunksInRadius(centerChunk, radius) {
    const chunks = [];
    for (let dy = -radius; dy <= radius; dy++) {
        for (let dx = -radius; dx <= radius; dx++) {
            if (dx * dx + dy * dy <= radius * radius) {
                chunks.push({ cx: centerChunk.cx + dx, cy: centerChunk.cy + dy });
            }
        }
    }
    return chunks;
}
function chunkKey(cx, cy) {
    return `${cx},${cy}`;
}
/** Simple seeded PRNG (xorshift32). */
function xorshift32(seed) {
    let state = seed | 0 || 1;
    return () => {
        state ^= state << 13;
        state ^= state >> 17;
        state ^= state << 5;
        return (state >>> 0) / 0xFFFFFFFF;
    };
}
/** Simple value noise. */
function valueNoise2D(x, y, seed) {
    const rng = xorshift32;
    const ix = Math.floor(x);
    const iy = Math.floor(y);
    const fx = x - ix;
    const fy = y - iy;
    const smooth = (t) => t * t * (3 - 2 * t);
    const sfx = smooth(fx);
    const sfy = smooth(fy);
    const n00 = rng(ix * 374761393 + iy * 668265263 + seed)();
    const n10 = rng((ix + 1) * 374761393 + iy * 668265263 + seed)();
    const n01 = rng(ix * 374761393 + (iy + 1) * 668265263 + seed)();
    const n11 = rng((ix + 1) * 374761393 + (iy + 1) * 668265263 + seed)();
    const nx0 = n00 + (n10 - n00) * sfx;
    const nx1 = n01 + (n11 - n01) * sfx;
    return nx0 + (nx1 - nx0) * sfy;
}
/** Fractal Brownian Motion. */
function fbm(x, y, config) {
    let value = 0;
    let freq = config.frequency;
    let amp = config.amplitude;
    let maxAmp = 0;
    for (let i = 0; i < config.octaves; i++) {
        value += valueNoise2D(x * freq, y * freq, config.seed + i * 31337) * amp;
        maxAmp += amp;
        freq *= config.lacunarity;
        amp *= config.persistence;
    }
    return (value / maxAmp) * config.heightScale;
}
function selectBiome(height, moisture, biomes) {
    var _a;
    for (const biome of biomes) {
        if (height >= biome.minHeight && height <= biome.maxHeight &&
            moisture >= biome.minMoisture && moisture <= biome.maxMoisture) {
            return biome;
        }
    }
    return (_a = biomes[0]) !== null && _a !== void 0 ? _a : null;
}
const DEFAULT_BIOMES = [
    { name: 'deep-water', minHeight: -1, maxHeight: -0.3, minMoisture: 0, maxMoisture: 1, color: '#1a3a5c', vegetation: [], tileType: 'water-deep' },
    { name: 'water', minHeight: -0.3, maxHeight: 0, minMoisture: 0, maxMoisture: 1, color: '#2e6ca4', vegetation: [], tileType: 'water' },
    { name: 'beach', minHeight: 0, maxHeight: 0.05, minMoisture: 0, maxMoisture: 1, color: '#e8d68c', vegetation: [], tileType: 'sand' },
    { name: 'grassland', minHeight: 0.05, maxHeight: 0.4, minMoisture: 0.3, maxMoisture: 0.7, color: '#4a8c2e', vegetation: [{ type: 'grass', density: 0.3, minScale: 0.8, maxScale: 1.2, clusterSize: 1, avoidRadius: 0 }], tileType: 'grass' },
    { name: 'forest', minHeight: 0.05, maxHeight: 0.5, minMoisture: 0.5, maxMoisture: 1, color: '#2d6b1e', vegetation: [{ type: 'tree', density: 0.15, minScale: 0.7, maxScale: 1.3, clusterSize: 3, avoidRadius: 2 }], tileType: 'grass' },
    { name: 'desert', minHeight: 0.05, maxHeight: 0.3, minMoisture: 0, maxMoisture: 0.3, color: '#c4a35a', vegetation: [{ type: 'cactus', density: 0.02, minScale: 0.8, maxScale: 1.5, clusterSize: 1, avoidRadius: 5 }], tileType: 'sand' },
    { name: 'mountain', minHeight: 0.5, maxHeight: 0.8, minMoisture: 0, maxMoisture: 1, color: '#8c7a5e', vegetation: [], tileType: 'rock' },
    { name: 'snow', minHeight: 0.8, maxHeight: 1, minMoisture: 0, maxMoisture: 1, color: '#f0f0f0', vegetation: [], tileType: 'snow' },
];
const WEATHER_PRESETS = {
    clear: { intensity: 0, windSpeed: 2, visibility: 1, lightingMultiplier: 1, particleDensity: 0 },
    cloudy: { intensity: 0.3, windSpeed: 5, visibility: 0.8, lightingMultiplier: 0.7, particleDensity: 0 },
    rain: { intensity: 0.5, windSpeed: 8, visibility: 0.6, lightingMultiplier: 0.5, particleDensity: 0.5 },
    'heavy-rain': { intensity: 0.8, windSpeed: 15, visibility: 0.3, lightingMultiplier: 0.3, particleDensity: 0.8 },
    storm: { intensity: 1, windSpeed: 25, visibility: 0.2, lightingMultiplier: 0.2, particleDensity: 1 },
    snow: { intensity: 0.4, windSpeed: 5, visibility: 0.5, lightingMultiplier: 0.6, particleDensity: 0.4 },
    blizzard: { intensity: 0.9, windSpeed: 20, visibility: 0.15, lightingMultiplier: 0.3, particleDensity: 0.9 },
    fog: { intensity: 0.6, windSpeed: 2, visibility: 0.15, lightingMultiplier: 0.5, particleDensity: 0.1 },
    sandstorm: { intensity: 0.7, windSpeed: 18, visibility: 0.2, lightingMultiplier: 0.4, particleDensity: 0.7 },
};
function lerpWeather(a, b, t) {
    const lerp = (v1, v2) => v1 + (v2 - v1) * t;
    return {
        type: t < 0.5 ? a.type : b.type,
        intensity: lerp(a.intensity, b.intensity),
        windDirection: lerp(a.windDirection, b.windDirection),
        windSpeed: lerp(a.windSpeed, b.windSpeed),
        temperature: lerp(a.temperature, b.temperature),
        humidity: lerp(a.humidity, b.humidity),
        visibility: lerp(a.visibility, b.visibility),
        lightingMultiplier: lerp(a.lightingMultiplier, b.lightingMultiplier),
        particleDensity: lerp(a.particleDensity, b.particleDensity),
        soundId: t < 0.5 ? a.soundId : b.soundId,
        transitionDuration: b.transitionDuration,
    };
}
const DEFAULT_DAY_NIGHT = {
    dayLengthSeconds: 600, // 10 minutes = 1 game day
    startHour: 8,
    sunriseHour: 6,
    sunsetHour: 18,
    ambientDay: '#ffffff',
    ambientNight: '#1a1a3e',
    ambientSunrise: '#ff8844',
    ambientSunset: '#ff6633',
    sunIntensityDay: 1,
    sunIntensityNight: 0.05,
    moonPhase: true,
    starsAtNight: true,
};
function getTimeOfDay(hour) {
    if (hour < 5)
        return 'night';
    if (hour < 7)
        return 'dawn';
    if (hour < 11)
        return 'morning';
    if (hour < 14)
        return 'noon';
    if (hour < 17)
        return 'afternoon';
    if (hour < 20)
        return 'dusk';
    return 'night';
}
function getGameHour(realElapsedSeconds, config) {
    const totalGameHours = (realElapsedSeconds / config.dayLengthSeconds) * 24;
    return (config.startHour + totalGameHours) % 24;
}
function getSunAngle(hour, config) {
    const dayLength = config.sunsetHour - config.sunriseHour;
    if (hour < config.sunriseHour || hour > config.sunsetHour)
        return -Math.PI / 6;
    const t = (hour - config.sunriseHour) / dayLength;
    return t * Math.PI; // 0 at sunrise, PI at sunset, PI/2 at noon
}
function getCurrentScheduleEntry(schedule, hour, dayOfWeek) {
    let best = null;
    let bestPriority = -1;
    for (const entry of schedule.entries) {
        if (entry.daysOfWeek && entry.daysOfWeek.indexOf(dayOfWeek) === -1)
            continue;
        const inTimeRange = entry.startHour <= entry.endHour
            ? (hour >= entry.startHour && hour < entry.endHour)
            : (hour >= entry.startHour || hour < entry.endHour);
        if (inTimeRange && entry.priority > bestPriority) {
            best = entry;
            bestPriority = entry.priority;
        }
    }
    return best;
}
function canStartQuest(questDef, journal) {
    // Check prerequisites
    for (const prereq of questDef.prerequisites) {
        if (journal.completedQuests.indexOf(prereq) === -1)
            return false;
    }
    // Check not already active or completed (unless repeatable)
    const alreadyActive = journal.activeQuests.some(q => q.questId === questDef.id);
    if (alreadyActive)
        return false;
    if (!questDef.repeatable && journal.completedQuests.indexOf(questDef.id) !== -1)
        return false;
    return true;
}
function startQuest(questDef, journal) {
    const instance = {
        questId: questDef.id,
        state: 'active',
        objectives: questDef.objectives.map(o => ({ ...o, currentCount: 0 })),
        startTime: Date.now(),
    };
    journal.activeQuests.push(instance);
    return instance;
}
function updateObjective(instance, objectiveId, incrementBy = 1) {
    const obj = instance.objectives.find(o => o.id === objectiveId);
    if (!obj)
        return false;
    obj.currentCount = Math.min(obj.currentCount + incrementBy, obj.targetCount);
    return obj.currentCount >= obj.targetCount;
}
function isQuestComplete(instance) {
    return instance.objectives.every(o => o.optional || o.currentCount >= o.targetCount);
}
function completeQuest(instance, journal) {
    instance.state = 'completed';
    instance.completedTime = Date.now();
    journal.completedQuests.push(instance.questId);
    const idx = journal.activeQuests.indexOf(instance);
    if (idx !== -1)
        journal.activeQuests.splice(idx, 1);
}

exports.DEFAULT_BIOMES = DEFAULT_BIOMES;
exports.DEFAULT_DAY_NIGHT = DEFAULT_DAY_NIGHT;
exports.DEFAULT_STREAMING = DEFAULT_STREAMING;
exports.WEATHER_PRESETS = WEATHER_PRESETS;
exports.canStartQuest = canStartQuest;
exports.chunkKey = chunkKey;
exports.completeQuest = completeQuest;
exports.fbm = fbm;
exports.getChunkCoord = getChunkCoord;
exports.getChunksInRadius = getChunksInRadius;
exports.getCurrentScheduleEntry = getCurrentScheduleEntry;
exports.getGameHour = getGameHour;
exports.getSunAngle = getSunAngle;
exports.getTimeOfDay = getTimeOfDay;
exports.isQuestComplete = isQuestComplete;
exports.lerpWeather = lerpWeather;
exports.selectBiome = selectBiome;
exports.startQuest = startQuest;
exports.updateObjective = updateObjective;
exports.valueNoise2D = valueNoise2D;
//# sourceMappingURL=WorldBuilding.js.map
