Merge pull request #100 from tomt610/feature/improved-clear-weather-effects
Feature/improved clear weather effects
This commit is contained in:
@@ -4,9 +4,106 @@
|
||||
*/
|
||||
|
||||
import { extensionSettings, lastGeneratedData, committedTrackerData } from '../../core/state.js';
|
||||
import { repairJSON } from '../../utils/jsonRepair.js';
|
||||
|
||||
let weatherContainer = null;
|
||||
let currentWeatherType = null;
|
||||
let currentTimeOfDay = null;
|
||||
let currentHour = null;
|
||||
|
||||
/**
|
||||
* Parse time string to extract hour (24-hour format)
|
||||
* Supports formats like "3:00 PM", "15:00", "3 PM", "Evening", etc.
|
||||
*/
|
||||
function parseHourFromTime(timeStr) {
|
||||
if (!timeStr) return null;
|
||||
|
||||
const text = timeStr.toLowerCase().trim();
|
||||
|
||||
// Check for descriptive time words first
|
||||
if (text.includes('dawn') || text.includes('sunrise')) return 6;
|
||||
if (text.includes('early morning')) return 7;
|
||||
if (text.includes('morning')) return 9;
|
||||
if (text.includes('midday') || text.includes('noon') || text.includes('mid-day')) return 12;
|
||||
if (text.includes('afternoon')) return 14;
|
||||
if (text.includes('late afternoon')) return 16;
|
||||
if (text.includes('evening') || text.includes('dusk') || text.includes('sunset')) return 19;
|
||||
if (text.includes('twilight')) return 20;
|
||||
if (text.includes('night') || text.includes('nighttime')) return 22;
|
||||
if (text.includes('midnight')) return 0;
|
||||
if (text.includes('late night')) return 2;
|
||||
|
||||
// Try to parse numeric time formats
|
||||
// Format: "3:00 PM" or "3:00PM" or "3 PM"
|
||||
const ampmMatch = text.match(/(\d{1,2})(?::(\d{2}))?\s*(am|pm)/i);
|
||||
if (ampmMatch) {
|
||||
let hour = parseInt(ampmMatch[1], 10);
|
||||
const isPM = ampmMatch[3].toLowerCase() === 'pm';
|
||||
if (isPM && hour !== 12) hour += 12;
|
||||
if (!isPM && hour === 12) hour = 0;
|
||||
return hour;
|
||||
}
|
||||
|
||||
// Format: "15:00" (24-hour)
|
||||
const militaryMatch = text.match(/(\d{1,2}):(\d{2})/);
|
||||
if (militaryMatch) {
|
||||
return parseInt(militaryMatch[1], 10);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine time of day based on hour
|
||||
*/
|
||||
function getTimeOfDay(hour) {
|
||||
if (hour === null) return 'unknown';
|
||||
|
||||
// Night: 8 PM (20:00) to 5 AM (05:00)
|
||||
if (hour >= 20 || hour < 5) return 'night';
|
||||
|
||||
// Dawn/Dusk: 5 AM - 7 AM and 6 PM - 8 PM
|
||||
if (hour >= 5 && hour < 7) return 'dawn';
|
||||
if (hour >= 18 && hour < 20) return 'dusk';
|
||||
|
||||
// Day: 7 AM to 6 PM
|
||||
return 'day';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract time from Info Box data
|
||||
*/
|
||||
function getCurrentTime() {
|
||||
const infoBoxData = lastGeneratedData.infoBox || committedTrackerData.infoBox || '';
|
||||
|
||||
// Try to parse as JSON first (new format)
|
||||
try {
|
||||
const parsed = typeof infoBoxData === 'string' ? repairJSON(infoBoxData) : infoBoxData;
|
||||
if (parsed && parsed.time) {
|
||||
// Use the end time if available (current time), otherwise start time
|
||||
return parsed.time.end || parsed.time.start || null;
|
||||
}
|
||||
} catch (e) {
|
||||
// Not JSON, try old text format
|
||||
}
|
||||
|
||||
// Fallback: Parse the old text format to find Time field
|
||||
const lines = infoBoxData.split('\n');
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed.startsWith('Time:')) {
|
||||
const timeStr = trimmed.substring('Time:'.length).trim();
|
||||
// If it contains →, take the end time (after arrow)
|
||||
if (timeStr.includes('→')) {
|
||||
const parts = timeStr.split('→');
|
||||
return parts[1]?.trim() || parts[0]?.trim();
|
||||
}
|
||||
return timeStr;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse weather text to determine effect type
|
||||
@@ -136,22 +233,171 @@ function createMist() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create sunshine rays effect
|
||||
* Calculate sun position based on hour (arc across sky)
|
||||
* Returns { left: vw%, top: dvh% }
|
||||
*/
|
||||
function createSunshine() {
|
||||
const container = document.createElement('div');
|
||||
container.className = 'rpg-weather-particles';
|
||||
function calculateSunPosition(hour) {
|
||||
// Daytime is roughly 6 AM to 8 PM (6-20)
|
||||
// Map hour to position along an arc
|
||||
// 6 AM = far left, low | 12 PM = center, high | 6 PM = far right, low
|
||||
|
||||
// Create 8 sun rays
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const ray = document.createElement('div');
|
||||
ray.className = 'rpg-weather-particle rpg-sunray';
|
||||
ray.style.left = `${10 + i * 12}%`;
|
||||
ray.style.animationDelay = `${i * 0.5}s`;
|
||||
ray.style.animationDuration = `${8 + Math.random() * 4}s`;
|
||||
container.appendChild(ray);
|
||||
if (hour === null) hour = 12; // Default to noon if unknown
|
||||
|
||||
// Clamp to daytime hours
|
||||
const clampedHour = Math.max(5, Math.min(20, hour));
|
||||
|
||||
// Normalize to 0-1 range (5 AM = 0, 20 PM = 1)
|
||||
const progress = (clampedHour - 5) / 15;
|
||||
|
||||
// Horizontal position: 5% to 85% (left to right)
|
||||
const left = 5 + progress * 80;
|
||||
|
||||
// Vertical position: parabolic arc (high at noon, low at dawn/dusk)
|
||||
// At progress 0.5 (noon), top should be ~8% (high)
|
||||
// At progress 0 or 1, top should be ~35% (low, near horizon)
|
||||
const normalizedProgress = (progress - 0.5) * 2; // -1 to 1
|
||||
const top = 8 + 27 * (normalizedProgress * normalizedProgress);
|
||||
|
||||
return { left, top };
|
||||
}
|
||||
|
||||
/**
|
||||
* Create clear/sunny weather effect with floating particles and warm glow
|
||||
*/
|
||||
function createSunshine(hour) {
|
||||
const container = document.createElement('div');
|
||||
container.className = 'rpg-weather-particles rpg-clear-weather';
|
||||
|
||||
// Create the sun based on current hour
|
||||
const sunPos = calculateSunPosition(hour);
|
||||
|
||||
const sun = document.createElement('div');
|
||||
sun.className = 'rpg-weather-particle rpg-clear-sun';
|
||||
sun.style.left = `${sunPos.left}vw`;
|
||||
sun.style.top = `${sunPos.top}dvh`;
|
||||
container.appendChild(sun);
|
||||
|
||||
// Create sun glow
|
||||
const sunGlow = document.createElement('div');
|
||||
sunGlow.className = 'rpg-weather-particle rpg-clear-sun-glow';
|
||||
sunGlow.style.left = `${sunPos.left}vw`;
|
||||
sunGlow.style.top = `${sunPos.top}dvh`;
|
||||
container.appendChild(sunGlow);
|
||||
|
||||
// Create warm ambient glow overlay
|
||||
const ambientGlow = document.createElement('div');
|
||||
ambientGlow.className = 'rpg-weather-particle rpg-clear-ambient-glow';
|
||||
container.appendChild(ambientGlow);
|
||||
|
||||
// Create floating dust motes / pollen particles (golden sparkles)
|
||||
for (let i = 0; i < 25; i++) {
|
||||
const particle = document.createElement('div');
|
||||
particle.className = 'rpg-weather-particle rpg-clear-dust-mote';
|
||||
particle.style.left = `${Math.random() * 100}vw`;
|
||||
particle.style.top = `${Math.random() * 100}dvh`;
|
||||
particle.style.animationDelay = `${Math.random() * 15}s`;
|
||||
particle.style.animationDuration = `${12 + Math.random() * 8}s`;
|
||||
// Vary the size slightly
|
||||
const size = 2 + Math.random() * 4;
|
||||
particle.style.width = `${size}px`;
|
||||
particle.style.height = `${size}px`;
|
||||
container.appendChild(particle);
|
||||
}
|
||||
|
||||
// Create soft light orbs that drift gently
|
||||
for (let i = 0; i < 6; i++) {
|
||||
const orb = document.createElement('div');
|
||||
orb.className = 'rpg-weather-particle rpg-clear-light-orb';
|
||||
orb.style.left = `${10 + Math.random() * 80}vw`;
|
||||
orb.style.top = `${10 + Math.random() * 80}dvh`;
|
||||
orb.style.animationDelay = `${i * 2}s`;
|
||||
orb.style.animationDuration = `${20 + Math.random() * 10}s`;
|
||||
// Vary the size
|
||||
const size = 80 + Math.random() * 120;
|
||||
orb.style.width = `${size}px`;
|
||||
orb.style.height = `${size}px`;
|
||||
container.appendChild(orb);
|
||||
}
|
||||
|
||||
// Create lens flare effect in corner
|
||||
const lensFlare = document.createElement('div');
|
||||
lensFlare.className = 'rpg-weather-particle rpg-clear-lens-flare';
|
||||
container.appendChild(lensFlare);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create clear nighttime weather effect with moon, stars, and fireflies
|
||||
*/
|
||||
function createNighttime(hour) {
|
||||
const container = document.createElement('div');
|
||||
container.className = 'rpg-weather-particles rpg-night-weather';
|
||||
|
||||
// Create dark blue ambient overlay
|
||||
const nightOverlay = document.createElement('div');
|
||||
nightOverlay.className = 'rpg-weather-particle rpg-night-overlay';
|
||||
container.appendChild(nightOverlay);
|
||||
|
||||
// Calculate moon position based on hour
|
||||
const moonPos = calculateMoonPosition(hour);
|
||||
|
||||
// Create the moon
|
||||
const moon = document.createElement('div');
|
||||
moon.className = 'rpg-weather-particle rpg-night-moon';
|
||||
moon.style.left = `${moonPos.left}vw`;
|
||||
moon.style.top = `${moonPos.top}dvh`;
|
||||
container.appendChild(moon);
|
||||
|
||||
// Create moon glow
|
||||
const moonGlow = document.createElement('div');
|
||||
moonGlow.className = 'rpg-weather-particle rpg-night-moon-glow';
|
||||
moonGlow.style.left = `${moonPos.left - 3}vw`;
|
||||
moonGlow.style.top = `${moonPos.top - 3}dvh`;
|
||||
container.appendChild(moonGlow);
|
||||
|
||||
// Create twinkling stars
|
||||
for (let i = 0; i < 60; i++) {
|
||||
const star = document.createElement('div');
|
||||
star.className = 'rpg-weather-particle rpg-night-star';
|
||||
star.style.left = `${Math.random() * 100}vw`;
|
||||
star.style.top = `${Math.random() * 60}dvh`; // Stars mostly in upper portion
|
||||
star.style.animationDelay = `${Math.random() * 5}s`;
|
||||
star.style.animationDuration = `${2 + Math.random() * 3}s`;
|
||||
// Vary the size
|
||||
const size = 1 + Math.random() * 2;
|
||||
star.style.width = `${size}px`;
|
||||
star.style.height = `${size}px`;
|
||||
container.appendChild(star);
|
||||
}
|
||||
|
||||
// Create a few brighter stars
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const brightStar = document.createElement('div');
|
||||
brightStar.className = 'rpg-weather-particle rpg-night-star rpg-night-star-bright';
|
||||
brightStar.style.left = `${Math.random() * 100}vw`;
|
||||
brightStar.style.top = `${Math.random() * 50}dvh`;
|
||||
brightStar.style.animationDelay = `${Math.random() * 4}s`;
|
||||
brightStar.style.animationDuration = `${3 + Math.random() * 2}s`;
|
||||
container.appendChild(brightStar);
|
||||
}
|
||||
|
||||
// Create fireflies / floating light particles
|
||||
for (let i = 0; i < 15; i++) {
|
||||
const firefly = document.createElement('div');
|
||||
firefly.className = 'rpg-weather-particle rpg-night-firefly';
|
||||
firefly.style.left = `${Math.random() * 100}vw`;
|
||||
firefly.style.top = `${40 + Math.random() * 55}dvh`; // Fireflies in lower portion
|
||||
firefly.style.animationDelay = `${Math.random() * 10}s`;
|
||||
firefly.style.animationDuration = `${8 + Math.random() * 7}s`;
|
||||
container.appendChild(firefly);
|
||||
}
|
||||
|
||||
// Create subtle shooting star occasionally
|
||||
const shootingStar = document.createElement('div');
|
||||
shootingStar.className = 'rpg-weather-particle rpg-night-shooting-star';
|
||||
container.appendChild(shootingStar);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
@@ -190,6 +436,75 @@ function createWind() {
|
||||
return container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate moon position based on hour (arc across sky at night)
|
||||
* Returns { left: vw%, top: dvh% }
|
||||
*/
|
||||
function calculateMoonPosition(hour) {
|
||||
// Nighttime is roughly 8 PM to 5 AM (20-5)
|
||||
// Map hour to position along an arc
|
||||
// 8 PM = far left, low | midnight = center-left, high | 5 AM = far right, low
|
||||
|
||||
if (hour === null) hour = 0; // Default to midnight if unknown
|
||||
|
||||
// Normalize night hours to 0-1 range
|
||||
// 20 (8 PM) = 0, 0 (midnight) = ~0.44, 5 (5 AM) = 1
|
||||
let progress;
|
||||
if (hour >= 20) {
|
||||
// 8 PM to midnight: 20-24 maps to 0-0.44
|
||||
progress = (hour - 20) / 9;
|
||||
} else {
|
||||
// Midnight to 5 AM: 0-5 maps to 0.44-1
|
||||
progress = (hour + 4) / 9;
|
||||
}
|
||||
|
||||
// Horizontal position: 10% to 80% (left to right)
|
||||
const left = 10 + progress * 70;
|
||||
|
||||
// Vertical position: parabolic arc (high at ~2 AM, low at dusk/dawn)
|
||||
// Peak should be around progress 0.67 (~2 AM)
|
||||
const peakProgress = 0.5;
|
||||
const normalizedProgress = (progress - peakProgress) * 2; // -1 to 1
|
||||
const top = 8 + 25 * (normalizedProgress * normalizedProgress);
|
||||
|
||||
return { left, top };
|
||||
}
|
||||
|
||||
/**
|
||||
* Update sun/moon position without recreating the whole effect
|
||||
*/
|
||||
function updateCelestialPosition(hour) {
|
||||
if (!weatherContainer) return false;
|
||||
|
||||
// Update sun position if it exists
|
||||
const sun = weatherContainer.querySelector('.rpg-clear-sun');
|
||||
const sunGlow = weatherContainer.querySelector('.rpg-clear-sun-glow');
|
||||
|
||||
if (sun && sunGlow) {
|
||||
const sunPos = calculateSunPosition(hour);
|
||||
sun.style.left = `${sunPos.left}vw`;
|
||||
sun.style.top = `${sunPos.top}dvh`;
|
||||
sunGlow.style.left = `${sunPos.left}vw`;
|
||||
sunGlow.style.top = `${sunPos.top}dvh`;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Update moon position if it exists
|
||||
const moon = weatherContainer.querySelector('.rpg-night-moon');
|
||||
const moonGlow = weatherContainer.querySelector('.rpg-night-moon-glow');
|
||||
|
||||
if (moon && moonGlow) {
|
||||
const moonPos = calculateMoonPosition(hour);
|
||||
moon.style.left = `${moonPos.left}vw`;
|
||||
moon.style.top = `${moonPos.top}dvh`;
|
||||
moonGlow.style.left = `${moonPos.left - 3}vw`;
|
||||
moonGlow.style.top = `${moonPos.top - 3}dvh`;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove current weather effect
|
||||
*/
|
||||
@@ -198,11 +513,13 @@ function removeWeatherEffect() {
|
||||
weatherContainer.remove();
|
||||
weatherContainer = null;
|
||||
currentWeatherType = null;
|
||||
currentTimeOfDay = null;
|
||||
currentHour = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update weather effect based on current weather
|
||||
* Update weather effect based on current weather and time
|
||||
*/
|
||||
export function updateWeatherEffect() {
|
||||
// Check if dynamic weather is enabled
|
||||
@@ -214,8 +531,21 @@ export function updateWeatherEffect() {
|
||||
const weather = getCurrentWeather();
|
||||
const weatherType = parseWeatherType(weather);
|
||||
|
||||
// Don't recreate if weather hasn't changed
|
||||
if (weatherType === currentWeatherType) {
|
||||
// Get current time of day
|
||||
const timeStr = getCurrentTime();
|
||||
const hour = parseHourFromTime(timeStr);
|
||||
const timeOfDay = getTimeOfDay(hour);
|
||||
|
||||
// If only the hour changed (same weather and time of day), just update celestial position
|
||||
if (weatherType === currentWeatherType && timeOfDay === currentTimeOfDay && hour !== currentHour) {
|
||||
if (updateCelestialPosition(hour)) {
|
||||
currentHour = hour;
|
||||
return; // Successfully updated position without recreating
|
||||
}
|
||||
}
|
||||
|
||||
// Don't recreate if nothing has changed
|
||||
if (weatherType === currentWeatherType && timeOfDay === currentTimeOfDay && hour === currentHour) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -228,6 +558,8 @@ export function updateWeatherEffect() {
|
||||
}
|
||||
|
||||
currentWeatherType = weatherType;
|
||||
currentTimeOfDay = timeOfDay;
|
||||
currentHour = hour;
|
||||
|
||||
switch (weatherType) {
|
||||
case 'snow':
|
||||
@@ -240,7 +572,12 @@ export function updateWeatherEffect() {
|
||||
weatherContainer = createMist();
|
||||
break;
|
||||
case 'sunny':
|
||||
weatherContainer = createSunshine();
|
||||
// Use nighttime effect for clear weather at night
|
||||
if (timeOfDay === 'night') {
|
||||
weatherContainer = createNighttime(hour);
|
||||
} else {
|
||||
weatherContainer = createSunshine(hour);
|
||||
}
|
||||
break;
|
||||
case 'wind':
|
||||
weatherContainer = createWind();
|
||||
@@ -273,8 +610,10 @@ export function updateWeatherEffect() {
|
||||
// Apply z-index based on background/foreground settings
|
||||
if (extensionSettings.weatherForeground) {
|
||||
weatherContainer.style.zIndex = '9998'; // In front of chat
|
||||
weatherContainer.classList.add('rpg-weather-foreground');
|
||||
} else if (extensionSettings.weatherBackground) {
|
||||
weatherContainer.style.zIndex = '1'; // Behind chat (default)
|
||||
weatherContainer.classList.remove('rpg-weather-foreground');
|
||||
} else {
|
||||
// Both disabled - don't show weather
|
||||
return;
|
||||
|
||||
@@ -9524,11 +9524,11 @@ body[data-theme="cyberpunk"] .rpg-music-widget-play {
|
||||
/* Snowfall animation */
|
||||
@keyframes rpg-snowfall {
|
||||
0% {
|
||||
transform: translateY(0vh) rotate(0deg);
|
||||
transform: translateY(0) rotate(0deg);
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(100vh) rotate(360deg);
|
||||
transform: translateY(100dvh) rotate(360deg);
|
||||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
@@ -9598,7 +9598,7 @@ body[data-theme="cyberpunk"] .rpg-music-widget-play {
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(100vh);
|
||||
transform: translateY(100dvh);
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
@@ -9643,40 +9643,487 @@ body[data-theme="cyberpunk"] .rpg-music-widget-play {
|
||||
background: radial-gradient(ellipse at center, rgba(220, 220, 240, 0.25) 0%, transparent 70%);
|
||||
}
|
||||
|
||||
/* Sunshine rays */
|
||||
.rpg-sunray {
|
||||
width: 3px;
|
||||
height: 100dvh;
|
||||
background: linear-gradient(to bottom,
|
||||
rgba(255, 250, 200, 0) 0%,
|
||||
rgba(255, 250, 200, 0.3) 20%,
|
||||
rgba(255, 250, 200, 0.15) 50%,
|
||||
rgba(255, 250, 200, 0) 100%);
|
||||
transform-origin: top;
|
||||
animation: rpg-sunrayShine ease-in-out infinite;
|
||||
top: -20%;
|
||||
filter: blur(2px);
|
||||
/* Clear/Sunny Weather Effects */
|
||||
|
||||
/* Container modifier for clear weather */
|
||||
.rpg-clear-weather {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes rpg-sunrayShine {
|
||||
/* Warm ambient glow overlay */
|
||||
.rpg-clear-ambient-glow {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: radial-gradient(ellipse at 15% 10%,
|
||||
rgba(255, 245, 200, 0.15) 0%,
|
||||
rgba(255, 240, 180, 0.08) 30%,
|
||||
transparent 70%);
|
||||
animation: rpg-clear-ambient-pulse 8s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes rpg-clear-ambient-pulse {
|
||||
0%, 100% {
|
||||
opacity: 0.2;
|
||||
transform: translateY(0) scaleY(1);
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.4;
|
||||
transform: translateY(5%) scaleY(1.05);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.rpg-sunray:nth-child(2n) {
|
||||
width: 4px;
|
||||
animation-duration: 10s;
|
||||
/* Floating dust motes / pollen particles */
|
||||
.rpg-clear-dust-mote {
|
||||
position: fixed;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 30% 30%,
|
||||
rgba(255, 250, 220, 0.9) 0%,
|
||||
rgba(255, 235, 180, 0.6) 50%,
|
||||
rgba(255, 220, 150, 0) 100%);
|
||||
box-shadow: 0 0 6px 2px rgba(255, 245, 200, 0.4);
|
||||
animation: rpg-clear-dust-float linear infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.rpg-sunray:nth-child(3n) {
|
||||
@keyframes rpg-clear-dust-float {
|
||||
0% {
|
||||
transform: translate(0, 0) scale(1);
|
||||
opacity: 0;
|
||||
}
|
||||
10% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
50% {
|
||||
transform: translate(30px, -50px) scale(1.2);
|
||||
opacity: 0.6;
|
||||
}
|
||||
90% {
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: translate(60px, -100px) scale(1);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Soft drifting light orbs */
|
||||
.rpg-clear-light-orb {
|
||||
position: fixed;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at center,
|
||||
rgba(255, 250, 230, 0.12) 0%,
|
||||
rgba(255, 245, 200, 0.06) 40%,
|
||||
transparent 70%);
|
||||
filter: blur(20px);
|
||||
animation: rpg-clear-orb-drift ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes rpg-clear-orb-drift {
|
||||
0%, 100% {
|
||||
transform: translate(0, 0) scale(1);
|
||||
opacity: 0.4;
|
||||
}
|
||||
25% {
|
||||
transform: translate(20px, 15px) scale(1.1);
|
||||
opacity: 0.6;
|
||||
}
|
||||
50% {
|
||||
transform: translate(-10px, 30px) scale(0.95);
|
||||
opacity: 0.5;
|
||||
}
|
||||
75% {
|
||||
transform: translate(-25px, 10px) scale(1.05);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sun - positioned dynamically based on hour */
|
||||
.rpg-clear-sun {
|
||||
position: fixed;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: radial-gradient(circle at 40% 40%,
|
||||
rgba(255, 255, 240, 1) 0%,
|
||||
rgba(255, 250, 200, 1) 30%,
|
||||
rgba(255, 220, 120, 0.9) 60%,
|
||||
rgba(255, 180, 80, 0.6) 80%,
|
||||
rgba(255, 150, 50, 0) 100%);
|
||||
border-radius: 50%;
|
||||
box-shadow:
|
||||
0 0 30px 10px rgba(255, 220, 100, 0.5),
|
||||
0 0 60px 20px rgba(255, 200, 80, 0.3),
|
||||
0 0 100px 40px rgba(255, 180, 60, 0.15);
|
||||
transform: translate(-50%, -50%);
|
||||
animation: rpg-clear-sun-pulse 8s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes rpg-clear-sun-pulse {
|
||||
0%, 100% {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
box-shadow:
|
||||
0 0 30px 10px rgba(255, 220, 100, 0.5),
|
||||
0 0 60px 20px rgba(255, 200, 80, 0.3),
|
||||
0 0 100px 40px rgba(255, 180, 60, 0.15);
|
||||
}
|
||||
50% {
|
||||
transform: translate(-50%, -50%) scale(1.05);
|
||||
box-shadow:
|
||||
0 0 35px 12px rgba(255, 220, 100, 0.6),
|
||||
0 0 70px 25px rgba(255, 200, 80, 0.35),
|
||||
0 0 110px 45px rgba(255, 180, 60, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sun glow aura */
|
||||
.rpg-clear-sun-glow {
|
||||
position: fixed;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
background: radial-gradient(circle at center,
|
||||
rgba(255, 240, 180, 0.25) 0%,
|
||||
rgba(255, 220, 150, 0.15) 30%,
|
||||
rgba(255, 200, 120, 0.08) 50%,
|
||||
transparent 70%);
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
filter: blur(8px);
|
||||
animation: rpg-clear-sun-glow-pulse 6s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes rpg-clear-sun-glow-pulse {
|
||||
0%, 100% {
|
||||
opacity: 0.7;
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) scale(1.15);
|
||||
}
|
||||
}
|
||||
|
||||
/* Lens flare effect */
|
||||
.rpg-clear-lens-flare {
|
||||
position: fixed;
|
||||
top: 5%;
|
||||
left: 10%;
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
background: radial-gradient(circle at center,
|
||||
rgba(255, 255, 255, 0.3) 0%,
|
||||
rgba(255, 250, 220, 0.15) 20%,
|
||||
rgba(255, 240, 180, 0.08) 40%,
|
||||
transparent 60%);
|
||||
border-radius: 50%;
|
||||
animation: rpg-clear-lens-flare-pulse 6s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.rpg-clear-lens-flare::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
transform: translate(-50%, -50%);
|
||||
background: radial-gradient(circle at center,
|
||||
rgba(255, 255, 255, 0.5) 0%,
|
||||
rgba(255, 250, 230, 0.2) 50%,
|
||||
transparent 70%);
|
||||
border-radius: 50%;
|
||||
animation: rpg-clear-lens-core-pulse 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.rpg-clear-lens-flare::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 120%;
|
||||
left: 150%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: radial-gradient(circle at center,
|
||||
rgba(255, 200, 150, 0.4) 0%,
|
||||
rgba(255, 180, 100, 0.1) 50%,
|
||||
transparent 70%);
|
||||
border-radius: 50%;
|
||||
animation: rpg-clear-secondary-flare 8s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes rpg-clear-lens-flare-pulse {
|
||||
0%, 100% {
|
||||
opacity: 0.7;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rpg-clear-lens-core-pulse {
|
||||
0%, 100% {
|
||||
opacity: 0.8;
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, -50%) scale(1.15);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rpg-clear-secondary-flare {
|
||||
0%, 100% {
|
||||
opacity: 0.3;
|
||||
transform: scale(1);
|
||||
}
|
||||
30% {
|
||||
opacity: 0.6;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
70% {
|
||||
opacity: 0.4;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== Nighttime Clear Weather Effects ===== */
|
||||
|
||||
/* Container modifier for night weather */
|
||||
.rpg-night-weather {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Dark blue night overlay */
|
||||
.rpg-night-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(to bottom,
|
||||
rgba(10, 15, 40, 0.3) 0%,
|
||||
rgba(15, 25, 55, 0.2) 40%,
|
||||
rgba(20, 30, 60, 0.15) 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Moon */
|
||||
.rpg-night-moon {
|
||||
position: fixed;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
background: radial-gradient(circle at 35% 35%,
|
||||
rgba(255, 255, 245, 1) 0%,
|
||||
rgba(240, 240, 230, 0.95) 40%,
|
||||
rgba(220, 220, 210, 0.9) 60%,
|
||||
rgba(200, 200, 195, 0.8) 80%,
|
||||
rgba(180, 180, 175, 0) 100%);
|
||||
border-radius: 50%;
|
||||
box-shadow:
|
||||
0 0 20px 5px rgba(255, 255, 240, 0.4),
|
||||
0 0 40px 10px rgba(200, 210, 255, 0.2),
|
||||
inset -8px -5px 15px rgba(150, 150, 140, 0.3);
|
||||
animation: rpg-night-moon-float 30s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes rpg-night-moon-float {
|
||||
0%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(10px);
|
||||
}
|
||||
}
|
||||
|
||||
/* Moon glow aura */
|
||||
.rpg-night-moon-glow {
|
||||
position: fixed;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
background: radial-gradient(circle at center,
|
||||
rgba(200, 220, 255, 0.15) 0%,
|
||||
rgba(180, 200, 240, 0.08) 40%,
|
||||
transparent 70%);
|
||||
border-radius: 50%;
|
||||
filter: blur(10px);
|
||||
animation: rpg-night-moon-glow-pulse 8s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes rpg-night-moon-glow-pulse {
|
||||
0%, 100% {
|
||||
opacity: 0.6;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Twinkling stars */
|
||||
.rpg-night-star {
|
||||
position: fixed;
|
||||
background: radial-gradient(circle at center,
|
||||
rgba(255, 255, 255, 1) 0%,
|
||||
rgba(200, 220, 255, 0.8) 40%,
|
||||
transparent 70%);
|
||||
border-radius: 50%;
|
||||
animation: rpg-night-star-twinkle ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes rpg-night-star-twinkle {
|
||||
0%, 100% {
|
||||
opacity: 0.3;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.3);
|
||||
}
|
||||
}
|
||||
|
||||
/* Brighter stars with cross-flare effect */
|
||||
.rpg-night-star-bright {
|
||||
width: 4px !important;
|
||||
height: 4px !important;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
box-shadow:
|
||||
0 0 4px 2px rgba(255, 255, 255, 0.8),
|
||||
0 0 8px 4px rgba(200, 220, 255, 0.4);
|
||||
}
|
||||
|
||||
.rpg-night-star-bright::before,
|
||||
.rpg-night-star-bright::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
rgba(255, 255, 255, 0.8) 45%,
|
||||
rgba(255, 255, 255, 1) 50%,
|
||||
rgba(255, 255, 255, 0.8) 55%,
|
||||
transparent 100%);
|
||||
}
|
||||
|
||||
.rpg-night-star-bright::before {
|
||||
width: 16px;
|
||||
height: 2px;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.rpg-night-star-bright::after {
|
||||
width: 2px;
|
||||
animation-duration: 12s;
|
||||
height: 16px;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
/* Fireflies */
|
||||
.rpg-night-firefly {
|
||||
position: fixed;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: radial-gradient(circle at center,
|
||||
rgba(200, 255, 150, 1) 0%,
|
||||
rgba(180, 255, 120, 0.8) 30%,
|
||||
rgba(150, 230, 100, 0.4) 60%,
|
||||
transparent 100%);
|
||||
border-radius: 50%;
|
||||
box-shadow:
|
||||
0 0 6px 3px rgba(180, 255, 120, 0.6),
|
||||
0 0 12px 6px rgba(150, 230, 100, 0.3);
|
||||
animation: rpg-night-firefly-float ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes rpg-night-firefly-float {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translate(0, 0) scale(0.5);
|
||||
}
|
||||
10% {
|
||||
opacity: 1;
|
||||
transform: translate(5px, -10px) scale(1);
|
||||
}
|
||||
30% {
|
||||
opacity: 0.3;
|
||||
transform: translate(-15px, -25px) scale(0.8);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: translate(10px, -40px) scale(1.1);
|
||||
}
|
||||
70% {
|
||||
opacity: 0.4;
|
||||
transform: translate(-10px, -55px) scale(0.9);
|
||||
}
|
||||
90% {
|
||||
opacity: 0.8;
|
||||
transform: translate(5px, -70px) scale(1);
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translate(0, -80px) scale(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* Shooting star */
|
||||
.rpg-night-shooting-star {
|
||||
position: fixed;
|
||||
top: 15%;
|
||||
left: -10%;
|
||||
width: 100px;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg,
|
||||
transparent 0%,
|
||||
rgba(255, 255, 255, 0.2) 20%,
|
||||
rgba(255, 255, 255, 0.8) 60%,
|
||||
rgba(255, 255, 255, 1) 100%);
|
||||
border-radius: 50%;
|
||||
transform: rotate(35deg);
|
||||
animation: rpg-night-shooting-star-fly 12s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.rpg-night-shooting-star::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: rgba(255, 255, 255, 1);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 10px 4px rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
@keyframes rpg-night-shooting-star-fly {
|
||||
0%, 85%, 100% {
|
||||
opacity: 0;
|
||||
left: -10%;
|
||||
top: 15%;
|
||||
}
|
||||
88% {
|
||||
opacity: 1;
|
||||
}
|
||||
95% {
|
||||
opacity: 1;
|
||||
left: 110%;
|
||||
top: 45%;
|
||||
}
|
||||
96% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile optimizations */
|
||||
@@ -9689,9 +10136,95 @@ body[data-theme="cyberpunk"] .rpg-music-widget-play {
|
||||
animation-duration: 20s;
|
||||
}
|
||||
|
||||
.rpg-sunray {
|
||||
/* Clear weather mobile optimizations */
|
||||
.rpg-clear-dust-mote {
|
||||
animation-duration: 15s;
|
||||
}
|
||||
|
||||
.rpg-clear-light-orb {
|
||||
animation-duration: 25s;
|
||||
filter: blur(15px);
|
||||
}
|
||||
|
||||
.rpg-clear-lens-flare {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
/* Sun mobile optimizations */
|
||||
.rpg-clear-sun {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.rpg-clear-sun-glow {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
/* Nighttime mobile optimizations */
|
||||
.rpg-night-moon {
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.rpg-night-moon-glow {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
}
|
||||
|
||||
.rpg-night-firefly {
|
||||
animation-duration: 12s;
|
||||
}
|
||||
|
||||
.rpg-night-shooting-star {
|
||||
animation-duration: 18s;
|
||||
}
|
||||
}
|
||||
|
||||
/* Foreground mode - reduced opacity for celestial bodies to not obstruct content */
|
||||
.rpg-weather-foreground .rpg-clear-sun {
|
||||
opacity: 0.5;
|
||||
box-shadow:
|
||||
0 0 20px 8px rgba(255, 220, 100, 0.3),
|
||||
0 0 40px 15px rgba(255, 200, 80, 0.15),
|
||||
0 0 60px 25px rgba(255, 180, 60, 0.08);
|
||||
}
|
||||
|
||||
.rpg-weather-foreground .rpg-clear-sun-glow {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.rpg-weather-foreground .rpg-clear-lens-flare {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.rpg-weather-foreground .rpg-clear-ambient-glow {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.rpg-weather-foreground .rpg-night-moon {
|
||||
opacity: 0.7;
|
||||
box-shadow:
|
||||
0 0 15px 4px rgba(255, 255, 240, 0.25),
|
||||
0 0 30px 8px rgba(200, 210, 255, 0.12);
|
||||
}
|
||||
|
||||
.rpg-weather-foreground .rpg-night-moon-glow {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.rpg-weather-foreground .rpg-night-overlay {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.rpg-weather-foreground .rpg-night-star,
|
||||
.rpg-weather-foreground .rpg-night-star-bright {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.rpg-weather-foreground .rpg-night-firefly {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Lightning flash effect */
|
||||
|
||||
Reference in New Issue
Block a user