package com.eu.habbo.habbohotel.achievements; import com.eu.habbo.Emulator; import com.eu.habbo.habbohotel.items.Item; import com.eu.habbo.habbohotel.users.Habbo; import com.eu.habbo.habbohotel.users.HabboBadge; import com.eu.habbo.habbohotel.users.HabboItem; import com.eu.habbo.messages.outgoing.achievements.AchievementProgressComposer; import com.eu.habbo.messages.outgoing.achievements.AchievementUnlockedComposer; import com.eu.habbo.messages.outgoing.achievements.talenttrack.TalentLevelUpdateComposer; import com.eu.habbo.messages.outgoing.inventory.AddHabboItemComposer; import com.eu.habbo.messages.outgoing.inventory.InventoryRefreshComposer; import com.eu.habbo.messages.outgoing.rooms.users.RoomUserDataComposer; import com.eu.habbo.messages.outgoing.users.AddUserBadgeComposer; import com.eu.habbo.messages.outgoing.users.UserBadgesComposer; import com.eu.habbo.plugin.Event; import com.eu.habbo.plugin.events.users.achievements.UserAchievementLeveledEvent; import com.eu.habbo.plugin.events.users.achievements.UserAchievementProgressEvent; import gnu.trove.map.hash.THashMap; import gnu.trove.procedure.TObjectIntProcedure; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.*; import java.util.LinkedHashMap; import java.util.Map; public class AchievementManager { private static final Logger LOGGER = LoggerFactory.getLogger(AchievementManager.class); public static boolean TALENTTRACK_ENABLED = false; private final THashMap achievements; private final THashMap> talentTrackLevels; public AchievementManager() { this.achievements = new THashMap<>(); this.talentTrackLevels = new THashMap<>(); } public static void progressAchievement(int habboId, Achievement achievement) { progressAchievement(habboId, achievement, 1); } public static void progressAchievement(int habboId, Achievement achievement, int amount) { if (achievement != null) { Habbo habbo = Emulator.getGameEnvironment().getHabboManager().getHabbo(habboId); if (habbo != null) { progressAchievement(habbo, achievement, amount); } else { try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("" + "INSERT INTO users_achievements_queue (user_id, achievement_id, amount) VALUES (?, ?, ?) " + "ON DUPLICATE KEY UPDATE amount = amount + ?")) { statement.setInt(1, habboId); statement.setInt(2, achievement.id); statement.setInt(3, amount); statement.setInt(4, amount); statement.execute(); } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } } } } public static void progressAchievement(Habbo habbo, Achievement achievement) { progressAchievement(habbo, achievement, 1); } public static void progressAchievement(Habbo habbo, Achievement achievement, int amount) { if (achievement == null) return; if (habbo == null) return; if (!habbo.isOnline()) return; int currentProgress = habbo.getHabboStats().getAchievementProgress(achievement); if (currentProgress == -1) { currentProgress = 0; createUserEntry(habbo, achievement); habbo.getHabboStats().setProgress(achievement, 0); } if (Emulator.getPluginManager().isRegistered(UserAchievementProgressEvent.class, true)) { Event userAchievementProgressedEvent = new UserAchievementProgressEvent(habbo, achievement, amount); Emulator.getPluginManager().fireEvent(userAchievementProgressedEvent); if (userAchievementProgressedEvent.isCancelled()) return; } AchievementLevel oldLevel = achievement.getLevelForProgress(currentProgress); if (oldLevel != null && (oldLevel.level == achievement.levels.size() && currentProgress >= oldLevel.progress)) //Maximum achievement gotten. return; habbo.getHabboStats().setProgress(achievement, currentProgress + amount); AchievementLevel newLevel = achievement.getLevelForProgress(currentProgress + amount); if (AchievementManager.TALENTTRACK_ENABLED) { for (TalentTrackType type : TalentTrackType.values()) { if (Emulator.getGameEnvironment().getAchievementManager().talentTrackLevels.containsKey(type)) { for (Map.Entry entry : Emulator.getGameEnvironment().getAchievementManager().talentTrackLevels.get(type).entrySet()) { if (entry.getValue().achievements.containsKey(achievement)) { Emulator.getGameEnvironment().getAchievementManager().handleTalentTrackAchievement(habbo, type, achievement); break; } } } } } if (newLevel == null || (oldLevel != null && (oldLevel.level == newLevel.level && newLevel.level < achievement.levels.size()))) { habbo.getClient().sendResponse(new AchievementProgressComposer(habbo, achievement)); } else { if (Emulator.getPluginManager().isRegistered(UserAchievementLeveledEvent.class, true)) { Event userAchievementLeveledEvent = new UserAchievementLeveledEvent(habbo, achievement, oldLevel, newLevel); Emulator.getPluginManager().fireEvent(userAchievementLeveledEvent); if (userAchievementLeveledEvent.isCancelled()) return; } habbo.getClient().sendResponse(new AchievementProgressComposer(habbo, achievement)); habbo.getClient().sendResponse(new AchievementUnlockedComposer(habbo, achievement)); //Exception could possibly arise when the user disconnects while being in tour. //The achievement is then progressed but the user is already disposed so fetching //the badge would result in an nullpointer exception. This is normal behaviour. HabboBadge badge = null; if (oldLevel != null) { try { badge = habbo.getInventory().getBadgesComponent().getBadge(("ACH_" + achievement.name + oldLevel.level).toLowerCase()); } catch (Exception e) { LOGGER.error("Caught exception", e); return; } } String newBadgCode = "ACH_" + achievement.name + newLevel.level; if (badge != null) { badge.setCode(newBadgCode); badge.needsInsert(false); badge.needsUpdate(true); } else { if (habbo.getInventory().getBadgesComponent().hasBadge(newBadgCode)) return; badge = new HabboBadge(0, newBadgCode, 0, habbo); habbo.getClient().sendResponse(new AddUserBadgeComposer(badge)); badge.needsInsert(true); badge.needsUpdate(true); habbo.getInventory().getBadgesComponent().addBadge(badge); } Emulator.getThreading().run(badge); if (badge.getSlot() > 0) { if (habbo.getHabboInfo().getCurrentRoom() != null) { habbo.getHabboInfo().getCurrentRoom().sendComposer(new UserBadgesComposer(habbo.getInventory().getBadgesComponent().getWearingBadges(), habbo.getHabboInfo().getId()).compose()); } } habbo.getClient().sendResponse(new AddHabboItemComposer(badge.getId(), AddHabboItemComposer.AddHabboItemCategory.BADGE)); habbo.getHabboStats().addAchievementScore(newLevel.points); if (newLevel.rewardAmount > 0) { habbo.givePoints(newLevel.rewardType, newLevel.rewardAmount); } if (habbo.getHabboInfo().getCurrentRoom() != null) { habbo.getHabboInfo().getCurrentRoom().sendComposer(new RoomUserDataComposer(habbo).compose()); } } } public static boolean hasAchieved(Habbo habbo, Achievement achievement) { int currentProgress = habbo.getHabboStats().getAchievementProgress(achievement); if (currentProgress == -1) { return false; } AchievementLevel level = achievement.getLevelForProgress(currentProgress); if (level == null) return false; AchievementLevel nextLevel = achievement.levels.get(level.level + 1); return nextLevel == null && currentProgress >= level.progress; } public static void createUserEntry(Habbo habbo, Achievement achievement) { try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("INSERT INTO users_achievements (user_id, achievement_name, progress) VALUES (?, ?, ?)")) { statement.setInt(1, habbo.getHabboInfo().getId()); statement.setString(2, achievement.name); statement.setInt(3, 1); statement.execute(); } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } } public static void saveAchievements(Habbo habbo) { try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("UPDATE users_achievements SET progress = ? WHERE achievement_name = ? AND user_id = ? LIMIT 1")) { statement.setInt(3, habbo.getHabboInfo().getId()); for (Map.Entry map : habbo.getHabboStats().getAchievementProgress().entrySet()) { statement.setInt(1, map.getValue()); statement.setString(2, map.getKey().name); statement.addBatch(); } statement.executeBatch(); } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } } public static int getAchievementProgressForHabbo(int userId, Achievement achievement) { try (Connection connection = Emulator.getDatabase().getDataSource().getConnection(); PreparedStatement statement = connection.prepareStatement("SELECT progress FROM users_achievements WHERE user_id = ? AND achievement_name = ? LIMIT 1")) { statement.setInt(1, userId); statement.setString(2, achievement.name); try (ResultSet set = statement.executeQuery()) { if (set.next()) { return set.getInt("progress"); } } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } return 0; } public void reload() { long millis = System.currentTimeMillis(); synchronized (this.achievements) { for (Achievement achievement : this.achievements.values()) { achievement.clearLevels(); } try (Connection connection = Emulator.getDatabase().getDataSource().getConnection()) { try (Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM achievements")) { while (set.next()) { if (!this.achievements.containsKey(set.getString("name"))) { this.achievements.put(set.getString("name"), new Achievement(set)); } else { this.achievements.get(set.getString("name")).addLevel(new AchievementLevel(set)); } } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); } catch (Exception e) { LOGGER.error("Caught exception", e); } synchronized (this.talentTrackLevels) { this.talentTrackLevels.clear(); try (Statement statement = connection.createStatement(); ResultSet set = statement.executeQuery("SELECT * FROM achievements_talents ORDER BY level ASC")) { while (set.next()) { TalentTrackLevel level = new TalentTrackLevel(set); if (!this.talentTrackLevels.containsKey(level.type)) { this.talentTrackLevels.put(level.type, new LinkedHashMap<>()); } this.talentTrackLevels.get(level.type).put(level.level, level); } } } } catch (SQLException e) { LOGGER.error("Caught SQL exception", e); LOGGER.error("Achievement Manager -> Failed to load!"); return; } } LOGGER.info("Achievement Manager -> Loaded! (" + (System.currentTimeMillis() - millis) + " MS)"); } public Achievement getAchievement(String name) { return this.achievements.get(name); } public Achievement getAchievement(int id) { synchronized (this.achievements) { for (Map.Entry set : this.achievements.entrySet()) { if (set.getValue().id == id) { return set.getValue(); } } } return null; } public THashMap getAchievements() { return this.achievements; } public LinkedHashMap getTalenTrackLevels(TalentTrackType type) { return this.talentTrackLevels.get(type); } public TalentTrackLevel calculateTalenTrackLevel(Habbo habbo, TalentTrackType type) { TalentTrackLevel level = null; for (Map.Entry entry : this.talentTrackLevels.get(type).entrySet()) { final boolean[] allCompleted = {true}; entry.getValue().achievements.forEachEntry(new TObjectIntProcedure() { @Override public boolean execute(Achievement a, int b) { if (habbo.getHabboStats().getAchievementProgress(a) < b) { allCompleted[0] = false; } return allCompleted[0]; } }); if (allCompleted[0]) { if (level == null || level.level < entry.getValue().level) { level = entry.getValue(); } } else { break; } } return level; } public void handleTalentTrackAchievement(Habbo habbo, TalentTrackType type, Achievement achievement) { TalentTrackLevel currentLevel = this.calculateTalenTrackLevel(habbo, type); if (currentLevel != null) { if (currentLevel.level > habbo.getHabboStats().talentTrackLevel(type)) { for (int i = habbo.getHabboStats().talentTrackLevel(type); i <= currentLevel.level; i++) { TalentTrackLevel level = this.getTalentTrackLevel(type, i); if (level != null) { if (level.items != null && !level.items.isEmpty()) { for (Item item : level.items) { HabboItem rewardItem = Emulator.getGameEnvironment().getItemManager().createItem(habbo.getHabboInfo().getId(), item, 0, 0, ""); habbo.getInventory().getItemsComponent().addItem(rewardItem); habbo.getClient().sendResponse(new AddHabboItemComposer(rewardItem)); habbo.getClient().sendResponse(new InventoryRefreshComposer()); } } if (level.badges != null && level.badges.length > 0) { for (String badge : level.badges) { if (!badge.isEmpty()) { if (!habbo.getInventory().getBadgesComponent().hasBadge(badge)) { HabboBadge b = new HabboBadge(0, badge, 0, habbo); Emulator.getThreading().run(b); habbo.getInventory().getBadgesComponent().addBadge(b); habbo.getClient().sendResponse(new AddUserBadgeComposer(b)); } } } } if (level.perks != null && level.perks.length > 0) { for (String perk : level.perks) { if (perk.equalsIgnoreCase("TRADE")) { habbo.getHabboStats().perkTrade = true; } } } habbo.getClient().sendResponse(new TalentLevelUpdateComposer(type, level)); } } } habbo.getHabboStats().setTalentLevel(type, currentLevel.level); } } public TalentTrackLevel getTalentTrackLevel(TalentTrackType type, int level) { return this.talentTrackLevels.get(type).get(level); } }