from flask import Flask, render_template, session, redirect, url_for, request import time from pathlib import Path import random app = Flask(__name__) # Set a secret key for session management. CHANGE THIS IN PRODUCTION! app.secret_key = 'your_super_secret_key_for_flask_session_security' # --- Game Data --- # Updated video paths to be relative to the 'static/videos' directory videos = [ "static/videos/Animated_Drought_Relief_Video_Creation.mp4", "static/videos/Animated_Rainwater_Harvesting_Video.mp4", "static/videos/Animated_Water_Filtration_Process_Video.mp4", "static/videos/Water_Filtration_Animation_Ready.mp4", "static/videos/Animated_Rainwater_Usage_Video.mp4", "static/videos/Water_Hero_Video_Generation_Complete.mp4" ] video_descriptions = [ "๐ŸŒ Water Crisis Alert", "๐Ÿ  Collection Master", "๐Ÿ’ง Purification Expert", "๐Ÿ—ƒ๏ธ Storage Specialist", "โ™ป๏ธ Usage Strategist", "๐ŸŒฑ Environmental Champion" ] # Enhanced task cards with detailed scenarios and consequences task_scenarios = [ # Video 0 - Water Crisis (has question) { "question": "๐ŸŒ After watching the video, what is the FIRST step to address water scarcity at your home?", "choices": [ { "action": "๐ŸŒง๏ธ Start collecting rainwater immediately", "description": "Begin harvesting nature's free water supply", "consequence": "โœ… SMART START! You're taking the first step toward water independence! +100 XP", "xp_reward": 100, "icon": "๐Ÿ†" }, { "action": "๐Ÿšฟ Take longer showers to enjoy water while it lasts", "description": "Use more water before it runs out", "consequence": "โŒ WRONG APPROACH! This worsens the problem. Conservation is key! -20 Health", "xp_reward": 0, "icon": "๐Ÿ’ธ" }, { "action": "โณ Wait for government to solve the problem", "description": "Rely on authorities to fix water issues", "consequence": "โŒ MISSED OPPORTUNITY! Individual action is crucial. Start now! -20 Health", "xp_reward": 0, "icon": "โฐ" } ] }, # Video 1 - Collection { "question": "๐ŸŒง๏ธ It's raining heavily! Your house has a metal roof. What's your FIRST action as a Water Conservation Hero?", "choices": [ { "action": "๐Ÿ  Install gutters and downspouts on your roof", "description": "Channel rainwater efficiently into collection system", "consequence": "โœ… SMART MOVE! You capture 80% of the rainfall from your roof area. +100 XP", "xp_reward": 100, "icon": "๐Ÿ†" }, { "action": "๐Ÿ›ฃ๏ธ Let the water flow down the streets", "description": "Allow natural drainage to carry water away", "consequence": "โŒ MISSED OPPORTUNITY! Thousands of liters wasted into storm drains. -20 Health", "xp_reward": 0, "icon": "๐Ÿ’ธ" }, { "action": "๐Ÿ›๏ธ Place plastic bags under the roof edge", "description": "Use bags to catch dripping water", "consequence": "โŒ INEFFICIENT! Bags tear easily and collect very little water. Try a proper system! -20 Health", "xp_reward": 0, "icon": "๐Ÿ—‘๏ธ" } ] }, # Video 2 - Filtration { "question": "๐Ÿ’ง Your collection tank is filling up with rainwater, but you notice leaves and debris floating in it. What's your next critical step?", "choices": [ { "action": "๐Ÿ” Install a first-flush diverter and filtration system", "description": "Remove initial dirty water and filter the rest", "consequence": "โœ… EXCELLENT CHOICE! Clean, safe water ready for storage. Your filtration efficiency: 95%! +100 XP", "xp_reward": 100, "icon": "๐Ÿงช" }, { "action": "โš ๏ธ Use the dirty water directly without treatment", "description": "Proceed with unfiltered rainwater", "consequence": "โŒ HEALTH HAZARD! Contaminated water can cause serious illness. Always filter first! -20 Health", "xp_reward": 0, "icon": "โ˜ ๏ธ" }, { "action": "๐Ÿงช Add household bleach to kill germs", "description": "Chemical treatment of collected water", "consequence": "โŒ WRONG CHEMICAL! Bleach is not safe for water treatment. Use proper filtration! -20 Health", "xp_reward": 0, "icon": "โš—๏ธ" } ] }, # Video 3 - Storage { "question": "๐Ÿบ You have 500 liters of clean, filtered rainwater. The monsoon season is ending soon. Where should you store this precious resource?", "choices": [ { "action": "๐Ÿบ Install an underground concrete tank with cover", "description": "Professional storage solution with temperature control", "consequence": "โœ… MASTER STRATEGY! Water stays cool, clean, and safe for months. Storage efficiency: 100%! +100 XP", "xp_reward": 100, "icon": "๐Ÿ›๏ธ" }, { "action": "๐Ÿชฃ Store in open buckets around your yard", "description": "Multiple container approach for easy access", "consequence": "โŒ BREEDING GROUND! Open water attracts mosquitoes and gets contaminated. -20 Health", "xp_reward": 0, "icon": "๐ŸฆŸ" }, { "action": "๐ŸŒ Dig a pit and let water sit directly in ground", "description": "Natural ground storage method", "consequence": "โŒ CONTAMINATION RISK! Soil bacteria and insects will pollute your water. -20 Health", "xp_reward": 0, "icon": "๐Ÿ•ณ๏ธ" } ] }, # Video 4 - Usage { "question": "โ™ป๏ธ Dry season has arrived! You have 200 liters of stored rainwater left. Your neighbor is asking for help with their garden. How do you maximize the impact?", "choices": [ { "action": "๐ŸŒฟ Use for drip irrigation and toilet flushing", "description": "Strategic usage for maximum water conservation", "consequence": "โœ… WATER WISDOM! You save 300L of municipal water and help the environment! +100 XP", "xp_reward": 100, "icon": "๐ŸŒฑ" }, { "action": "โฐ Save all water for potential emergencies later", "description": "Conservative approach for future needs", "consequence": "โŒ MISSED IMPACT! Water can spoil if stored too long. Use it wisely now! -20 Health", "xp_reward": 0, "icon": "โฐ" }, { "action": "๐Ÿšฝ Mix with sewage water for disposal", "description": "Combine with waste water system", "consequence": "โŒ TERRIBLE WASTE! Never contaminate clean water with sewage! -20 Health", "xp_reward": 0, "icon": "๐Ÿคฎ" } ] }, # Video 5 - Environmental Impact (has question) { "question": "๐ŸŒฑ Now that you've mastered rainwater harvesting, how will you inspire your community to join the movement?", "choices": [ { "action": "๐Ÿ‘ฅ Organize community workshops and share your knowledge", "description": "Teach others about water conservation benefits", "consequence": "โœ… COMMUNITY HERO! You inspire 50 families to start rainwater harvesting! +100 XP", "xp_reward": 100, "icon": "๐ŸŒŸ" }, { "action": "๐Ÿค Keep the knowledge to yourself for competitive advantage", "description": "Maintain exclusive access to water conservation techniques", "consequence": "โŒ SELFISH APPROACH! Environmental problems need collective solutions! -20 Health", "xp_reward": 0, "icon": "๐Ÿšซ" }, { "action": "๐Ÿ“ฑ Only post about it on social media occasionally", "description": "Share water conservation tips online when convenient", "consequence": "โŒ MINIMAL IMPACT! Real change needs active community engagement! -20 Health", "xp_reward": 0, "icon": "๐Ÿ“ฑ" } ] } ] # Achievement definitions achievements_list = [ {"name": "๐ŸŒŸ First Steps", "desc": "Started your water journey", "condition": "start"}, {"name": "๐Ÿ’ง Drop Collector", "desc": "Learned about collection", "condition": "video_1"}, {"name": "๐Ÿ”ฌ Purification Pro", "desc": "Mastered filtration", "condition": "video_2"}, {"name": "๐Ÿบ Storage Sage", "desc": "Understood storage methods", "condition": "video_3"}, {"name": "โ™ป๏ธ Usage Expert", "desc": "Optimized water usage", "condition": "video_4"}, {"name": "๐ŸŽฏ Perfect Score", "desc": "100% accuracy achieved", "condition": "perfect"}, {"name": "๐Ÿ”ฅ On Fire", "desc": "3 correct answers in a row", "condition": "streak_3"}, {"name": "๐ŸŒ Water Hero", "desc": "Completed the full quest", "condition": "complete"} ] # --- Helper Functions (adapted for Flask session) --- def calculate_level() -> int: return min(10, 1 + session.get('xp_points', 0) // 100) def add_xp(amount: int): old_level = session.get('player_level', 1) session['xp_points'] = session.get('xp_points', 0) + amount session['player_level'] = calculate_level() if session['player_level'] > old_level: session['message'] = f"๐ŸŽ‰ LEVEL UP! You reached Level {session['player_level']}!" def unlock_achievement(condition: str): for achievement in achievements_list: if achievement["condition"] == condition and achievement not in session.get('achievements', []): session['achievements'] = session.get('achievements', []) + [achievement] session['message'] = f"๐Ÿ† Achievement Unlocked: {achievement['name']} - {achievement['desc']}" add_xp(50) def initialize_game_state(): session.setdefault("video_index", 0) session.setdefault("feedback", "") session.setdefault("quiz_correct_count", 0) session.setdefault("quiz_attempts", 0) session.setdefault("player_level", 1) session.setdefault("xp_points", 0) session.setdefault("achievements", []) session.setdefault("streak", 0) session.setdefault("health", 100) session.setdefault("video_watched", False) session.setdefault("question_answered", False) session.setdefault("message", "") # For temporary messages like level-up def next_video_logic(): if session['video_index'] < len(videos) - 1: session['video_index'] += 1 session['feedback'] = "" session['video_watched'] = False session['question_answered'] = False session['message'] = "" if session['video_index'] > 0: unlock_achievement(f"video_{session['video_index']}") return redirect(url_for('main_app')) else: unlock_achievement("complete") return redirect(url_for('summary_page')) def mark_video_watched_logic(): session['video_watched'] = True session['message'] = "" def replay_video_logic(): session['feedback'] = "" session['video_watched'] = False session['question_answered'] = False session['message'] = "๐Ÿ”„ Video ready for replay! Click play to watch again." def reset_quest_logic(): session.clear() # Clears all session data initialize_game_state() # Reinitialize with default values def select_card_logic(choice_index: int): current_scenario = task_scenarios[session['video_index']] choice = current_scenario["choices"][choice_index] session['quiz_attempts'] = session.get('quiz_attempts', 0) + 1 session['question_answered'] = True session['feedback'] = choice["consequence"] session['message'] = "" # Clear any previous messages if choice["xp_reward"] > 0: session['quiz_correct_count'] = session.get('quiz_correct_count', 0) + 1 session['streak'] = session.get('streak', 0) + 1 add_xp(choice["xp_reward"]) if session['streak'] >= 3: unlock_achievement("streak_3") else: session['streak'] = 0 session['health'] = max(0, session.get('health', 100) - 20) # --- Flask Routes --- @app.route('/') def welcome_page(): if 'player_level' not in session: # Only initialize if not already in session (e.g., first visit or after full reset) initialize_game_state() return render_template('welcome.html') @app.route('/begin_quest', methods=['POST']) def begin_quest(): unlock_achievement("start") return redirect(url_for('main_app')) @app.route('/main', methods=['GET', 'POST']) def main_app(): if 'player_level' not in session: # If somehow landed here without initializing return redirect(url_for('welcome_page')) # Handle form submissions if request.method == 'POST': if 'mark_watched' in request.form: mark_video_watched_logic() elif 'select_choice' in request.form: choice_index = int(request.form['select_choice']) select_card_logic(choice_index) elif 'next_level' in request.form or 'continue_quest' in request.form: return next_video_logic() # This function handles redirection elif 'replay_video' in request.form: replay_video_logic() return redirect(url_for('main_app')) # Redirect to show replay message elif 'main_menu' in request.form: return redirect(url_for('welcome_page')) return redirect(url_for('main_app')) # Redirect after POST to prevent resubmission current_video_index = session.get('video_index', 0) current_video_path = videos[current_video_index] # Determine if it's a local video (starts with 'videos/') or a URL is_local_video = current_video_path.startswith('videos/') if is_local_video: video_src = url_for('static', filename=current_video_path) else: video_src = current_video_path # It's a full URL # Check if local file exists if it's a local video local_file_exists = True if is_local_video: full_path = Path(app.static_folder) / current_video_path if not full_path.exists(): local_file_exists = False context = { 'current_video_index': current_video_index, 'total_videos': len(videos), 'video_src': video_src, 'is_local_video': is_local_video, 'local_file_exists': local_file_exists, 'video_description': video_descriptions[current_video_index], 'current_scenario': task_scenarios[current_video_index], 'player_level': session.get('player_level', 1), 'xp_points': session.get('xp_points', 0), 'health': session.get('health', 100), 'streak': session.get('streak', 0), 'quiz_correct_count': session.get('quiz_correct_count', 0), 'quiz_attempts': session.get('quiz_attempts', 0), 'video_watched': session.get('video_watched', False), 'question_answered': session.get('question_answered', False), 'feedback': session.get('feedback', ''), 'achievements': session.get('achievements', []), 'message': session.pop('message', '') # Pop message so it only shows once } return render_template('main.html', **context) @app.route('/summary') def summary_page(): if 'player_level' not in session: return redirect(url_for('welcome_page')) total_quizzes = len([s for s in task_scenarios if s.get("choices")]) score = session.get('quiz_correct_count', 0) if score == total_quizzes and total_quizzes > 0: unlock_achievement("perfect") context = { 'player_level': session.get('player_level', 1), 'xp_points': session.get('xp_points', 0), 'quiz_score': score, 'total_quizzes': total_quizzes, 'accuracy': (score / max(1, total_quizzes)) * 100, 'streak': session.get('streak', 0), 'achievements': session.get('achievements', []), 'current_date': time.strftime("%B %d, %Y"), 'message': session.pop('message', '') } return render_template('summary.html', **context) @app.route('/restart_quest', methods=['POST']) def restart_quest(): reset_quest_logic() return redirect(url_for('welcome_page')) @app.route('/view_stats', methods=['POST']) def view_stats(): session['message'] = "Detailed stats coming soon!" return redirect(url_for('summary_page')) @app.route('/share_impact', methods=['POST']) def share_impact(): session['message'] = "๐ŸŒŸ Your impact is shared!" return redirect(url_for('summary_page')) if __name__ == '__main__': app.run(debug=True) # Set debug=False in production