diff --git a/.gitignore b/.gitignore index 25788bb..50bdcf6 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ node-modules/ game-assets/ backup/ update_users.py +updateusers.py skinimporter.py package.json package-lock.json diff --git a/game_server.py b/game_server.py index f5f79f6..536805c 100644 --- a/game_server.py +++ b/game_server.py @@ -43,6 +43,10 @@ app = Flask(__name__) app.config['SECRET_KEY'] = secret_key socketio = SocketIO(app, cors_allowed_origins="*") +socket_to_user_id = {} + + + # MongoDB setup client = MongoClient('mongodb://localhost:27017/') db = client['resonance_rumble'] @@ -56,6 +60,44 @@ active_sockets_collection = db['active_sockets'] oauth_states = db['oauth_states'] achievements_collection = db['achievements'] +def update_user_stat(user_id, stat_name, value, operation='inc'): + if user_id is None: + print("Cannot update stats for guest or unknown user") + return + + update = {} + if operation == 'inc': + update = {'$inc': {f'stats.{stat_name}': value}} + elif operation == 'max': + update = {'$max': {f'stats.{stat_name}': value}} + else: + raise ValueError("Invalid operation") + + result = users_collection.update_one({'_id': user_id}, update) + if result.modified_count > 0: + check_achievements(user_id) + else: + print(f"Failed to update stat for user_id: {user_id}") + +def check_achievements(user_id): + user = users_collection.find_one({'_id': user_id}) + if not user: + print(f"User not found for id: {user_id}") + return + + user_stats = user.get('stats', {}) + + achievements_to_check = [ + {'id': 'kill_10_enemies', 'name': 'Killing spree', 'condition': lambda s: s['enemies_killed'] >= 10}, + {'id': 'kill_100_enemies', 'name': 'Killing frenzy', 'condition': lambda s: s['enemies_killed'] >= 100}, + {'id': 'collect_50_coins', 'name': 'Coin collector', 'condition': lambda s: s['coins_collected'] >= 50}, + {'id': 'reach_level_10', 'name': 'Ding!', 'condition': lambda s: s['highest_level'] >= 10}, + ] + + for achievement in achievements_to_check: + if achievement['id'] not in user['achievements'] and achievement['condition'](user_stats): + award_achievement(user_id, achievement['id']) + def decode_token(token): try: payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256']) @@ -124,6 +166,20 @@ def award_achievement(user_id, achievement_id): {'$push': {'achievements': achievement_id}} ) + achievement = achievements_collection.find_one({'id': achievement_id}) + if achievement: + # Find the socket ID for this user + player_socket_id = next((sid for sid, player in game_state['players'].items() if player.name == user['username']), None) + + if player_socket_id: + socketio.emit('achievement_unlocked', { + 'name': achievement['name'], + 'description': achievement['description'], + 'image': achievement.get('image', '') + }, room=player_socket_id) + else: + print(f"Could not find socket ID for user {user['username']}") + send_discord_alert(f"🏆 Player {user['username']} has earned the '{achievement['name']}' achievement!", user['username']) # Send a DM to the user if they have a linked Discord account @@ -851,7 +907,14 @@ def signup(): }, "unlocked_skins": [ObjectId("669e1272813db368f3b19519")], # Glowy Blue "unlocked_classes": default_class_ids, - "selected_class": default_class_ids[0] if default_class_ids else None + "selected_class": default_class_ids[0] if default_class_ids else None, + 'achievements': [], + 'stats': { + 'enemies_killed': 0, + 'coins_collected': 0, + 'highest_level': 1, + } + } users_collection.insert_one(new_user) @@ -1025,6 +1088,8 @@ def on_disconnect(): socketio.emit('player_disconnected', player_id, to=main_room) send_discord_alert(f"👋 Player {username} has disconnected!", username) print(f"Player {player_id} disconnected") + if player_id in socket_to_user_id: + del socket_to_user_id[player_id] active_sockets_collection.delete_one({'_id': player_id}) @socketio.on('player_paused') @@ -1053,6 +1118,7 @@ def on_join(data): # Get player skin user = users_collection.find_one({'username': username}) if user: + socket_to_user_id[player_id] = user['_id'] selected_skins = user.get('selected_skins', {}) skin_data = {} for skin_type, skin_id in selected_skins.items(): @@ -1161,6 +1227,8 @@ def on_collect_synth_coin(data): if math.hypot(coin.x - coin_x, coin.y - coin_y) < 0.1: player.synth_coins += 100 game_state['synth_coins'].remove(coin) + if player_id in socket_to_user_id: + update_user_stat(socket_to_user_id[player_id], 'coins_collected', 1) print(f"Player {player.name} collected a coin. New count: {player.synth_coins}") socketio.emit('coin_collected', { 'player_id': player_id, @@ -1303,7 +1371,9 @@ def add_experience(player, amount): player.level += 1 player.experience -= player.max_experience player.max_experience = int(player.max_experience * 1.2) - + # if player_id in socket_to_user_id: + # update_user_stat(socket_to_user_id[player_id], 'highest_level', player.level, operation='max') + # Generate upgrade options and store them player_id = next(pid for pid, p in game_state['players'].items() if p == player) game_state['player_upgrade_options'][player_id] = get_random_upgrades(3) @@ -1458,6 +1528,8 @@ def game_loop(): if enemy['current_health'] <= 0: game_state['enemies'].remove(enemy) + if player_id in socket_to_user_id: + update_user_stat(socket_to_user_id[player_id], 'enemies_killed', 1) coin = SynthCoin(enemy['x'], enemy['y']) game_state['synth_coins'].append(coin) diff --git a/static/js/game.js b/static/js/game.js index 67153fb..41147af 100644 --- a/static/js/game.js +++ b/static/js/game.js @@ -75,10 +75,6 @@ let lastUpdateTime = Date.now(); let impactEffects = []; let currentSongName = ''; - - - - class NotificationSystem { constructor() { this.notificationContainer = null; @@ -91,46 +87,73 @@ class NotificationSystem { document.body.appendChild(this.notificationContainer); } - showNotification(message, image = null) { + showNotification(message, image) { + console.log('showNotification called with message:', message, 'and image:', image); const notificationElement = document.createElement('div'); notificationElement.className = 'custom-notification draggable'; let content = `
${message}