docs: Update levels.md to reflect actual XP system and add user feedback analysis
- Fixed level XP requirements to match exponential scaling system - Updated achievements to reflect actual implementation - Removed unlock features that aren't implemented yet - Added comprehensive user feedback analysis with actionable items - Corrected Level 14 name from Hedonistic to Submissive - Updated progression tips for realistic XP earning strategies
This commit is contained in:
parent
c40ed278e0
commit
fabba36fe6
Binary file not shown.
|
|
@ -0,0 +1,154 @@
|
||||||
|
# 🎯 User Feedback Analysis - November 2025
|
||||||
|
|
||||||
|
## 📝 Feedback Summary
|
||||||
|
|
||||||
|
**Overall Impression**: Positive user experience with regular usage during gooning sessions. User appreciates responsiveness and speed, but identifies key areas for improvement in customization, persistence, and bug fixes.
|
||||||
|
|
||||||
|
**User Profile**: Self-described "Porn Designer" with experience in captions and video creation, offering asset collaboration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 UI/UX Feedback
|
||||||
|
|
||||||
|
### ✅ **Positive**
|
||||||
|
- Background video deactivation feature appreciated
|
||||||
|
- Button responsiveness and speed are excellent
|
||||||
|
- Overall interface functions well
|
||||||
|
|
||||||
|
### 🔧 **Areas for Improvement**
|
||||||
|
- **Background Video**: Either disable by default or add opacity mask
|
||||||
|
- **Quick Play Mode**: Needs more detailed descriptions and guidance
|
||||||
|
- **User Guidance**: More explanatory text throughout interface
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 Design & Customization Requests
|
||||||
|
|
||||||
|
### 🎯 **High Priority**
|
||||||
|
- **Theme Customization**: Beyond purple color scheme
|
||||||
|
- **Profile System**: Personal profile with custom image upload
|
||||||
|
- **Preset Management**: Save/load session configurations
|
||||||
|
- **Persistence**: Retain customizations between sessions
|
||||||
|
|
||||||
|
### 💡 **User Suggestion**
|
||||||
|
- User offers collaboration on asset creation (captions, videos)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🐛 Bug Reports
|
||||||
|
|
||||||
|
### 🚨 **Critical Issues**
|
||||||
|
1. **Cinema Breakage**: Cinema mode crashes requiring restart
|
||||||
|
2. **Task-Porn Attribution**: Some tasks missing associated content (tagging issue)
|
||||||
|
3. **Gamification Tracking**: No interaction detection - 20 minute idle session still counted
|
||||||
|
|
||||||
|
### 🔍 **Technical Details**
|
||||||
|
- Interaction detection system needs implementation
|
||||||
|
- Content tagging system requires review
|
||||||
|
- Cinema stability issues need investigation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎮 Gamification & Features
|
||||||
|
|
||||||
|
### ✅ **Working Well**
|
||||||
|
- User sees improvements in gaming tracking
|
||||||
|
- Regular usage indicates good engagement
|
||||||
|
|
||||||
|
### 🎯 **Enhancement Requests**
|
||||||
|
- **More Achievements**: Expand achievement system
|
||||||
|
- **Story Mode**: Narrative-driven gooning experience
|
||||||
|
- **Better Progress Tracking**: More sophisticated interaction detection
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Action Item Priorities
|
||||||
|
|
||||||
|
### 🔥 **Critical (Fix Immediately)**
|
||||||
|
|
||||||
|
#### 🐛 Bug Fixes
|
||||||
|
- [ ] **Fix Cinema Crashes** - Investigate and resolve cinema mode stability
|
||||||
|
- [ ] **Implement Interaction Detection** - Prevent idle time from counting toward statistics
|
||||||
|
- [ ] **Fix Task-Porn Attribution** - Review and fix content tagging system
|
||||||
|
|
||||||
|
#### 🎨 UX Improvements
|
||||||
|
- [ ] **Default Background Video Off** - Make background video disabled by default OR add opacity mask
|
||||||
|
- [ ] **Add Quick Play Descriptions** - Provide detailed explanations for quick play features
|
||||||
|
|
||||||
|
### 🎯 **High Priority (Next Sprint)**
|
||||||
|
|
||||||
|
#### 💾 Persistence System
|
||||||
|
- [ ] **Implement Preset Saving** - Allow users to save session configurations
|
||||||
|
- [ ] **Add Preset Loading** - Quick restoration of saved settings
|
||||||
|
- [ ] **Session Memory** - Retain customizations between app restarts
|
||||||
|
|
||||||
|
#### 🎨 Customization System
|
||||||
|
- [ ] **Theme Engine** - Expandable color scheme system beyond purple
|
||||||
|
- [ ] **Profile System** - User profile with custom image upload
|
||||||
|
- [ ] **Custom Asset Management** - Framework for user-contributed content
|
||||||
|
|
||||||
|
### 🚀 **Medium Priority (Future Development)**
|
||||||
|
|
||||||
|
#### 🎮 Enhanced Gamification
|
||||||
|
- [ ] **Expanded Achievement System** - More varied and engaging achievements
|
||||||
|
- [ ] **Interaction Tracking** - Sophisticated user engagement detection
|
||||||
|
- [ ] **Progress Analytics** - Better tracking and feedback systems
|
||||||
|
|
||||||
|
#### 📖 New Features
|
||||||
|
- [ ] **Story Mode Development** - Narrative-driven experience system
|
||||||
|
- [ ] **Asset Collaboration Framework** - System for community contributions
|
||||||
|
- [ ] **Advanced Customization Options** - Deeper personalization features
|
||||||
|
|
||||||
|
### 📝 **Documentation & Planning**
|
||||||
|
- [ ] **User Onboarding Guide** - Comprehensive quick play explanations
|
||||||
|
- [ ] **Asset Contribution Guidelines** - Framework for user-generated content
|
||||||
|
- [ ] **Story Mode Design Document** - Detailed narrative system planning
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🤝 Collaboration Opportunities
|
||||||
|
|
||||||
|
**User Offers**:
|
||||||
|
- Caption creation assistance
|
||||||
|
- Video asset development
|
||||||
|
- Story mode content collaboration
|
||||||
|
|
||||||
|
**Action**: Reach out to establish collaboration framework and contribution guidelines.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 Success Metrics
|
||||||
|
|
||||||
|
**Current State**:
|
||||||
|
- ✅ Regular user engagement
|
||||||
|
- ✅ Positive response to core functionality
|
||||||
|
- ✅ User willing to contribute content
|
||||||
|
|
||||||
|
**Target Improvements**:
|
||||||
|
- 🎯 Zero critical crashes
|
||||||
|
- 🎯 Persistent user preferences
|
||||||
|
- 🎯 Enhanced customization options
|
||||||
|
- 🎯 Robust interaction detection
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗓️ Implementation Timeline
|
||||||
|
|
||||||
|
### **Week 1-2**: Critical Bug Fixes
|
||||||
|
- Cinema stability
|
||||||
|
- Interaction detection
|
||||||
|
- Task attribution
|
||||||
|
|
||||||
|
### **Week 3-4**: Core UX Improvements
|
||||||
|
- Background video defaults
|
||||||
|
- Quick play descriptions
|
||||||
|
- Basic preset system
|
||||||
|
|
||||||
|
### **Month 2**: Advanced Features
|
||||||
|
- Full theme engine
|
||||||
|
- Profile system
|
||||||
|
- Story mode foundation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*This feedback indicates a engaged user with valuable insights and collaboration potential. Priority should be on fixing critical bugs while building the persistence and customization systems that will significantly improve user experience.*
|
||||||
200
docs/levels.md
200
docs/levels.md
|
|
@ -1,17 +1,18 @@
|
||||||
# 🎯 Level System - webGame
|
# 🎯 Level System - webGame
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
The webGame uses an XP-based progression system where players advance through levels by earning Experience Points (XP) across all game modes. Each level requires 100 XP to advance, providing a steady and rewarding progression curve.
|
The webGame uses an exponential XP-based progression system where players advance through levels by earning Experience Points (XP) across all game modes. Each level requires increasing amounts of XP, creating a challenging but rewarding progression curve.
|
||||||
|
|
||||||
**Base Formula:** Level = (Total XP ÷ 100) + 1
|
**Level Formula:** Dynamic XP requirements with exponential scaling for higher levels
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## XP Sources
|
## XP Sources
|
||||||
|
|
||||||
### 📺 Porn Cinema
|
### 📺 Porn Cinema
|
||||||
- **Watch Time XP**: 1 XP per 5 minutes of viewing
|
- **Watch Time XP**: 1 XP per 5 minutes of continuous viewing
|
||||||
- **Video Completion**: Videos must reach 90% completion to count toward stats
|
- **Video Completion**: Videos must reach 90% completion to count toward statistics
|
||||||
|
- **Continuous Tracking**: XP awarded every 30 seconds during active viewing
|
||||||
|
|
||||||
### ⚡ Quick Play
|
### ⚡ Quick Play
|
||||||
- **Task Completion XP**:
|
- **Task Completion XP**:
|
||||||
|
|
@ -34,219 +35,136 @@ The webGame uses an XP-based progression system where players advance through le
|
||||||
## Level Breakdown (1-20)
|
## Level Breakdown (1-20)
|
||||||
|
|
||||||
### 🌱 **Level 1** - *Virgin*
|
### 🌱 **Level 1** - *Virgin*
|
||||||
- **XP Required**: 0-99 XP
|
- **XP Required**: 0 XP
|
||||||
- **Description**: Welcome to webGame! Your first taste of pleasure awaits.
|
- **Description**: Welcome to webGame! Your first taste of pleasure awaits.
|
||||||
- **Unlocks**:
|
|
||||||
- Access to all basic game modes
|
|
||||||
- Profile customization
|
|
||||||
- Basic statistics tracking
|
|
||||||
|
|
||||||
### 🌿 **Level 2** - *Curious*
|
### 🌿 **Level 2** - *Curious*
|
||||||
- **XP Required**: 100-199 XP
|
- **XP Required**: 10 XP
|
||||||
- **Description**: Your curiosity is growing! Start exploring your desires.
|
- **Description**: Your curiosity is growing! Start exploring your desires.
|
||||||
- **Unlocks**:
|
|
||||||
- Achievement "First Steps" available
|
|
||||||
- Enhanced stat tracking
|
|
||||||
- Basic playlist creation
|
|
||||||
|
|
||||||
### 🌱 **Level 3** - *Eager*
|
### 🌱 **Level 3** - *Eager*
|
||||||
- **XP Required**: 200-299 XP
|
- **XP Required**: 25 XP
|
||||||
- **Description**: An eager participant! You're developing your appetites.
|
- **Description**: An eager participant! You're developing your appetites.
|
||||||
- **Unlocks**:
|
|
||||||
- Achievement "Early Bird" available
|
|
||||||
- Extended session tracking
|
|
||||||
- Profile bio customization
|
|
||||||
|
|
||||||
### 🌿 **Level 4** - *Aroused*
|
### 🌿 **Level 4** - *Aroused*
|
||||||
- **XP Required**: 300-399 XP
|
- **XP Required**: 48 XP
|
||||||
- **Description**: Your arousal is building! Keep feeding your desires.
|
- **Description**: Your arousal is building! Keep feeding your desires.
|
||||||
- **Unlocks**:
|
|
||||||
- Advanced statistics dashboard
|
|
||||||
- Streak tracking begins
|
|
||||||
- Achievement "Marathon Viewer" available
|
|
||||||
|
|
||||||
### 🌟 **Level 5** - *Lustful*
|
### 🌟 **Level 5** - *Lustful*
|
||||||
- **XP Required**: 400-499 XP
|
- **XP Required**: 82 XP
|
||||||
- **Description**: Consumed by lust and craving more experiences!
|
- **Description**: Consumed by lust and craving more experiences!
|
||||||
- **Unlocks**:
|
|
||||||
- Achievement "Level Up" unlocked
|
|
||||||
- Enhanced profile features
|
|
||||||
- Priority support features
|
|
||||||
|
|
||||||
### 🔥 **Level 6** - *Passionate*
|
### 🔥 **Level 6** - *Passionate*
|
||||||
- **XP Required**: 500-599 XP
|
- **XP Required**: 133 XP
|
||||||
- **Description**: Your passion burns hot with unbridled desire.
|
- **Description**: Your passion burns hot with unbridled desire.
|
||||||
- **Unlocks**:
|
|
||||||
- Achievement "XP Master" unlocked
|
|
||||||
- Advanced playlist management
|
|
||||||
- Custom themes (future feature)
|
|
||||||
|
|
||||||
### ⭐ **Level 7** - *Addicted*
|
### ⭐ **Level 7** - *Addicted*
|
||||||
- **XP Required**: 600-699 XP
|
- **XP Required**: 209 XP
|
||||||
- **Description**: Hopelessly addicted to the rush of pleasure!
|
- **Description**: Hopelessly addicted to the rush of pleasure!
|
||||||
- **Unlocks**:
|
|
||||||
- Achievement "Consistency King" available
|
|
||||||
- Extended activity history
|
|
||||||
- Advanced export options
|
|
||||||
|
|
||||||
### 🎯 **Level 8** - *Obsessed*
|
### 🎯 **Level 8** - *Obsessed*
|
||||||
- **XP Required**: 700-799 XP
|
- **XP Required**: 323 XP
|
||||||
- **Description**: Your obsession drives you to new heights of ecstasy.
|
- **Description**: Your obsession drives you to new heights of ecstasy.
|
||||||
- **Unlocks**:
|
|
||||||
- Achievement "Playlist Curator" available
|
|
||||||
- Enhanced statistics breakdown
|
|
||||||
- Advanced profile customization
|
|
||||||
|
|
||||||
### 🏆 **Level 9** - *Deviant*
|
### 🏆 **Level 9** - *Deviant*
|
||||||
- **XP Required**: 800-899 XP
|
- **XP Required**: 494 XP
|
||||||
- **Description**: A true deviant exploring the depths of desire!
|
- **Description**: A true deviant exploring the depths of desire!
|
||||||
- **Unlocks**:
|
|
||||||
- Multiple achievement categories
|
|
||||||
- Detailed performance analytics
|
|
||||||
- Achievement showcase features
|
|
||||||
|
|
||||||
### 💎 **Level 10** - *Kinky*
|
### 💎 **Level 10** - *Kinky*
|
||||||
- **XP Required**: 900-999 XP
|
- **XP Required**: 751 XP
|
||||||
- **Description**: Welcome to the kinky elite! Your fetishes are flourishing.
|
- **Description**: Welcome to the kinky elite! Your fetishes are flourishing.
|
||||||
- **Unlocks**:
|
|
||||||
- Elite status badge
|
|
||||||
- Advanced features access
|
|
||||||
- Achievement "Video Collector" available
|
|
||||||
|
|
||||||
### 🌟 **Level 11** - *Perverted*
|
### 🌟 **Level 11** - *Perverted*
|
||||||
- **XP Required**: 1000-1099 XP
|
- **XP Required**: 1,137 XP
|
||||||
- **Description**: A seasoned pervert with extensive experience in debauchery.
|
- **Description**: A seasoned pervert with extensive experience in debauchery.
|
||||||
- **Unlocks**:
|
|
||||||
- Veteran status recognition
|
|
||||||
- Historical data archives
|
|
||||||
- Legacy achievement tracking
|
|
||||||
|
|
||||||
### 🔮 **Level 12** - *Depraved*
|
### 🔮 **Level 12** - *Depraved*
|
||||||
- **XP Required**: 1100-1199 XP
|
- **XP Required**: 1,716 XP
|
||||||
- **Description**: You've mastered the art of depravity and corruption!
|
- **Description**: You've mastered the art of depravity and corruption!
|
||||||
- **Unlocks**:
|
|
||||||
- Master tier privileges
|
|
||||||
- Advanced customization options
|
|
||||||
- Exclusive features access
|
|
||||||
|
|
||||||
### 👑 **Level 13** - *Dominant*
|
### 👑 **Level 13** - *Dominant*
|
||||||
- **XP Required**: 1200-1299 XP
|
- **XP Required**: 2,585 XP
|
||||||
- **Description**: A dominant force commanding respect and submission!
|
- **Description**: A dominant force commanding respect and submission!
|
||||||
- **Unlocks**:
|
|
||||||
- Champion status badge
|
|
||||||
- Premium feature previews
|
|
||||||
- Community recognition features
|
|
||||||
|
|
||||||
### 🚀 **Level 14** - *Hedonistic*
|
### 🚀 **Level 14** - *Submissive*
|
||||||
- **XP Required**: 1300-1399 XP
|
- **XP Required**: 3,889 XP
|
||||||
|
- **Description**: Embracing submission and control in the pursuit of pleasure!
|
||||||
|
|
||||||
|
### 🌌 **Level 15** - *Hedonist*
|
||||||
|
- **XP Required**: 5,844 XP
|
||||||
- **Description**: Pure hedonism guides your every pleasure-seeking move!
|
- **Description**: Pure hedonism guides your every pleasure-seeking move!
|
||||||
- **Unlocks**:
|
|
||||||
- Legendary badge and privileges
|
|
||||||
- Beta feature access
|
|
||||||
- Advanced analytics dashboard
|
|
||||||
|
|
||||||
### 🌌 **Level 15** - *Decadent*
|
|
||||||
- **XP Required**: 1400-1499 XP
|
|
||||||
- **Description**: You've reached decadent levels of indulgence!
|
|
||||||
- **Unlocks**:
|
|
||||||
- Cosmic tier recognition
|
|
||||||
- Exclusive content access
|
|
||||||
- Advanced progression tracking
|
|
||||||
|
|
||||||
### ⚡ **Level 16** - *Insatiable*
|
### ⚡ **Level 16** - *Insatiable*
|
||||||
- **XP Required**: 1500-1599 XP
|
- **XP Required**: 8,777 XP
|
||||||
- **Description**: Your insatiable appetite knows no bounds!
|
- **Description**: Your insatiable appetite knows no bounds!
|
||||||
- **Unlocks**:
|
|
||||||
- Transcendent status
|
|
||||||
- Ultimate customization options
|
|
||||||
- Developer preview features
|
|
||||||
|
|
||||||
### 🔥 **Level 17** - *Sinful*
|
### 🔥 **Level 17** - *Transcendent*
|
||||||
- **XP Required**: 1600-1699 XP
|
- **XP Required**: 13,176 XP
|
||||||
- **Description**: Deliciously sinful dedication to carnal pleasures!
|
- **Description**: Transcending mortal desire and reaching new heights!
|
||||||
- **Unlocks**:
|
|
||||||
- Immortal tier privileges
|
|
||||||
- Lifetime achievement tracking
|
|
||||||
- Exclusive immortal features
|
|
||||||
|
|
||||||
### 🌠 **Level 18** - *Forbidden*
|
### 🌠 **Level 18** - *Enlightened*
|
||||||
- **XP Required**: 1700-1799 XP
|
- **XP Required**: 19,775 XP
|
||||||
- **Description**: You've achieved forbidden status in the realm of desire!
|
- **Description**: You've achieved enlightened pleasure in the realm of desire!
|
||||||
- **Unlocks**:
|
|
||||||
- Celestial recognition
|
|
||||||
- Advanced achievement categories
|
|
||||||
- Special celestial features
|
|
||||||
|
|
||||||
### 🏛️ **Level 19** - *Godlike*
|
### 🏛️ **Level 19** - *Godlike*
|
||||||
- **XP Required**: 1800-1899 XP
|
- **XP Required**: 29,673 XP
|
||||||
- **Description**: Godlike levels of sexual prowess and commitment!
|
- **Description**: Godlike levels of sexual prowess and commitment!
|
||||||
- **Unlocks**:
|
|
||||||
- Godlike status badge
|
|
||||||
- Ultimate privileges
|
|
||||||
- All premium features unlocked
|
|
||||||
|
|
||||||
### 👁️ **Level 20** - *Omnipotent*
|
### 👁️ **Level 20** - *Omnipotent*
|
||||||
- **XP Required**: 1900+ XP
|
- **XP Required**: 44,520 XP
|
||||||
- **Description**: You are the ultimate master of pleasure and desire!
|
- **Description**: You are the ultimate master of pleasure and desire!
|
||||||
- **Unlocks**:
|
|
||||||
- Omnipotent status (highest achievable)
|
|
||||||
- All features and privileges
|
|
||||||
- Exclusive omnipotent recognition
|
|
||||||
- Developer acknowledgment
|
|
||||||
- Infinite progression tracking
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🏆 Achievement Integration
|
## 🏆 Achievement Integration
|
||||||
|
|
||||||
### Level-Based Achievements
|
### Level-Based Achievements
|
||||||
- **Level Up** - Reach Level 5 (400+ XP)
|
- **Lustful Awakening** - Reach Level 5 (82 XP)
|
||||||
- **XP Master** - Earn 500+ total XP (Level 6)
|
- **Passionate Soul** - Earn 500+ total XP
|
||||||
- **Elite Status** - Reach Level 10 (900+ XP)
|
- **Kinky Elite** - Reach Level 10 (751 XP)
|
||||||
- **Legendary** - Reach Level 14 (1300+ XP)
|
- **Depraved Master** - Reach Level 12 (1,716 XP)
|
||||||
- **Omnipotent** - Reach Level 20 (1900+ XP)
|
|
||||||
|
|
||||||
### Progression Achievements
|
### Progression Achievements
|
||||||
- **First Steps** - Watch your first video
|
- **First Taste** - Watch your first video
|
||||||
- **Early Bird** - Watch 10 videos
|
- **Early Bird** - Watch 10 videos
|
||||||
- **Marathon Viewer** - Watch for 2+ hours total
|
- **Marathon Viewer** - Watch for 2+ hours total
|
||||||
- **Playlist Curator** - Create 5 playlists
|
- **Playlist Curator** - Create 5 playlists
|
||||||
- **Consistency King** - Maintain a 7-day streak
|
- **Consistency King** - Maintain a 7-day streak
|
||||||
- **Video Collector** - Watch 100 different videos
|
- **Desire Collector** - Watch 100 different videos
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 Progression Tips
|
## 📊 Progression Tips
|
||||||
|
|
||||||
### Efficient XP Earning
|
### Efficient XP Earning
|
||||||
1. **Consistent Daily Activity**: Maintain streaks for bonus achievements
|
1. **Consistent Video Watching**: 1 XP per 5 minutes of viewing in Cinema mode
|
||||||
2. **Complete Tasks**: Finish Quick Play tasks for maximum XP
|
2. **Complete Quick Play Sessions**: Task completion and time bonuses
|
||||||
3. **Watch Full Videos**: Reach 90% completion in Cinema mode
|
3. **Watch Full Videos**: Reach 90% completion for statistics tracking
|
||||||
4. **Engage with Scenarios**: Use webcam features for bonus XP
|
4. **Engage with All Game Modes**: Each mode contributes to total XP
|
||||||
5. **Take Photos**: Earn 2 XP per photo in scenario modes
|
5. **Stay Active**: XP is awarded continuously during active sessions
|
||||||
|
|
||||||
### Level Milestones
|
### Level Milestone Ranges
|
||||||
- **Levels 1-5**: Sexual awakening and building desires (500 XP)
|
- **Levels 1-5**: Initial awakening and exploration (0-82 XP)
|
||||||
- **Levels 6-10**: Corruption and exploring kinks (500 XP)
|
- **Levels 6-10**: Developing passion and addiction (133-751 XP)
|
||||||
- **Levels 11-15**: Embracing perversion and depravity (500 XP)
|
- **Levels 11-15**: Advanced perversion and hedonism (1,137-5,844 XP)
|
||||||
- **Levels 16-20**: Transcendent mastery of carnal arts (400+ XP)
|
- **Levels 16-20**: Transcendent mastery requiring dedication (8,777-44,520 XP)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔮 Future Expansions
|
## 🔮 Future Expansions
|
||||||
|
|
||||||
### Planned Features
|
### Planned Features
|
||||||
- **Level 25+**: Extended progression for dedicated users
|
- **Extended Levels**: Higher level caps for dedicated users
|
||||||
- **Prestige System**: Reset levels for additional bonuses
|
- **Achievement Expansion**: More varied and challenging achievements
|
||||||
- **Seasonal Events**: Temporary XP multipliers and special achievements
|
- **XP Multiplier Events**: Special periods with bonus XP rates
|
||||||
- **Community Features**: Level-based social interactions
|
- **Progress Analytics**: Detailed tracking of XP sources and trends
|
||||||
- **Custom Rewards**: Personalized unlocks based on play style
|
- **Level Rewards**: Visual customizations and special features for high levels
|
||||||
|
|
||||||
### Level Cap Expansion
|
### System Scalability
|
||||||
The system is designed to easily extend beyond Level 20, with potential for:
|
The exponential XP system is designed to accommodate:
|
||||||
- **Level 50**: Ultra-Legendary status
|
- **Higher Level Caps**: Mathematical progression allows infinite scaling
|
||||||
- **Level 100**: Mythical achievements
|
- **Balanced Progression**: Maintains challenge while rewarding dedication
|
||||||
- **Infinite Progression**: No level cap for ultimate dedication
|
- **Achievement Integration**: New achievements can be easily added at any level
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,242 @@
|
||||||
height: 20px;
|
height: 20px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grid Layout Styles */
|
||||||
|
.grid-mode-selector {
|
||||||
|
background: rgba(102, 126, 234, 0.15);
|
||||||
|
border: 2px solid #667eea;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 70vh;
|
||||||
|
background: #000;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid #667eea;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container.single {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container.grid-2x2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
grid-template-rows: 1fr 1fr;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-container.grid-3x3 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
|
grid-template-rows: 1fr 1fr 1fr;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-cell {
|
||||||
|
position: relative;
|
||||||
|
background: #111;
|
||||||
|
border: 1px solid #333;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-cell-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-cell-image.visible {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-cell-info {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
left: 5px;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
color: #fff;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-cell-controls {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-cell-btn {
|
||||||
|
background: rgba(102, 126, 234, 0.8);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-cell-btn:hover {
|
||||||
|
background: rgba(102, 126, 234, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-progress-container {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 3px;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-progress-bar {
|
||||||
|
height: 100%;
|
||||||
|
background: #667eea;
|
||||||
|
width: 0%;
|
||||||
|
transition: width linear;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-controls {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-assignment {
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-assignment {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-assignment label {
|
||||||
|
min-width: 80px;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-assignment select {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0.25rem;
|
||||||
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
border: 1px solid #667eea;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fullscreen Grid Container */
|
||||||
|
.grid-slideshow-container {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: #000;
|
||||||
|
z-index: 1000;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-slideshow-display {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-slideshow-display .grid-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-slideshow-display .grid-cell {
|
||||||
|
border: 1px solid #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-slideshow-info {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
left: 20px;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
z-index: 10;
|
||||||
|
min-width: 200px;
|
||||||
|
opacity: 0.9;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-slideshow-info:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-slideshow-controls {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 30px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
padding: 15px 25px;
|
||||||
|
border-radius: 50px;
|
||||||
|
z-index: 10;
|
||||||
|
opacity: 0.9;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-slideshow-controls:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-slideshow-controls button {
|
||||||
|
background: rgba(102, 126, 234, 0.8);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 25px;
|
||||||
|
padding: 10px 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-slideshow-controls button:hover {
|
||||||
|
background: rgba(102, 126, 234, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-slideshow-controls button:disabled {
|
||||||
|
background: rgba(102, 126, 234, 0.3);
|
||||||
|
cursor: not-allowed;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -533,6 +769,50 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Grid Mode Selection -->
|
||||||
|
<div class="settings-panel">
|
||||||
|
<div class="settings-section">
|
||||||
|
<h3>🔲 Display Mode</h3>
|
||||||
|
<div class="setting-group">
|
||||||
|
<div class="setting-row">
|
||||||
|
<span class="setting-label">Layout Mode:</span>
|
||||||
|
<div class="setting-control">
|
||||||
|
<select id="displayMode" onchange="updateDisplayMode()">
|
||||||
|
<option value="single">Single Slideshow</option>
|
||||||
|
<option value="grid-2x2">2x2 Grid</option>
|
||||||
|
<option value="grid-3x3">3x3 Grid</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Grid View Container -->
|
||||||
|
<div id="gridView" style="display: none;">
|
||||||
|
<div class="grid-container single" id="gridContainer"></div>
|
||||||
|
|
||||||
|
<div class="grid-controls">
|
||||||
|
<button onclick="startGridSlideshow()" class="btn" id="startGridBtn">
|
||||||
|
▶️ Start Grid
|
||||||
|
</button>
|
||||||
|
<button onclick="pauseGridSlideshow()" class="btn" id="pauseGridBtn" disabled>
|
||||||
|
⏸️ Pause Grid
|
||||||
|
</button>
|
||||||
|
<button onclick="stopGridSlideshow()" class="btn" id="stopGridBtn" disabled>
|
||||||
|
⏹️ Stop Grid
|
||||||
|
</button>
|
||||||
|
<button onclick="nextGridAll()" class="btn" id="nextGridBtn">
|
||||||
|
⏭️ Next All
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-assignment" id="gridAssignment">
|
||||||
|
<h4 style="color: #667eea; margin: 0 0 1rem 0;">📋 Slideshow Assignment</h4>
|
||||||
|
<div id="cellAssignments"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Settings Panel -->
|
<!-- Settings Panel -->
|
||||||
<div class="settings-panel">
|
<div class="settings-panel">
|
||||||
<!-- Timing Settings -->
|
<!-- Timing Settings -->
|
||||||
|
|
@ -766,6 +1046,23 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Grid Slideshow Container (Fullscreen) -->
|
||||||
|
<div class="grid-slideshow-container" id="gridSlideshowContainer">
|
||||||
|
<div class="grid-slideshow-display" id="gridSlideshowDisplay"></div>
|
||||||
|
|
||||||
|
<div class="grid-slideshow-info" id="gridSlideshowInfo">
|
||||||
|
<div>Grid Mode: <span id="gridModeDisplay">2x2</span></div>
|
||||||
|
<div>Active Cells: <span id="activeCells">0</span></div>
|
||||||
|
<div>Status: <span id="gridSlideshowStatus">Ready</span></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-slideshow-controls">
|
||||||
|
<button onclick="pauseGridSlideshow()" id="gridPauseBtn">⏸️ Pause All</button>
|
||||||
|
<button onclick="nextGridAll()" id="gridNextBtn">⏭️ Next All</button>
|
||||||
|
<button onclick="stopGridSlideshow()" id="gridStopBtn">⏹️ Stop Grid</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Core Scripts -->
|
<!-- Core Scripts -->
|
||||||
<script>
|
<script>
|
||||||
// Only load Electron-specific scripts if in Electron environment
|
// Only load Electron-specific scripts if in Electron environment
|
||||||
|
|
@ -821,6 +1118,511 @@
|
||||||
waveTime: 0
|
waveTime: 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Grid Slideshow Variables
|
||||||
|
let gridSlideshow = {
|
||||||
|
mode: 'single',
|
||||||
|
cells: [],
|
||||||
|
isPlaying: false,
|
||||||
|
isPaused: false,
|
||||||
|
configurations: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// GridCell Class
|
||||||
|
class GridCell {
|
||||||
|
constructor(index) {
|
||||||
|
this.index = index;
|
||||||
|
this.slideshow = null;
|
||||||
|
this.images = [];
|
||||||
|
this.currentIndex = 0;
|
||||||
|
this.timer = null;
|
||||||
|
this.element = null;
|
||||||
|
this.imageElement = null;
|
||||||
|
this.infoElement = null;
|
||||||
|
this.progressElement = null;
|
||||||
|
this.isPlaying = false;
|
||||||
|
// Copy current settings safely
|
||||||
|
this.settings = {
|
||||||
|
timingMode: currentSettings.timingMode,
|
||||||
|
duration: currentSettings.duration,
|
||||||
|
minDuration: currentSettings.minDuration,
|
||||||
|
maxDuration: currentSettings.maxDuration,
|
||||||
|
waveMin: currentSettings.waveMin,
|
||||||
|
waveMax: currentSettings.waveMax,
|
||||||
|
waveRate: currentSettings.waveRate,
|
||||||
|
transitionDuration: currentSettings.transitionDuration
|
||||||
|
};
|
||||||
|
console.log(`GridCell ${index} initialized with settings:`, this.settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeElement() {
|
||||||
|
this.element = document.createElement('div');
|
||||||
|
this.element.className = 'grid-cell';
|
||||||
|
this.element.innerHTML = `
|
||||||
|
<img class="grid-cell-image" alt="Grid Cell Image">
|
||||||
|
<div class="grid-cell-info">
|
||||||
|
<div>Cell ${this.index + 1}: <span class="cell-slideshow">Empty</span></div>
|
||||||
|
<div>Image: <span class="cell-current">0</span> / <span class="cell-total">0</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="grid-cell-controls">
|
||||||
|
<button class="grid-cell-btn" onclick="pauseGridCell(${this.index})">⏸️</button>
|
||||||
|
<button class="grid-cell-btn" onclick="nextGridCell(${this.index})">⏭️</button>
|
||||||
|
</div>
|
||||||
|
<div class="grid-progress-container">
|
||||||
|
<div class="grid-progress-bar"></div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.imageElement = this.element.querySelector('.grid-cell-image');
|
||||||
|
this.infoElement = this.element.querySelector('.grid-cell-info');
|
||||||
|
this.progressElement = this.element.querySelector('.grid-progress-bar');
|
||||||
|
|
||||||
|
return this.element;
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadSlideshow(slideshow) {
|
||||||
|
console.log(`Cell ${this.index}: Loading slideshow:`, slideshow ? slideshow.name : 'null');
|
||||||
|
this.slideshow = slideshow;
|
||||||
|
|
||||||
|
if (slideshow) {
|
||||||
|
// Load images for this specific slideshow
|
||||||
|
this.images = await getGridCellImages(slideshow);
|
||||||
|
} else {
|
||||||
|
this.images = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Cell ${this.index}: Loaded ${this.images.length} images`);
|
||||||
|
this.currentIndex = 0;
|
||||||
|
this.updateInfo();
|
||||||
|
this.displayCurrentImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateInfo() {
|
||||||
|
if (this.infoElement) {
|
||||||
|
const slideshowSpan = this.infoElement.querySelector('.cell-slideshow');
|
||||||
|
const currentSpan = this.infoElement.querySelector('.cell-current');
|
||||||
|
const totalSpan = this.infoElement.querySelector('.cell-total');
|
||||||
|
if (slideshowSpan) slideshowSpan.textContent = this.slideshow ? this.slideshow.name : 'Empty';
|
||||||
|
if (currentSpan) currentSpan.textContent = this.images.length > 0 ? this.currentIndex + 1 : 0;
|
||||||
|
if (totalSpan) totalSpan.textContent = this.images.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
displayCurrentImage() {
|
||||||
|
if (this.images.length === 0 || !this.imageElement) return;
|
||||||
|
|
||||||
|
const imageData = this.images[this.currentIndex];
|
||||||
|
console.log(`Cell ${this.index}: Image data structure:`, imageData);
|
||||||
|
|
||||||
|
let imageSrc;
|
||||||
|
if (typeof imageData === 'string') {
|
||||||
|
imageSrc = imageData;
|
||||||
|
} else {
|
||||||
|
// Try different possible path properties in order of preference
|
||||||
|
imageSrc = imageData.cachedPath || imageData.fullPath || imageData.path || imageData.src || imageData.dataURL;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`Cell ${this.index}: Using image src: ${imageSrc}`);
|
||||||
|
|
||||||
|
if (!imageSrc) {
|
||||||
|
console.error(`Cell ${this.index}: No valid image source found in:`, imageData);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.imageElement.style.opacity = '0';
|
||||||
|
setTimeout(() => {
|
||||||
|
this.imageElement.src = imageSrc;
|
||||||
|
this.imageElement.style.opacity = '1';
|
||||||
|
this.imageElement.classList.add('visible');
|
||||||
|
}, this.settings.transitionDuration / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
nextImage() {
|
||||||
|
if (this.images.length === 0) return;
|
||||||
|
|
||||||
|
this.currentIndex = (this.currentIndex + 1) % this.images.length;
|
||||||
|
this.updateInfo();
|
||||||
|
this.displayCurrentImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
startTimer() {
|
||||||
|
console.log(`Cell ${this.index}: Starting timer`);
|
||||||
|
if (this.timer) clearTimeout(this.timer);
|
||||||
|
|
||||||
|
const duration = this.calculateDuration();
|
||||||
|
console.log(`Cell ${this.index}: Duration = ${duration}ms`);
|
||||||
|
this.updateProgress(duration);
|
||||||
|
|
||||||
|
this.timer = setTimeout(() => {
|
||||||
|
console.log(`Cell ${this.index}: Timer fired, advancing image`);
|
||||||
|
this.nextImage();
|
||||||
|
if (this.isPlaying) {
|
||||||
|
this.startTimer();
|
||||||
|
}
|
||||||
|
}, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateDuration() {
|
||||||
|
switch (this.settings.timingMode) {
|
||||||
|
case 'random':
|
||||||
|
return Math.random() * (this.settings.maxDuration - this.settings.minDuration) + this.settings.minDuration;
|
||||||
|
case 'wave':
|
||||||
|
const wavePosition = Date.now() / 1000 / this.settings.waveRate;
|
||||||
|
const waveValue = (Math.sin(wavePosition) + 1) / 2;
|
||||||
|
return this.settings.waveMin + (this.settings.waveMax - this.settings.waveMin) * waveValue;
|
||||||
|
default:
|
||||||
|
return this.settings.duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateProgress(duration) {
|
||||||
|
if (!this.progressElement) return;
|
||||||
|
|
||||||
|
this.progressElement.style.transition = 'none';
|
||||||
|
this.progressElement.style.width = '0%';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.progressElement.style.transition = `width ${duration}ms linear`;
|
||||||
|
this.progressElement.style.width = '100%';
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
pause() {
|
||||||
|
this.isPlaying = false;
|
||||||
|
if (this.timer) {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.timer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resume() {
|
||||||
|
this.isPlaying = true;
|
||||||
|
this.startTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
this.isPlaying = false;
|
||||||
|
if (this.timer) {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.timer = null;
|
||||||
|
}
|
||||||
|
this.currentIndex = 0;
|
||||||
|
this.updateInfo();
|
||||||
|
if (this.progressElement) {
|
||||||
|
this.progressElement.style.width = '0%';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grid Management Functions
|
||||||
|
function updateDisplayMode() {
|
||||||
|
const mode = document.getElementById('displayMode').value;
|
||||||
|
const gridView = document.getElementById('gridView');
|
||||||
|
|
||||||
|
if (mode === 'single') {
|
||||||
|
gridView.style.display = 'none';
|
||||||
|
stopGridSlideshow();
|
||||||
|
} else {
|
||||||
|
gridView.style.display = 'block';
|
||||||
|
setupGridLayout(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
gridSlideshow.mode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupGridLayout(mode) {
|
||||||
|
const gridContainer = document.getElementById('gridContainer');
|
||||||
|
|
||||||
|
// Clear existing grid
|
||||||
|
gridContainer.innerHTML = '';
|
||||||
|
gridSlideshow.cells = [];
|
||||||
|
|
||||||
|
// Set grid class
|
||||||
|
gridContainer.className = `grid-container ${mode}`;
|
||||||
|
|
||||||
|
// Create cells based on mode
|
||||||
|
let cellCount = 1;
|
||||||
|
switch (mode) {
|
||||||
|
case 'grid-2x2':
|
||||||
|
cellCount = 4;
|
||||||
|
break;
|
||||||
|
case 'grid-3x3':
|
||||||
|
cellCount = 9;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cellCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create grid cells
|
||||||
|
for (let i = 0; i < cellCount; i++) {
|
||||||
|
const cell = new GridCell(i);
|
||||||
|
gridSlideshow.cells.push(cell);
|
||||||
|
|
||||||
|
const cellElement = cell.initializeElement();
|
||||||
|
gridContainer.appendChild(cellElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCellAssignments();
|
||||||
|
updateGridControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCellAssignments() {
|
||||||
|
const container = document.getElementById('cellAssignments');
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
container.innerHTML = '';
|
||||||
|
console.log('📋 Updating cell assignments, available slideshows:', Object.keys(savedSlideshows));
|
||||||
|
|
||||||
|
gridSlideshow.cells.forEach((cell, index) => {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.className = 'cell-assignment';
|
||||||
|
|
||||||
|
const select = document.createElement('select');
|
||||||
|
select.innerHTML = '<option value="">Select slideshow...</option>';
|
||||||
|
|
||||||
|
Object.keys(savedSlideshows).forEach(name => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = name;
|
||||||
|
option.textContent = `${name} (${savedSlideshows[name].images?.length || 0} images)`;
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
select.onchange = async () => {
|
||||||
|
console.log(`Assigning slideshow "${select.value}" to cell ${index}`);
|
||||||
|
if (select.value) {
|
||||||
|
await cell.loadSlideshow(savedSlideshows[select.value]);
|
||||||
|
} else {
|
||||||
|
await cell.loadSlideshow(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
div.innerHTML = `<label>Cell ${index + 1}:</label>`;
|
||||||
|
div.appendChild(select);
|
||||||
|
container.appendChild(div);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startGridSlideshow() {
|
||||||
|
console.log('🟢 Starting grid slideshow...');
|
||||||
|
console.log('Grid cells:', gridSlideshow.cells.length);
|
||||||
|
|
||||||
|
if (gridSlideshow.cells.length === 0) {
|
||||||
|
alert('No grid cells configured');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enter fullscreen grid mode
|
||||||
|
enterGridFullscreen();
|
||||||
|
|
||||||
|
gridSlideshow.isPlaying = true;
|
||||||
|
gridSlideshow.isPaused = false;
|
||||||
|
|
||||||
|
let activeCellCount = 0;
|
||||||
|
gridSlideshow.cells.forEach((cell, index) => {
|
||||||
|
console.log(`Cell ${index}: ${cell.images.length} images`);
|
||||||
|
if (cell.images.length > 0) {
|
||||||
|
console.log(`Starting cell ${index}`);
|
||||||
|
cell.isPlaying = true;
|
||||||
|
cell.startTimer();
|
||||||
|
activeCellCount++;
|
||||||
|
} else {
|
||||||
|
console.log(`Cell ${index} has no images, skipping`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update fullscreen info
|
||||||
|
document.getElementById('gridModeDisplay').textContent = gridSlideshow.mode;
|
||||||
|
document.getElementById('activeCells').textContent = activeCellCount;
|
||||||
|
document.getElementById('gridSlideshowStatus').textContent = 'Playing';
|
||||||
|
|
||||||
|
updateGridControls();
|
||||||
|
console.log('✅ Grid slideshow start complete');
|
||||||
|
}
|
||||||
|
|
||||||
|
function pauseGridSlideshow() {
|
||||||
|
if (gridSlideshow.isPaused) {
|
||||||
|
resumeGridSlideshow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gridSlideshow.isPaused = true;
|
||||||
|
gridSlideshow.cells.forEach(cell => {
|
||||||
|
cell.pause();
|
||||||
|
});
|
||||||
|
|
||||||
|
updateGridControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
function resumeGridSlideshow() {
|
||||||
|
gridSlideshow.isPaused = false;
|
||||||
|
gridSlideshow.cells.forEach(cell => {
|
||||||
|
if (cell.images.length > 0) {
|
||||||
|
cell.resume();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
updateGridControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopGridSlideshow() {
|
||||||
|
gridSlideshow.isPlaying = false;
|
||||||
|
gridSlideshow.isPaused = false;
|
||||||
|
gridSlideshow.cells.forEach(cell => {
|
||||||
|
cell.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Exit fullscreen mode
|
||||||
|
exitGridFullscreen();
|
||||||
|
|
||||||
|
// Update status
|
||||||
|
const gridStatus = document.getElementById('gridSlideshowStatus');
|
||||||
|
if (gridStatus) {
|
||||||
|
gridStatus.textContent = 'Stopped';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGridControls();
|
||||||
|
console.log('🔲 Grid slideshow stopped');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fullscreen grid control functions
|
||||||
|
function pauseGridSlideshow() {
|
||||||
|
if (!gridSlideshow.isPlaying) return;
|
||||||
|
|
||||||
|
gridSlideshow.isPaused = !gridSlideshow.isPaused;
|
||||||
|
|
||||||
|
gridSlideshow.cells.forEach(cell => {
|
||||||
|
if (gridSlideshow.isPaused) {
|
||||||
|
cell.pause();
|
||||||
|
} else {
|
||||||
|
cell.resume();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update status
|
||||||
|
const gridStatus = document.getElementById('gridSlideshowStatus');
|
||||||
|
if (gridStatus) {
|
||||||
|
gridStatus.textContent = gridSlideshow.isPaused ? 'Paused' : 'Playing';
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGridControls();
|
||||||
|
console.log('⏸️ Grid slideshow', gridSlideshow.isPaused ? 'paused' : 'resumed');
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextGridAll() {
|
||||||
|
if (!gridSlideshow.isPlaying) return;
|
||||||
|
|
||||||
|
gridSlideshow.cells.forEach(cell => {
|
||||||
|
if (cell.images.length > 0) {
|
||||||
|
cell.next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('⏭️ All grid cells advanced to next image');
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextGridAll() {
|
||||||
|
gridSlideshow.cells.forEach(cell => {
|
||||||
|
if (cell.images.length > 0) {
|
||||||
|
cell.nextImage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function pauseGridCell(cellIndex) {
|
||||||
|
const cell = gridSlideshow.cells[cellIndex];
|
||||||
|
if (cell) {
|
||||||
|
if (cell.isPlaying) {
|
||||||
|
cell.pause();
|
||||||
|
} else {
|
||||||
|
cell.resume();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextGridCell(cellIndex) {
|
||||||
|
const cell = gridSlideshow.cells[cellIndex];
|
||||||
|
if (cell && cell.images.length > 0) {
|
||||||
|
cell.nextImage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateGridControls() {
|
||||||
|
const startBtn = document.getElementById('startGridBtn');
|
||||||
|
const pauseBtn = document.getElementById('pauseGridBtn');
|
||||||
|
const stopBtn = document.getElementById('stopGridBtn');
|
||||||
|
|
||||||
|
if (startBtn && pauseBtn && stopBtn) {
|
||||||
|
if (gridSlideshow.isPlaying) {
|
||||||
|
startBtn.disabled = true;
|
||||||
|
pauseBtn.disabled = false;
|
||||||
|
pauseBtn.textContent = gridSlideshow.isPaused ? '▶️ Resume Grid' : '⏸️ Pause Grid';
|
||||||
|
stopBtn.disabled = false;
|
||||||
|
} else {
|
||||||
|
startBtn.disabled = false;
|
||||||
|
pauseBtn.disabled = true;
|
||||||
|
pauseBtn.textContent = '⏸️ Pause Grid';
|
||||||
|
stopBtn.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update fullscreen controls if visible
|
||||||
|
const gridPauseBtn = document.getElementById('gridPauseBtn');
|
||||||
|
const gridStopBtn = document.getElementById('gridStopBtn');
|
||||||
|
|
||||||
|
if (gridPauseBtn && gridStopBtn) {
|
||||||
|
if (gridSlideshow.isPlaying) {
|
||||||
|
gridPauseBtn.disabled = false;
|
||||||
|
gridPauseBtn.textContent = gridSlideshow.isPaused ? '▶️ Resume All' : '⏸️ Pause All';
|
||||||
|
gridStopBtn.disabled = false;
|
||||||
|
} else {
|
||||||
|
gridPauseBtn.disabled = true;
|
||||||
|
gridPauseBtn.textContent = '⏸️ Pause All';
|
||||||
|
gridStopBtn.disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enterGridFullscreen() {
|
||||||
|
console.log('🔲 Entering grid fullscreen mode');
|
||||||
|
|
||||||
|
// Hide main content
|
||||||
|
document.querySelector('.main-content').style.display = 'none';
|
||||||
|
|
||||||
|
// Show grid fullscreen container
|
||||||
|
document.getElementById('gridSlideshowContainer').style.display = 'flex';
|
||||||
|
|
||||||
|
// Move grid container to fullscreen display
|
||||||
|
const gridContainer = document.getElementById('gridContainer');
|
||||||
|
const fullscreenDisplay = document.getElementById('gridSlideshowDisplay');
|
||||||
|
|
||||||
|
if (gridContainer && fullscreenDisplay) {
|
||||||
|
fullscreenDisplay.appendChild(gridContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function exitGridFullscreen() {
|
||||||
|
console.log('🔲 Exiting grid fullscreen mode');
|
||||||
|
|
||||||
|
// Hide grid fullscreen container
|
||||||
|
document.getElementById('gridSlideshowContainer').style.display = 'none';
|
||||||
|
|
||||||
|
// Show main content
|
||||||
|
document.querySelector('.main-content').style.display = 'block';
|
||||||
|
|
||||||
|
// Move grid container back to settings
|
||||||
|
const gridContainer = document.getElementById('gridContainer');
|
||||||
|
const gridView = document.getElementById('gridView');
|
||||||
|
|
||||||
|
if (gridContainer && gridView) {
|
||||||
|
// Find the original position (after the controls)
|
||||||
|
const gridControls = gridView.querySelector('.grid-controls');
|
||||||
|
if (gridControls && gridControls.nextElementSibling) {
|
||||||
|
gridView.insertBefore(gridContainer, gridControls.nextElementSibling);
|
||||||
|
} else {
|
||||||
|
gridView.appendChild(gridContainer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize Hypno Gallery
|
// Initialize Hypno Gallery
|
||||||
async function initializeHypnoGallery() {
|
async function initializeHypnoGallery() {
|
||||||
console.log('🌀 Initializing Hypno Gallery...');
|
console.log('🌀 Initializing Hypno Gallery...');
|
||||||
|
|
@ -1929,6 +2731,10 @@
|
||||||
console.log('📋 No saved slideshows found, initialized empty object');
|
console.log('📋 No saved slideshows found, initialized empty object');
|
||||||
}
|
}
|
||||||
updateSlideshowsList();
|
updateSlideshowsList();
|
||||||
|
// Refresh grid cell assignments if in grid mode
|
||||||
|
if (gridSlideshow.mode !== 'single' && gridSlideshow.cells.length > 0) {
|
||||||
|
updateCellAssignments();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Error loading slideshows:', error);
|
console.error('❌ Error loading slideshows:', error);
|
||||||
savedSlideshows = {};
|
savedSlideshows = {};
|
||||||
|
|
@ -1939,6 +2745,10 @@
|
||||||
try {
|
try {
|
||||||
localStorage.setItem('hypnoGallerySlideshows', JSON.stringify(savedSlideshows));
|
localStorage.setItem('hypnoGallerySlideshows', JSON.stringify(savedSlideshows));
|
||||||
console.log('💾 Slideshows saved to storage');
|
console.log('💾 Slideshows saved to storage');
|
||||||
|
// Refresh grid cell assignments if in grid mode
|
||||||
|
if (gridSlideshow.mode !== 'single' && gridSlideshow.cells.length > 0) {
|
||||||
|
updateCellAssignments();
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Error saving slideshows:', error);
|
console.error('❌ Error saving slideshows:', error);
|
||||||
}
|
}
|
||||||
|
|
@ -2251,6 +3061,11 @@
|
||||||
updateCurrentSessionDisplay();
|
updateCurrentSessionDisplay();
|
||||||
updateSlideshowDetails();
|
updateSlideshowDetails();
|
||||||
|
|
||||||
|
// Refresh grid cell assignments if in grid mode
|
||||||
|
if (gridSlideshow.mode !== 'single' && gridSlideshow.cells.length > 0) {
|
||||||
|
updateCellAssignments();
|
||||||
|
}
|
||||||
|
|
||||||
console.log('📂 Auto-loaded slideshow:', name);
|
console.log('📂 Auto-loaded slideshow:', name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2976,6 +3791,44 @@ ${invalidDirs.length > 0 ? `\nInvalid directories:\n${invalidDirs.join('\n')}` :
|
||||||
return images;
|
return images;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Grid-specific image loading function for individual cells
|
||||||
|
async function getGridCellImages(slideshow) {
|
||||||
|
let images = [];
|
||||||
|
|
||||||
|
console.log(`🔲 Loading images for grid cell slideshow: ${slideshow.name}`);
|
||||||
|
|
||||||
|
// Load images from slideshow-specific directories
|
||||||
|
const directories = slideshow.directories || [];
|
||||||
|
console.log(`📁 Slideshow directories: ${directories.length}`);
|
||||||
|
|
||||||
|
for (const dir of directories) {
|
||||||
|
const dirPath = typeof dir === 'string' ? dir : dir.path;
|
||||||
|
try {
|
||||||
|
const dirImages = await loadImagesFromDirectory(dirPath);
|
||||||
|
images.push(...dirImages);
|
||||||
|
console.log(`📁 Grid cell loaded ${dirImages.length} images from: ${dirPath}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ Grid cell error loading directory ${dirPath}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include captured photos if enabled
|
||||||
|
if (slideshow.includeCapturedPhotos) {
|
||||||
|
const capturedPhotos = getCapturedPhotos();
|
||||||
|
images.push(...capturedPhotos);
|
||||||
|
console.log(`📷 Grid cell added ${capturedPhotos.length} captured photos`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to main library if no directories configured
|
||||||
|
if (images.length === 0) {
|
||||||
|
console.log('📚 Grid cell using main image library as fallback');
|
||||||
|
images = [...imageLibrary];
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`🖼️ Grid cell total images: ${images.length}`);
|
||||||
|
return images;
|
||||||
|
}
|
||||||
|
|
||||||
async function loadImagesFromDirectory(directoryPath) {
|
async function loadImagesFromDirectory(directoryPath) {
|
||||||
// Load images from a directory recursively using the main process function
|
// Load images from a directory recursively using the main process function
|
||||||
console.log(`🔍 Starting recursive image scan of: ${directoryPath}`);
|
console.log(`🔍 Starting recursive image scan of: ${directoryPath}`);
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ copy training-academy.html "%OUTPUT_DIR%\"
|
||||||
copy porn-cinema.html "%OUTPUT_DIR%\"
|
copy porn-cinema.html "%OUTPUT_DIR%\"
|
||||||
copy player-stats.html "%OUTPUT_DIR%\"
|
copy player-stats.html "%OUTPUT_DIR%\"
|
||||||
copy user-profile.html "%OUTPUT_DIR%\"
|
copy user-profile.html "%OUTPUT_DIR%\"
|
||||||
|
copy hypno-gallery.html "%OUTPUT_DIR%\"
|
||||||
copy package.json "%OUTPUT_DIR%\"
|
copy package.json "%OUTPUT_DIR%\"
|
||||||
|
|
||||||
:: Copy documentation
|
:: Copy documentation
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue