import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TreeMap; import org.json.JSONArray; import org.json.JSONObject; import org.luaj.vm2.LuaTable; import org.luaj.vm2.LuaValue; import org.luaj.vm2.lib.OneArgFunction; import org.luaj.vm2.lib.ZeroArgFunction; import org.pircbotx.Channel; import org.pircbotx.PircBotX; import org.pircbotx.ShockyBot; import org.pircbotx.User; import org.pircbotx.hooks.events.MessageEvent; import pl.shockah.shocky.Data; import pl.shockah.shocky.Module; import pl.shockah.shocky.Shocky; import pl.shockah.shocky.Utils; import pl.shockah.shocky.cmds.Command; import pl.shockah.shocky.interfaces.ILua; public class ModuleIdleRPG extends Module implements ILua { public static DecimalFormat formatXP = new DecimalFormat("###,###", new DecimalFormatSymbols(Locale.ENGLISH)); public static DecimalFormat formatXPPercent = new DecimalFormat("###,###.#", new DecimalFormatSymbols(Locale.ENGLISH)); //private Player.LinkedList players = new Player.LinkedList(); private Map<String,Player> players = Collections.synchronizedMap(new TreeMap<String,Player>(new ComparatorIgnoreCase())); public String name() { return "idlerpg"; } public boolean isListener() { return true; } public void onDisable() { players.clear(); } public void onEnable(File dir) { Data.config.setNotExists("idlerpg-channel", "#shockyidlerpg"); Data.config.setNotExists("idlerpg-announce", true); Data.config.setNotExists("idlerpg-leaderboards-print", 5); if (!Data.protectedKeys.contains("idlerpg-channel")) Data.protectedKeys.addAll(Arrays.asList(new String[] { "idlerpg-channel", "idlerpg-announce", "idlerpg-leaderboards-print" })); try { File f = new File(dir, "idlerpg.json"); if (f.exists()) { BufferedReader br = new BufferedReader(new FileReader(f)); StringBuilder sb = new StringBuilder(); String line = br.readLine(); while (line != null) { sb.append(line); sb.append('\n'); line = br.readLine(); } readJSON(new JSONObject(sb.toString())); br.close(); } } catch (Exception e) { e.printStackTrace(); } } public void onDataSave(File dir) { File file = new File(dir, "idlerpg.json"); File temp = null; try { try { temp = File.createTempFile("shocky", ".tmp"); } catch (IOException e1) { throw new RuntimeException(e1); } System.out.printf("File: %s Temp: %s", file.getAbsolutePath(), temp.getAbsolutePath()).println(); PrintWriter pw = new PrintWriter(temp); pw.write(writeJSON().toString()); pw.close(); if (file.exists()) file.delete(); temp.renameTo(file); } catch (Exception e) { e.printStackTrace(); } finally { if (temp != null && temp.exists()) temp.delete(); } } public void readJSON(JSONObject json) { try { if (json.has("players")) { JSONArray jPlayers = json.getJSONArray("players"); for (int i = 0; i < jPlayers.length(); i++) { JSONObject jPlayer = jPlayers.getJSONObject(i); Player player = new Player(jPlayer.getString("name")); player.level = jPlayer.getInt("level"); player.xp = jPlayer.getInt("xp"); player.lastUpdate = jPlayer.getInt("lastUpdate"); //Player player2 = this.players.findByName(player.name); Player player2 = this.players.get(player.name); if (player2 != null && player2.level >= player.level) continue; //this.players.add(player); this.players.put(player.name,player); } } } catch (Exception localException) { } } public JSONObject writeJSON() { JSONObject json = new JSONObject(); try { JSONArray jPlayers = new JSONArray(); for (Player player : this.players.values()) { JSONObject jPlayer = new JSONObject(); jPlayer.put("name", player.name); jPlayer.put("level", player.level); jPlayer.put("xp", player.xp); jPlayer.put("lastUpdate", player.lastUpdate); jPlayers.put(jPlayer); } json.put("players", jPlayers); } catch (Exception localException) { } return json; } public static void send(MessageEvent<ShockyBot> ev, String message) { Command.EType type = Command.EType.Channel; String allowedChannel = Data.config.getString("idlerpg-channel"); if ((!allowedChannel.isEmpty()) && (!allowedChannel.equalsIgnoreCase(ev.getChannel().getName()))) type = Command.EType.Notice; Shocky.send(ev.getBot(), type, ev.getChannel(), ev.getUser(), message); } public static void send(Session session, String message) { Command.EType type = Command.EType.Channel; String allowedChannel = Data.config.getString("idlerpg-channel"); if ((!allowedChannel.isEmpty()) && (!allowedChannel.equalsIgnoreCase(session.channel.getName()))) type = Command.EType.Notice; Shocky.send(session.bot, type, session.channel, session.user, message); } public static void announce(MessageEvent<ShockyBot> ev, String message) { Command.EType type = Command.EType.Channel; String allowedChannel = Data.config.getString("idlerpg-channel"); if ((!allowedChannel.isEmpty()) && (!allowedChannel.equalsIgnoreCase(ev.getChannel().getName())) && (!Data.config.getBoolean("idlerpg-announce"))) type = Command.EType.Notice; Shocky.send(ev.getBot(), type, ev.getChannel(), ev.getUser(), message); } public static void announce(Session session, String message) { Command.EType type = Command.EType.Channel; String allowedChannel = Data.config.getString("idlerpg-channel"); if ((!allowedChannel.isEmpty()) && (!allowedChannel.equalsIgnoreCase(session.channel.getName())) && (!Data.config.getBoolean("idlerpg-announce"))) type = Command.EType.Notice; Shocky.send(session.bot, type, session.channel, session.user, message); } public void onMessage(MessageEvent<ShockyBot> ev) { if (Data.isBlacklisted(ev.getUser())) return; String msg = ev.getMessage(); if (!msg.startsWith(">")) return; String identify = Shocky.getLogin(ev.getUser()); if (identify == null) { send(ev, ev.getUser().getNick()+": You need to be identified to NickServ to play IdleRPG."); return; } Session session = new Session(ev.getBot(), ev.getChannel(), ev.getUser(), identify); session.player = this.players.get(identify); msg = msg.substring(1).trim(); String[] spl = msg.isEmpty() ? new String[0] : msg.split(" "); if ((spl.length == 0) || ((spl.length <= 2) && ("status".startsWith(spl[0] .toLowerCase())))) { Player check = session.player; if (spl.length == 2) check = this.players.get(spl[1]); if (check == null) { if (spl.length == 2) { send(ev, ev.getUser().getNick() + ": Player '" + spl[1] + "' doesn't exist."); return; } this.players.put(identify,new Player(identify)); send(ev, ev.getUser().getNick() + ": Welcome to the IdleRPG, " + identify + '!'); return; } check.update(session); send(ev, check.printStatus(session, true, true)); } else if ((spl.length == 1) && ("leaderboards".startsWith(spl[0].toLowerCase()))) { for (Player p : this.players.values()) p.update(session); ArrayList<Player> list = new ArrayList<Player>(this.players.values()); Collections.sort(list, new ComparatorLevel(false)); StringBuilder print = new StringBuilder(); StringBuilder paste = new StringBuilder(); int maxPrint = Data.config.getInt("idlerpg-leaderboards-print"); for (int i = 0; i < list.size(); i++) { if (i != 0) { if (i < maxPrint) print.append(" | "); paste.append('\n'); } Player p = (Player) list.get(i); if (i < maxPrint) print.append(i + 1).append(". ") .append(p.printStatus(session, list.size() <= maxPrint, false)); paste.append(i + 1).append(". ").append(p.printStatus(session, true, false)); } if (list.size() > maxPrint) { String url = Utils.paste(paste); if ((url != null) && (!url.isEmpty())) print.append(" | Full leaderboards: ").append(url); } send(ev, Utils.mungeAllNicks(ev.getChannel(), 0, print, ev.getUser())); } } public static class Player { public String name; public int level; public int xp; public long lastUpdate; private static final Map<Integer,Integer> xpTable = new HashMap<Integer,Integer>(); public static int getXPForLevel(int level) { if (level <= 1) return 0; if (xpTable.containsKey(level)) return xpTable.get(level); long a = 0L; for (int x = 1; x < level; ++x) a += (int) (x + 300.0D * Math.pow(2.0D, x / 7.0D)); int value = (int) (a / 4.0D); xpTable.put(level, value); return value; } public int getXPForNextLevel() { return getXPForLevel(this.level + 1); } public static String printBar(double value, int length) { double f = 1.0D / length; char[] c = new char[length + 2]; int i = 0; c[i++]='['; for (; i <= length; ++i) c[i]=(value >= i * f) ? '=' : ' '; c[i]=']'; return new String(c); } public Player(String name) { this.name = name; this.level = 1; this.xp = 0; this.lastUpdate = (System.currentTimeMillis() / 1000L); } public void update(Session session) { long time = System.currentTimeMillis() / 1000L; long diff = time - this.lastUpdate; this.lastUpdate = time; int xp2l = getXPForNextLevel(); this.xp = (int) (this.xp + diff); if ((session != null) && (session.player == this) && (this.xp >= xp2l)) { this.xp = 0; this.level += 1; } if (this.xp > xp2l) this.xp = xp2l; } public String printStatus(Session session, boolean printXP, boolean printTime) { StringBuilder sb = new StringBuilder(); sb.append(this.name); if ((session.identify.equalsIgnoreCase(this.name)) && (!session.user.getNick().equalsIgnoreCase(this.name))) sb.append(" / ").append(session.user.getNick()); sb.append(", level ").append(this.level); int xp2l = getXPForNextLevel(); if (printXP) { if ((this.level != 1) && (this.xp == 0)) { sb.append(", LEVEL UP!"); if (this.level % 5 == 0) { StringBuilder sb2 = new StringBuilder(); sb2.append(">>> CONGRATULATIONS! ").append(this.name); if ((session.identify.equalsIgnoreCase(this.name)) && (!session.user.getNick() .equalsIgnoreCase(this.name))) sb2.append(" / ").append(session.user.getNick()); sb2.append(" achieved level ").append(this.level).append("! <<<"); ModuleIdleRPG.announce(session, sb2.toString()); } } else if (this.xp == xp2l) sb.append(", level up available"); else sb.append(", XP: ").append(formatXP.format(this.xp)).append(" / ").append(formatXP.format(xp2l)); if ((this.xp != 0) && (this.xp != xp2l)) sb.append(' ') .append(printBar(1.0D * this.xp / xp2l, 20)) .append(" (").append(formatXPPercent.format(100.0D * this.xp / xp2l)).append("%)"); } if ((printTime) && (this.xp != xp2l)) sb.append(" | ").append(Utils.timeAgo(xp2l - this.xp)).append(" until level up"); return sb.toString(); } } public static class ComparatorLevel implements Comparator<Player> { public final int dir; public ComparatorLevel(boolean asc) { dir = asc ? 1 : -1; } public int compare(Player p1, Player p2) { if (p1.level != p2.level) return p1.level < p2.level ? -dir : dir; if (p1.xp != p2.xp) return p1.xp < p2.xp ? -dir : dir; return 0; } } public static class ComparatorIgnoreCase implements Comparator<String> { public int compare(String s1, String s2) { return s1.compareToIgnoreCase(s2); } } public static class Session { public final PircBotX bot; public final Channel channel; public final User user; public final String identify; public Player player; public Session(PircBotX bot, Channel channel, User user, String identify) { this.bot = bot; this.channel = channel; this.user = user; this.identify = identify; } } public LuaValue getPlayerTable(String name) { if (!players.containsKey(name)) return LuaValue.NIL; Player player = players.get(name); player.update(null); return getPlayerTable(player); } public LuaValue getPlayerTable(Player player) { LuaTable t = new LuaTable(); t.rawset("name", player.name); t.rawset("xp", player.xp); t.rawset("need", player.getXPForNextLevel()); t.rawset("level", player.level); t.rawset("lastUpdate", player.lastUpdate); return t; } public class StatusFunction extends OneArgFunction { @Override public LuaValue call(LuaValue arg) { return getPlayerTable(arg.checkjstring()); } } public class LeaderboardFunction extends ZeroArgFunction { @Override public LuaValue call() { int maxPrint = Data.config.getInt("idlerpg-leaderboards-print"); List<Player> list = new ArrayList<Player>(players.values()); for (Player p : list) p.update(null); Collections.sort(list, new ComparatorLevel(false)); LuaTable t = new LuaTable(); for (int i = 0; i < maxPrint && i < list.size(); ++i) t.rawset(i+1, getPlayerTable(list.get(i))); return t; } } @Override public void setupLua(LuaTable env) { try { Class.forName("ModuleIdleRPG$Player"); Class.forName("ModuleIdleRPG$ComparatorLevel"); } catch (Exception e) { e.printStackTrace(); } LuaTable t = new LuaTable(); t.rawset("status", new StatusFunction()); t.rawset("leaders", new LeaderboardFunction()); env.set("idlerpg", t); } }